Compare commits

...

30 Commits

Author SHA1 Message Date
KingRainbow44
c1a41bec65 Push custom-options 2022-07-06 01:38:34 -04:00
SpikeHD
a3e1898d82 Update README.md 2022-07-05 12:26:14 -07:00
SpikeHD
5bca75629c Merge pull request #15 from TukanDev/main
Documentation for classes used for react components under /common and some small fixes
2022-07-05 11:49:53 -07:00
TukanDev
0c1fb3e25e remove hover and disabled state docs for bigbutton class 2022-07-05 20:48:34 +02:00
SpikeHD
9558fb0eaf Update THEMES.md 2022-07-05 11:46:18 -07:00
TukanDev
f0d08f5e67 Fix index.json example not having customBackgroundFile key 2022-07-05 20:31:14 +02:00
TukanDev
2af33ccc33 Added documentation for react components under /common and fix for some leftover element id's on commonly used components that I missed. 2022-07-05 20:23:20 +02:00
SpikeHD
8f6ae91a8a Merge pull request #13 from timius100/main
Added latvian language file
2022-07-04 18:54:51 -07:00
SpikeHD
49e77366d1 Merge pull request #12 from yann48gmx/main
French language file
2022-07-04 18:54:10 -07:00
Timur
a826c8e835 Added latvian language file 2022-07-05 01:47:21 +03:00
yann48gmx
bdc7c28ba7 Update fr.json 2022-07-04 22:07:26 +02:00
yann48gmx
1fb8a49a20 French language file 2022-07-04 22:06:29 +02:00
SpikeHD
c57a4ac56d Merge pull request #11 from timius100/main
Added russian language file
2022-07-04 09:33:39 -07:00
Timur
b72dfb8802 Added russian language file 2022-07-04 15:28:31 +03:00
SpikeHD
b00325ca68 Merge pull request #9 from Seeker14491/fix-news
Fix fetching commits from API
2022-07-03 19:23:43 -07:00
SpikeHD
8eaab56787 Merge pull request #10 from TukanDev/main
Fix reusable react components duplicate id...
2022-07-03 19:21:38 -07:00
TukanDev
9ea92a23ee Fix reusable react components duplicate id... dumbass 2022-07-04 04:14:35 +02:00
Brian Bowman
5d2f803f3c Fix fetching commits from API 2022-07-03 17:36:45 -05:00
SpikeHD
29716c43b6 Merge pull request #8 from TukanDev/main
Add element id's and their documentation
2022-07-03 15:15:45 -07:00
SpikeHD
2590082041 reword 2022-07-03 15:15:20 -07:00
SpikeHD
0ec9ef92ee cleanup 2022-07-03 15:10:49 -07:00
TukanDev
98f530eb0c Add element id's and their documentation 2022-07-03 23:46:02 +02:00
SpikeHD
d9416b4fb6 Merge pull request #6 from DasIschBims/main
Added german language file
2022-07-03 11:48:07 -07:00
DasIschBims
73a7d08b8e Added german language file 2022-07-03 17:01:25 +02:00
SpikeHD
3931fa25d6 Update THEMES.md 2022-07-02 21:12:54 -07:00
SpikeHD
84c3645ae2 theming documentation 2022-07-02 21:06:14 -07:00
SpikeHD
54b20a93ef Merge pull request #5 from Kimi898246/patch-2
minor edits cause i fucked up again
2022-07-02 20:48:40 -07:00
Kimi
20130061ab minor edits cause i fucked up again
oops
2022-07-03 11:40:56 +08:00
SpikeHD
e2393d7900 Merge pull request #4 from Kimi898246/patch-1
Traditional Chinese | Translation Patches
2022-07-02 20:30:05 -07:00
Kimi
a42718f6c5 Traditional Chinese | Translation Patches 2022-07-03 11:29:29 +08:00
31 changed files with 7114 additions and 6296 deletions

View File

@@ -33,6 +33,7 @@
"semi": [
"error",
"never"
]
],
"no-explicit-any": "off"
}
}

View File

@@ -21,6 +21,7 @@ A game launcher designed to easily proxy traffic from anime game to private serv
* [Setup](#setup)
* [Building](#building)
* [Troubleshooting](#troubleshooting)
* [Theming](#theming)
# Download
[Find release builds here!](https://github.com/Grasscutters/Cultivation/releases)
@@ -30,6 +31,7 @@ Once downloaded, extract somewhere and open as administrator.
# Developer Quickstart
### Setup
* Install [NodeJS >12](https://nodejs.org/en/)
* Install [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) & [Rust compiler](https://www.rust-lang.org/tools/install)
* `npm install` or `yarn install`
* `npm run start:dev` or `yarn start:dev`
@@ -48,6 +50,10 @@ Add `--release` or `--debug` depending on what release you are creating. This de
# Troubleshooting
TODO. Collect common issues before updating.
# Theming
A full theming reference can be found [here!](/THEMES.md)
# Screenshots
![image](https://user-images.githubusercontent.com/25207995/173211603-e5e85df7-7fd3-430b-9246-749ebbc1e483.png)
![image](https://user-images.githubusercontent.com/25207995/173211543-b7e88943-cfd2-418b-ac48-7f856868129b.png)
@@ -58,4 +64,4 @@ TODO. Collect common issues before updating.
## Credits
* [SpikeHD](https://github.com/SpikeHD): For originally creating **GrassClipper** and creating the amazing UI of Cultivation.
* [KingRainbow44](https://github.com/KingRainbow44): For building a proxy daemon from scratch and integrating it with Cultivation.
* [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.
* [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.

106
THEMES.md Normal file
View File

@@ -0,0 +1,106 @@
# Downloading/Installing Themes
1. Download your favorite theme! (You can find some in the `#themes` channel on Discord)
2. Place the unzipped theme folder inside of `%appdata%/cultivation/themes` (The path should look something like this: `cultivation/themes/theme_name/index.json`)
4. Enable within Cultivation!
# Creating your own theme
Themes support entirely custom JS and CSS, enabling you to potentially change every single thing about Cultivation with relative ease.
You can refer to the example theme [found here.](https://cdn.discordapp.com/attachments/992943872479084614/992993575652565002/Example.zip)
You will need CSS and JS experience if you want to do anything cool.
## Creating index.json
`index.json` is where you tell Cultivation which files and images to include. It supports the following properties:
| Property | Description |
| :--- | :--- |
| `name` | The name of the theme. |
| `version` | Not shown anywhere, the version of the theme. |
| `description` | Not shown anywhere, the description of the theme. |
| `includes` | The files and folders to include. |
| `includes.css` | Array of CSS files to include. Example: `css: ["index.css"]` |
| `includes.js` | Array of JS files to includes. Example `js: ["index.js"]` |
| `customBackgroundURL` | A custom image URL to set as the background. Backgrounds that users set in their config supercede this. Example: `"https://website.com/image.png"` |
| `customBackgroundFile` | Path to a custom background image file. Backgrounds that users set in their config supercede this. Example: `"/image.png"` |
A full, complete `index.json` will look something like this:
```json
{
"name": "Example",
"version": "1.0.0",
"description": "This is an example theme. Made by SpikeHD.",
"includes": {
"css": ["/index.css"],
"js": ["/index.js"]
},
"customBackgroundURL": "https://website.com/image.png",
"customBackgroundFile": "/image.png"
}
```
**Important Note:**
All paths are relative to the theme folder. This means you only need to do `"/index.css"` to include `index.css` from the same folder `index.json` is located.
## Writing your CSS
You are welcome to use the DevTools to debug your theme, or view what properties an element has already, etc.
Below are some small examples of what you can do:
```css
/* Change the font */
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
}
```
```css
/* Remove the news section */
.NewsSection {
display: none;
}
```
```css
/* Change the right bar width */
.RightBar {
width: 300px;
}
```
## How can I change XYZ element?
Every element is documented and describe [here](/docs/elementIds.md). Every\* single DOM element is assigned an ID to allow for easy and hyper-specific editing.
## Writing your JS
There are no limitations to what you can do with JS. It is safe, as it is sandboxed within the webpage, so there is no possibility of it being able to do anything more than what any regular webpage can do.
Below are some examples of what you can do:
```js
/* Change the version number every 500ms */
setInterval(() => {
document.getElementById("version").innerHTML = "v" + Math.floor(Math.random() * 100);
}, 500);
```
```js
/* Load a custom font */
const head = document.head
const link = document.createElement("link")
link.href = "https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
link.rel = "stylesheet"
link.type = "text/css"
head.appendChild(link)
```
```js
/* Create a new button that does nothing */
const newButton = document.createElement("button");
newButton.innerHTML = "New Button";
document.body.appendChild(newButton);
```

133
docs/elementIds.md Normal file
View File

@@ -0,0 +1,133 @@
# Documentation of Element ID's and Classes for custom theming
## IDs
This does not include commonly used components (buttons, divider lines, commit author and message, etc...) for accessing and modifying those elements, please check `Classes` section bellow.
| #ID | Description |
|----------------------------------------|-----------------------------------------------------------------|
| `#miniDialogContainer` | Main container of MiniDialog |
| `#miniDialogContainerTop` | Affects only top section of MiniDialog |
| `#miniDialogButtonClose` | Close button (SVG) of MiniDialog |
| `#miniDialogContent` | MiniDialog content |
| `#rightBarContainer` | Main container of RightBar |
| `#rightBarContent` | RightBar content |
| `#rightBarButtonDiscord` | Discord button on the RightBar |
| `#rightBarButtonGithub` | Github button on the RightBar |
| `#playButton` | Main container for whole launch buttons section |
| `#serverControls` | Container of "play on grasscutter" checkbox |
| `#enableGC` | "play on grasscutter" checkbox |
| `#ip` | Server ip input if play on grasscutter is enabled |
| `#port` | Server port input if play on grasscutter is enabled |
| `#httpsEnable` | "Enable https" checkbox if play on grasscutter is enabled |
| `#officialPlay` | Launch button |
| `#serverLaunch` | Launch server button |
| `#serverlaunchIcon` | Icon (SVG) of server launch button |
| `#serverConfigContainer` | Main container of server configuration section |
| `#serverLaunchContainer` | Main container of launch buttons (includes launch server) |
| `#topBarContainer` | Main container of launcher TopBar (minimize, exit, settings...) |
| `#title` | Title of the TopBar |
| `#version` | Version of the launcher in TopBar |
| `#topBarButtonContainer` | Container of launcher TopBar buttons only |
| `#closeBtn` | Exit launcher button |
| `#minBtn` | Minimize launcher button |
| `#settingsBtn` | Settings button |
| `#downloadsBtn` | Downloads button (grasscutter resources, grasscutter...) |
| `#newsContainer` | Main container of the news section |
| `#newsTabsContainer` | Container for news tabs |
| `#commits` | News tabs container commits button |
| `#latest_version` | News tabs for latest version button |
| `#newsContent` | Content section of news container |
| `#newsCommitsTable` | Commits table of news section |
| `#downloadMenuContainerGCStable` | Grasscutter stable update container |
| `#downloadMenuLabelGCStable` | Label for stable update button |
| `#downloadMenuButtonGCStable` | Button container for stable update button |
| `#grasscutterStableBtn` | "Update grasscutter stable" button |
| `#downloadMenuContainerGCDev` | Grasscutter development update container |
| `#downloadMenuLabelGCDev` | Label for latest update button |
| `#downloadMenuButtonGCDev` | Button container for latest update button |
| `grasscutterLatestBtn` | "Update grasscutter latest" button |
| `#downloadMenuContainerGCStableData` | Grasscutter stable data update container |
| `#downloadMenuLabelGCStableData` | Label for stable data update |
| `#downloadMenuButtonGCStableData` | Button container for stable data update button |
| `#grasscutterStableRepo` | "Update grasscutter stable data" button |
| `#downloadMenuContainerGCDevData` | Grasscutter latest data update container |
| `#downloadMenuLabelGCDevData` | Label for latest data update |
| `#downloadMenuButtonGCDevData` | Button container for latest data update button |
| `#grasscutterDevRepo` | "Update grasscutter latest data" button |
| `#downloadMenuContainerResources` | Container for grasscutter resources download |
| `#downloadMenuLabelResources` | label for resources download |
| `#downloadMenuButtonResources` | Button container for resources download button |
| `#resourcesBtn` | "Download grasscutter resources" button |
| `#menuContainer` | Generic Popup modal like menu container |
| `#menuContainerTop` | Top section of menu container |
| `#menuHeading` | Menu title |
| `#menuButtonCloseContainer` | Container for menu close button |
| `#menuButtonCloseIcon` | Menu close icon (SVG) |
| `#menuContent` | Content section of the menu |
| `#menuOptionsContainerGameExec` | Container for game executable option section |
| `#menuOptionsLabelGameExec` | Label for game executable option |
| `#menuOptionsDirGameExec` | Set game executable file browser |
| `#menuOptionsContainerGCJar` | Container for grasscutter jar option |
| `#menuOptionsLabelGCJar` | Label for grasscutter jar option |
| `#menuOptionsDirGCJar` | Set grasscutter jar file browser |
| `#menuOptionsContainerToggleEnc` | Container for toggle encryption option |
| `#menuOptionsLabelToggleEnc` | Label for toggle encryption option |
| `#menuOptionsButtonToggleEnc` | Toggle encryption button container |
| `#toggleEnc` | Toggle encryption button |
| `#menuOptionsContainerGCWGame` | Container for "grasscutter with game" option |
| `#menuOptionsLabelGCWDame` | Label for "grasscutter with game" option |
| `#menuOptionsCheckboxGCWGame` | Container for "grasscutter with game" option checkbox |
| `#gcWithGame` | Grasscutter with game checkbox |
| `#menuOptionsContainerThemes` | Container for themes section |
| `#menuOptionsLabelThemes` | Label for set themes option |
| `#menuOptionsSelectThemes` | Container for themes select menu |
| `#menuOptionsSelectMenuThemes` | Set theme select menu |
| `#menuOptionsContainerJavaPath` | Container for Java Path option |
| `#menuOptionsLabelJavaPath` | Label for Java path option |
| `#menuOptionsDirJavaPath` | Container for java path file browser |
| `#menuOptionsContainerBG` | Container for Background option |
| `#menuOptionsLabelBG` | Label for background option |
| `#menuOptionsDirBG` | Container for background url/local path option |
| `#menuOptionsContainerLang` | Container for language change option |
| `#menuOptionsLabelLang` | Label for language change option |
| `#menuOptionsSelectLang` | Container for language change select menu |
| `#menuOptionsSelectMenuLang` | Language select menu |
| `#DownloadProgress` | Download progress container |
| `#bottomSectionContainer` | Bottom section container |
| `#miniDownloadContainer` | Container for mini download |
## Classes
This is not full list of all classes, rather its list of classes for commonly used components that can not be accessed using element id system.
| .Class | Description |
|-----------------------------|---------------------------------------------------------|
| `.BigButton` | Class for all buttons |
| `.BigButtonText` | Text inside a button | |
| `.Checkbox` | Checkbox container |
| `.CheckboxDisplay` | Content of checkbox |
| `.DirInput` | Container for DirInput |
| `.FileSelectIcon` | Icon of DirInput |
| `.DownloadList` | List of all downloads |
| `.DownloadSection` | Container for each download |
| `.DownloadTitle` | Contains file download path and current status |
| `.DownloadPath` | Path of a download |
| `.DownloadStatus` | Status of a download |
| `.DownloadSectionInner` | Contains progressbar of the download section |
| `.HelpSection` | Container for help "?" circle button |
| `.HelpButton` | HelpButton itself |
| `.HelpContents` | Content of help button once expanded |
| `.MainProgressBarWrapper` | Container for MainProgressBar |
| `.ProgressBar` | ProgressBar (creativity left the brain) |
| `.InnerProgress` | ProgressBar percentage |
| `.MainProgressText` | Text for MainProgressBar |
| `.ProgressBarWrapper` | Container for ProgressBar |
| `.DownloadControls` | DownloadControls of ProgressBar |
| `.downloadStop` | Container for download stop icon (SVG) |
| `.ProgressText` | Text of the ProgressBar display current download status |
| `.TextInputWrapper` | Container for TextInput |
| `.TextClear` | Container for clear input content button |
| `.TextInputClear` | TextInput clear button icon (SVG) |
| `.Divider` | Container for line dividers |
| `.DividerLine` | Divider line itself |
| `.CommitAuthor` | Author of a commit |
| `.CommitMessage` | Message of a commit |

View File

@@ -12,7 +12,9 @@
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Cultivation</title>
<script src="%PUBLIC_URL%/theme-engine.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

14
public/theme-engine.js Normal file
View File

@@ -0,0 +1,14 @@
/**
* Passes a message through to the React backend.
* @param type The message type.
* @param data The message data.
*/
function passthrough(type, data) {
document.dispatchEvent(new CustomEvent('domMessage', {
type, msg: data
}))
}
function setConfigValue(key, value) {
passthrough('updateConfig', {setting: key, value})
}

12
src-tauri/Cargo.lock generated
View File

@@ -740,9 +740,8 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "cultivation"
version = "0.1.0"
version = "1.0.1"
dependencies = [
"base64",
"duct",
"futures-util",
"http",
@@ -2104,6 +2103,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minisign-verify"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
[[package]]
name = "miniz_oxide"
version = "0.5.1"
@@ -3887,6 +3892,7 @@ checksum = "a34cef4a0ebee0230baaa319b1709c4336f4add550149d2b005a9a5dc5d33617"
dependencies = [
"anyhow",
"attohttpc",
"base64",
"bincode",
"cocoa",
"dirs-next",
@@ -3900,6 +3906,7 @@ dependencies = [
"heck 0.4.0",
"http",
"ignore",
"minisign-verify",
"notify-rust",
"objc",
"once_cell",
@@ -3931,6 +3938,7 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"windows 0.30.0",
"zip 0.6.2",
]
[[package]]

View File

@@ -1,9 +1,9 @@
[package]
name = "cultivation"
version = "0.1.0"
version = "1.0.1"
description = "A custom launcher for anime game."
authors = ["KingRainbow44", "SpikeHD"]
license = ""
license = "Apache-2.0"
repository = "https://github.com/Grasscutters/Cultivation.git"
default-run = "cultivation"
edition = "2021"
@@ -16,7 +16,7 @@ tauri-build = { version = "1.0.0-rc.8", features = [] }
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-rc.9", features = ["api-all"] }
tauri = { version = "1.0.0-rc.9", features = ["api-all", "updater"] }
# Access system process info.
sysinfo = "0.23.12"
@@ -37,7 +37,6 @@ duct = "0.13.5"
# Serialization.
serde_json = "1"
base64 = "0.13.0"
# System process elevation.
is_elevated = "0.1.2"

View File

@@ -11,13 +11,16 @@
"files_extracting": "檔案解壓縮中:"
},
"options": {
"enabled": "已啟用",
"disabled": "未啟用",
"game_exec": "選擇遊戲執行檔",
"grasscutter_jar": "選擇伺服器JAR檔案",
"java_path": "設置自定義Java路徑",
"toggle_encryption": "設定加密",
"java_path": "設定自定義Java路徑",
"grasscutter_with_game": "伴隨遊戲一起啟動Grasscutter",
"language": "語言",
"background": "設自定義背景(網址或檔案)",
"theme": "設主題"
"background": "設自定義背景(網址或檔案)",
"theme": "設主題"
},
"downloads": {
"grasscutter_stable_data": "下載Grasscutter穩定版數據Data",

61
src-tauri/lang/de.json Normal file
View File

@@ -0,0 +1,61 @@
{
"lang_name": "Deutsch",
"main": {
"title": "Cultivation",
"launch_button": "Starten",
"gc_enable": "Über Grasscutter verbinden",
"https_enable": "HTTPS nutzen",
"ip_placeholder": "Server Adresse...",
"port_placeholder": "Port...",
"files_downloading": "Herunterladen von Dateien: ",
"files_extracting": "Extrahieren von Dateien: "
},
"options": {
"enabled": "Aktiviert",
"disabled": "Deaktiviert",
"game_exec": "Spiel Datei auswählen",
"grasscutter_jar": "Grasscuter JAR auswählen",
"toggle_encryption": "Verschlüsselung umschalten",
"java_path": "Benutzerdefinierten Java Pfad setzen",
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
"language": "Sprache auswählen",
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
"theme": "Theme auswählen"
},
"downloads": {
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
"grasscutter_latest_data_update": "Aktuellste Grasscutter Daten aktualisieren",
"grasscutter_stable": "Stabile Grasscutter Version herunterladen",
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
"resources": "Grasscutter Ressourcen herunterladen"
},
"download_status": {
"downloading": "Lädt herunter",
"extracting": "Extrahiert",
"error": "Fehler",
"finished": "Fertig",
"stopped": "Gestoppt"
},
"components": {
"select_file": "Datei oder Ordner auswählen...",
"select_folder": "Ordner auswählen...",
"download": "Herunterladen"
},
"news": {
"latest_commits": "Letzte Commits",
"latest_version": "Letzte Version"
},
"help": {
"port_help_text": "Vergewissern Sie sich, dass es sich um den Port des Dispatch-Servers handelt, nicht um den Port des Spiel-Servers. Dieser ist fast immer '443'.",
"game_help_text": "Sie müssen keine separate Kopie verwenden, um mit Grasscutter zu spielen. Dies ist entweder für ein Downgrade auf die Version 2.6 oder wenn Sie das Spiel nicht installiert haben.",
"gc_stable_jar": "Laden Sie den aktuellen stabilen Grasscutter-Build herunter, der eine Jar-Datei und Datendateien enthält.",
"gc_dev_jar": "Laden Sie die neueste Grasscutter-Entwicklungsversion herunter, welche eine Jar-Datei und Datendateien enthält.",
"gc_stable_data": "Laden Sie die stabilen Grasscutter Daten herunter, welche keine Jar-Datei enthalten. Dies ist nützlich zum Aktualisieren.",
"gc_dev_data": "Laden Sie die neuesten Grasscutter-Entwicklungsdateien herunter, welche keine Jar-Datei enthält. Dies ist nützlich zum Aktualisieren.",
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben"
}
}

61
src-tauri/lang/fr.json Normal file
View File

@@ -0,0 +1,61 @@
{
"lang_name": "Francais",
"main": {
"title": "Cultivation",
"launch_button": "Lancer",
"gc_enable": "Se connecter avec Grasscutter",
"https_enable": "Utiliser HTTPS",
"ip_placeholder": "Adresse du serveur...",
"port_placeholder": "Port...",
"files_downloading": "Fichiers en cours de telechargement: ",
"files_extracting": "Fichiers en cours d'extraction: "
},
"options": {
"enabled": "active",
"disabled": "desactiver",
"game_exec": "definir l'executable du jeu",
"grasscutter_jar": "definir le Jar Grasscutter",
"toggle_encryption": "activer l'encryption",
"java_path": "definir un chemin java personnalise",
"grasscutter_with_game": "Lancer Grasscutter automatiquement avec le jeu",
"language": "Choisir la langue",
"background": "definir un arriere plan personnalise (lien ou fichier image)",
"theme": "definir un theme"
},
"downloads": {
"grasscutter_stable_data": "Telecharger les donnees de Grasscutter (version stable)",
"grasscutter_latest_data": "Telecharger les donnees de Grasscutter (derniere version)",
"grasscutter_stable_data_update": "Mettre a jour les donnees de Grasscutter (version stable)",
"grasscutter_latest_data_update": "Mettre a jour les donnees de Grasscutter (derniere version)",
"grasscutter_stable": "Telecharger Grasscutter (version stable)",
"grasscutter_latest": "Telecharger Grasscutter (derniere version)",
"grasscutter_stable_update": "Mettre a jour Grasscutter (version stable)",
"grasscutter_latest_update": "Mettre a jour Grasscutter (derniere version)",
"resources": "Telecharger les ressources logicielles de Grasscutter"
},
"download_status": {
"downloading": "Telechargement",
"extracting": "Extraction",
"error": "Erreur",
"finished": "Termine",
"stopped": "Arrete"
},
"components": {
"select_file": "choisir fichier ou dossier...",
"select_folder": "choisir dossier...",
"download": "Telecharger"
},
"news": {
"latest_commits": "Recents Commits",
"latest_version": "Derniere version"
},
"help": {
"port_help_text": "Assurez-vous que c'est le port serveur Dispatch, et non le port du serveur de jeu. C'est presque toujours '433'.",
"game_help_text": "Vous n'avez pas besoin d'une copie differente du jeu pour jouer avec Grasscutter. Cela est ou pour retrograder en 2.6 ou si vous n'avez pas le jeu d'installe",
"gc_stable_jar": "Telecharger le dernier build stable de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
"gc_dev_jar": "Telecharger le dernier build en development de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
"gc_stable_data": "Telecharger le dernier build stable de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
"gc_dev_data": "Telecharger le dernier build en development de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
"resources": "Les ressources sont aussi necessaires pour lancer un serveur Grasscutter. Ce bouton deviendra gris si vous avez deja un fichier ressources avec les donnees dedans."
}
}

61
src-tauri/lang/lv.json Normal file
View File

@@ -0,0 +1,61 @@
{
"lang_name": "Latviešu",
"main": {
"title": "Cultivation",
"launch_button": "Palaist",
"gc_enable": "Savienot ar Grasscutter",
"https_enable": "Izm. HTTPS",
"ip_placeholder": "Servera Adrese...",
"port_placeholder": "Ports...",
"files_downloading": "Failu Lejupielāde: ",
"files_extracting": "Failu Izvilkšana: "
},
"options": {
"enabled": "Iespējots",
"disabled": "Atspējots",
"game_exec": "Iestatīt spēles izpildāmu",
"grasscutter_jar": "Iestatiet Grasscutter JAR",
"toggle_encryption": "Pārslēgt Šifrēšanu",
"java_path": "Iestatiet pielāgotu Java ceļu",
"grasscutter_with_game": "Automātiski palaidiet Grasscutter ar spēli",
"language": "Izvēlēties valodu",
"background": "Iestatīt pielāgotu fonu (saite vai attēla fails)",
"theme": "Iestatīt tēmu"
},
"downloads": {
"grasscutter_stable_data": "Lejupielādējiet Grasscutter stabilos datus",
"grasscutter_latest_data": "Lejupielādējiet Grasscutter jaunākos datus",
"grasscutter_stable_data_update": "Atjauniniet Grasscutter stabilos datus",
"grasscutter_latest_data_update": "Atjauniniet Grasscutter jaunākos datus",
"grasscutter_stable": "Lejupielādēt Grasscutter stabilo",
"grasscutter_latest": "Lejupielādēt Grasscutter jaunāko",
"grasscutter_stable_update": "Atjauniet Grasscutter stabilo",
"grasscutter_latest_update": "Atjauniet Grasscutter jaunāko",
"resources": "Lejupielādējiet Grasscutter resursi"
},
"download_status": {
"downloading": "Notiek lejupielāde",
"extracting": "Notiek izvilkšana",
"error": "Kļūda",
"finished": "Pabeigts",
"stopped": "Partraukta"
},
"components": {
"select_file": "Izvēlēties failu vai mapu...",
"select_folder": "Izvēlēties mapu...",
"download": "Lejupielādēt"
},
"news": {
"latest_commits": "Nesen kommitus",
"latest_version": "Jaunākā versija"
},
"help": {
"port_help_text": "Pārliecinieties, vai tas ir Dispatch-servera ports, nevis spēļu servera ports. Tas gandrīz vienmēr ir '443'.",
"game_help_text": "Lai spēlētu ar Grasscutter, jums nav jāizmanto atsevišķa kopija. Tas ir izveidots, lai pazeminātu versiju uz 2.6 vai ja jums nav instalēta spēle.",
"gc_stable_jar": "Lejupielādējiet pašreizējo stabilo Grasscutter versiju, kuram ir jar failu un datu failus.",
"gc_dev_jar": "Lejupielādējiet jaunāko izstrāde Grasscutter versiju, kuram ir jar failu un datu failus.",
"gc_stable_data": "Lejupielādējiet pašreizējos stabilos Grasscutter datu failus, kuriem nav jar fails. Tas ir noderīgi atjaunināšanai.",
"gc_dev_data": "Lejupielādējiet jaunāko izstrāde Grasscutter datu failus, kuriem nav pievienots jar fails. Tas ir noderīgi atjaunināšanai.",
"resources": "Tie ir nepieciešami arī Grasscutter servera darbināšanai. Šī poga būs pelēka, ja jums ir resursu mape ar saturu."
}
}

61
src-tauri/lang/ru.json Normal file
View File

@@ -0,0 +1,61 @@
{
"lang_name": "Русский",
"main": {
"title": "Cultivation",
"launch_button": "Запустить",
"gc_enable": "Подключиться с Grasscutter",
"https_enable": "Исп. HTTPS",
"ip_placeholder": "Айпи адрес...",
"port_placeholder": "Порт...",
"files_downloading": "Файлов скачано: ",
"files_extracting": "Извлечено файлов: "
},
"options": {
"enabled": "Включено",
"disabled": "Выключено",
"game_exec": "Установить исполняемый файл игры",
"grasscutter_jar": "Установить Grasscutter JAR",
"toggle_encryption": "Переключить шифрование",
"java_path": "Установить пользовательский путь Java",
"grasscutter_with_game": "Автоматически запускать Grasscutter вместе с игрой",
"language": "Установить язык",
"background": "Установить свой фон (ссылка или файл)",
"theme": "Установить тему"
},
"downloads": {
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
"grasscutter_latest_data_update": "Обновить последние данные Grasscutter",
"grasscutter_stable": "Скачать стабильную версию Grasscutter",
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
"resources": "Скачать ресурсы Grasscutter"
},
"download_status": {
"downloading": "Скачивание",
"extracting": "Извлечение",
"error": "Ошибка",
"finished": "Закончено",
"stopped": "Остановлено"
},
"components": {
"select_file": "Выберите файл или папку...",
"select_folder": "Выберите папку...",
"download": "Скачать"
},
"news": {
"latest_commits": "Последние коммиты",
"latest_version": "Последняя версия"
},
"help": {
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами."
}
}

View File

@@ -42,7 +42,6 @@ fn main() {
disconnect,
req_get,
get_bg_file,
base64_decode,
is_game_running,
get_theme_list,
system_helpers::run_command,
@@ -233,9 +232,3 @@ async fn get_bg_file(bg_path: String, appdata: String) -> String {
}
};
}
#[tauri::command]
fn base64_decode(encoded: String) -> String {
let decoded = base64::decode(&encoded).unwrap();
return String::from_utf8(decoded).unwrap();
}

View File

@@ -72,7 +72,7 @@
"csp": "default-src 'self' https://asset.localhost; img-src 'self'; img-src https://* asset: https://asset.localhost"
},
"updater": {
"active": false,
"active": true,
"dialog": true,
"endpoints": [
"https://api.grasscutter.io/cultivation/updater?version={{current_version}}",

View File

@@ -17,6 +17,7 @@ let isDebug = false;
isDebug = await getConfigOption('debug_enabled')
})
// Render the app.
root.render(
<React.StrictMode>
{
@@ -25,5 +26,10 @@ root.render(
</React.StrictMode>
)
// Enable web vitals if needed.
import reportWebVitals from './utils/reportWebVitals'
isDebug && reportWebVitals(console.log)
isDebug && reportWebVitals(console.log)
// Setup DOM message passing.
import { parseMessageFromDOM } from './utils/dom'
document.addEventListener<string>('domMessage', parseMessageFromDOM)

View File

@@ -0,0 +1,34 @@
{
"name": "Example Theme",
"version": "420.69",
"description": "Show off some of the abilities of the Cultivation theme system",
"includes": {
"_README": "You can include any amount of CSS and JS files here. Paths are relative to the theme directory.",
"css": ["/index.css"],
"js": ["/index.js"]
},
"settings": [
{
"label": "Example Setting",
"type": "input",
"className": "Input",
"data": {
"placeholder": "Enter a value",
"initialValue": "Change this value"
}
},
{
"label": "Example Setting",
"type": "checkbox",
"className": "Checkbox"
}
],
"_README": "These are optional. Including neither will result in the launcher simply using the default background choice.",
"customBackgroundPath": "/background/bg.png",
"customBackgroundURL": ""
}

View File

@@ -166,7 +166,7 @@ class App extends React.Component<IProps, IState> {
{
// Mini downloads section
this.state.miniDownloadsOpen ? (
<div className="MiniDownloads">
<div className="MiniDownloads" id="miniDownloadContainer">
<MiniDialog
title="Downloads"
closeFn={() => {
@@ -209,7 +209,7 @@ class App extends React.Component<IProps, IState> {
) : null
}
<div className="BottomSection">
<div className="BottomSection" id="bottomSectionContainer">
<ServerLaunchSection />
<div id="DownloadProgress"

View File

@@ -32,16 +32,16 @@ export default class MiniDialog extends React.Component<IProps, never> {
render() {
return (
<div className="MiniDialog">
<div className="MiniDialog" id="miniDialogContainer">
{
this.props.closeable !== undefined && this.props.closeable ?
<div className="MiniDialogTop" onClick={this.props.closeFn}>
<div className="MiniDialogTop" id="miniDialogContainerTop" onClick={this.props.closeFn}>
<span>{this.props?.title}</span>
<img src={Close} className="MiniDialogClose" />
<img src={Close} className="MiniDialogClose" id="miniDialogButtonClose" />
</div> : null
}
<div className="MiniDialogInner">
<div className="MiniDialogInner" id="miniDialogContent">
{this.props.children}
</div>
</div>

View File

@@ -16,12 +16,12 @@ export default class RightBar extends React.Component {
render() {
return (
<div className="RightBar">
<div className="RightBarInner">
<div className="BarDiscord BarImg" onClick={() => this.openInBrowser(DISCORD)}>
<div className="RightBar" id="rightBarContainer">
<div className="RightBarInner" id="rightBarContent">
<div className="BarDiscord BarImg" id="rightBarButtonDiscord" onClick={() => this.openInBrowser(DISCORD)}>
<img src={Discord} />
</div>
<div className="BarGithub BarImg" onClick={() => this.openInBrowser(GITHUB)}>
<div className="BarGithub BarImg" id="rightBarButtonGithub" onClick={() => this.openInBrowser(GITHUB)}>
<img src={Github} />
</div>
</div>

View File

@@ -33,7 +33,7 @@
background: #fff;
}
.BottomSection .CheckboxDisplay {
.BottomSection .CheckboxDisplay {
margin-right: 6px;
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.2);
}

View File

@@ -196,7 +196,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
{
this.state.grasscutterEnabled && (
<div>
<div className="ServerConfig">
<div className="ServerConfig" id="serverConfigContainer">
<TextInput id="ip" key="ip" placeholder={this.state.ipPlaceholder} onChange={this.setIp} initalValue={this.state.ip} />
<TextInput style={{
width: '10%',
@@ -210,10 +210,10 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
}
<div className="ServerLaunchButtons">
<div className="ServerLaunchButtons" id="serverLaunchContainer">
<BigButton onClick={this.playGame} id="officialPlay">{this.state.buttonLabel}</BigButton>
<BigButton onClick={this.launchServer} id="serverLaunch">
<img className="ServerIcon" src={Server} />
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
</BigButton>
</div>
</div>

View File

@@ -43,14 +43,14 @@ export default class TopBar extends React.Component<IProps, IState> {
render() {
return (
<div className="TopBar" data-tauri-drag-region>
<div className="TopBar" id="topBarContainer" data-tauri-drag-region>
<div id="title">
<span data-tauri-drag-region>
<Tr text="main.title" />
</span>
<span data-tauri-drag-region id="version">{this.state?.version}</span>
</div>
<div className="TopBtns">
<div className="TopBtns" id="topBarButtonContainer">
<div id="closeBtn" onClick={this.handleClose} className='TopButton'>
<img src={closeIcon} alt="close" />
</div>

View File

@@ -0,0 +1,105 @@
import React from 'react'
import TextInput from './TextInput'
import Checkbox from './Checkbox'
/*
* Valid types for the theme option value.
* - input: A text input.
* - dropdown: A select/dropdown input.
* - checkbox: A toggle.
* - button: A button.
*/
interface IProps {
type: string;
className?: string;
jsCallback?: string;
data: InputSettings;
}
interface IState {
toggled: boolean
}
export interface InputSettings {
/* Input. */
placeholder?: string;
initialValue?: string;
/* Dropdown. */
options?: string[];
/* Checkbox. */
toggled?: boolean
id?: string;
/* Button. */
text?: string;
}
export default class ThemeOptionValue extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props)
this.state = {
toggled: false
}
}
static getDerivedStateFromProps(props: IProps, state: IState) {
return { toggled: props.data.toggled || state.toggled }
}
async componentDidMount() {
const data = this.props.data
if(this.props.type == 'checkbox')
this.setState({ toggled: data.toggled || false })
}
async onChange() {
// Change toggled state if needed.
if(this.props.type == 'checkbox')
this.setState({
toggled: !this.state.toggled
})
if(!this.props.jsCallback)
return
}
render() {
const data = this.props.data
switch(this.props.type) {
case 'input':
return (
<div className={this.props.className}>
<TextInput placeholder={data.placeholder} initalValue={data.initialValue} />
</div>
)
case 'dropdown':
return (
<div className={this.props.className}>
<select>
{data.options ? data.options.map((option, index) => {
return <option key={index}>{option}</option>
}) : null}
</select>
</div>
)
case 'button':
return (
<div className={this.props.className}>
<button>{data.text}</button>
</div>
)
default:
return (
<div className={this.props.className}>
<Checkbox checked={this.state?.toggled} onChange={this.onChange} id={this.props.className || 'a'} />
</div>
)
}
}
}

View File

@@ -190,8 +190,8 @@ export default class Downloads extends React.Component<IProps, IState> {
render() {
return (
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
<div className='DownloadMenuSection'>
<div className='DownloadLabel'>
<div className='DownloadMenuSection' id="downloadMenuContainerGCStable">
<div className='DownloadLabel' id="downloadMenuLabelGCStable">
<Tr text={
this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'
} />
@@ -199,14 +199,14 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="help.gc_stable_jar" />
</HelpButton>
</div>
<div className='DownloadValue'>
<div className='DownloadValue' id="downloadMenuButtonGCStable">
<BigButton disabled={this.state.grasscutter_downloading} onClick={this.downloadGrasscutterStable} id="grasscutterStableBtn" >
<Tr text="components.download" />
</BigButton>
</div>
</div>
<div className='DownloadMenuSection'>
<div className='DownloadLabel'>
<div className='DownloadMenuSection' id="downloadMenuContainerGCDev">
<div className='DownloadLabel' id="downloadMenuLabelGCDev">
<Tr text={
this.state.grasscutter_set ? 'downloads.grasscutter_latest' : 'downloads.grasscutter_latest_update'
} />
@@ -214,7 +214,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="help.gc_dev_jar" />
</HelpButton>
</div>
<div className='DownloadValue'>
<div className='DownloadValue' id="downloadMenuButtonGCDev">
<BigButton disabled={this.state.grasscutter_downloading} onClick={this.downloadGrasscutterLatest} id="grasscutterLatestBtn" >
<Tr text="components.download" />
</BigButton>
@@ -223,8 +223,8 @@ export default class Downloads extends React.Component<IProps, IState> {
<Divider />
<div className='DownloadMenuSection'>
<div className='DownloadLabel'>
<div className='DownloadMenuSection' id="downloadMenuContainerGCStableData">
<div className='DownloadLabel' id="downloadMenuLabelGCStableData">
<Tr text={
this.state.grasscutter_set ? 'downloads.grasscutter_stable_data' : 'downloads.grasscutter_stable_data_update'
} />
@@ -232,14 +232,14 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="help.gc_stable_data" />
</HelpButton>
</div>
<div className='DownloadValue'>
<div className='DownloadValue' id="downloadMenuButtonGCStableData">
<BigButton disabled={this.state.repo_downloading} onClick={this.downloadGrasscutterStableRepo} id="grasscutterStableRepo" >
<Tr text="components.download" />
</BigButton>
</div>
</div>
<div className='DownloadMenuSection'>
<div className='DownloadLabel'>
<div className='DownloadMenuSection' id="downloadMenuContainerGCDevData">
<div className='DownloadLabel' id="downloadMenuLabelGCDevData">
<Tr text={
this.state.grasscutter_set ? 'downloads.grasscutter_latest_data' : 'downloads.grasscutter_latest_data_update'
} />
@@ -247,7 +247,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="help.gc_dev_data" />
</HelpButton>
</div>
<div className='DownloadValue'>
<div className='DownloadValue' id="downloadMenuButtonGCDevData">
<BigButton disabled={this.state.repo_downloading} onClick={this.downloadGrasscutterStableRepo} id="grasscutterDevRepo" >
<Tr text="components.download" />
</BigButton>
@@ -256,14 +256,14 @@ export default class Downloads extends React.Component<IProps, IState> {
<Divider />
<div className='DownloadMenuSection'>
<div className='DownloadLabel'>
<div className='DownloadMenuSection' id="downloadMenuContainerResources">
<div className='DownloadLabel' id="downloadMenuLabelResources">
<Tr text="downloads.resources" />
<HelpButton>
<Tr text="help.resources" />
</HelpButton>
</div>
<div className='DownloadValue'>
<div className='DownloadValue' id="downloadMenuButtonResources">
<BigButton disabled={this.state.resources_downloading || !this.state.grasscutter_set || this.state.resources_exist} onClick={this.downloadResources} id="resourcesBtn" >
<Tr text="components.download" />
</BigButton>

View File

@@ -17,14 +17,14 @@ export default class Menu extends React.Component<IProps, never> {
render() {
return (
<div className={'Menu ' + this.props.className}>
<div className='MenuTop'>
<div className="MenuHeading">{this.props.heading}</div>
<div className="MenuExit" onClick={this.props.closeFn}>
<img src={Close} className="MenuClose" />
<div className={'Menu ' + this.props.className} id="menuContainer">
<div className='MenuTop' id="menuContainerTop">
<div className="MenuHeading" id="menuHeading">{this.props.heading}</div>
<div className="MenuExit" id="menuButtonCloseContainer" onClick={this.props.closeFn}>
<img src={Close} className="MenuClose" id="menuButtonCloseIcon" />
</div>
</div>
<div className='MenuInner'>
<div className='MenuInner' id="menuContent">
{this.props.children}
</div>
</div>

View File

@@ -7,11 +7,12 @@ import Tr, { getLanguages, translate } from '../../../utils/language'
import { setConfigOption, getConfig, getConfigOption } from '../../../utils/configuration'
import Checkbox from '../common/Checkbox'
import Divider from './Divider'
import { getThemeList } from '../../../utils/themes'
import { getTheme, getThemeList, ThemeList } from '../../../utils/themes'
import * as server from '../../../utils/server'
import './Options.css'
import BigButton from '../common/BigButton'
import ThemeOptionValue from '../common/ThemeOptionValue'
interface IProps {
closeFn: () => void;
@@ -28,6 +29,8 @@ interface IState {
themes: string[]
theme: string
encryption: boolean
theme_object: ThemeList|null;
}
export default class Options extends React.Component<IProps, IState> {
@@ -44,7 +47,9 @@ export default class Options extends React.Component<IProps, IState> {
bg_url_or_path: '',
themes: ['default'],
theme: '',
encryption: false
encryption: false,
theme_object: null
}
this.setGameExec = this.setGameExec.bind(this)
@@ -74,7 +79,9 @@ export default class Options extends React.Component<IProps, IState> {
bg_url_or_path: config.customBackground || '',
themes: (await getThemeList()).map(t => t.name),
theme: config.theme || 'default',
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled')
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
theme_object: (await getTheme(config.theme))
})
this.forceUpdate()
@@ -124,7 +131,7 @@ export default class Options extends React.Component<IProps, IState> {
}
async setCustomBackground(value: string) {
const isUrl = /^(?:http(s)?:\/\/)/gm.test(value)
const isUrl = /^http(s)?:\/\//gm.test(value)
if (!value) return await setConfigOption('customBackground', '')
@@ -168,29 +175,33 @@ export default class Options extends React.Component<IProps, IState> {
}
render() {
const themeSettings = this.state.theme_object?.settings
return (
<Menu closeFn={this.props.closeFn} className="Options" heading="Options">
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerGameExec">
<div className='OptionLabel' id="menuOptionsLabelGameExec">
<Tr text="options.game_exec" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsDirGameExec">
<DirInput onChange={this.setGameExec} value={this.state?.game_install_path} extensions={['exe']} />
</div>
</div>
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerGCJar">
<div className='OptionLabel' id="menuOptionsLabelGCJar">
<Tr text="options.grasscutter_jar" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsDirGCJar">
<DirInput onChange={this.setGrasscutterJar} value={this.state?.grasscutter_path} extensions={['jar']} />
</div>
</div>
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerToggleEnc">
<div className='OptionLabel' id="menuOptionsLabelToggleEnc">
<Tr text="options.toggle_encryption" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsButtonToggleEnc">
<BigButton onClick={this.toggleEncryption} id="toggleEnc">
{
this.state.encryption
@@ -201,23 +212,23 @@ export default class Options extends React.Component<IProps, IState> {
<Divider />
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerGCWGame">
<div className='OptionLabel' id="menuOptionsLabelGCWDame">
<Tr text="options.grasscutter_with_game" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsCheckboxGCWGame">
<Checkbox onChange={this.toggleGrasscutterWithGame} checked={this.state?.grasscutter_with_game} id="gcWithGame" />
</div>
</div>
<Divider />
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerThemes">
<div className='OptionLabel' id="menuOptionsLabelThemes">
<Tr text="options.theme" />
</div>
<div className='OptionValue'>
<select value={this.state.theme} onChange={(event) => {
<div className='OptionValue' id="menuOptionsSelectThemes">
<select value={this.state.theme} id="menuOptionsSelectMenuThemes" onChange={(event) => {
this.setTheme(event.target.value)
}}>
{this.state.themes.map(t => (
@@ -234,20 +245,20 @@ export default class Options extends React.Component<IProps, IState> {
<Divider />
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerJavaPath">
<div className='OptionLabel' id="menuOptionsLabelJavaPath">
<Tr text="options.java_path" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsDirJavaPath">
<DirInput onChange={this.setJavaPath} value={this.state?.java_path} extensions={['exe']} />
</div>
</div>
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerBG">
<div className='OptionLabel' id="menuOptionsLabelBG">
<Tr text="options.background" />
</div>
<div className='OptionValue'>
<div className='OptionValue' id="menuOptionsDirBG">
<DirInput
onChange={this.setCustomBackground}
value={this.state?.bg_url_or_path}
@@ -262,12 +273,12 @@ export default class Options extends React.Component<IProps, IState> {
</div>
</div>
<div className='OptionSection'>
<div className='OptionLabel'>
<div className='OptionSection' id="menuOptionsContainerLang">
<div className='OptionLabel' id="menuOptionsLabelLang">
<Tr text="options.language" />
</div>
<div className='OptionValue'>
<select value={this.state.current_language} onChange={(event) => {
<div className='OptionValue' id="menuOptionsSelectLang">
<select value={this.state.current_language} id="menuOptionsSelectMenuLang" onChange={(event) => {
this.setLanguage(event.target.value)
}}>
{this.state.language_options.map(lang => (
@@ -281,6 +292,23 @@ export default class Options extends React.Component<IProps, IState> {
</select>
</div>
</div>
<Divider />
{
themeSettings ? themeSettings.map((settings, index) => {
return (
<div className='OptionSection' key={index}>
<div className='OptionLabel'>
{settings.label}
</div>
<div className='OptionValue'>
<ThemeOptionValue type={settings.type} className={settings.className} data={settings.data} />
</div>
</div>
)
}) : null
}
</Menu>
)
}

View File

@@ -56,9 +56,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
const commits: string = await invoke('req_get', { url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits' })
obj = JSON.parse(commits)
} else {
const decoded: string = await invoke('base64_decode', { encoded: obj.commits })
const commitData = JSON.parse(decoded)
obj = commitData.gc_stable
obj = obj.commits.gc_stable
}
// Probably rate-limited
@@ -68,7 +66,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
const commitsList = obj.slice(0, 10)
const commitsListHtml = commitsList.map((commit: any) => {
return (
<tr className="Commit" key={commit.sha}>
<tr className="Commit" id="newsCommitsTable" key={commit.sha}>
<td className="CommitAuthor"><span>{commit.commit.author.name}</span></td>
<td className="CommitMessage"><span>{commit.commit.message}</span></td>
</tr>
@@ -108,8 +106,8 @@ export default class NewsSection extends React.Component<IProps, IState> {
render() {
return (
<div className="NewsSection">
<div className="NewsTabs">
<div className="NewsSection" id="newsContainer">
<div className="NewsTabs" id="newsTabsContainer">
<div className={'NewsTab ' + (this.state.selected === 'commits' ? 'selected' : '')} id="commits" onClick={() => this.setSelected('commits')}>
<Tr text="news.latest_commits" />
</div>
@@ -117,7 +115,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
<Tr text="news.latest_version" />
</div>
</div>
<table className="NewsContent">
<table className="NewsContent" id="newsContent">
<tbody>
{this.state.news}
</tbody>

31
src/utils/dom.ts Normal file
View File

@@ -0,0 +1,31 @@
import { setConfigOption } from './configuration'
interface DOMMessage {
type: string
data: ConfigUpdate
}
interface ConfigUpdate {
setting: string
value: any
}
/**
* Parses a message received from the DOM.
* @param document The document.
* @param msg The message received from the DOM.
*/
export function parseMessageFromDOM(document: Document, msg: any): void {
msg = msg.detail
if(!msg || !msg.type || !msg.data)
return
switch(msg.type) {
case 'updateConfig':
if(!msg.data.setting || !msg.data.value)
return
setConfigOption(msg.data.setting, msg.data.value)
return
}
}

View File

@@ -1,7 +1,9 @@
import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import { getConfig, setConfigOption } from './configuration'
import {invoke} from '@tauri-apps/api'
import {dataDir} from '@tauri-apps/api/path'
import {convertFileSrc} from '@tauri-apps/api/tauri'
import {getConfig, setConfigOption} from './configuration'
import {InputSettings} from '../ui/components/common/ThemeOptionValue'
interface Theme {
name: string
@@ -13,6 +15,16 @@ interface Theme {
css: string[]
js: string[]
}
// Custom settings.
settings?: {
label: string // The setting's label.
type: string // The setting's type.
data: InputSettings // The data for the setting.
className?: string // The name of the class this setting should take.
jsCallback?: string // The name of the callback method that should be invoked.
}[]
customBackgroundURL?: string
customBackgroundPath?: string
@@ -23,7 +35,7 @@ interface BackendThemeList {
path: string
}
interface ThemeList extends Theme {
export interface ThemeList extends Theme {
path: string
}
@@ -37,6 +49,7 @@ const defaultTheme = {
},
path: 'default'
}
export async function getThemeList() {
// Do some invoke to backend to get the theme list
const themes = await invoke('get_theme_list', {
@@ -77,6 +90,11 @@ export async function getTheme(name: string) {
return themes.find(t => t.name === name) || defaultTheme
}
export async function getSelectedTheme() {
const config = await getConfig()
return await getTheme(config.theme)
}
export async function loadTheme(theme: ThemeList, document: Document) {
// Get config, since we will set the custom background in there
const config = await getConfig()

12483
yarn.lock

File diff suppressed because it is too large Load Diff