aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeckoEidechse <gecko.eidechse+git@pm.me>2023-04-26 21:58:55 +0200
committerGeckoEidechse <gecko.eidechse+git@pm.me>2023-04-26 21:58:55 +0200
commit10616b295eb23c8250a0d874fe05211f73a8ba81 (patch)
treeaa9496bd16d973f69f6c4d9df7695cae469fad83
parent85bb5253657c16d9674a9be2f6c8090b413ca7fb (diff)
parente38ab60e1e4f565f0dafdb7b539e386a390594d7 (diff)
downloadFlightCore-10616b295eb23c8250a0d874fe05211f73a8ba81.tar.gz
FlightCore-10616b295eb23c8250a0d874fe05211f73a8ba81.zip
Merge branch 'main' into fix/handle-failed-download
-rw-r--r--.github/workflows/push-test.yml48
-rw-r--r--.github/workflows/release.yml18
-rw-r--r--LICENSE21
-rw-r--r--README.md2
-rw-r--r--docs/DEV-TOOLS.md28
-rw-r--r--docs/DEVELOPMENT.md111
-rw-r--r--docs/FAQ.md4
-rw-r--r--docs/TROUBLESHOOTING.md14
-rw-r--r--docs/assets/dev-view-screenshot.pngbin0 -> 716658 bytes
-rw-r--r--docs/assets/webview2-download-screenshot.pngbin0 -> 277124 bytes
-rw-r--r--src-tauri/Cargo.lock219
-rw-r--r--src-tauri/Cargo.toml16
-rw-r--r--src-tauri/bindings/InstallProgress.ts4
-rw-r--r--src-tauri/bindings/InstallState.ts3
-rw-r--r--src-tauri/bindings/PullRequestType.ts2
-rw-r--r--src-tauri/bindings/Tag.ts3
-rw-r--r--src-tauri/bindings/TagWrapper.ts4
-rw-r--r--src-tauri/src/constants.rs16
-rw-r--r--src-tauri/src/github/mod.rs181
-rw-r--r--src-tauri/src/github/pull_requests.rs25
-rw-r--r--src-tauri/src/github/release_notes.rs10
-rw-r--r--src-tauri/src/lib.rs276
-rw-r--r--src-tauri/src/main.rs87
-rw-r--r--src-tauri/src/mod_management/mod.rs88
-rw-r--r--src-tauri/src/northstar/mod.rs18
-rw-r--r--src-tauri/src/platform_specific/linux.rs4
-rw-r--r--src-tauri/src/platform_specific/windows.rs6
-rw-r--r--src-tauri/src/repair_and_verify/mod.rs10
-rw-r--r--src-tauri/src/thunderstore/mod.rs4
-rw-r--r--src-tauri/tauri.conf.json2
-rw-r--r--src-vue/package-lock.json1393
-rw-r--r--src-vue/package.json3
-rw-r--r--src-vue/src/App.vue41
-rw-r--r--src-vue/src/components/InstallProgressBar.vue102
-rw-r--r--src-vue/src/components/LanguageSelector.vue51
-rw-r--r--src-vue/src/components/ModsMenu.vue14
-rw-r--r--src-vue/src/components/PlayButton.vue64
-rw-r--r--src-vue/src/components/PullRequestsSelector.vue89
-rw-r--r--src-vue/src/components/ThunderstoreModCard.vue60
-rw-r--r--src-vue/src/i18n/lang/de.json164
-rw-r--r--src-vue/src/i18n/lang/en.json164
-rw-r--r--src-vue/src/i18n/lang/fr.json164
-rw-r--r--src-vue/src/main.ts13
-rw-r--r--src-vue/src/plugins/modules/pull_requests.ts79
-rw-r--r--src-vue/src/plugins/store.ts137
-rw-r--r--src-vue/src/style.css5
-rw-r--r--src-vue/src/utils/SortOptions.d.ts12
-rw-r--r--src-vue/src/utils/ui.ts29
-rw-r--r--src-vue/src/views/ChangelogView.vue2
-rw-r--r--src-vue/src/views/DeveloperView.vue149
-rw-r--r--src-vue/src/views/PlayView.vue32
-rw-r--r--src-vue/src/views/RepairView.vue95
-rw-r--r--src-vue/src/views/SettingsView.vue119
-rw-r--r--src-vue/src/views/mods/LocalModsView.vue78
-rw-r--r--src-vue/src/views/mods/ThunderstoreModsView.vue21
55 files changed, 3117 insertions, 1187 deletions
diff --git a/.github/workflows/push-test.yml b/.github/workflows/push-test.yml
index cb7491e9..4cac9da3 100644
--- a/.github/workflows/push-test.yml
+++ b/.github/workflows/push-test.yml
@@ -27,6 +27,34 @@ jobs:
- name: Format check
run: cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
+ clippy:
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [ubuntu-22.04, windows-latest]
+
+ name: clippy-check
+ runs-on: ${{ matrix.platform }}
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ toolchain: stable
+ components: clippy
+ - name: install dependencies (ubuntu only)
+ if: matrix.platform == 'ubuntu-22.04'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
+ - name: Create dist folder (Ubuntu only)
+ if: matrix.platform == 'ubuntu-22.04'
+ run: mkdir --parent src-vue/dist
+ - name: Create dist folder (Windows only)
+ if: matrix.platform == 'windows-latest'
+ run: New-Item -ItemType Directory -Force -Path "src-vue\dist"
+ - name: Run clippy
+ run: cargo clippy --manifest-path src-tauri/Cargo.toml -- --no-deps --deny warnings
+
# Ensure committed bindings correct
autogen-ts-bindings-check:
runs-on: ubuntu-latest
@@ -95,23 +123,39 @@ jobs:
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
run: |
- npm install
- cd src-vue && npm install && cd ..
+ npm clean-install
+ cd src-vue && npm clean-install && cd ..
npm run tauri build
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
- name: Upload Linux artifact
+ if: matrix.platform == 'ubuntu-22.04'
uses: actions/upload-artifact@v3
with:
name: linux-artifacts
path: |
src-tauri/target/release/bundle/appimage/*
+ - name: Upload Linux AppImage
+ if: matrix.platform == 'ubuntu-22.04'
+ uses: actions/upload-artifact@v3
+ with:
+ name: linux-appimage
+ path: |
+ src-tauri/target/release/bundle/appimage/*.AppImage
- name: Upload Windows artifact
+ if: matrix.platform == 'windows-latest'
uses: actions/upload-artifact@v3
with:
name: windows-artifacts
path: |
src-tauri/target/release/bundle/msi/*
src-tauri/target/release/app.pdb
+ - name: Additionally upload Windows installer separately
+ if: matrix.platform == 'windows-latest'
+ uses: actions/upload-artifact@v3
+ with:
+ name: windows-msi
+ path: |
+ src-tauri/target/release/bundle/msi/*.msi
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 23c504aa..8b2235e5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,7 +1,11 @@
name: "release"
on:
- release:
- types: [ created ]
+ push:
+ tags:
+ - 'v*'
+
+permissions:
+ contents: write # Needed to write to GitHub draft release
jobs:
# Ensure version numbers in various places match up
@@ -14,7 +18,7 @@ jobs:
pip install toml
- name: Run check
run: |
- python3 scripts/check_version_numbers.py --release ${{github.event.release.tag_name}}
+ python3 scripts/check_version_numbers.py --release ${{github.ref_name}}
build:
needs: ensure-same-version
@@ -47,8 +51,8 @@ jobs:
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
run: |
- npm install
- cd src-vue && npm install && cd ..
+ npm clean-install
+ cd src-vue && npm clean-install && cd ..
npm run tauri build
- uses: tauri-apps/tauri-action@v0
env:
@@ -76,6 +80,7 @@ jobs:
- name: Release
uses: softprops/action-gh-release@v1
with:
+ draft: true
files: |
src-tauri/target/release/bundle/appimage/*AppImage*
src-tauri/target/release/bundle/msi/*msi*
@@ -88,10 +93,11 @@ jobs:
- uses: actions/download-artifact@v3
- name: Create release file
run: |
- python3 scripts/create-release-file.py --version ${{github.event.release.tag_name}}
+ python3 scripts/create-release-file.py --version ${{github.ref_name}}
- name: upload release file
uses: softprops/action-gh-release@v1
with:
+ draft: true
files: |
latest-release.json
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..b28dbe36
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 R2NorthstarTools
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 0bd93816..619cce47 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,8 @@ Answers to frequently asked questions can be found in [docs/FAQ.md](docs/FAQ.md)
If you'd like to contribute to FlightCore, see [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)
+If you are a Northstar developer/contributor, you might want to look at [docs/DEV-TOOLS.md](docs/DEV-TOOLS.md)
+
## Roadmap
--> See https://github.com/R2NorthstarTools/FlightCore/issues/1
diff --git a/docs/DEV-TOOLS.md b/docs/DEV-TOOLS.md
new file mode 100644
index 00000000..cf880d9d
--- /dev/null
+++ b/docs/DEV-TOOLS.md
@@ -0,0 +1,28 @@
+# Dev tools
+
+![dev view screenshot](assets/dev-view-screenshot.png)
+
+FlightCore features a hidden view that contains development features.
+
+It's targetted at both Northstar and FlightCore contributors. Among other things it contains buttons for unreleased features in FlightCore and tools to help with Northstar development.
+
+To activate it, spam click the FlightCore version number in the settings view at least 6 times. After that a new entry named _DEV_ should appear in the menubar.
+
+## Northstar
+
+### Pull request install
+
+The dev view offers a way to install pull request from the [NorthstarLauncher](https://github.com/R2Northstar/NorthstarLauncher) and [NorthstarMods](https://github.com/R2Northstar/NorthstarMods) repositories.
+
+Launcher pull requests overwrite `NorthstarLauncher.exe` and `Northstar.dll`.
+
+Mod pull requests install into a separate profile called `R2Northstar-PR-test-managed-folder`. \
+When installing a mods PR, FlightCore will place `r2ns-launch-mod-pr-version.bat` into your Titanfall2 directory that can be used to run that PR profile directly. \
+The batch file simply runs `NorthstarLauncher.exe -profile=R2Northstar-PR-test-managed-folder`
+
+
+## FlightCore
+
+The dev view contains various buttons that call functions that might not be fully implemented or tested yet.
+
+Additionally it has some buttons for testing like the _Panic button_ which force crashes the application to test automatic log uploading on crash among other things.
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
index 8dbb1cea..ce6ba010 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/DEVELOPMENT.md
@@ -14,7 +14,6 @@ As for splitting logic between _frontend_ and _backend_, state and UI related lo
Make sure you have the necessary dependencies for Tauri installed as described in this link: https://tauri.app/v1/guides/getting-started/prerequisites
-
Then, install `npm` dependencies with
```sh
@@ -24,7 +23,7 @@ npm install
Install UI dependencies too
```sh
-cd src-vue && npm install
+cd src-vue && npm install && cd ..
```
Then for developing
@@ -35,6 +34,30 @@ npx tauri dev
Automatic recompiling on save is enabled for both the Rust and the Typescript/Vue code.
+If you want to build FlightCore from source, run
+
+```sh
+npx tauri build
+```
+
+This will build the executable and bundles, such as `AppImage`, `.deb` or `.msi`.
+
+To build just the executable, edit [tauri.conf.json](https://github.com/R2NorthstarTools/FlightCore/blob/main/src-tauri/tauri.conf.json) in the same folder:
+
+```json
+ "bundle": {
+ "active": true,
+```
+
+Change `active` from `true` to `false`, and bundles won't be included afterwards.
+
+To disable the updater (which requires a private key) change `active` value to `false`:
+
+```json
+ "updater": {
+ "active": true,
+```
+
## Tauri
An introduction to Tauri can be seen in this short YouTube video: https://youtu.be/-X8evddpu7M
@@ -145,6 +168,7 @@ const persistentStore = new Store('flight-core-settings.json');
// Save change in persistent store
await persistentStore.set('northstar-release-canal', { value: "NorthstarReleasecandidate" });
+await persistentStore.save(); // explicit save to disk
// Grab Northstar release canal value from store if exists
var persistent_northstar_release_canal = (await persistentStore.get('northstar-release-canal')) as any;
@@ -178,6 +202,89 @@ struct User {
then simply run `cargo test`. The generated bindings are placed in `src-tauri/bindings/`. Make sure to add and commit them as well!
+### Internationalization
+
+For FlightCore to be used by the largest number, its interface is translated in several languages; users can choose used language through FlightCore settings.
+
+Localization files are located in `src-vue/src/i18n/lang`.
+
+To add a new language, you have to create associated file, *e.g. `src-vue/src/i18n/lang/de.json`*, and import it in the i18n application object in `main.ts`:
+```javascript
+import de from "./i18n/lang/de.json";
+
+export const i18n = createI18n({
+ locale: 'en',
+ fallbackLocale: 'en',
+ messages: {
+ en, fr, de
+ }
+});
+```
+
+In order to be able to select it, make sure to that it to the `LanguageSelector` componenent in `src-vue/src/components/LanguageSelector.vue`.
+
+```vue
+export default defineComponent({
+ name: 'LanguageSelector',
+ data: () => ({
+ value: '',
+ options: [
+ {
+ value: 'en',
+ label: 'English'
+ },
+ <!-- ... -->
+ {
+ value: 'de',
+ label: 'Deutsch'
+ },
+ ]
+ }),
+ <!-- ... -->
+})
+```
+
+There are different ways to use translations in views; in HTML template, invoke the `$t` method with translation key:
+
+```html
+<div>
+ {{ $t('menu.play') }}
+</div>
+```
+
+For use in Typescript code (inside components), invoke the `this.$t` method:
+
+```javascript
+return this.$t("play.button.select_game_dir");
+```
+
+For Typescript code outside components, translations are still accessible:
+
+```javascript
+import { i18n } from '../main';
+i18n.global.tc('notification.game_folder.new.text');
+```
+
+---
+
+It is possible to inject variables into translations:
+
+```json
+"channels": {
+ "release": {
+ "component": {
+ "text": "Switched release channel to {canal}."
+ }
+ }
+}
+```
+
+```javascript
+return this.$t("channels.release.component.text", {canal: "MyCanalName"});
+```
+
+
+
## Other
This repo uses [EditorConfig](https://editorconfig.org/) to define some basic formatting rules. Find a plugin for your IDE [here](https://editorconfig.org/#download).
diff --git a/docs/FAQ.md b/docs/FAQ.md
index eb381d68..8dd2dfa4 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -16,6 +16,10 @@ You use it to do stuff like
- [or this](https://www.youtube.com/watch?v=suhBGqzDbNA)
- [or this](https://www.youtube.com/watch?v=vyUxAwobY60)
+## I have an issue with FlightCore, where do I go?
+
+Check [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
+
## Why yet another Northstar intaller/updater/mod-manager instead of contributing to an existing one?
The 3 main GUI tools for handling such tasks with Norhtstar are
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md
new file mode 100644
index 00000000..aed174e0
--- /dev/null
+++ b/docs/TROUBLESHOOTING.md
@@ -0,0 +1,14 @@
+# Troubleshooting
+
+Got an issue with FlightCore? Hopefully one of the steps below will help you resolve it. If not open an [issue on GitHub](https://github.com/R2NorthstarTools/FlightCore/issues/new) or ping `Gecko#7945` on the Northstar Discord.
+
+## FlightCore won't launch
+
+If you are on Windows on FlightCore won't start, make sure you have WebView2 installed. You can grab the latest version from the Microsoft website: \
+https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section
+
+![webview2 download screenshot](assets/webview2-download-screenshot.png)
+
+(make sure to select _Evergreen Bootstrapper_ -> _Download_).
+
+
diff --git a/docs/assets/dev-view-screenshot.png b/docs/assets/dev-view-screenshot.png
new file mode 100644
index 00000000..5cf28043
--- /dev/null
+++ b/docs/assets/dev-view-screenshot.png
Binary files differ
diff --git a/docs/assets/webview2-download-screenshot.png b/docs/assets/webview2-download-screenshot.png
new file mode 100644
index 00000000..2f0bfdc8
--- /dev/null
+++ b/docs/assets/webview2-download-screenshot.png
Binary files differ
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 70e50a8e..6c64bf7b 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -87,7 +87,7 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "app"
-version = "1.10.2"
+version = "1.13.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -96,9 +96,13 @@ dependencies = [
"game-scanner",
"json5",
"libthermite",
+ "log",
+ "open",
+ "pretty_env_logger",
"regex",
"reqwest",
"sentry",
+ "sentry-log",
"serde",
"serde_json",
"steamlocate",
@@ -164,6 +168,17 @@ dependencies = [
]
[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -413,6 +428,15 @@ dependencies = [
]
[[package]]
+name = "cmake"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "cocoa"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -786,16 +810,16 @@ version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
dependencies = [
- "dirs-sys",
+ "dirs-sys 0.3.7",
]
[[package]]
name = "dirs"
-version = "3.0.2"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
+checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd"
dependencies = [
- "dirs-sys",
+ "dirs-sys 0.4.0",
]
[[package]]
@@ -820,6 +844,17 @@ dependencies = [
]
[[package]]
+name = "dirs-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b"
+dependencies = [
+ "libc",
+ "redox_users",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -879,6 +914,19 @@ dependencies = [
]
[[package]]
+name = "env_logger"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1092,9 +1140,8 @@ dependencies = [
[[package]]
name = "game-scanner"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eff11681d503562e5ff8fbd362ec8dd18d72f1470119e82708a55c3158eeaa0c"
+version = "1.2.0"
+source = "git+https://github.com/EqualGames/game-scanner.git?rev=94dcbd086a5987361a2847988a060da9b5dd3908#94dcbd086a5987361a2847988a060da9b5dd3908"
dependencies = [
"bytes",
"case",
@@ -1107,9 +1154,9 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "sysinfo 0.23.13",
+ "sysinfo 0.28.4",
"url",
- "winreg",
+ "winreg 0.10.1",
]
[[package]]
@@ -1416,26 +1463,20 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.11.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
name = "hashlink"
-version = "0.7.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
+checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
dependencies = [
- "hashbrown 0.11.2",
+ "hashbrown",
]
[[package]]
@@ -1455,6 +1496,15 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
@@ -1543,6 +1593,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
name = "hyper"
version = "0.14.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1667,7 +1726,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
- "hashbrown 0.12.3",
+ "hashbrown",
]
[[package]]
@@ -1820,6 +1879,20 @@ dependencies = [
]
[[package]]
+name = "keyvalues-serde"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f"
+dependencies = [
+ "keyvalues-parser",
+ "once_cell",
+ "paste",
+ "regex",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
name = "kuchiki"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1854,9 +1927,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
-version = "0.24.2"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
+checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
dependencies = [
"cc",
"pkg-config",
@@ -2146,15 +2219,6 @@ dependencies = [
[[package]]
name = "ntapi"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "ntapi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc"
@@ -2208,7 +2272,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.2.6",
"libc",
]
@@ -2673,6 +2737,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
+name = "pretty_env_logger"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
+dependencies = [
+ "env_logger",
+ "log",
+]
+
+[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2723,9 +2797,9 @@ dependencies = [
[[package]]
name = "prost"
-version = "0.9.0"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
+checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e"
dependencies = [
"bytes",
"prost-derive",
@@ -2733,12 +2807,14 @@ dependencies = [
[[package]]
name = "prost-build"
-version = "0.9.0"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5"
+checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab"
dependencies = [
"bytes",
- "heck 0.3.3",
+ "cfg-if",
+ "cmake",
+ "heck 0.4.1",
"itertools",
"lazy_static",
"log",
@@ -2753,9 +2829,9 @@ dependencies = [
[[package]]
name = "prost-derive"
-version = "0.9.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
+checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc"
dependencies = [
"anyhow",
"itertools",
@@ -2766,15 +2842,21 @@ dependencies = [
[[package]]
name = "prost-types"
-version = "0.9.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
+checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68"
dependencies = [
"bytes",
"prost",
]
[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
name = "quick-xml"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2993,7 +3075,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "winreg",
+ "winreg 0.10.1",
]
[[package]]
@@ -3037,16 +3119,15 @@ dependencies = [
[[package]]
name = "rusqlite"
-version = "0.27.0"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
+checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
- "memchr",
"smallvec",
]
@@ -3303,6 +3384,16 @@ dependencies = [
]
[[package]]
+name = "sentry-log"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa3a3f4477e77541c26eb84d0e355729dfa35c74c682eb8678f146db5126013"
+dependencies = [
+ "log",
+ "sentry-core",
+]
+
+[[package]]
name = "sentry-panic"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3581,14 +3672,16 @@ dependencies = [
[[package]]
name = "steamlocate"
-version = "1.1.1"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf364a9446f9f4423694ad8db5be8c478088970138e557ff2ddb3167fb0a2944"
+checksum = "6b3962619891cd4c87dc0ae9332b97285eb59ab7cf65c9bec3fba62188390e74"
dependencies = [
"dirs",
"keyvalues-parser",
+ "keyvalues-serde",
+ "serde",
"steamy-vdf",
- "winreg",
+ "winreg 0.11.0",
]
[[package]]
@@ -3672,14 +3765,14 @@ dependencies = [
[[package]]
name = "sysinfo"
-version = "0.23.13"
+version = "0.26.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9"
+checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
- "ntapi 0.3.7",
+ "ntapi",
"once_cell",
"rayon",
"winapi",
@@ -3687,14 +3780,14 @@ dependencies = [
[[package]]
name = "sysinfo"
-version = "0.26.9"
+version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5"
+checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
- "ntapi 0.4.0",
+ "ntapi",
"once_cell",
"rayon",
"winapi",
@@ -3898,8 +3991,8 @@ dependencies = [
[[package]]
name = "tauri-plugin-store"
-version = "0.0.0"
-source = "git+https://github.com/tauri-apps/tauri-plugin-store?rev=9bd993aa67766596638bbfd91e79a1bf8f632014#9bd993aa67766596638bbfd91e79a1bf8f632014"
+version = "0.1.0"
+source = "git+https://github.com/tauri-apps/plugins-workspace?rev=5a6abd3203dc94c38f96d0c4bf7ecbef399f8c25#5a6abd3203dc94c38f96d0c4bf7ecbef399f8c25"
dependencies = [
"log",
"serde",
@@ -4932,6 +5025,16 @@ dependencies = [
]
[[package]]
+name = "winreg"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
name = "winres"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 0e972064..76315fae 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "app"
-version = "1.10.2"
+version = "1.13.2"
description = "A Tauri App"
authors = ["you"]
license = ""
@@ -25,9 +25,11 @@ serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.2", features = ["api-all", "updater"] }
tokio = { version = "1", features = ["full"] }
+# Sentry (crash) logging
sentry = "0.30"
+sentry-log = "0.30.0"
# Find steam games
-steamlocate = "1.0.2"
+steamlocate = "1.2"
# Error messages
anyhow = "1.0"
# libthermite for Northstar/mod install handling
@@ -41,21 +43,27 @@ sysinfo = "0.26.2"
# HTTP requests
reqwest = { version = "0.11", features = ["blocking"] }
# Persistent store for settings
-tauri-plugin-store = { git = "https://github.com/tauri-apps/tauri-plugin-store", rev = "9bd993aa67766596638bbfd91e79a1bf8f632014" }
+tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "5a6abd3203dc94c38f96d0c4bf7ecbef399f8c25" }
# JSON5 parsing support (allows comments in JSON)
json5 = "0.4.1"
# Async recursion for recursive mod install
async-recursion = "1.0.0"
# Game scanner
-game-scanner = "1.1.4"
+# Use specific commit that updates deprecated library
+game-scanner = { git = "https://github.com/EqualGames/game-scanner.git", rev = "94dcbd086a5987361a2847988a060da9b5dd3908" }
# For parsing timestamps
chrono = "0.4.23"
# TypeScript bindings
ts-rs = "6.1"
# const formatting
const_format = "0.2.30"
+# Logging libraries
+pretty_env_logger = "0.4.0"
+log = "0.4.17"
# Extracting zip files easily
zip-extract = "0.1.2"
+# open urls
+open = "3.2.0"
[features]
# by default Tauri runs in production mode
diff --git a/src-tauri/bindings/InstallProgress.ts b/src-tauri/bindings/InstallProgress.ts
new file mode 100644
index 00000000..7bea9bb8
--- /dev/null
+++ b/src-tauri/bindings/InstallProgress.ts
@@ -0,0 +1,4 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { InstallState } from "./InstallState";
+
+export interface InstallProgress { current_downloaded: bigint, total_size: bigint, state: InstallState, } \ No newline at end of file
diff --git a/src-tauri/bindings/InstallState.ts b/src-tauri/bindings/InstallState.ts
new file mode 100644
index 00000000..21dbc0c7
--- /dev/null
+++ b/src-tauri/bindings/InstallState.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export type InstallState = "DOWNLOADING" | "EXTRACTING" | "DONE"; \ No newline at end of file
diff --git a/src-tauri/bindings/PullRequestType.ts b/src-tauri/bindings/PullRequestType.ts
index 2d1fd0a8..582f831a 100644
--- a/src-tauri/bindings/PullRequestType.ts
+++ b/src-tauri/bindings/PullRequestType.ts
@@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
-export type PullRequestType = "MODS" | "LAUNCHER"; \ No newline at end of file
+export type PullRequestType = "Mods" | "Launcher"; \ No newline at end of file
diff --git a/src-tauri/bindings/Tag.ts b/src-tauri/bindings/Tag.ts
new file mode 100644
index 00000000..adbbff33
--- /dev/null
+++ b/src-tauri/bindings/Tag.ts
@@ -0,0 +1,3 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+
+export interface Tag { name: string, } \ No newline at end of file
diff --git a/src-tauri/bindings/TagWrapper.ts b/src-tauri/bindings/TagWrapper.ts
new file mode 100644
index 00000000..f9f56a51
--- /dev/null
+++ b/src-tauri/bindings/TagWrapper.ts
@@ -0,0 +1,4 @@
+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
+import type { Tag } from "./Tag";
+
+export interface TagWrapper { label: string, value: Tag, } \ No newline at end of file
diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs
index 57a10e02..8c88df1e 100644
--- a/src-tauri/src/constants.rs
+++ b/src-tauri/src/constants.rs
@@ -1,6 +1,6 @@
// This file stores various global constants values
-
use const_format::concatcp;
+use std::time::Duration;
// FlightCore user agent for web requests
pub const APP_USER_AGENT: &str = concatcp!("FlightCore/", env!("CARGO_PKG_VERSION"));
@@ -29,8 +29,22 @@ pub const BLACKLISTED_MODS: [&str; 3] = [
// Titanfall2 game IDs on Origin/EA-App
pub const TITANFALL2_ORIGIN_IDS: [&str; 2] = ["Origin.OFR.50.0001452", "Origin.OFR.50.0001456"];
+// Titanfall2 Steam App ID
+pub const TITANFALL2_STEAM_ID: &str = "1237970";
+
+// Order in which the sections for release notes should be displayed
+pub const SECTION_ORDER: [&str; 9] = [
+ "feat", "fix", "docs", "style", "refactor", "build", "test", "chore", "other",
+];
+
// GitHub API endpoints for launcher/mods PRs
pub const PULLS_API_ENDPOINT_LAUNCHER: &str =
"https://api.github.com/repos/R2Northstar/NorthstarLauncher/pulls";
pub const PULLS_API_ENDPOINT_MODS: &str =
"https://api.github.com/repos/R2Northstar/NorthstarMods/pulls";
+
+// Statistics (players and servers counts) refresh delay
+pub const REFRESH_DELAY: Duration = Duration::from_secs(5 * 60);
+
+// Flightcore repo name and org name on GitHub
+pub const FLIGHTCORE_REPO_NAME: &str = "R2NorthstarTools/FlightCore";
diff --git a/src-tauri/src/github/mod.rs b/src-tauri/src/github/mod.rs
index 942f0db2..b336ab5c 100644
--- a/src-tauri/src/github/mod.rs
+++ b/src-tauri/src/github/mod.rs
@@ -1,2 +1,183 @@
pub mod pull_requests;
pub mod release_notes;
+
+use app::constants::{APP_USER_AGENT, FLIGHTCORE_REPO_NAME, SECTION_ORDER};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use ts_rs::TS;
+
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub struct Tag {
+ name: String,
+}
+
+/// Wrapper type needed for frontend
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub struct TagWrapper {
+ label: String,
+ value: Tag,
+}
+
+#[derive(Debug, Deserialize)]
+struct CommitInfo {
+ sha: String,
+ commit: Commit,
+}
+
+#[derive(Debug, Deserialize)]
+struct Commit {
+ message: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct Comparison {
+ commits: Vec<CommitInfo>,
+}
+
+/// Get a list of tags on the FlightCore repo
+#[tauri::command]
+pub fn get_list_of_tags() -> Result<Vec<TagWrapper>, String> {
+ // Set the repository name.
+
+ // Create a `reqwest` client with a user agent.
+ let client = reqwest::blocking::Client::builder()
+ .user_agent(APP_USER_AGENT)
+ .build()
+ .unwrap();
+
+ // Fetch the list of tags for the repository as a `Vec<Tag>`.
+ let tags_url = format!("https://api.github.com/repos/{}/tags", FLIGHTCORE_REPO_NAME);
+ let tags: Vec<Tag> = client.get(tags_url).send().unwrap().json().unwrap();
+
+ // Map each `Tag` element to a `TagWrapper` element with the desired label and `Tag` value.
+ let tag_wrappers: Vec<TagWrapper> = tags
+ .into_iter()
+ .map(|tag| TagWrapper {
+ label: tag.name.clone(),
+ value: tag,
+ })
+ .collect();
+
+ Ok(tag_wrappers)
+}
+
+/// Use GitHub API to compare two tags of the same repo against each other and get the resulting changes
+#[tauri::command]
+pub fn compare_tags(first_tag: Tag, second_tag: Tag) -> Result<String, String> {
+ // Fetch the list of commits between the two tags.
+
+ // Create a `reqwest` client with a user agent.
+ let client = reqwest::blocking::Client::builder()
+ .user_agent(APP_USER_AGENT)
+ .build()
+ .unwrap();
+
+ let repo = "R2NorthstarTools/FlightCore";
+
+ let mut full_patch_notes = "".to_string();
+
+ let mut patch_notes: Vec<String> = [].to_vec();
+ println!("{}", repo);
+ // let repo = "R2Northstar/NorthstarLauncher";
+ let comparison_url = format!(
+ "https://api.github.com/repos/{}/compare/{}...{}",
+ repo, first_tag.name, second_tag.name
+ );
+
+ let comparison: Comparison = client.get(comparison_url).send().unwrap().json().unwrap();
+ let commits = comparison.commits;
+
+ // Display the list of commits.
+ println!(
+ "Commits between {} and {}:",
+ first_tag.name, second_tag.name
+ );
+
+ // Iterate over all commits in the diff
+ for commit in commits {
+ println!(
+ " * {} : {}",
+ commit.sha,
+ commit.commit.message.split('\n').next().unwrap()
+ );
+ patch_notes.push(
+ commit
+ .commit
+ .message
+ .split('\n')
+ .next()
+ .unwrap()
+ .to_string(),
+ );
+ }
+
+ full_patch_notes += &generate_flightcore_release_notes(patch_notes);
+
+ Ok(full_patch_notes.to_string())
+}
+
+/// Generate release notes in the format used for FlightCore
+fn generate_flightcore_release_notes(commits: Vec<String>) -> String {
+ let grouped_commits = group_commits_by_type(commits);
+ let mut release_notes = String::new();
+
+ // Go over commit types and generate notes
+ for commit_type in SECTION_ORDER {
+ if let Some(commit_list) = grouped_commits.get(commit_type) {
+ if !commit_list.is_empty() {
+ let section_title = match commit_type {
+ "feat" => "**Features:**",
+ "fix" => "**Bug Fixes:**",
+ "docs" => "**Documentation:**",
+ "style" => "**Styles:**",
+ "refactor" => "**Code Refactoring:**",
+ "build" => "**Build:**",
+ "test" => "**Tests:**",
+ "chore" => "**Chores:**",
+ _ => "**Other:**",
+ };
+
+ release_notes.push_str(&format!("{}\n", section_title));
+
+ for commit_message in commit_list {
+ release_notes.push_str(&format!("- {}\n", commit_message));
+ }
+
+ release_notes.push('\n');
+ }
+ }
+ }
+
+ release_notes
+}
+
+/// Group semantic commit messages by type
+/// Commmit messages that are not formatted accordingly are marked as "other"
+fn group_commits_by_type(commits: Vec<String>) -> HashMap<String, Vec<String>> {
+ let mut grouped_commits: HashMap<String, Vec<String>> = HashMap::new();
+ let mut other_commits: Vec<String> = vec![];
+
+ for commit in commits {
+ let commit_parts: Vec<&str> = commit.splitn(2, ':').collect();
+ if commit_parts.len() == 2 {
+ let commit_type = commit_parts[0].to_lowercase();
+ let commit_description = commit_parts[1].trim().to_string();
+
+ // Check if known commit type
+ if SECTION_ORDER.contains(&commit_type.as_str()) {
+ let commit_list = grouped_commits.entry(commit_type.to_string()).or_default();
+ commit_list.push(commit_description);
+ } else {
+ // otherwise add to list of "other"
+ other_commits.push(commit.to_string());
+ }
+ } else {
+ other_commits.push(commit.to_string());
+ }
+ }
+ grouped_commits.insert("other".to_string(), other_commits);
+
+ grouped_commits
+}
diff --git a/src-tauri/src/github/pull_requests.rs b/src-tauri/src/github/pull_requests.rs
index 586a4fb3..96ac623f 100644
--- a/src-tauri/src/github/pull_requests.rs
+++ b/src-tauri/src/github/pull_requests.rs
@@ -60,15 +60,15 @@ struct ArtifactsResponse {
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub enum PullRequestType {
- MODS,
- LAUNCHER,
+ Mods,
+ Launcher,
}
/// Parse pull requests from specified URL
pub async fn get_pull_requests(url: String) -> Result<Vec<PullsApiResponseElement>, String> {
let json_response = match fetch_github_releases_api(&url).await {
Ok(result) => result,
- Err(err) => return Err(err.to_string()),
+ Err(err) => return Err(err),
};
let pulls_response: Vec<PullsApiResponseElement> = match serde_json::from_str(&json_response) {
@@ -85,8 +85,8 @@ pub async fn get_pull_requests_wrapper(
install_type: PullRequestType,
) -> Result<Vec<PullsApiResponseElement>, String> {
let api_pr_url = match install_type {
- PullRequestType::MODS => PULLS_API_ENDPOINT_MODS,
- PullRequestType::LAUNCHER => PULLS_API_ENDPOINT_LAUNCHER,
+ PullRequestType::Mods => PULLS_API_ENDPOINT_MODS,
+ PullRequestType::Launcher => PULLS_API_ENDPOINT_LAUNCHER,
};
get_pull_requests(api_pr_url.to_string()).await
@@ -142,7 +142,8 @@ fn get_mods_download_link(pull_request: PullsApiResponseElement) -> Result<Strin
}
/// Gets `nightly.link` artifact download link of a launcher PR
-async fn get_launcher_download_link(
+#[tauri::command]
+pub async fn get_launcher_download_link(
pull_request: PullsApiResponseElement,
) -> Result<String, String> {
// Iterate over the first 10 pages of
@@ -207,7 +208,7 @@ fn add_batch_file(game_install_path: &str) {
match file.write_all(batch_file_content.as_bytes()) {
Err(why) => panic!("couldn't write to {}: {}", display, why),
- Ok(_) => println!("successfully wrote to {}", display),
+ Ok(_) => log::info!("successfully wrote to {}", display),
}
}
@@ -258,7 +259,7 @@ pub async fn apply_launcher_pr(
for file_name in files_to_copy {
let source_file_path = format!("{}/{}", extract_directory, file_name);
let destination_file_path = format!("{}/{}", game_install_path, file_name);
- match std::fs::copy(&source_file_path, &destination_file_path) {
+ match std::fs::copy(source_file_path, destination_file_path) {
Ok(_result) => (),
Err(err) => {
return Err(format!(
@@ -280,7 +281,7 @@ pub async fn apply_launcher_pr(
}
}
- println!("All done with installing launcher PR");
+ log::info!("All done with installing launcher PR");
Ok(())
}
@@ -308,9 +309,9 @@ pub async fn apply_mods_pr(
// Delete previously managed folder
if std::fs::remove_dir_all(profile_folder.clone()).is_err() {
if std::path::Path::new(&profile_folder).exists() {
- println!("Failed removing previous dir");
+ log::error!("Failed removing previous dir");
} else {
- println!("Failed removing folder that doesn't exist. Probably cause first run");
+ log::warn!("Failed removing folder that doesn't exist. Probably cause first run");
}
};
@@ -330,6 +331,6 @@ pub async fn apply_mods_pr(
// Add batch file to launch right profile
add_batch_file(game_install_path);
- println!("All done with installing mods PR");
+ log::info!("All done with installing mods PR");
Ok(())
}
diff --git a/src-tauri/src/github/release_notes.rs b/src-tauri/src/github/release_notes.rs
index b59358ec..b45442e0 100644
--- a/src-tauri/src/github/release_notes.rs
+++ b/src-tauri/src/github/release_notes.rs
@@ -20,7 +20,7 @@ pub struct FlightCoreVersion {
// Fetches repo release API and returns response as string
pub async fn fetch_github_releases_api(url: &str) -> Result<String, String> {
- println!("Fetching releases notes from GitHub API");
+ log::info!("Fetching releases notes from GitHub API");
let client = reqwest::Client::new();
let res = client
@@ -40,13 +40,13 @@ pub async fn fetch_github_releases_api(url: &str) -> Result<String, String> {
#[tauri::command]
pub async fn get_newest_flightcore_version() -> Result<FlightCoreVersion, String> {
// Get newest version number from GitHub API
- println!("Checking GitHub API");
+ log::info!("Checking GitHub API");
let url = "https://api.github.com/repos/R2NorthstarTools/FlightCore/releases/latest";
let res = fetch_github_releases_api(url).await?;
let flightcore_version: FlightCoreVersion =
serde_json::from_str(&res).expect("JSON was not well-formatted");
- println!("Done checking GitHub API");
+ log::info!("Done checking GitHub API");
Ok(flightcore_version)
}
@@ -98,7 +98,7 @@ pub async fn get_northstar_release_notes() -> Result<Vec<ReleaseInfo>, String> {
let release_info_vector: Vec<ReleaseInfo> =
serde_json::from_str(&res).expect("JSON was not well-formatted");
- println!("Done checking GitHub API");
+ log::info!("Done checking GitHub API");
- return Ok(release_info_vector);
+ Ok(release_info_vector)
}
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 12c839fe..8ec7e4b1 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,7 +1,6 @@
-use std::env;
+use std::{cell::RefCell, env, fs, path::Path, time::Duration, time::Instant};
use anyhow::{anyhow, Context, Result};
-use sentry::{add_breadcrumb, Breadcrumb, Level};
mod northstar;
@@ -15,11 +14,14 @@ use platform_specific::linux;
use serde::{Deserialize, Serialize};
use sysinfo::SystemExt;
+use tokio::time::sleep;
use ts_rs::TS;
use zip::ZipArchive;
use northstar::get_northstar_version_number;
+use crate::constants::TITANFALL2_STEAM_ID;
+
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum InstallType {
STEAM,
@@ -50,10 +52,26 @@ pub struct NorthstarServer {
pub player_count: i32,
}
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub enum InstallState {
+ DOWNLOADING,
+ EXTRACTING,
+ DONE,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+struct InstallProgress {
+ current_downloaded: u64,
+ total_size: u64,
+ state: InstallState,
+}
+
/// Check version number of a mod
-pub fn check_mod_version_number(path_to_mod_folder: String) -> Result<String, anyhow::Error> {
+pub fn check_mod_version_number(path_to_mod_folder: &str) -> Result<String, anyhow::Error> {
// println!("{}", format!("{}/mod.json", path_to_mod_folder));
- let data = std::fs::read_to_string(format!("{}/mod.json", path_to_mod_folder))?;
+ let data = std::fs::read_to_string(format!("{path_to_mod_folder}/mod.json"))?;
let parsed_json: serde_json::Value = serde_json::from_str(&data)?;
// println!("{}", parsed_json);
let mod_version_number = match parsed_json.get("Version").and_then(|value| value.as_str()) {
@@ -61,7 +79,7 @@ pub fn check_mod_version_number(path_to_mod_folder: String) -> Result<String, an
None => return Err(anyhow!("No version number found")),
};
- println!("{}", mod_version_number);
+ log::info!("{}", mod_version_number);
Ok(mod_version_number.to_string())
}
@@ -81,8 +99,7 @@ pub fn linux_checks_librs() -> Result<(), String> {
return Err(format!(
"GLIBC is not version {} or greater",
min_required_ldd_version
- )
- .to_string());
+ ));
};
// All checks passed
@@ -94,7 +111,7 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
// Attempt parsing Steam library directly
match steamlocate::SteamDir::locate() {
Some(mut steamdir) => {
- let titanfall2_steamid = 1237970;
+ let titanfall2_steamid = TITANFALL2_STEAM_ID.parse().unwrap();
match steamdir.app(&titanfall2_steamid) {
Some(app) => {
// println!("{:#?}", app);
@@ -104,10 +121,10 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
};
return Ok(game_install);
}
- None => println!("Couldn't locate Titanfall2"),
+ None => log::info!("Couldn't locate Titanfall2 Steam instal"),
}
}
- None => println!("Couldn't locate Steam on this computer!"),
+ None => log::info!("Couldn't locate Steam on this computer!"),
}
// (On Windows only) try parsing Windows registry for Origin install path
@@ -115,13 +132,13 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
match windows::origin_install_location_detection() {
Ok(game_path) => {
let game_install = GameInstall {
- game_path: game_path,
+ game_path,
install_type: InstallType::ORIGIN,
};
return Ok(game_install);
}
Err(err) => {
- println!("{}", err);
+ log::info!("{}", err);
}
};
@@ -130,13 +147,13 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
/// Checks whether the provided path is a valid Titanfall2 gamepath by checking against a certain set of criteria
pub fn check_is_valid_game_path(game_install_path: &str) -> Result<(), String> {
- let path_to_titanfall2_exe = format!("{}/Titanfall2.exe", game_install_path);
+ let path_to_titanfall2_exe = format!("{game_install_path}/Titanfall2.exe");
let is_correct_game_path = std::path::Path::new(&path_to_titanfall2_exe).exists();
- println!("Titanfall2.exe exists in path? {}", is_correct_game_path);
+ log::info!("Titanfall2.exe exists in path? {}", is_correct_game_path);
// Exit early if wrong game path
if !is_correct_game_path {
- return Err(format!("Incorrect game path \"{}\"", game_install_path)); // Return error cause wrong game path
+ return Err(format!("Incorrect game path \"{game_install_path}\"")); // Return error cause wrong game path
}
Ok(())
}
@@ -159,12 +176,12 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
);
if (*f.name()).ends_with('/') {
- println!("Create directory {}", f.name());
+ log::info!("Create directory {}", f.name());
std::fs::create_dir_all(target.join(f.name()))
.context("Unable to create directory")?;
continue;
} else if let Some(p) = out.parent() {
- std::fs::create_dir_all(&p).context("Unable to create directory")?;
+ std::fs::create_dir_all(p).context("Unable to create directory")?;
}
let mut outfile = std::fs::OpenOptions::new()
@@ -173,7 +190,7 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
.truncate(true)
.open(&out)?;
- println!("Write file {}", out.display());
+ log::info!("Write file {}", out.display());
std::io::copy(&mut f, &mut outfile).context("Unable to write to file")?;
}
@@ -186,33 +203,83 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
///Install N* from the provided mod
///
///Checks cache, else downloads the latest version
-async fn do_install(nmod: &thermite::model::ModVersion, game_path: &std::path::Path) -> Result<()> {
+async fn do_install(
+ window: tauri::Window,
+ nmod: &thermite::model::ModVersion,
+ game_path: &std::path::Path,
+) -> Result<()> {
let filename = format!("northstar-{}.zip", nmod.version);
let download_directory = format!("{}/___flightcore-temp-download-dir/", game_path.display());
std::fs::create_dir_all(download_directory.clone())?;
- let download_path = format!("{}/{}", download_directory.clone(), filename);
- println!("{}", download_path);
-
- let nfile = match thermite::core::manage::download_file(&nmod.url, download_path) {
+ let download_path = format!("{}/{}", download_directory, filename);
+ log::info!("Download path: {download_path}");
+
+ let last_emit = RefCell::new(Instant::now()); // Keep track of the last time a signal was emitted
+ let nfile = match thermite::core::manage::download_file_with_progress(
+ &nmod.url,
+ download_path,
+ |delta, current, total| {
+ if delta != 0 {
+ // Only emit a signal once every 100ms
+ // This way we don't bombard the frontend with events on fast download speeds
+ let time_since_last_emit = Instant::now().duration_since(*last_emit.borrow());
+ if time_since_last_emit >= Duration::from_millis(100) {
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: current,
+ total_size: total,
+ state: InstallState::DOWNLOADING,
+ },
+ )
+ .unwrap();
+ *last_emit.borrow_mut() = Instant::now();
+ }
+ }
+ },
+ ) {
Ok(res) => res,
Err(err) => return Err(anyhow!("Failed downloading Northstar {}", err)),
};
- println!("Extracting Northstar...");
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: 0,
+ total_size: 0,
+ state: InstallState::EXTRACTING,
+ },
+ )
+ .unwrap();
+
+ log::info!("Extracting Northstar...");
extract(nfile, game_path)?;
// Delete old copy
- println!("Delete temp folder again");
+ log::info!("Delete temp folder again");
std::fs::remove_dir_all(download_directory).unwrap();
- println!("Done!");
+ log::info!("Done installing Northstar!");
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: 0,
+ total_size: 0,
+ state: InstallState::DONE,
+ },
+ )
+ .unwrap();
Ok(())
}
pub async fn install_northstar(
+ window: tauri::Window,
game_path: &str,
northstar_package_name: Option<String>,
) -> Result<String, String> {
@@ -234,20 +301,30 @@ pub async fn install_northstar(
.ok_or_else(|| panic!("Couldn't find Northstar on thunderstore???"))
.unwrap();
- // Breadcrumb for sentry to debug crash
- add_breadcrumb(Breadcrumb {
- // category: Some("auth".into()),
- message: Some(format!("Install path \"{}\"", game_path)),
- level: Level::Info,
- ..Default::default()
- });
+ log::info!("Install path \"{}\"", game_path);
- do_install(
+ match do_install(
+ window,
nmod.versions.get(&nmod.latest).unwrap(),
std::path::Path::new(game_path),
)
.await
- .unwrap();
+ {
+ Ok(_) => (),
+ Err(err) => {
+ if game_path
+ .to_lowercase()
+ .contains(&r#"C:\Program Files\"#.to_lowercase())
+ // default is `C:\Program Files\EA Games\Titanfall2`
+ {
+ return Err(
+ "Cannot install to default EA App install path, please move Titanfall2 to a different install location.".to_string(),
+ );
+ } else {
+ return Err(err.to_string());
+ }
+ }
+ }
Ok(nmod.latest.clone())
}
@@ -258,7 +335,7 @@ pub fn get_host_os() -> String {
}
pub fn launch_northstar(
- game_install: GameInstall,
+ game_install: &GameInstall,
bypass_checks: Option<bool>,
) -> Result<String, String> {
dbg!(game_install.clone());
@@ -278,15 +355,12 @@ pub fn launch_northstar(
));
}
- let bypass_checks = match bypass_checks {
- Some(bypass_checks) => bypass_checks,
- None => false,
- };
+ let bypass_checks = bypass_checks.unwrap_or(false);
// Only check guards if bypassing checks is not enabled
if !bypass_checks {
// Some safety checks before, should have more in the future
- if get_northstar_version_number(game_install.game_path.clone()).is_err() {
+ if get_northstar_version_number(&game_install.game_path).is_err() {
return Err(anyhow!("Not all checks were met").to_string());
}
@@ -314,7 +388,7 @@ pub fn launch_northstar(
{
let ns_exe_path = format!("{}/NorthstarLauncher.exe", game_install.game_path);
let _output = std::process::Command::new("C:\\Windows\\System32\\cmd.exe")
- .args(&["/C", "start", "", &ns_exe_path])
+ .args(["/C", "start", "", &ns_exe_path])
.spawn()
.expect("failed to execute process");
return Ok("Launched game".to_string());
@@ -327,31 +401,109 @@ pub fn launch_northstar(
))
}
-pub fn check_origin_running() -> bool {
- let s = sysinfo::System::new_all();
- for _process in s.processes_by_name("Origin.exe") {
- // check here if this is your process
- // dbg!(process);
- // There's at least one Origin process, so we can launch
- return true;
+/// Prepare Northstar and Launch through Steam using the Browser Protocol
+pub fn launch_northstar_steam(
+ game_install: &GameInstall,
+ _bypass_checks: Option<bool>,
+) -> Result<String, String> {
+ if !matches!(game_install.install_type, InstallType::STEAM) {
+ return Err("Titanfall2 was not installed via Steam".to_string());
}
- // Alternatively, check for EA Desktop
- for _process in s.processes_by_name("EADesktop.exe") {
- // There's at least one EADesktop process, so we can launch
- return true;
+
+ match steamlocate::SteamDir::locate() {
+ Some(mut steamdir) => {
+ if get_host_os() != "windows" {
+ let titanfall2_steamid: u32 = TITANFALL2_STEAM_ID.parse().unwrap();
+ match steamdir.compat_tool(&titanfall2_steamid) {
+ Some(compat) => {
+ if !compat
+ .name
+ .clone()
+ .unwrap()
+ .to_ascii_lowercase()
+ .contains("northstarproton")
+ {
+ return Err(
+ "Titanfall2 was not configured to use NorthstarProton".to_string()
+ );
+ }
+ }
+ None => {
+ return Err(
+ "Titanfall2 was not configured to use a compatibility tool".to_string()
+ );
+ }
+ }
+ }
+ }
+ None => {
+ return Err("Couldn't access Titanfall2 directory".to_string());
+ }
}
- false
+
+ // Switch to Titanfall2 directory to set everything up
+ if std::env::set_current_dir(game_install.game_path.clone()).is_err() {
+ // We failed to get to Titanfall2 directory
+ return Err("Couldn't access Titanfall2 directory".to_string());
+ }
+
+ let run_northstar = "run_northstar.txt";
+ let run_northstar_bak = "run_northstar.txt.bak";
+
+ if Path::new(run_northstar).exists() {
+ // rename should ovewrite existing files
+ fs::rename(run_northstar, run_northstar_bak).unwrap();
+ }
+
+ // Passing arguments gives users a prompt, so we use run_northstar.txt
+ fs::write(run_northstar, b"1").unwrap();
+
+ let retval = match open::that(format!("steam://run/{}/", TITANFALL2_STEAM_ID)) {
+ Ok(()) => Ok("Started game".to_string()),
+ Err(_err) => Err("Failed to launch Titanfall 2 via Steam".to_string()),
+ };
+
+ let is_err = retval.is_err();
+
+ // Handle the rest in the backround
+ tauri::async_runtime::spawn(async move {
+ // Starting the EA app and Titanfall might take a good minute or three
+ let mut wait_countdown = 60 * 3;
+ while wait_countdown > 0 && !check_northstar_running() && !is_err {
+ sleep(Duration::from_millis(1000)).await;
+ wait_countdown -= 1;
+ }
+
+ // Northstar may be running, but it may not have loaded the file yet
+ sleep(Duration::from_millis(2000)).await;
+
+ // intentionally ignore Result
+ let _ = fs::remove_file(run_northstar);
+
+ if Path::new(run_northstar_bak).exists() {
+ fs::rename(run_northstar_bak, run_northstar).unwrap();
+ }
+ });
+
+ retval
+}
+
+pub fn check_origin_running() -> bool {
+ let s = sysinfo::System::new_all();
+ let x = s.processes_by_name("Origin.exe").next().is_some()
+ || s.processes_by_name("EADesktop.exe").next().is_some();
+ x
}
/// Checks if Northstar process is running
pub fn check_northstar_running() -> bool {
let s = sysinfo::System::new_all();
- for _process in s.processes_by_name("NorthstarLauncher.exe") {
- // check here if this is your process
- // dbg!(process);
- return true;
- }
- false
+ let x = s
+ .processes_by_name("NorthstarLauncher.exe")
+ .next()
+ .is_some()
+ || s.processes_by_name("Titanfall2.exe").next().is_some();
+ x
}
/// Helps with converting release candidate numbers which are different on Thunderstore
@@ -364,7 +516,7 @@ pub fn convert_release_candidate_number(version_number: String) -> String {
}
/// Returns a serde json object of the parsed `enabledmods.json` file
-pub fn get_enabled_mods(game_install: GameInstall) -> Result<serde_json::value::Value, String> {
+pub fn get_enabled_mods(game_install: &GameInstall) -> Result<serde_json::value::Value, String> {
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
// Check for JSON file
@@ -381,7 +533,7 @@ pub fn get_enabled_mods(game_install: GameInstall) -> Result<serde_json::value::
// Parse JSON
let res: serde_json::Value = match serde_json::from_str(&data) {
Ok(result) => result,
- Err(err) => return Err(format!("Failed to read JSON due to: {}", err.to_string())),
+ Err(err) => return Err(format!("Failed to read JSON due to: {}", err)),
};
// Return parsed data
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 50e439c8..3aa17459 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -10,15 +10,18 @@ use std::{
};
use app::{
- constants::{APP_USER_AGENT, MASTER_SERVER_URL, SERVER_BROWSER_ENDPOINT},
+ constants::{APP_USER_AGENT, MASTER_SERVER_URL, REFRESH_DELAY, SERVER_BROWSER_ENDPOINT},
*,
};
mod github;
-use github::pull_requests::{apply_launcher_pr, apply_mods_pr, get_pull_requests_wrapper};
+use github::pull_requests::{
+ apply_launcher_pr, apply_mods_pr, get_launcher_download_link, get_pull_requests_wrapper,
+};
use github::release_notes::{
check_is_flightcore_outdated, get_newest_flightcore_version, get_northstar_release_notes,
};
+use github::{compare_tags, get_list_of_tags};
mod repair_and_verify;
use repair_and_verify::{
@@ -38,13 +41,20 @@ mod thunderstore;
use thunderstore::query_thunderstore_packages_api;
use tauri::{Manager, Runtime};
-use tauri_plugin_store::PluginBuilder;
use tokio::time::sleep;
#[derive(Default)]
struct Counter(Arc<Mutex<i32>>);
fn main() {
+ // Setup logger
+ let mut log_builder = pretty_env_logger::formatted_builder();
+ log_builder.parse_filters("info");
+ let logger = sentry_log::SentryLogger::with_dest(log_builder.build());
+
+ log::set_boxed_logger(Box::new(logger)).unwrap();
+ log::set_max_level(log::LevelFilter::Info);
+
// Only enable Sentry crash logs on release
#[cfg(not(debug_assertions))]
let _guard = sentry::init((
@@ -57,7 +67,7 @@ fn main() {
));
tauri::Builder::default()
- .plugin(PluginBuilder::default().build())
+ .plugin(tauri_plugin_store::Builder::default().build())
.setup(|app| {
let app_handle = app.app_handle();
tauri::async_runtime::spawn(async move {
@@ -86,6 +96,17 @@ fn main() {
}
});
+ // Emit updated player and server count to GUI
+ let app_handle = app.app_handle();
+ tauri::async_runtime::spawn(async move {
+ loop {
+ sleep(REFRESH_DELAY).await;
+ app_handle
+ .emit_all("northstar-statistics", get_server_player_count().await)
+ .unwrap();
+ }
+ });
+
Ok(())
})
.manage(Counter(Default::default()))
@@ -100,6 +121,7 @@ fn main() {
install_northstar_caller,
update_northstar_caller,
launch_northstar_caller,
+ launch_northstar_steam_caller,
check_is_flightcore_outdated_caller,
get_log_list,
verify_game_files,
@@ -117,9 +139,12 @@ fn main() {
delete_thunderstore_mod,
open_repair_window,
query_thunderstore_packages_api,
+ get_list_of_tags,
+ compare_tags,
get_pull_requests_wrapper,
apply_launcher_pr,
apply_mods_pr,
+ get_launcher_download_link,
close_application,
])
.run(tauri::generate_context!())
@@ -142,7 +167,7 @@ fn force_panic() {
#[tauri::command]
/// Returns true if built in debug mode
async fn is_debug_mode() -> bool {
- return cfg!(debug_assertions);
+ cfg!(debug_assertions)
}
#[tauri::command]
@@ -177,7 +202,7 @@ async fn get_flightcore_version_number() -> String {
#[tauri::command]
async fn get_northstar_version_number_caller(game_path: String) -> Result<String, String> {
- match get_northstar_version_number(game_path) {
+ match get_northstar_version_number(&game_path) {
Ok(version_number) => Ok(version_number),
Err(err) => Err(err.to_string()),
}
@@ -209,10 +234,10 @@ async fn check_is_northstar_outdated(
.expect("Couldn't find Northstar on thunderstore???");
// .ok_or_else(|| anyhow!("Couldn't find Northstar on thunderstore???"))?;
- let version_number = match get_northstar_version_number(game_path) {
+ let version_number = match get_northstar_version_number(&game_path) {
Ok(version_number) => version_number,
Err(err) => {
- println!("{}", err);
+ log::warn!("{}", err);
// If we fail to get new version just assume we are up-to-date
return Err(err.to_string());
}
@@ -222,10 +247,10 @@ async fn check_is_northstar_outdated(
let version_number = convert_release_candidate_number(version_number);
if version_number != nmod.latest {
- println!("Installed Northstar version outdated");
+ log::info!("Installed Northstar version outdated");
Ok(true)
} else {
- println!("Installed Northstar version up-to-date");
+ log::info!("Installed Northstar version up-to-date");
Ok(false)
}
}
@@ -244,7 +269,7 @@ async fn verify_install_location(game_path: String) -> bool {
match check_is_valid_game_path(&game_path) {
Ok(()) => true,
Err(err) => {
- println!("{}", err);
+ log::warn!("{}", err);
false
}
}
@@ -259,15 +284,16 @@ async fn get_host_os_caller() -> String {
#[tauri::command]
/// Installs Northstar to the given path
async fn install_northstar_caller(
+ window: tauri::Window,
game_path: String,
northstar_package_name: Option<String>,
) -> Result<bool, String> {
- println!("Running");
- match install_northstar(&game_path, northstar_package_name).await {
+ log::info!("Running");
+ match install_northstar(window, &game_path, northstar_package_name).await {
Ok(_) => Ok(true),
Err(err) => {
- println!("{}", err);
- Err(err.to_string())
+ log::error!("{}", err);
+ Err(err)
}
}
}
@@ -275,17 +301,18 @@ async fn install_northstar_caller(
#[tauri::command]
/// Update Northstar install in the given path
async fn update_northstar_caller(
+ window: tauri::Window,
game_path: String,
northstar_package_name: Option<String>,
) -> Result<bool, String> {
- println!("Updating");
+ log::info!("Updating Northstar");
// Simply re-run install with up-to-date version for upate
- match install_northstar(&game_path, northstar_package_name).await {
+ match install_northstar(window, &game_path, northstar_package_name).await {
Ok(_) => Ok(true),
Err(err) => {
- println!("{}", err);
- Err(err.to_string())
+ log::error!("{}", err);
+ Err(err)
}
}
}
@@ -296,7 +323,16 @@ async fn launch_northstar_caller(
game_install: GameInstall,
bypass_checks: Option<bool>,
) -> Result<String, String> {
- launch_northstar(game_install, bypass_checks)
+ launch_northstar(&game_install, bypass_checks)
+}
+
+#[tauri::command]
+/// Launches Northstar
+async fn launch_northstar_steam_caller(
+ game_install: GameInstall,
+ bypass_checks: Option<bool>,
+) -> Result<String, String> {
+ launch_northstar_steam(&game_install, bypass_checks)
}
#[tauri::command]
@@ -305,11 +341,11 @@ async fn install_mod_caller(
game_install: GameInstall,
thunderstore_mod_string: String,
) -> Result<(), String> {
- fc_download_mod_and_install(game_install.clone(), thunderstore_mod_string).await?;
- match clean_up_download_folder(game_install, false) {
+ fc_download_mod_and_install(&game_install, &thunderstore_mod_string).await?;
+ match clean_up_download_folder(&game_install, false) {
Ok(()) => Ok(()),
Err(err) => {
- println!("Failed to delete download folder due to {}", err);
+ log::info!("Failed to delete download folder due to {}", err);
// Failure to delete download folder is not an error in mod install
// As such ignore. User can still force delete if need be
Ok(())
@@ -323,7 +359,7 @@ async fn clean_up_download_folder_caller(
game_install: GameInstall,
force: bool,
) -> Result<(), String> {
- match clean_up_download_folder(game_install, force) {
+ match clean_up_download_folder(&game_install, force) {
Ok(()) => Ok(()),
Err(err) => Err(err.to_string()),
}
@@ -353,7 +389,8 @@ async fn get_server_player_count() -> Result<(i32, usize), String> {
// Sum up player count
let total_player_count: i32 = ns_servers.iter().map(|server| server.player_count).sum();
- dbg!((total_player_count, server_count));
+ log::info!("total_player_count: {}", total_player_count);
+ log::info!("server_count: {}", server_count);
Ok((total_player_count, server_count))
}
diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs
index d2b5040b..728e72c0 100644
--- a/src-tauri/src/mod_management/mod.rs
+++ b/src-tauri/src/mod_management/mod.rs
@@ -12,8 +12,6 @@ use std::path::PathBuf;
use app::get_enabled_mods;
use app::GameInstall;
-use json5;
-
#[derive(Debug, Clone)]
struct ParsedThunderstoreModString {
author_name: String,
@@ -25,7 +23,7 @@ impl std::str::FromStr for ParsedThunderstoreModString {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let mut parts = s.split("-");
+ let mut parts = s.split('-');
let author_name = parts.next().unwrap().to_string();
let mod_name = parts.next().unwrap().to_string();
@@ -56,9 +54,9 @@ pub struct ModJson {
}
/// Gets all currently installed and enabled/disabled mods to rebuild `enabledmods.json`
-pub fn rebuild_enabled_mods_json(game_install: GameInstall) -> Result<(), String> {
+pub fn rebuild_enabled_mods_json(game_install: &GameInstall) -> Result<(), String> {
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
- let mods_and_properties = get_installed_mods_and_properties(game_install)?;
+ let mods_and_properties = get_installed_mods_and_properties(game_install.clone())?;
// Create new mapping
let mut my_map = serde_json::Map::new();
@@ -91,28 +89,27 @@ pub fn set_mod_enabled_status(
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
// Parse JSON
- let mut res: serde_json::Value = match get_enabled_mods(game_install.clone()) {
+ let mut res: serde_json::Value = match get_enabled_mods(&game_install) {
Ok(res) => res,
Err(err) => {
- println!("Couldn't parse `enabledmod.json`: {}", err);
- println!("Rebuilding file.");
+ log::warn!("Couldn't parse `enabledmod.json`: {}", err);
+ log::warn!("Rebuilding file.");
- rebuild_enabled_mods_json(game_install.clone())?;
+ rebuild_enabled_mods_json(&game_install)?;
// Then try again
- let res = get_enabled_mods(game_install.clone())?;
- res
+ get_enabled_mods(&game_install)?
}
};
// Check if key exists
if res.get(mod_name.clone()).is_none() {
// If it doesn't exist, rebuild `enabledmod.json`
- println!("Value not found in `enabledmod.json`. Rebuilding file");
- rebuild_enabled_mods_json(game_install.clone())?;
+ log::info!("Value not found in `enabledmod.json`. Rebuilding file");
+ rebuild_enabled_mods_json(&game_install)?;
// Then try again
- res = get_enabled_mods(game_install)?;
+ res = get_enabled_mods(&game_install)?;
}
// Update value
@@ -129,7 +126,7 @@ pub fn set_mod_enabled_status(
}
/// Parses `manifest.json` for Thunderstore mod string
-fn parse_for_thunderstore_mod_string(nsmod_path: String) -> Result<String, anyhow::Error> {
+fn parse_for_thunderstore_mod_string(nsmod_path: &str) -> Result<String, anyhow::Error> {
let manifest_json_path = format!("{}/manifest.json", nsmod_path);
let ts_author_txt_path = format!("{}/thunderstore_author.txt", nsmod_path);
@@ -152,7 +149,7 @@ fn parse_for_thunderstore_mod_string(nsmod_path: String) -> Result<String, anyho
}
/// Parse `mods` folder for installed mods.
-fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>, anyhow::Error> {
+fn parse_installed_mods(game_install: &GameInstall) -> Result<Vec<NorthstarMod>, anyhow::Error> {
let ns_mods_folder = format!("{}/R2Northstar/mods/", game_install.game_path);
let paths = match std::fs::read_dir(ns_mods_folder) {
@@ -189,7 +186,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
let parsed_mod_json: ModJson = match json5::from_str(&data) {
Ok(parsed_json) => parsed_json,
Err(err) => {
- println!("Failed parsing {} with {}", mod_json_path, err.to_string());
+ log::warn!("Failed parsing {} with {}", mod_json_path, err.to_string());
continue;
}
};
@@ -198,7 +195,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
// Attempt legacy method for getting Thunderstore string first
Some(ts_mod_string) => Some(ts_mod_string),
// Legacy method failed
- None => match parse_for_thunderstore_mod_string(directory_str) {
+ None => match parse_for_thunderstore_mod_string(&directory_str) {
Ok(thunderstore_mod_string) => Some(thunderstore_mod_string),
Err(_err) => None,
},
@@ -209,7 +206,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
let ns_mod = NorthstarMod {
name: parsed_mod_json.name,
version: parsed_mod_json.version,
- thunderstore_mod_string: thunderstore_mod_string,
+ thunderstore_mod_string,
enabled: false, // Placeholder
directory: mod_directory,
};
@@ -229,13 +226,13 @@ pub fn get_installed_mods_and_properties(
game_install: GameInstall,
) -> Result<Vec<NorthstarMod>, String> {
// Get actually installed mods
- let found_installed_mods = match parse_installed_mods(game_install.clone()) {
+ let found_installed_mods = match parse_installed_mods(&game_install) {
Ok(res) => res,
Err(err) => return Err(err.to_string()),
};
// Get enabled mods as JSON
- let enabled_mods: serde_json::Value = match get_enabled_mods(game_install) {
+ let enabled_mods: serde_json::Value = match get_enabled_mods(&game_install) {
Ok(enabled_mods) => enabled_mods,
Err(_) => serde_json::from_str("{}").unwrap(), // `enabledmods.json` not found, create empty object
};
@@ -256,7 +253,7 @@ pub fn get_installed_mods_and_properties(
Ok(installed_mods)
}
-async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<String, String> {
+async fn get_ns_mod_download_url(thunderstore_mod_string: &str) -> Result<String, String> {
// TODO: This will crash the thread if not internet connection exist. `match` should be used instead
let index = thermite::api::get_package_index().unwrap().to_vec();
@@ -276,7 +273,7 @@ async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<Stri
for ns_mod in index {
// Iterate over all versions of a given mod
- for (_key, ns_mod) in &ns_mod.versions {
+ for ns_mod in ns_mod.versions.values() {
if ns_mod.url.contains(&ts_mod_string_url) {
dbg!(ns_mod.clone());
return Ok(ns_mod.url.clone());
@@ -288,21 +285,19 @@ async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<Stri
}
/// Returns a vector of modstrings containing the dependencies of a given mod
-async fn get_mod_dependencies(
- thunderstore_mod_string: String,
-) -> Result<Vec<String>, anyhow::Error> {
- dbg!(thunderstore_mod_string.clone());
+async fn get_mod_dependencies(thunderstore_mod_string: &str) -> Result<Vec<String>, anyhow::Error> {
+ dbg!(thunderstore_mod_string);
// TODO: This will crash the thread if not internet connection exist. `match` should be used instead
let index = thermite::api::get_package_index().unwrap().to_vec();
// String replace works but more care should be taken in the future
- let ts_mod_string_url = thunderstore_mod_string.replace("-", "/");
+ let ts_mod_string_url = thunderstore_mod_string.replace('-', "/");
// Iterate over index
for ns_mod in index {
// Iterate over all versions of a given mod
- for (_key, ns_mod) in &ns_mod.versions {
+ for ns_mod in ns_mod.versions.values() {
if ns_mod.url.contains(&ts_mod_string_url) {
dbg!(ns_mod.clone());
return Ok(ns_mod.deps.clone());
@@ -317,8 +312,8 @@ async fn get_mod_dependencies(
/// Download and install mod to the specified target.
#[async_recursion]
pub async fn fc_download_mod_and_install(
- game_install: GameInstall,
- thunderstore_mod_string: String,
+ game_install: &GameInstall,
+ thunderstore_mod_string: &str,
) -> Result<(), String> {
// Get mods and download directories
let download_directory = format!(
@@ -328,11 +323,11 @@ pub async fn fc_download_mod_and_install(
let mods_directory = format!("{}/R2Northstar/mods/", game_install.game_path);
// Early return on empty string
- if thunderstore_mod_string.len() == 0 {
+ if thunderstore_mod_string.is_empty() {
return Err("Passed empty string".to_string());
}
- let deps = match get_mod_dependencies(thunderstore_mod_string.clone()).await {
+ let deps = match get_mod_dependencies(thunderstore_mod_string).await {
Ok(deps) => deps,
Err(err) => return Err(err.to_string()),
};
@@ -340,13 +335,13 @@ pub async fn fc_download_mod_and_install(
// Recursively install dependencies
for dep in deps {
- match fc_download_mod_and_install(game_install.clone(), dep).await {
+ match fc_download_mod_and_install(game_install, &dep).await {
Ok(()) => (),
Err(err) => {
- if err.to_string() == "Cannot install Northstar as a mod!" {
+ if err == "Cannot install Northstar as a mod!" {
continue; // For Northstar as a dependency, we just skip it
} else {
- return Err(err.to_string());
+ return Err(err);
}
}
};
@@ -361,7 +356,7 @@ pub async fn fc_download_mod_and_install(
}
// Get download URL for the specified mod
- let download_url = get_ns_mod_download_url(thunderstore_mod_string.clone()).await?;
+ let download_url = get_ns_mod_download_url(thunderstore_mod_string).await?;
// Create download directory
match std::fs::create_dir_all(download_directory.clone()) {
@@ -369,20 +364,19 @@ pub async fn fc_download_mod_and_install(
Err(err) => return Err(err.to_string()),
};
- let name = thunderstore_mod_string.clone();
let path = format!(
- "{}/___flightcore-temp-download-dir/{}.zip",
- game_install.game_path, name
+ "{}/___flightcore-temp-download-dir/{thunderstore_mod_string}.zip",
+ game_install.game_path
);
// Download the mod
- let f = match thermite::core::manage::download_file(&download_url, path.clone()) {
+ let f = match thermite::core::manage::download_file(download_url, path.clone()) {
Ok(f) => f,
Err(e) => return Err(e.to_string()),
};
// Get Thunderstore mod author
- let author = thunderstore_mod_string.split("-").next().unwrap();
+ let author = thunderstore_mod_string.split('-').next().unwrap();
// Extract the mod to the mods directory
match thermite::core::manage::install_mod(author, &f, std::path::Path::new(&mods_directory)) {
@@ -397,7 +391,7 @@ pub async fn fc_download_mod_and_install(
}
/// Deletes a given Northstar mod folder
-fn delete_mod_folder(ns_mod_directory: String) -> Result<(), String> {
+fn delete_mod_folder(ns_mod_directory: &str) -> Result<(), String> {
let ns_mod_dir_path = std::path::Path::new(&ns_mod_directory);
// Safety check: Check whether `mod.json` exists and exit early if not
@@ -408,7 +402,7 @@ fn delete_mod_folder(ns_mod_directory: String) -> Result<(), String> {
return Err(format!("mod.json does not exist in {}", ns_mod_directory));
}
- match std::fs::remove_dir_all(&ns_mod_directory) {
+ match std::fs::remove_dir_all(ns_mod_directory) {
Ok(()) => Ok(()),
Err(err) => Err(format!("Failed deleting mod: {err}")),
}
@@ -432,7 +426,7 @@ pub fn delete_northstar_mod(game_install: GameInstall, nsmod_name: String) -> Re
// Installed mod matches specified mod
if installed_ns_mod.name == nsmod_name {
// Delete folder
- return delete_mod_folder(installed_ns_mod.directory);
+ return delete_mod_folder(&installed_ns_mod.directory);
}
}
@@ -483,7 +477,7 @@ pub fn delete_thunderstore_mod(
}
}
- if !(mod_folders_to_remove.len() > 0) {
+ if mod_folders_to_remove.is_empty() {
return Err(format!(
"No mods removed as no Northstar mods matching {thunderstore_mod_string} were found to be installed."
));
@@ -491,7 +485,7 @@ pub fn delete_thunderstore_mod(
// Delete given folders
for mod_folder in mod_folders_to_remove {
- delete_mod_folder(mod_folder)?;
+ delete_mod_folder(&mod_folder)?;
}
Ok(())
diff --git a/src-tauri/src/northstar/mod.rs b/src-tauri/src/northstar/mod.rs
index a043632c..7bd0b0a3 100644
--- a/src-tauri/src/northstar/mod.rs
+++ b/src-tauri/src/northstar/mod.rs
@@ -5,25 +5,23 @@ use crate::{check_mod_version_number, constants::CORE_MODS};
use anyhow::anyhow;
/// Returns the current Northstar version number as a string
-pub fn get_northstar_version_number(game_path: String) -> Result<String, anyhow::Error> {
- println!("{}", game_path);
- // println!("{:?}", install_type);
+pub fn get_northstar_version_number(game_path: &str) -> Result<String, anyhow::Error> {
+ log::info!("{}", game_path);
// TODO:
// Check if NorthstarLauncher.exe exists and check its version number
let profile_folder = "R2Northstar";
- let initial_version_number = match check_mod_version_number(format!(
- "{}/{}/mods/{}",
- game_path, profile_folder, CORE_MODS[0]
+ let initial_version_number = match check_mod_version_number(&format!(
+ "{game_path}/{profile_folder}/mods/{}",
+ CORE_MODS[0]
)) {
Ok(version_number) => version_number,
Err(err) => return Err(err),
};
for core_mod in CORE_MODS {
- let current_version_number = match check_mod_version_number(format!(
- "{}/{}/mods/{}",
- game_path, profile_folder, core_mod
+ let current_version_number = match check_mod_version_number(&format!(
+ "{game_path}/{profile_folder}/mods/{core_mod}",
)) {
Ok(version_number) => version_number,
Err(err) => return Err(err),
@@ -33,7 +31,7 @@ pub fn get_northstar_version_number(game_path: String) -> Result<String, anyhow:
return Err(anyhow!("Found version number mismatch"));
}
}
- println!("All mods same version");
+ log::info!("All mods same version");
Ok(initial_version_number)
}
diff --git a/src-tauri/src/platform_specific/linux.rs b/src-tauri/src/platform_specific/linux.rs
index eb9fbea6..4b9964e9 100644
--- a/src-tauri/src/platform_specific/linux.rs
+++ b/src-tauri/src/platform_specific/linux.rs
@@ -14,10 +14,10 @@ pub fn check_glibc_v() -> f32 {
let lddvl: Vec<&str> = lddva.split('\n').collect();
let lddvlo = &lddvl[0];
let reg = Regex::new(r"(2.\d{2}$)").unwrap();
- for caps in reg.captures_iter(lddvlo) {
+ if let Some(caps) = reg.captures_iter(lddvlo).next() {
return caps.get(1).unwrap().as_str().parse::<f32>().unwrap(); // theres prolly a better way ijdk how tho
}
- return 0.0; // this shouldnt ever be reached but it has to be here
+ 0.0 // this shouldnt ever be reached but it has to be here
}
/*
diff --git a/src-tauri/src/platform_specific/windows.rs b/src-tauri/src/platform_specific/windows.rs
index 21772b91..004beb6e 100644
--- a/src-tauri/src/platform_specific/windows.rs
+++ b/src-tauri/src/platform_specific/windows.rs
@@ -3,7 +3,7 @@ use anyhow::{anyhow, Result};
use crate::{check_is_valid_game_path, constants::TITANFALL2_ORIGIN_IDS};
-/// Runs a powershell command and parses output to get Titanfall2 install location on Origin
+/// Gets Titanfall2 install location on Origin
pub fn origin_install_location_detection() -> Result<String, anyhow::Error> {
// Iterate over known Titanfall2 Origin IDs
for origin_id in TITANFALL2_ORIGIN_IDS {
@@ -18,14 +18,14 @@ pub fn origin_install_location_detection() -> Result<String, anyhow::Error> {
return Ok(game_path_str.to_string());
}
Err(err) => {
- println!("{}", err.to_string());
+ log::warn!("{}", err);
continue; // Not a valid game path
}
}
}
}
Err(err) => {
- println!("Couldn't find {origin_id}: {err}")
+ log::warn!("Couldn't find {origin_id}: {err}")
}
}
}
diff --git a/src-tauri/src/repair_and_verify/mod.rs b/src-tauri/src/repair_and_verify/mod.rs
index 9d020f15..ecf5e1ec 100644
--- a/src-tauri/src/repair_and_verify/mod.rs
+++ b/src-tauri/src/repair_and_verify/mod.rs
@@ -15,9 +15,9 @@ pub fn verify_game_files(game_install: GameInstall) -> Result<String, String> {
#[tauri::command]
pub fn disable_all_but_core(game_install: GameInstall) -> Result<(), String> {
// Rebuild `enabledmods.json` first to ensure all mods are added
- rebuild_enabled_mods_json(game_install.clone())?;
+ rebuild_enabled_mods_json(&game_install)?;
- let current_mods = get_enabled_mods(game_install.clone())?;
+ let current_mods = get_enabled_mods(&game_install)?;
// Disable all mods, set core mods to enabled
for (key, _value) in current_mods.as_object().unwrap() {
@@ -37,7 +37,7 @@ pub fn disable_all_but_core(game_install: GameInstall) -> Result<(), String> {
/// If `force` is FALSE, bails on non-empty folder
/// If `force` is TRUE, deletes folder even if non-empty
pub fn clean_up_download_folder(
- game_install: GameInstall,
+ game_install: &GameInstall,
force: bool,
) -> Result<(), anyhow::Error> {
// Get download directory
@@ -51,7 +51,7 @@ pub fn clean_up_download_folder(
// dbg!(download_dir_contents);
let mut count = 0;
- download_dir_contents.inspect(|_| count += 1).for_each(drop);
+ download_dir_contents.for_each(|_| count += 1);
if count > 0 && !force {
return Err(anyhow!("Folder not empty, not deleting"));
@@ -84,7 +84,7 @@ pub fn get_log_list(game_install: GameInstall) -> Result<Vec<std::path::PathBuf>
}
}
- if log_files.len() > 0 {
+ if !log_files.is_empty() {
Ok(log_files)
} else {
Err("No logs found".to_string())
diff --git a/src-tauri/src/thunderstore/mod.rs b/src-tauri/src/thunderstore/mod.rs
index 9151ba7c..e9eb30d7 100644
--- a/src-tauri/src/thunderstore/mod.rs
+++ b/src-tauri/src/thunderstore/mod.rs
@@ -43,7 +43,7 @@ pub struct ThunderstoreModVersion {
/// Queries Thunderstore packages API
#[tauri::command]
pub async fn query_thunderstore_packages_api() -> Result<Vec<ThunderstoreMod>, String> {
- println!("Fetching Thunderstore API");
+ log::info!("Fetching Thunderstore API");
// Fetches
let url = "https://northstar.thunderstore.io/api/v1/package/";
@@ -66,7 +66,7 @@ pub async fn query_thunderstore_packages_api() -> Result<Vec<ThunderstoreMod>, S
};
// Remove some mods from listing
- let to_remove_set: HashSet<&str> = BLACKLISTED_MODS.iter().map(|s| s.as_ref()).collect();
+ let to_remove_set: HashSet<&str> = BLACKLISTED_MODS.iter().copied().collect();
let filtered_packages = parsed_json
.into_iter()
.filter(|package| !to_remove_set.contains(&package.full_name.as_ref()))
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 77a10d73..c24251e1 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -8,7 +8,7 @@
},
"package": {
"productName": "FlightCore",
- "version": "1.10.2"
+ "version": "1.13.2"
},
"tauri": {
"allowlist": {
diff --git a/src-vue/package-lock.json b/src-vue/package-lock.json
index bf311673..31cee6e0 100644
--- a/src-vue/package-lock.json
+++ b/src-vue/package-lock.json
@@ -1,7 +1,7 @@
{
"name": "src-vue",
"version": "0.0.0",
- "lockfileVersion": 3,
+ "lockfileVersion": 2,
"requires": true,
"packages": {
"": {
@@ -11,8 +11,9 @@
"@element-plus/icons-vue": "^2.0.9",
"element-plus": "^2.2.17",
"marked": "^4.1.1",
- "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#dev",
+ "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#9bd993aa67766596638bbfd91e79a1bf8f632014",
"vue": "^3.2.37",
+ "vue-i18n": "^9.2.2",
"vue-router": "^4.1.5",
"vuex": "^4.0.2"
},
@@ -25,9 +26,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.21.2",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz",
- "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==",
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
+ "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -36,9 +37,9 @@
}
},
"node_modules/@ctrl/tinycolor": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
- "integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==",
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
+ "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==",
"engines": {
"node": ">=10"
}
@@ -51,49 +52,74 @@
"vue": "^3.2.0"
}
},
- "node_modules/@esbuild/android-arm": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
- "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
+ "node_modules/@floating-ui/core": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.2.tgz",
+ "integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg=="
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.7.tgz",
+ "integrity": "sha512-6RsqvCYe0AYWtsGvuWqCm7mZytnXAZCjWtsWu1Kg8dI3INvj/DbKlDsZO+mKSaQdPT12uxIW9W2dAWJkPx4Y5g==",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.2"
+ }
+ },
+ "node_modules/@intlify/core-base": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
+ "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+ "dependencies": {
+ "@intlify/devtools-if": "9.2.2",
+ "@intlify/message-compiler": "9.2.2",
+ "@intlify/shared": "9.2.2",
+ "@intlify/vue-devtools": "9.2.2"
+ },
"engines": {
- "node": ">=12"
+ "node": ">= 14"
}
},
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
- "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
+ "node_modules/@intlify/devtools-if": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+ "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+ "dependencies": {
+ "@intlify/shared": "9.2.2"
+ },
"engines": {
- "node": ">=12"
+ "node": ">= 14"
}
},
- "node_modules/@floating-ui/core": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
- "integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
+ "node_modules/@intlify/message-compiler": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+ "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+ "dependencies": {
+ "@intlify/shared": "9.2.2",
+ "source-map": "0.6.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
},
- "node_modules/@floating-ui/dom": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
- "integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
+ "node_modules/@intlify/shared": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
+ "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@intlify/vue-devtools": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+ "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
"dependencies": {
- "@floating-ui/core": "^1.2.1"
+ "@intlify/core-base": "9.2.2",
+ "@intlify/shared": "9.2.2"
+ },
+ "engines": {
+ "node": ">= 14"
}
},
"node_modules/@popperjs/core": {
@@ -107,11 +133,11 @@
}
},
"node_modules/@tauri-apps/api": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.2.0.tgz",
- "integrity": "sha512-lsI54KI6HGf7VImuf/T9pnoejfgkNoXveP14pVV7XarrQ46rOejIVJLFqHI9sRReJMGdh2YuCoI3cc/yCWCsrw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.1.0.tgz",
+ "integrity": "sha512-n13pIqdPd3KtaMmmAcrU7BTfdMtIlGNnfZD0dNX8L4p8dgmuNyikm6JAA+yCpl9gqq6I8x5cV2Y0muqdgD0cWw==",
"engines": {
- "node": ">= 14.6.0",
+ "node": ">= 12.22.0",
"npm": ">= 6.6.0",
"yarn": ">= 1.19.1"
},
@@ -134,9 +160,9 @@
}
},
"node_modules/@types/marked": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz",
- "integrity": "sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==",
"dev": true
},
"node_modules/@types/web-bluetooth": {
@@ -158,90 +184,91 @@
}
},
"node_modules/@volar/language-core": {
- "version": "1.3.0-alpha.0",
- "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.3.0-alpha.0.tgz",
- "integrity": "sha512-W3uMzecHPcbwddPu4SJpUcPakRBK/y/BP+U0U6NiPpUX1tONLC4yCawt+QBJqtgJ+sfD6ztf5PyvPL3hQRqfOA==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.0.11.tgz",
+ "integrity": "sha512-YwUYKxIyDc+Fq3kQ6BGGfkrKCG5JzE2Yr6vMxrxEXW2rg/gsq3JgMk/4sI8ybRsaTirhCB4V8+AIVYsvcRxgig==",
"dev": true,
"dependencies": {
- "@volar/source-map": "1.3.0-alpha.0"
+ "@volar/source-map": "1.0.11",
+ "@vue/reactivity": "^3.2.45",
+ "muggle-string": "^0.1.0"
}
},
"node_modules/@volar/source-map": {
- "version": "1.3.0-alpha.0",
- "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.3.0-alpha.0.tgz",
- "integrity": "sha512-jSdizxWFvDTvkPYZnO6ew3sBZUnS0abKCbuopkc0JrIlFbznWC/fPH3iPFIMS8/IIkRxq1Jh9VVG60SmtsdaMQ==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.0.11.tgz",
+ "integrity": "sha512-tkuV9MD+OuiZfHA0qZXrPdW6F7TvnpnuTan6Qe7UGUs9+sflezlMJdjaYdGgQObfP+06pcT1E3xdkOoi08ZyyQ==",
"dev": true,
"dependencies": {
- "muggle-string": "^0.2.2"
+ "muggle-string": "^0.1.0"
}
},
"node_modules/@volar/typescript": {
- "version": "1.3.0-alpha.0",
- "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.3.0-alpha.0.tgz",
- "integrity": "sha512-5UItyW2cdH2mBLu4RrECRNJRgtvvzKrSCn2y3v/D61QwIDkGx4aeil6x8RFuUL5TFtV6QvVHXnsOHxNgd+sCow==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.0.11.tgz",
+ "integrity": "sha512-mq7wDDAs0Eb43jev2FxbowuiwWqvL3kb+tar1we8VQbdabpyQ5dmbWPwo/IglevMmW3SKo1Et+6rqAeZpXNnPQ==",
"dev": true,
"dependencies": {
- "@volar/language-core": "1.3.0-alpha.0"
+ "@volar/language-core": "1.0.11"
}
},
"node_modules/@volar/vue-language-core": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@volar/vue-language-core/-/vue-language-core-1.2.0.tgz",
- "integrity": "sha512-w7yEiaITh2WzKe6u8ZdeLKCUz43wdmY/OqAmsB/PGDvvhTcVhCJ6f0W/RprZL1IhqH8wALoWiwEh/Wer7ZviMQ==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz",
+ "integrity": "sha512-A3ODs0/ua7BcpSSnE7KtO8bzWsYsbOJRyW2Q/2uktxlfHj8srln3JdgK/mNlIgfnWtACbE5K+EfMJOgJKv864A==",
"dev": true,
"dependencies": {
- "@volar/language-core": "1.3.0-alpha.0",
- "@volar/source-map": "1.3.0-alpha.0",
- "@vue/compiler-dom": "^3.2.47",
- "@vue/compiler-sfc": "^3.2.47",
- "@vue/reactivity": "^3.2.47",
- "@vue/shared": "^3.2.47",
- "minimatch": "^6.1.6",
- "muggle-string": "^0.2.2",
+ "@volar/language-core": "1.0.11",
+ "@volar/source-map": "1.0.11",
+ "@vue/compiler-dom": "^3.2.45",
+ "@vue/compiler-sfc": "^3.2.45",
+ "@vue/reactivity": "^3.2.45",
+ "@vue/shared": "^3.2.45",
+ "minimatch": "^5.1.0",
"vue-template-compiler": "^2.7.14"
}
},
"node_modules/@volar/vue-typescript": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@volar/vue-typescript/-/vue-typescript-1.2.0.tgz",
- "integrity": "sha512-zjmRi9y3J1EkG+pfuHp8IbHmibihrKK485cfzsHjiuvJMGrpkWvlO5WVEk8oslMxxeGC5XwBFE9AOlvh378EPA==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz",
+ "integrity": "sha512-jlnFPvBcTyPiAbGlgjhKK7fp3Q+Z7Z5eU1NTbTSS0lQC8Gog3sh2UxLAFG5Voe1gHIxasoOEPXzMR0CWF4bKbA==",
"dev": true,
"dependencies": {
- "@volar/typescript": "1.3.0-alpha.0",
- "@volar/vue-language-core": "1.2.0"
+ "@volar/typescript": "1.0.11",
+ "@volar/vue-language-core": "1.0.11"
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
- "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
+ "integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"dependencies": {
"@babel/parser": "^7.16.4",
- "@vue/shared": "3.2.47",
+ "@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
- "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
+ "integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"dependencies": {
- "@vue/compiler-core": "3.2.47",
- "@vue/shared": "3.2.47"
+ "@vue/compiler-core": "3.2.45",
+ "@vue/shared": "3.2.45"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
- "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
+ "integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
"dependencies": {
"@babel/parser": "^7.16.4",
- "@vue/compiler-core": "3.2.47",
- "@vue/compiler-dom": "3.2.47",
- "@vue/compiler-ssr": "3.2.47",
- "@vue/reactivity-transform": "3.2.47",
- "@vue/shared": "3.2.47",
+ "@vue/compiler-core": "3.2.45",
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/compiler-ssr": "3.2.45",
+ "@vue/reactivity-transform": "3.2.45",
+ "@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
@@ -249,83 +276,83 @@
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
- "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
+ "integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"dependencies": {
- "@vue/compiler-dom": "3.2.47",
- "@vue/shared": "3.2.47"
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/shared": "3.2.45"
}
},
"node_modules/@vue/devtools-api": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
- "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
+ "integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
},
"node_modules/@vue/reactivity": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
- "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
+ "integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
"dependencies": {
- "@vue/shared": "3.2.47"
+ "@vue/shared": "3.2.45"
}
},
"node_modules/@vue/reactivity-transform": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
- "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
+ "integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
"dependencies": {
"@babel/parser": "^7.16.4",
- "@vue/compiler-core": "3.2.47",
- "@vue/shared": "3.2.47",
+ "@vue/compiler-core": "3.2.45",
+ "@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
- "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
+ "integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
"dependencies": {
- "@vue/reactivity": "3.2.47",
- "@vue/shared": "3.2.47"
+ "@vue/reactivity": "3.2.45",
+ "@vue/shared": "3.2.45"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
- "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
+ "integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
"dependencies": {
- "@vue/runtime-core": "3.2.47",
- "@vue/shared": "3.2.47",
+ "@vue/runtime-core": "3.2.45",
+ "@vue/shared": "3.2.45",
"csstype": "^2.6.8"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
- "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
+ "integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
"dependencies": {
- "@vue/compiler-ssr": "3.2.47",
- "@vue/shared": "3.2.47"
+ "@vue/compiler-ssr": "3.2.45",
+ "@vue/shared": "3.2.45"
},
"peerDependencies": {
- "vue": "3.2.47"
+ "vue": "3.2.45"
}
},
"node_modules/@vue/shared": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
- "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
+ "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/@vueuse/core": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
- "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz",
+ "integrity": "sha512-qGUcjKQXHgN+jqXEgpeZGoxdCbIDCdVPz3QiF1uyecVGbMuM63o96I1GjYx5zskKgRI0FKSNsVWM7rwrRMTf6A==",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
- "@vueuse/metadata": "9.13.0",
- "@vueuse/shared": "9.13.0",
+ "@vueuse/metadata": "9.6.0",
+ "@vueuse/shared": "9.6.0",
"vue-demi": "*"
},
"funding": {
@@ -358,17 +385,17 @@
}
},
"node_modules/@vueuse/metadata": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
- "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.6.0.tgz",
+ "integrity": "sha512-sIC8R+kWkIdpi5X2z2Gk8TRYzmczDwHRhEFfCu2P+XW2JdPoXrziqsGpDDsN7ykBx4ilwieS7JUIweVGhvZ93w==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
- "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.6.0.tgz",
+ "integrity": "sha512-/eDchxYYhkHnFyrb00t90UfjCx94kRHxc7J1GtBCqCG4HyPMX+krV9XJgVtWIsAMaxKVU4fC8NSUviG1JkwhUQ==",
"dependencies": {
"vue-demi": "*"
},
@@ -427,9 +454,9 @@
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/dayjs": {
- "version": "1.11.7",
- "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
- "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
+ "integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
},
"node_modules/de-indent": {
"version": "1.0.2",
@@ -438,9 +465,9 @@
"dev": true
},
"node_modules/element-plus": {
- "version": "2.2.32",
- "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.32.tgz",
- "integrity": "sha512-DTJMhYOy6MApbmh6z/95hPTK5WrBiNHGzV4IN+uEkup1WoimQ+Qyt8RxKdTe/X1LWEJ8YgWv/Cl8P4ocrt5z5g==",
+ "version": "2.2.26",
+ "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.26.tgz",
+ "integrity": "sha512-O/rdY5m9DkclpVg8r3GynyqCunm7MxSR142xSsjrZA77bi7bcwA3SIy6SPEDqHi5R4KqgkGYgKSp4Q4e3irbYg==",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.0.6",
@@ -499,294 +526,6 @@
"esbuild-windows-arm64": "0.15.18"
}
},
- "node_modules/esbuild-android-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
- "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-android-arm64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
- "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-darwin-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
- "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-darwin-arm64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
- "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-freebsd-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
- "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-freebsd-arm64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
- "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-32": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
- "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
- "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-arm": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
- "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-arm64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
- "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-mips64le": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
- "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-ppc64le": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
- "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-riscv64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
- "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-linux-s390x": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
- "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-netbsd-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
- "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-openbsd-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
- "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-sunos-64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
- "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/esbuild-windows-32": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
- "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/esbuild-windows-64": {
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
@@ -803,22 +542,6 @@
"node": ">=12"
}
},
- "node_modules/esbuild-windows-arm64": {
- "version": "0.15.18",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
- "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -829,20 +552,6 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
- "node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -911,9 +620,9 @@
}
},
"node_modules/marked": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz",
- "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz",
+ "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw==",
"bin": {
"marked": "bin/marked.js"
},
@@ -927,24 +636,21 @@
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/minimatch": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz",
- "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/muggle-string": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.2.2.tgz",
- "integrity": "sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==",
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.1.0.tgz",
+ "integrity": "sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==",
"dev": true
},
"node_modules/nanoid": {
@@ -975,9 +681,9 @@
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "version": "8.4.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
+ "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
"funding": [
{
"type": "opencollective",
@@ -1064,17 +770,24 @@
}
},
"node_modules/tauri-plugin-store-api": {
- "version": "0.0.0",
- "resolved": "git+ssh://git@github.com/tauri-apps/tauri-plugin-store.git#916165e4e4ad9821095584dc02ecd9c295cc9971",
- "license": "MIT or APACHE-2.0",
+ "version": "0.1.0",
+ "resolved": "git+ssh://git@github.com/tauri-apps/tauri-plugin-store.git#9bd993aa67766596638bbfd91e79a1bf8f632014",
+ "integrity": "sha512-X0cDDcEVLY2X8qCLISgAjzuBKDn7bJkj4S7LnXbEPFbPRe+NzhmFGHSAdFCuQuPzQYjmrVg18mZx9NAg4GBHag==",
+ "license": "MIT",
"dependencies": {
- "@tauri-apps/api": "^1.2.0"
+ "@tauri-apps/api": "1.1.0",
+ "tslib": "2.4.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+ },
"node_modules/typescript": {
- "version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "version": "4.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
+ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -1134,15 +847,32 @@
}
},
"node_modules/vue": {
- "version": "3.2.47",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
- "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
+ "integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
"dependencies": {
- "@vue/compiler-dom": "3.2.47",
- "@vue/compiler-sfc": "3.2.47",
- "@vue/runtime-dom": "3.2.47",
- "@vue/server-renderer": "3.2.47",
- "@vue/shared": "3.2.47"
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/compiler-sfc": "3.2.45",
+ "@vue/runtime-dom": "3.2.45",
+ "@vue/server-renderer": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "node_modules/vue-i18n": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
+ "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+ "dependencies": {
+ "@intlify/core-base": "9.2.2",
+ "@intlify/shared": "9.2.2",
+ "@intlify/vue-devtools": "9.2.2",
+ "@vue/devtools-api": "^6.2.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
}
},
"node_modules/vue-router": {
@@ -1170,13 +900,13 @@
}
},
"node_modules/vue-tsc": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.2.0.tgz",
- "integrity": "sha512-rIlzqdrhyPYyLG9zxsVRa+JEseeS9s8F2BbVVVWRRsTZvJO2BbhLEb2HW3MY+DFma0378tnIqs+vfTzbcQtRFw==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.0.11.tgz",
+ "integrity": "sha512-lj+6dEroPsE4wmQOPtjCzAf8x363Km5/tuEvMEoQaoRnzs9myBM46FNvCGIIPStYUGuaqF1W1bORmP2KDQEORA==",
"dev": true,
"dependencies": {
- "@volar/vue-language-core": "1.2.0",
- "@volar/vue-typescript": "1.2.0"
+ "@volar/vue-language-core": "1.0.11",
+ "@volar/vue-typescript": "1.0.11"
},
"bin": {
"vue-tsc": "bin/vue-tsc.js"
@@ -1196,5 +926,666 @@
"vue": "^3.2.0"
}
}
+ },
+ "dependencies": {
+ "@babel/parser": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
+ "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA=="
+ },
+ "@ctrl/tinycolor": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
+ "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw=="
+ },
+ "@element-plus/icons-vue": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz",
+ "integrity": "sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ==",
+ "requires": {}
+ },
+ "@floating-ui/core": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.2.tgz",
+ "integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg=="
+ },
+ "@floating-ui/dom": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.7.tgz",
+ "integrity": "sha512-6RsqvCYe0AYWtsGvuWqCm7mZytnXAZCjWtsWu1Kg8dI3INvj/DbKlDsZO+mKSaQdPT12uxIW9W2dAWJkPx4Y5g==",
+ "requires": {
+ "@floating-ui/core": "^1.0.2"
+ }
+ },
+ "@intlify/core-base": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
+ "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+ "requires": {
+ "@intlify/devtools-if": "9.2.2",
+ "@intlify/message-compiler": "9.2.2",
+ "@intlify/shared": "9.2.2",
+ "@intlify/vue-devtools": "9.2.2"
+ }
+ },
+ "@intlify/devtools-if": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+ "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+ "requires": {
+ "@intlify/shared": "9.2.2"
+ }
+ },
+ "@intlify/message-compiler": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+ "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+ "requires": {
+ "@intlify/shared": "9.2.2",
+ "source-map": "0.6.1"
+ }
+ },
+ "@intlify/shared": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
+ "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
+ },
+ "@intlify/vue-devtools": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+ "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+ "requires": {
+ "@intlify/core-base": "9.2.2",
+ "@intlify/shared": "9.2.2"
+ }
+ },
+ "@popperjs/core": {
+ "version": "npm:@sxzz/popperjs-es@2.11.7",
+ "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+ "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
+ },
+ "@tauri-apps/api": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.1.0.tgz",
+ "integrity": "sha512-n13pIqdPd3KtaMmmAcrU7BTfdMtIlGNnfZD0dNX8L4p8dgmuNyikm6JAA+yCpl9gqq6I8x5cV2Y0muqdgD0cWw=="
+ },
+ "@types/lodash": {
+ "version": "4.14.191",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
+ "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+ },
+ "@types/lodash-es": {
+ "version": "4.17.6",
+ "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
+ "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
+ "@types/marked": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==",
+ "dev": true
+ },
+ "@types/web-bluetooth": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+ "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
+ },
+ "@vitejs/plugin-vue": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz",
+ "integrity": "sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==",
+ "dev": true,
+ "requires": {}
+ },
+ "@volar/language-core": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.0.11.tgz",
+ "integrity": "sha512-YwUYKxIyDc+Fq3kQ6BGGfkrKCG5JzE2Yr6vMxrxEXW2rg/gsq3JgMk/4sI8ybRsaTirhCB4V8+AIVYsvcRxgig==",
+ "dev": true,
+ "requires": {
+ "@volar/source-map": "1.0.11",
+ "@vue/reactivity": "^3.2.45",
+ "muggle-string": "^0.1.0"
+ }
+ },
+ "@volar/source-map": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.0.11.tgz",
+ "integrity": "sha512-tkuV9MD+OuiZfHA0qZXrPdW6F7TvnpnuTan6Qe7UGUs9+sflezlMJdjaYdGgQObfP+06pcT1E3xdkOoi08ZyyQ==",
+ "dev": true,
+ "requires": {
+ "muggle-string": "^0.1.0"
+ }
+ },
+ "@volar/typescript": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.0.11.tgz",
+ "integrity": "sha512-mq7wDDAs0Eb43jev2FxbowuiwWqvL3kb+tar1we8VQbdabpyQ5dmbWPwo/IglevMmW3SKo1Et+6rqAeZpXNnPQ==",
+ "dev": true,
+ "requires": {
+ "@volar/language-core": "1.0.11"
+ }
+ },
+ "@volar/vue-language-core": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz",
+ "integrity": "sha512-A3ODs0/ua7BcpSSnE7KtO8bzWsYsbOJRyW2Q/2uktxlfHj8srln3JdgK/mNlIgfnWtACbE5K+EfMJOgJKv864A==",
+ "dev": true,
+ "requires": {
+ "@volar/language-core": "1.0.11",
+ "@volar/source-map": "1.0.11",
+ "@vue/compiler-dom": "^3.2.45",
+ "@vue/compiler-sfc": "^3.2.45",
+ "@vue/reactivity": "^3.2.45",
+ "@vue/shared": "^3.2.45",
+ "minimatch": "^5.1.0",
+ "vue-template-compiler": "^2.7.14"
+ }
+ },
+ "@volar/vue-typescript": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz",
+ "integrity": "sha512-jlnFPvBcTyPiAbGlgjhKK7fp3Q+Z7Z5eU1NTbTSS0lQC8Gog3sh2UxLAFG5Voe1gHIxasoOEPXzMR0CWF4bKbA==",
+ "dev": true,
+ "requires": {
+ "@volar/typescript": "1.0.11",
+ "@volar/vue-language-core": "1.0.11"
+ }
+ },
+ "@vue/compiler-core": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
+ "integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
+ "requires": {
+ "@babel/parser": "^7.16.4",
+ "@vue/shared": "3.2.45",
+ "estree-walker": "^2.0.2",
+ "source-map": "^0.6.1"
+ }
+ },
+ "@vue/compiler-dom": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
+ "integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
+ "requires": {
+ "@vue/compiler-core": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "@vue/compiler-sfc": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
+ "integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
+ "requires": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.45",
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/compiler-ssr": "3.2.45",
+ "@vue/reactivity-transform": "3.2.45",
+ "@vue/shared": "3.2.45",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7",
+ "postcss": "^8.1.10",
+ "source-map": "^0.6.1"
+ }
+ },
+ "@vue/compiler-ssr": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
+ "integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
+ "requires": {
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "@vue/devtools-api": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
+ "integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
+ },
+ "@vue/reactivity": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
+ "integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
+ "requires": {
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "@vue/reactivity-transform": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
+ "integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
+ "requires": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.45",
+ "@vue/shared": "3.2.45",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7"
+ }
+ },
+ "@vue/runtime-core": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
+ "integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
+ "requires": {
+ "@vue/reactivity": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "@vue/runtime-dom": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
+ "integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
+ "requires": {
+ "@vue/runtime-core": "3.2.45",
+ "@vue/shared": "3.2.45",
+ "csstype": "^2.6.8"
+ }
+ },
+ "@vue/server-renderer": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
+ "integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
+ "requires": {
+ "@vue/compiler-ssr": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "@vue/shared": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
+ "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
+ },
+ "@vueuse/core": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz",
+ "integrity": "sha512-qGUcjKQXHgN+jqXEgpeZGoxdCbIDCdVPz3QiF1uyecVGbMuM63o96I1GjYx5zskKgRI0FKSNsVWM7rwrRMTf6A==",
+ "requires": {
+ "@types/web-bluetooth": "^0.0.16",
+ "@vueuse/metadata": "9.6.0",
+ "@vueuse/shared": "9.6.0",
+ "vue-demi": "*"
+ },
+ "dependencies": {
+ "vue-demi": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+ "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+ "requires": {}
+ }
+ }
+ },
+ "@vueuse/metadata": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.6.0.tgz",
+ "integrity": "sha512-sIC8R+kWkIdpi5X2z2Gk8TRYzmczDwHRhEFfCu2P+XW2JdPoXrziqsGpDDsN7ykBx4ilwieS7JUIweVGhvZ93w=="
+ },
+ "@vueuse/shared": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.6.0.tgz",
+ "integrity": "sha512-/eDchxYYhkHnFyrb00t90UfjCx94kRHxc7J1GtBCqCG4HyPMX+krV9XJgVtWIsAMaxKVU4fC8NSUviG1JkwhUQ==",
+ "requires": {
+ "vue-demi": "*"
+ },
+ "dependencies": {
+ "vue-demi": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+ "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+ "requires": {}
+ }
+ }
+ },
+ "async-validator": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
+ "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "csstype": {
+ "version": "2.6.21",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+ "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+ },
+ "dayjs": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
+ "integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
+ },
+ "de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true
+ },
+ "element-plus": {
+ "version": "2.2.26",
+ "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.26.tgz",
+ "integrity": "sha512-O/rdY5m9DkclpVg8r3GynyqCunm7MxSR142xSsjrZA77bi7bcwA3SIy6SPEDqHi5R4KqgkGYgKSp4Q4e3irbYg==",
+ "requires": {
+ "@ctrl/tinycolor": "^3.4.1",
+ "@element-plus/icons-vue": "^2.0.6",
+ "@floating-ui/dom": "^1.0.1",
+ "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+ "@types/lodash": "^4.14.182",
+ "@types/lodash-es": "^4.17.6",
+ "@vueuse/core": "^9.1.0",
+ "async-validator": "^4.2.5",
+ "dayjs": "^1.11.3",
+ "escape-html": "^1.0.3",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "lodash-unified": "^1.0.2",
+ "memoize-one": "^6.0.0",
+ "normalize-wheel-es": "^1.2.0"
+ }
+ },
+ "esbuild": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
+ "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
+ "dev": true,
+ "requires": {
+ "@esbuild/android-arm": "0.15.18",
+ "@esbuild/linux-loong64": "0.15.18",
+ "esbuild-android-64": "0.15.18",
+ "esbuild-android-arm64": "0.15.18",
+ "esbuild-darwin-64": "0.15.18",
+ "esbuild-darwin-arm64": "0.15.18",
+ "esbuild-freebsd-64": "0.15.18",
+ "esbuild-freebsd-arm64": "0.15.18",
+ "esbuild-linux-32": "0.15.18",
+ "esbuild-linux-64": "0.15.18",
+ "esbuild-linux-arm": "0.15.18",
+ "esbuild-linux-arm64": "0.15.18",
+ "esbuild-linux-mips64le": "0.15.18",
+ "esbuild-linux-ppc64le": "0.15.18",
+ "esbuild-linux-riscv64": "0.15.18",
+ "esbuild-linux-s390x": "0.15.18",
+ "esbuild-netbsd-64": "0.15.18",
+ "esbuild-openbsd-64": "0.15.18",
+ "esbuild-sunos-64": "0.15.18",
+ "esbuild-windows-32": "0.15.18",
+ "esbuild-windows-64": "0.15.18",
+ "esbuild-windows-arm64": "0.15.18"
+ }
+ },
+ "esbuild-windows-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
+ "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
+ "dev": true,
+ "optional": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
+ "lodash-unified": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
+ "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+ "requires": {}
+ },
+ "magic-string": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+ "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "marked": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz",
+ "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw=="
+ },
+ "memoize-one": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "muggle-string": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.1.0.tgz",
+ "integrity": "sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ },
+ "normalize-wheel-es": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+ "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "postcss": {
+ "version": "8.4.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
+ "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "rollup": {
+ "version": "2.79.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
+ "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "tauri-plugin-store-api": {
+ "version": "git+ssh://git@github.com/tauri-apps/tauri-plugin-store.git#9bd993aa67766596638bbfd91e79a1bf8f632014",
+ "integrity": "sha512-X0cDDcEVLY2X8qCLISgAjzuBKDn7bJkj4S7LnXbEPFbPRe+NzhmFGHSAdFCuQuPzQYjmrVg18mZx9NAg4GBHag==",
+ "from": "tauri-plugin-store-api@github:tauri-apps/tauri-plugin-store#9bd993aa67766596638bbfd91e79a1bf8f632014",
+ "requires": {
+ "@tauri-apps/api": "1.1.0",
+ "tslib": "2.4.0"
+ }
+ },
+ "tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+ },
+ "typescript": {
+ "version": "4.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
+ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
+ "dev": true
+ },
+ "vite": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.5.tgz",
+ "integrity": "sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.15.9",
+ "fsevents": "~2.3.2",
+ "postcss": "^8.4.18",
+ "resolve": "^1.22.1",
+ "rollup": "^2.79.1"
+ }
+ },
+ "vue": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
+ "integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
+ "requires": {
+ "@vue/compiler-dom": "3.2.45",
+ "@vue/compiler-sfc": "3.2.45",
+ "@vue/runtime-dom": "3.2.45",
+ "@vue/server-renderer": "3.2.45",
+ "@vue/shared": "3.2.45"
+ }
+ },
+ "vue-i18n": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
+ "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+ "requires": {
+ "@intlify/core-base": "9.2.2",
+ "@intlify/shared": "9.2.2",
+ "@intlify/vue-devtools": "9.2.2",
+ "@vue/devtools-api": "^6.2.1"
+ }
+ },
+ "vue-router": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
+ "integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
+ "requires": {
+ "@vue/devtools-api": "^6.4.5"
+ }
+ },
+ "vue-template-compiler": {
+ "version": "2.7.14",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
+ "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
+ "dev": true,
+ "requires": {
+ "de-indent": "^1.0.2",
+ "he": "^1.2.0"
+ }
+ },
+ "vue-tsc": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.0.11.tgz",
+ "integrity": "sha512-lj+6dEroPsE4wmQOPtjCzAf8x363Km5/tuEvMEoQaoRnzs9myBM46FNvCGIIPStYUGuaqF1W1bORmP2KDQEORA==",
+ "dev": true,
+ "requires": {
+ "@volar/vue-language-core": "1.0.11",
+ "@volar/vue-typescript": "1.0.11"
+ }
+ },
+ "vuex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
+ "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+ "requires": {
+ "@vue/devtools-api": "^6.0.0-beta.11"
+ }
+ }
}
}
diff --git a/src-vue/package.json b/src-vue/package.json
index fbe3fc1a..04c1904b 100644
--- a/src-vue/package.json
+++ b/src-vue/package.json
@@ -12,8 +12,9 @@
"@element-plus/icons-vue": "^2.0.9",
"element-plus": "^2.2.17",
"marked": "^4.1.1",
- "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#dev",
+ "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#9bd993aa67766596638bbfd91e79a1bf8f632014",
"vue": "^3.2.37",
+ "vue-i18n": "^9.2.2",
"vue-router": "^4.1.5",
"vuex": "^4.0.2"
},
diff --git a/src-vue/src/App.vue b/src-vue/src/App.vue
index f740bd2f..f80e000d 100644
--- a/src-vue/src/App.vue
+++ b/src-vue/src/App.vue
@@ -6,7 +6,8 @@ import ModsView from './views/ModsView.vue';
import SettingsView from './views/SettingsView.vue';
import { appWindow } from '@tauri-apps/api/window';
import { store } from './plugins/store';
-import { invoke, window as tauriWindow } from "@tauri-apps/api";
+import { Store } from 'tauri-plugin-store-api';
+import { invoke } from "@tauri-apps/api";
export default {
components: {
@@ -19,8 +20,18 @@ export default {
data() {
return {}
},
- mounted: () => {
+ mounted: async function() {
store.commit('initialize');
+
+ // Initialize interface language
+ const persistentStore = new Store('flight-core-settings.json');
+ let lang: string | null = await persistentStore.get('lang');
+ if (lang === null) {
+ lang = navigator.language.substring(0, 2);
+ persistentStore.set('lang', lang);
+ await persistentStore.save();
+ }
+ this.$root!.$i18n.locale = lang;
},
methods: {
async toggleMaximize() {
@@ -56,11 +67,11 @@ export default {
id="fc__menu_items"
data-tauri-drag-region
>
- <el-menu-item index="/">Play</el-menu-item>
- <el-menu-item index="/changelog">Changelog</el-menu-item>
- <el-menu-item index="/mods">Mods</el-menu-item>
- <el-menu-item index="/settings">Settings</el-menu-item>
- <el-menu-item index="/dev" v-if="$store.state.developer_mode">Dev</el-menu-item>
+ <el-menu-item index="/">{{ $t('menu.play') }}</el-menu-item>
+ <el-menu-item index="/changelog">{{ $t('menu.changelog') }}</el-menu-item>
+ <el-menu-item index="/mods">{{ $t('menu.mods') }}</el-menu-item>
+ <el-menu-item index="/settings">{{ $t('menu.settings') }}</el-menu-item>
+ <el-menu-item index="/dev" v-if="$store.state.developer_mode">{{ $t('menu.dev') }}</el-menu-item>
</el-menu>
<!-- Window controls -->
@@ -82,8 +93,20 @@ export default {
top: 0;
width: 100%;
height: var(--fc-menu_height);
- background-image: radial-gradient(transparent 1px);
- backdrop-filter: saturate(50%) blur(4px);
+}
+
+#fc__menu_bar::before {
+ position: absolute;
+ content: "";
+ inset: 0; /* same as { top: 0; right: 0; bottom: 0; left: 0; } */
+ background-image: linear-gradient(to bottom, red, orange);
+ z-index: 1;
+ opacity: 0;
+ transition: opacity 1s linear;
+}
+
+#fc__menu_bar:hover::before {
+ opacity: 1;
}
/* Borders reset */
diff --git a/src-vue/src/components/InstallProgressBar.vue b/src-vue/src/components/InstallProgressBar.vue
new file mode 100644
index 00000000..d0c2047c
--- /dev/null
+++ b/src-vue/src/components/InstallProgressBar.vue
@@ -0,0 +1,102 @@
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { appWindow } from '@tauri-apps/api/window';
+import { InstallProgress } from '../../../src-tauri/bindings/InstallProgress';
+
+export default defineComponent({
+ name: 'InstallProgressBar',
+ computed: {
+ progressBarStyle(): string {
+ return !this.install_or_update ? 'hide-progress' : '';
+ }
+ },
+ data() {
+ return {
+ percentage: 0,
+ color: '#409EFF',
+ install_or_update: false,
+ status: "unknown",
+ current_downloaded: -1,
+ total_size: -1,
+ };
+ },
+ methods: {
+ formatBytes(bytes: number, decimals = 2) {
+ if (bytes === 0) return '0 Bytes';
+ const k = 1000;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
+ },
+ formatText() {
+ if (this.status == "DOWNLOADING") {
+ const current_downloaded_string = this.formatBytes(this.current_downloaded);
+ const total_size_string = this.formatBytes(this.total_size);
+ const status = this.$t("generic.downloading");
+ return `${status}: ${current_downloaded_string}/${total_size_string}`;
+ }
+ if (this.status == "EXTRACTING") {
+ return this.$t("generic.extracting");
+ }
+ return "Inactive"; // Needed to keep same size format when progress bar is hidden
+ }
+ },
+ mounted() {
+ appWindow.listen<InstallProgress>(
+ 'northstar-install-download-progress',
+ ({ event, payload }) => {
+ this.install_or_update = true;
+ let progress = payload;
+ this.status = progress.state;
+ if (progress.state == "DOWNLOADING") {
+ this.percentage = ((Number(progress.current_downloaded) / Number(progress.total_size)) * 100);
+ this.color = '#409EFF';
+ this.current_downloaded = Number(progress.current_downloaded);
+ this.total_size = Number(progress.total_size);
+ }
+ if (progress.state == "EXTRACTING") {
+ this.percentage = 100;
+ this.color = '#67C23A';
+ }
+ if (progress.state == "DONE") {
+ // Clear state again
+ this.install_or_update = false
+ }
+ }
+ );
+ }
+});
+</script>
+
+<template>
+ <el-progress
+ :class="progressBarStyle"
+ :format="formatText"
+ :percentage="percentage"
+ :color="color"
+ :indeterminate="status === 'EXTRACTING'"
+ :duration="1"
+ >
+ </el-progress>
+</template>
+
+<style scoped>
+.el-progress {
+ margin-top: 10px;
+}
+
+/* Set progress bar width */
+.el-progress:deep(.el-progress-bar) {
+ width: 200px;
+ flex-grow: initial;
+}
+
+.el-progress:deep(.el-progress__text) {
+ line-height: 1.2;
+}
+
+.hide-progress {
+ opacity: 0;
+}
+</style>
diff --git a/src-vue/src/components/LanguageSelector.vue b/src-vue/src/components/LanguageSelector.vue
new file mode 100644
index 00000000..5b1e7ebd
--- /dev/null
+++ b/src-vue/src/components/LanguageSelector.vue
@@ -0,0 +1,51 @@
+<template>
+ <el-select v-model="value" class="m-2"
+ :placeholder="$t('settings.language_select')" size="large"
+ @change="onChange"
+ >
+ <el-option
+ v-for="item in options"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { Store } from 'tauri-plugin-store-api';
+const persistentStore = new Store('flight-core-settings.json');
+
+export default defineComponent({
+ name: 'LanguageSelector',
+ data: () => ({
+ value: '',
+ options: [
+ {
+ value: 'en',
+ label: 'English'
+ },
+ {
+ value: 'fr',
+ label: 'Français'
+ },
+ {
+ value: 'de',
+ label: 'Deutsch'
+ },
+ ]
+ }),
+ mounted: async function() {
+ const lang: string = await persistentStore.get('lang') as string;
+ this.value = lang;
+ },
+ methods: {
+ async onChange(value: string) {
+ this.$root!.$i18n.locale = value;
+ persistentStore.set('lang', value);
+ await persistentStore.save();
+ }
+ }
+})
+</script>
diff --git a/src-vue/src/components/ModsMenu.vue b/src-vue/src/components/ModsMenu.vue
index 9b62fcfa..656c05a6 100644
--- a/src-vue/src/components/ModsMenu.vue
+++ b/src-vue/src/components/ModsMenu.vue
@@ -7,20 +7,20 @@
<h5>Mods</h5>
<el-menu-item index="1" @click="$emit('showLocalMods', true)">
<el-icon><Folder /></el-icon>
- <span>Local</span>
+ <span>{{ $t('mods.menu.local') }}</span>
</el-menu-item>
<el-menu-item index="2" @click="$emit('showLocalMods', false)">
<el-icon><Connection /></el-icon>
- <span>Online</span>
+ <span>{{ $t('mods.menu.online') }}</span>
</el-menu-item>
<!-- Search inputs -->
- <h5>Filter</h5>
- <el-input v-model="$store.state.search.searchValue" placeholder="Search" clearable />
+ <h5>{{ $t('mods.menu.filter') }}</h5>
+ <el-input v-model="$store.state.search.searchValue" :placeholder="$t('mods.menu.search')" clearable />
<el-select
v-if="!showingLocalMods"
v-model="$store.state.search.sortValue"
- placeholder="Sort mods"
+ :placeholder="$t('mods.menu.sort_mods')"
>
<el-option
v-for="item of sortValues"
@@ -33,7 +33,7 @@
v-if="!showingLocalMods"
v-model="$store.state.search.selectedCategories"
multiple
- placeholder="Select categories"
+ :placeholder="$t('mods.menu.select_categories')"
>
<el-option
v-for="item in $store.state.thunderstoreModsCategories"
@@ -66,7 +66,7 @@ export default defineComponent({
sortValues(): {label: string, value: string}[] {
return Object.keys(SortOptions).map((key: string) => ({
value: key,
- label: Object.values(SortOptions)[Object.keys(SortOptions).indexOf(key)]
+ label: this.$t('mods.menu.sort.' + Object.values(SortOptions)[Object.keys(SortOptions).indexOf(key)])
}));
}
}
diff --git a/src-vue/src/components/PlayButton.vue b/src-vue/src/components/PlayButton.vue
index 687f12a4..208b4703 100644
--- a/src-vue/src/components/PlayButton.vue
+++ b/src-vue/src/components/PlayButton.vue
@@ -18,22 +18,22 @@ export default defineComponent({
},
playButtonLabel(): string {
if (this.$store.state.northstar_is_running) {
- return "Game is running";
+ return this.$t("play.button.northstar_is_running");
}
switch(this.$store.state.northstar_state) {
case NorthstarState.GAME_NOT_FOUND:
- return "Select Titanfall2 game folder";
+ return this.$t("play.button.select_game_dir");
case NorthstarState.INSTALL:
- return "Install";
+ return this.$t("play.button.install");
case NorthstarState.INSTALLING:
- return "Installing..."
+ return this.$t("play.button.installing");
case NorthstarState.MUST_UPDATE:
- return "Update";
+ return this.$t("play.button.update");
case NorthstarState.UPDATING:
- return "Updating...";
+ return this.$t("play.button.updating");
case NorthstarState.READY_TO_PLAY:
- return "Launch game";
+ return this.$t("play.button.ready_to_play");
default:
return "";
@@ -57,7 +57,7 @@ export default defineComponent({
options: [
{
value: ReleaseCanal.RELEASE_CANDIDATE,
- label: 'Northstar release candidate',
+ label: this.$t('channels.names.NorthstarReleaseCandidate'),
},
]
},
@@ -83,10 +83,10 @@ export default defineComponent({
return this.showReleaseSwitch
? 'border-radius: 2px 0 0 2px;'
: 'border-radius: 2px';
- }
+ },
},
methods: {
- launchGame() {
+ async launchGame() {
this.$store.commit('launchGame');
}
}
@@ -94,30 +94,31 @@ export default defineComponent({
</script>
<template>
- <el-button :disabled="northstarIsRunning"
- type="primary" size="large" @click="launchGame"
- class="fc_launch__button" :style="buttonRadiusStyle">
- {{ playButtonLabel }}
- </el-button>
- <el-select v-if="showReleaseSwitch" :disabled="northstarIsRunning"
- v-model="currentCanal" placeholder="Select">
- <el-option-group
- v-for="group in selectOptions"
- :key="group.label"
- :label="group.label"
- >
- <el-option
- v-for="item in group.options"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-option-group>
- </el-select>
+ <nav>
+ <el-button :disabled="northstarIsRunning"
+ type="primary" size="large" @click="launchGame"
+ class="fc_launch__button" :style="buttonRadiusStyle">
+ {{ playButtonLabel }}
+ </el-button>
+ <el-select v-if="showReleaseSwitch" :disabled="northstarIsRunning"
+ v-model="currentCanal" placeholder="Select">
+ <el-option-group
+ v-for="group in selectOptions"
+ :key="group.label"
+ :label="group.label"
+ >
+ <el-option
+ v-for="item in group.options"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-option-group>
+ </el-select>
+ </nav>
</template>
<style scoped>
-
button {
text-transform: uppercase;
padding: 30px;
@@ -130,7 +131,6 @@ button {
}
/* Release canal selector */
-
.el-select {
width: 0;
margin-right: 50px;
diff --git a/src-vue/src/components/PullRequestsSelector.vue b/src-vue/src/components/PullRequestsSelector.vue
index 58a355f4..ba95624a 100644
--- a/src-vue/src/components/PullRequestsSelector.vue
+++ b/src-vue/src/components/PullRequestsSelector.vue
@@ -1,7 +1,12 @@
<template>
<div>
<el-collapse @change="onChange">
- <el-collapse-item title="Launcher PRs" name="1">
+ <el-collapse-item name="1">
+ <template #title>
+ Launcher PRs
+ <el-input class="pr_search_input" v-model="launcherSearch" placeholder="Filter pull requests" @click.stop="() => false"></el-input>
+ </template>
+
<p v-if="pull_requests_launcher.length === 0">
<el-progress
:show-text="false"
@@ -12,16 +17,28 @@
style="margin: 15px"
/>
</p>
- <el-card v-else shadow="hover" v-for="pull_request in pull_requests_launcher"
- v-bind:key="pull_request.url">
+ <el-card
+ v-else-if="filtered_launcher_pull_requests.length !== 0"
+ shadow="hover"
+ v-for="pull_request in filtered_launcher_pull_requests"
+ v-bind:key="pull_request.url"
+ >
<el-button type="primary" @click="installLauncherPR(pull_request)">Install</el-button>
+ <el-button type="primary" @click="downloadLauncherPR(pull_request)">Download</el-button>
<a target="_blank" :href="pull_request.html_url">
{{ pull_request.number }}: {{ pull_request.title }}
</a>
</el-card>
+ <div v-else class="no_matching_pr">
+ No matching PR found.
+ </div>
</el-collapse-item>
- <el-collapse-item title="Mods PRs" name="2">
+ <el-collapse-item name="2">
+ <template #title>
+ Mods PRs
+ <el-input class="pr_search_input" v-model="modsSearch" placeholder="Filter pull requests" @click.stop="() => false"></el-input>
+ </template>
<div style="margin: 15px">
<el-alert title="Warning" type="warning" :closable="false" show-icon>
Mod PRs are installed into a separate profile. Make sure to launch via
@@ -39,12 +56,21 @@
style="margin: 15px"
/>
</p>
- <el-card v-else shadow="hover" v-for="pull_request in pull_requests_mods" v-bind:key="pull_request.url">
+ <el-card
+ v-else-if="filtered_mods_pull_requests.length !== 0"
+ shadow="hover"
+ v-for="pull_request in filtered_mods_pull_requests"
+ v-bind:key="pull_request.url"
+ >
<el-button type="primary" @click="installModsPR(pull_request)">Install</el-button>
+ <el-button type="primary" @click="downloadModsPR(pull_request)">Download</el-button>
<a target="_blank" :href="pull_request.html_url">
{{ pull_request.number }}: {{ pull_request.title }}
</a>
</el-card>
+ <div v-else class="no_matching_pr">
+ No matching PR found.
+ </div>
</el-collapse-item>
</el-collapse>
</div>
@@ -54,11 +80,13 @@
import { defineComponent } from 'vue'
import { PullRequestType } from '../../../src-tauri/bindings/PullRequestType';
import { PullsApiResponseElement } from '../../../src-tauri/bindings/PullsApiResponseElement';
-import { invoke } from "@tauri-apps/api";
-import { ElNotification } from "element-plus";
export default defineComponent({
name: 'PullRequestsSelector',
+ data: () => ({
+ launcherSearch: '',
+ modsSearch: ''
+ }),
computed: {
pull_requests_launcher(): PullsApiResponseElement[] {
return this.$store.state.pullrequests.pull_requests_launcher;
@@ -66,20 +94,49 @@ export default defineComponent({
pull_requests_mods(): PullsApiResponseElement[] {
return this.$store.state.pullrequests.pull_requests_mods;
},
+
+ filtered_launcher_pull_requests(): PullsApiResponseElement[] {
+ if (this.launcherSearch.length === 0) {
+ return this.pull_requests_launcher;
+ }
+
+ return this.pull_requests_launcher.filter(pr =>
+ // Check PR id
+ pr.number.toString().indexOf(this.launcherSearch) !== -1
+ // Check PR title
+ || pr.title.toLowerCase().indexOf(this.launcherSearch.toLowerCase()) !== -1);
+ },
+ filtered_mods_pull_requests(): PullsApiResponseElement[] {
+ if (this.modsSearch.length === 0) {
+ return this.pull_requests_mods;
+ }
+
+ return this.pull_requests_mods.filter(pr =>
+ // Check PR id
+ pr.number.toString().indexOf(this.modsSearch) !== -1
+ // Check PR title
+ || pr.title.toLowerCase().indexOf(this.modsSearch.toLowerCase()) !== -1);
+ },
},
methods: {
onChange(e: Object) {
const openedCollapseNames = Object.values(e);
if (openedCollapseNames.includes('1') && this.pull_requests_launcher.length === 0) {
- this.getPullRequests('LAUNCHER');
+ this.getPullRequests('Launcher');
}
if (openedCollapseNames.includes('2') && this.pull_requests_mods.length === 0) {
- this.getPullRequests('MODS');
+ this.getPullRequests('Mods');
}
},
async getPullRequests(pull_request_type: PullRequestType) {
this.$store.commit('getPullRequests', pull_request_type);
},
+ async downloadLauncherPR(pull_request: PullsApiResponseElement) {
+ this.$store.commit('downloadLauncherPR', pull_request);
+ },
+ async downloadModsPR(pull_request: PullsApiResponseElement) {
+ this.$store.commit('downloadModsPR', pull_request);
+ },
async installLauncherPR(pull_request: PullsApiResponseElement) {
this.$store.commit('installLauncherPR', pull_request);
},
@@ -100,4 +157,18 @@ export default defineComponent({
padding-left: 10px;
font-size: 14px;
}
+
+.el-collapse:deep(.el-collapse-item__arrow) {
+ margin: 0 8px;
+}
+
+.pr_search_input {
+ width: 200px;
+ margin: 0 0 0 auto;
+}
+
+.no_matching_pr {
+ margin: 0 auto;
+ width: max-content;
+}
</style>
diff --git a/src-vue/src/components/ThunderstoreModCard.vue b/src-vue/src/components/ThunderstoreModCard.vue
index c9f6768c..fec95f14 100644
--- a/src-vue/src/components/ThunderstoreModCard.vue
+++ b/src-vue/src/components/ThunderstoreModCard.vue
@@ -21,7 +21,7 @@
<br/>
<div class="name hide-text-overflow">{{ mod.name }}</div>
- <div class="author hide-text-overflow">by {{ mod.owner }}</div>
+ <div class="author hide-text-overflow">{{ $t('mods.card.by') }} {{ mod.owner }}</div>
<div class="desc">
{{ latestVersion.description }}
</div>
@@ -33,7 +33,7 @@
:loading="isBeingInstalled || isBeingUpdated"
@click.stop="installMod(mod)"
>
- {{ modButtonText }}
+ {{ $t(modButtonText) }}
</el-button>
<!-- Information dropdown menu -->
@@ -51,10 +51,10 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openURL(mod.package_url)">
- More info
+ {{ $t('mods.card.more_info') }}
</el-dropdown-item>
<el-dropdown-item @click="deleteMod(mod)">
- Remove mod
+ {{ $t('mods.card.remove') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
@@ -72,9 +72,9 @@ import {invoke, shell} from "@tauri-apps/api";
import {ThunderstoreModStatus} from "../utils/thunderstore/ThunderstoreModStatus";
import {NorthstarMod} from "../../../src-tauri/bindings/NorthstarMod";
import {GameInstall} from "../utils/GameInstall";
-import {ElNotification} from "element-plus";
import { NorthstarState } from "../utils/NorthstarState";
import { ElMessageBox } from "element-plus";
+import { showErrorNotification, showNotification } from "../utils/ui";
export default defineComponent({
name: "ThunderstoreModCard",
@@ -129,15 +129,15 @@ export default defineComponent({
modButtonText(): string {
switch (this.modStatus) {
case ThunderstoreModStatus.BEING_INSTALLED:
- return "Installing...";
+ return "mods.card.button.being_installed";
case ThunderstoreModStatus.BEING_UPDATED:
- return "Updating...";
+ return "mods.card.button.being_updated";
case ThunderstoreModStatus.INSTALLED:
- return "Installed";
+ return "mods.card.button.installed";
case ThunderstoreModStatus.NOT_INSTALLED:
- return "Install";
+ return "mods.card.button.install";
case ThunderstoreModStatus.OUTDATED:
- return "Update";
+ return "mods.card.button.outdated";
}
},
@@ -200,11 +200,11 @@ export default defineComponent({
// Show pop-up to confirm delete
ElMessageBox.confirm(
- 'Delete Thunderstore mod?',
- 'Warning',
+ this.$t('mods.card.remove_dialog_text'),
+ this.$t('mods.card.remove_dialog_title'),
{
- confirmButtonText: 'OK',
- cancelButtonText: 'Cancel',
+ confirmButtonText: this.$t('generic.yes'),
+ cancelButtonText: this.$t('generic.cancel'),
type: 'warning',
}
)
@@ -214,22 +214,12 @@ export default defineComponent({
install_type: this.$store.state.install_type
} as GameInstall;
- await invoke("delete_thunderstore_mod", { gameInstall: game_install, thunderstoreModString: this.latestVersion.full_name })
+ await invoke<string>("delete_thunderstore_mod", { gameInstall: game_install, thunderstoreModString: this.latestVersion.full_name })
.then((message) => {
- ElNotification({
- title: `Removed ${mod.name}`,
- message: message as string,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(this.$t('mods.card.remove_success', {modName: mod.name}), message);
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
})
.finally(() => {
this.$store.commit('loadInstalledMods');
@@ -253,21 +243,11 @@ export default defineComponent({
this.isBeingInstalled = true;
}
- await invoke("install_mod_caller", { gameInstall: game_install, thunderstoreModString: this.latestVersion.full_name }).then((message) => {
- ElNotification({
- title: `Installed ${mod.name}`,
- message: message as string,
- type: 'success',
- position: 'bottom-right'
- });
+ await invoke<string>("install_mod_caller", { gameInstall: game_install, thunderstoreModString: this.latestVersion.full_name }).then((message) => {
+ showNotification(this.$t('mods.card.install_success', {modName: mod.name}), message);
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
})
.finally(() => {
this.isBeingInstalled = false;
diff --git a/src-vue/src/i18n/lang/de.json b/src-vue/src/i18n/lang/de.json
new file mode 100644
index 00000000..1955b717
--- /dev/null
+++ b/src-vue/src/i18n/lang/de.json
@@ -0,0 +1,164 @@
+{
+ "menu": {
+ "play": "Spielen",
+ "changelog": "Versionhistorie",
+ "mods": "Mods",
+ "settings": "Einstellungen",
+ "dev": "Dev"
+ },
+
+ "generic": {
+ "yes": "Ja",
+ "no": "Nein",
+ "error": "Fehler",
+ "cancel": "Abbrechen",
+ "informationShort": "Info",
+ "downloading": "Herunterladen",
+ "extracting": "Extrahieren",
+ "done": "Fertig",
+ "success": "Erfolg"
+ },
+
+ "play": {
+ "button": {
+ "northstar_is_running": "Spiel läuft",
+ "select_game_dir": "Titanfall2 ordner wählen",
+ "install": "Installieren",
+ "installing": "Installiert...",
+ "update": "Aktualisieren",
+ "updating": "Aktualisiert...",
+ "ready_to_play": "Spiel starten"
+ },
+
+ "unknown_version": "Unbekannte Version",
+ "see_patch_notes": "Siehe Patch-Notizen",
+ "players": "Spieler",
+ "servers": "Server",
+ "unable_to_load_playercount": "Spielerzahl konnte nicht geladen werden",
+ "northstar_running": "Northstar läuft:",
+ "origin_running": "Origin läuft:"
+ },
+
+ "mods": {
+ "local": {
+ "no_mods": "Keine Mods gefunden.",
+ "delete_confirm": "Bist du dir sicher, dass du diesen Mod löschen möchtest?",
+ "delete": "Löschen",
+ "part_of_ts_mod": "Dieser Northstar Mod ist teil eines Thunderstore Mods",
+ "success_deleting": "{modName} erfolgreich gelöscht"
+ },
+
+ "online": {
+ "no_match": "Keine passenden Mods gefunden.",
+ "try_another_search": "Versuche eine andere Suchanfrage!"
+ },
+
+ "menu": {
+ "local": "Lokal",
+ "online": "Online",
+ "filter": "Filter",
+ "search": "Suche",
+ "sort_mods": "Mods sortieren",
+ "select_categories": "Kategorien wählen",
+
+ "sort": {
+ "name_asc": "Nach Name (A to Z)",
+ "name_desc": "Nach Name (Z to A)",
+ "date_asc": "Nach Datum (älteste zuerst)",
+ "date_desc": "Nach Datum (neuste zuerst)",
+ "most_downloaded": "Am meisten heruntergeladen",
+ "top_rated": "Am besten bewerted"
+ }
+ },
+
+ "card": {
+ "button": {
+ "being_installed": "Installiert...",
+ "being_updated": "Aktualisiert...",
+ "installed": "Installiert",
+ "install": "Installieren",
+ "outdated": "Aktualisieren"
+ },
+
+ "by": "von",
+ "more_info": "Mehr Info",
+ "remove": "Mod entfernen",
+ "remove_dialog_title": "Warnung",
+ "remove_dialog_text": "Thunderstore Mod entfernen?",
+ "remove_success": "{modName} entfernt",
+ "install_success": "{modName} installiert"
+ }
+ },
+
+ "settings": {
+ "manage_install": "Installation verwalten",
+ "choose_folder": "Installationsordner wählen",
+ "nb_ts_mods_per_page": "Anzahl an Thunderstore Mods pro Seite",
+ "nb_ts_mods_per_page_desc1": "Ändern dieser Einstellung kann die Leistung beim Suchen von Thunderstore Mods beeinflussen.",
+ "nb_ts_mods_per_page_desc2": "Setze diesen Wert auf 0 um alle Mods auf einer einzelnen Seite anzuzeigen",
+ "nb_ts_mods_reset": "Standard wiederherstellen",
+ "language": "Sprache",
+ "language_select": "Bevorzugte Sprache wählen",
+ "about": "Ãœber:",
+ "flightcore_version": "FlightCore Version:",
+ "testing": "Testen:",
+ "enable_test_channels": "Testversionen aktivieren",
+ "dev_mode_enabled_title": "Vorsicht!",
+ "dev_mod_enabled_text": "Entwicklermodus aktiviert.",
+
+ "repair": {
+ "title": "Reparieren",
+ "open_window": "Reparierfenster öffnen",
+
+ "window": {
+ "title": "FlightCore Reparierfenster",
+ "warning": "Dieses Fenster enthält verschiedene Funktionen um gängige Probleme mit Northstar und FlightCore zu beheben.",
+ "disable_all_but_core": "Alle außer notwendige Mods deaktivieren",
+ "disable_all_but_core_success": "Alle außer notwendige Mods wurden deaktiviert",
+ "force_reinstall_ns": "Northstar reinstallieren",
+ "force_delete_temp_dl": "Temporären FlightCore Downloadordner löschen",
+ "delete_persistent_store": "FlightCore Einstellungen zurücksetzen",
+ "reinstall_title": "Northstar wird neu installiert",
+ "reinstall_text": "Bitte warten",
+ "reinstall_success": "Northstar erfolgreich neu installiert"
+ }
+ }
+ },
+
+ "notification": {
+ "game_folder": {
+ "new": {
+ "title": "Neuer Spielordner",
+ "text": "Spielordner erfolgreich aktualisiert."
+ },
+
+ "wrong": {
+ "title": "Falscher Ordner",
+ "text": "Der gewählte Ordner enthält keine valide Titanfall2 Installation."
+ },
+
+ "not_found": {
+ "title": "Titanfall2 nicht gefunden!",
+ "text": "Bitte wähle den Installationsordner manuell aus"
+ }
+ },
+
+ "flightcore_outdated": {
+ "title": "FlightCore veraltet!",
+ "text": "Bitte aktualisiere FlightCore.\nDu hast die veraltetet Version {oldVersion}.\nNeuste Version ist {newVersion}!"
+ }
+ },
+
+ "channels": {
+ "release": {
+ "switch": {
+ "text": "Releasekanal zu \"{canal}\" gewechselt."
+ }
+ },
+
+ "names": {
+ "Northstar": "Northstar",
+ "NorthstarReleaseCandidate": "Northstar Release Candidate"
+ }
+ }
+}
diff --git a/src-vue/src/i18n/lang/en.json b/src-vue/src/i18n/lang/en.json
new file mode 100644
index 00000000..407b69d1
--- /dev/null
+++ b/src-vue/src/i18n/lang/en.json
@@ -0,0 +1,164 @@
+{
+ "menu": {
+ "play": "Play",
+ "changelog": "Changelog",
+ "mods": "Mods",
+ "settings": "Settings",
+ "dev": "Dev"
+ },
+
+ "generic": {
+ "yes": "Yes",
+ "no": "No",
+ "error": "Error",
+ "cancel": "Cancel",
+ "informationShort": "Info",
+ "downloading": "Downloading",
+ "extracting": "Extracting",
+ "done": "Done",
+ "success": "Success"
+ },
+
+ "play": {
+ "button": {
+ "northstar_is_running": "Game is running",
+ "select_game_dir": "Select Titanfall2 game folder",
+ "install": "Install",
+ "installing": "Installing...",
+ "update": "Update",
+ "updating": "Updating...",
+ "ready_to_play": "Launch game"
+ },
+
+ "unknown_version": "Unknown version",
+ "see_patch_notes": "see patch notes",
+ "players": "players",
+ "servers": "servers",
+ "unable_to_load_playercount": "Unable to load playercount",
+ "northstar_running": "Northstar is running:",
+ "origin_running": "Origin is running:"
+ },
+
+ "mods": {
+ "local": {
+ "no_mods": "No mods were found.",
+ "delete_confirm": "Are you sure to delete this mod?",
+ "delete": "Delete",
+ "part_of_ts_mod": "This Northstar mod is part of a Thunderstore mod",
+ "success_deleting": "Success deleting {modName}"
+ },
+
+ "online": {
+ "no_match": "No matching mod has been found.",
+ "try_another_search": "Try another search!"
+ },
+
+ "menu": {
+ "local": "Local",
+ "online": "Online",
+ "filter": "Filter",
+ "search": "Search",
+ "sort_mods": "Sort mods",
+ "select_categories": "Select categories",
+
+ "sort": {
+ "name_asc": "By name (A to Z)",
+ "name_desc": "By name (Z to A)",
+ "date_asc": "By date (from oldest)",
+ "date_desc": "By date (from newest)",
+ "most_downloaded": "Most downloaded",
+ "top_rated": "Top rated"
+ }
+ },
+
+ "card": {
+ "button": {
+ "being_installed": "Installing...",
+ "being_updated": "Updating...",
+ "installed": "Installed",
+ "install": "Install",
+ "outdated": "Update"
+ },
+
+ "by": "by",
+ "more_info": "More info",
+ "remove": "Remove mod",
+ "remove_dialog_title": "Warning",
+ "remove_dialog_text": "Delete Thunderstore mod?",
+ "remove_success": "Removed {modName}",
+ "install_success": "Installed {modName}"
+ }
+ },
+
+ "settings": {
+ "manage_install": "Manage installation",
+ "choose_folder": "Choose installation folder",
+ "nb_ts_mods_per_page": "Number of Thunderstore mods per page",
+ "nb_ts_mods_per_page_desc1": "This has an impact on display performances when browsing Thunderstore mods.",
+ "nb_ts_mods_per_page_desc2": "Set this value to 0 to disable pagination.",
+ "nb_ts_mods_reset": "Reset to default",
+ "language": "Language",
+ "language_select": "Select your favorite language",
+ "about": "About:",
+ "flightcore_version": "FlightCore version:",
+ "testing": "Testing:",
+ "enable_test_channels": "Enable testing release channels",
+ "dev_mode_enabled_title": "Watch out!",
+ "dev_mode_enabled_text": "Developer mode enabled.",
+
+ "repair": {
+ "title": "Repair",
+ "open_window": "Open repair window",
+
+ "window": {
+ "title": "FlightCore repair window",
+ "warning": "This window contains various functionality to repair common issues with Northstar and FlightCore.",
+ "disable_all_but_core": "Disable all but core mods",
+ "disable_all_but_core_success": "Disabled all mods but core",
+ "force_reinstall_ns": "Force reinstall Northstar",
+ "force_delete_temp_dl": "Force delete temp download folder",
+ "delete_persistent_store": "Delete FlightCore persistent store",
+ "reinstall_title": "Force reinstalling Northstar",
+ "reinstall_text": "Please wait",
+ "reinstall_success": "Successfully reinstalled Northstar"
+ }
+ }
+ },
+
+ "notification": {
+ "game_folder": {
+ "new": {
+ "title": "New game folder",
+ "text": "Game folder was successfully updated."
+ },
+
+ "wrong": {
+ "title": "Wrong folder",
+ "text": "Selected folder is not a valid Titanfall2 install."
+ },
+
+ "not_found": {
+ "title": "Titanfall2 not found!",
+ "text": "Please manually select install location"
+ }
+ },
+
+ "flightcore_outdated": {
+ "title": "FlightCore outdated!",
+ "text": "Please update FlightCore.\nRunning outdated version {oldVersion}.\nNewest is {newVersion}!"
+ }
+ },
+
+ "channels": {
+ "release": {
+ "switch": {
+ "text": "Switched release channel to \"{canal}\"."
+ }
+ },
+
+ "names": {
+ "Northstar": "Northstar",
+ "NorthstarReleaseCandidate": "Northstar release candidate"
+ }
+ }
+}
diff --git a/src-vue/src/i18n/lang/fr.json b/src-vue/src/i18n/lang/fr.json
new file mode 100644
index 00000000..aae11bb7
--- /dev/null
+++ b/src-vue/src/i18n/lang/fr.json
@@ -0,0 +1,164 @@
+{
+ "menu": {
+ "play": "Jouer",
+ "changelog": "Notes",
+ "mods": "Mods",
+ "settings": "Paramètres",
+ "dev": "Dev"
+ },
+
+ "generic": {
+ "yes": "Oui",
+ "no": "Non",
+ "error": "Erreur",
+ "cancel": "Annuler",
+ "informationShort": "Info",
+ "downloading": "Téléchargement",
+ "extracting": "Extraction",
+ "done": "Fait",
+ "success": "Succès"
+ },
+
+ "play": {
+ "button": {
+ "northstar_is_running": "En cours d'utilisation",
+ "select_game_dir": "Sélectionner le dossier du jeu",
+ "install": "Installer",
+ "installing": "Installation...",
+ "update": "Mettre à jour",
+ "updating": "Mise à jour...",
+ "ready_to_play": "Jouer"
+ },
+
+ "unknown_version": "Version inconnue",
+ "see_patch_notes": "voir les notes de version",
+ "players": "joueurs",
+ "servers": "serveurs",
+ "unable_to_load_playercount": "Impossible de charger les statistiques",
+ "northstar_running": "Northstar est en cours d'exécution :",
+ "origin_running": "Origin est en cours d'exécution :"
+ },
+
+ "mods": {
+ "local": {
+ "no_mods": "Aucun mod trouvé.",
+ "delete_confirm": "Êtes-vous certain de vouloir supprimer ce mod ?",
+ "delete": "Supprimer",
+ "part_of_ts_mod": "Ce mod Northstar fait partie d'un mod Thunderstore",
+ "success_deleting": "Succès de la suppression de {modName}"
+ },
+
+ "online": {
+ "no_match": "Aucun mod correspondant n'a été trouvé.",
+ "try_another_search": "Essayez une autre recherche !"
+ },
+
+ "menu": {
+ "local": "Local",
+ "online": "En ligne",
+ "filter": "Filtrer",
+ "search": "Chercher",
+ "sort_mods": "Trier les mods",
+ "select_categories": "Choisir les catégories",
+
+ "sort": {
+ "name_asc": "Par nom (de A à Z)",
+ "name_desc": "Par nom (de Z à A)",
+ "date_asc": "Par date (du plus vieux)",
+ "date_desc": "Par date (du plus récent)",
+ "most_downloaded": "Plus téléchargés",
+ "top_rated": "Mieux notés"
+ }
+ },
+
+ "card": {
+ "button": {
+ "being_installed": "Installation...",
+ "being_updated": "Mise à jour...",
+ "installed": "Installé",
+ "install": "Installer",
+ "outdated": "Mettre à jour"
+ },
+
+ "by": "par",
+ "more_info": "Plus d'informations",
+ "remove": "Supprimer le mod",
+ "remove_dialog_title": "Attention !",
+ "remove_dialog_text": "Voulez-vous vraiment supprimer ce mod Thunderstore ?",
+ "remove_success": "{modName} supprimé",
+ "install_success": "{modName} installé"
+ }
+ },
+
+ "settings": {
+ "manage_install": "Gérer l'installation",
+ "choose_folder": "Choisir le dossier d'installation du jeu",
+ "nb_ts_mods_per_page": "Nombre de mods Thunderstore par page",
+ "nb_ts_mods_per_page_desc1": "Ce paramètre a un impact sur les performances d'affichage des mods Thunderstore.",
+ "nb_ts_mods_per_page_desc2": "Réglez-le sur 0 pour désactiver la pagination.",
+ "nb_ts_mods_reset": "Valeur par défaut",
+ "language": "Langue",
+ "language_select": "Sélectionnez votre langue",
+ "about": "À propos",
+ "flightcore_version": "Version de FlightCore :",
+ "testing": "Tests :",
+ "enable_test_channels": "Activer le test de versions de pré-production",
+ "dev_mode_enabled_title": "Attention !",
+ "dev_mode_enabled_text": "Mode développeur activé.",
+
+ "repair": {
+ "title": "Dépannage",
+ "open_window": "Ouvrir la fenêtre de dépannage",
+
+ "window": {
+ "title": "Fenêtre de dépannage FlightCore",
+ "warning": "Cette fenêtre contient plusieurs fonctionnalité de résolution de problèmes courants avec Northstar et FlightCore.",
+ "disable_all_but_core": "Désactiver tous les mods (sauf ceux de Northstar)",
+ "disable_all_but_core_success": "Tous les mods sauf ceux de Northstar ont été désactivés",
+ "force_reinstall_ns": "Forcer la réinstallation de Northstar",
+ "force_delete_temp_dl": "Supprimer le dossier de téléchargement temporaire",
+ "delete_persistent_store": "Supprimer l'espace de stockage local de FlightCore",
+ "reinstall_title": "Forcer la réinstallation de Northstar",
+ "reinstall_text": "Veuillez patienter",
+ "reinstall_success": "Northstar réinstallé avec succès"
+ }
+ }
+ },
+
+ "notification": {
+ "game_folder": {
+ "new": {
+ "title": "Nouveau dossier",
+ "text": "Le dossier du jeu a bien été mis à jour."
+ },
+
+ "wrong": {
+ "title": "Mauvais dossier",
+ "text": "Le dossier sélectionné ne contient pas d'installation de Titanfall2."
+ },
+
+ "not_found": {
+ "title": "Titanfall2 non trouvé",
+ "text": "Veuillez sélectionner manuellement le dossier du jeu."
+ }
+ },
+
+ "flightcore_outdated": {
+ "title": "Mise à jour disponible !",
+ "text": "Veuillez mettre à jour FlightCore.\nVersion actuelle : {oldVersion}.\nNouvelle version : {newVersion}."
+ }
+ },
+
+ "channels": {
+ "release": {
+ "switch": {
+ "text": "Le canal de téléchargement a été réglé sur \"{canal}\"."
+ }
+ },
+
+ "names": {
+ "Northstar": "Northstar",
+ "NorthstarReleaseCandidate": "Version de pré-release"
+ }
+ }
+}
diff --git a/src-vue/src/main.ts b/src-vue/src/main.ts
index 57d41865..94a0196b 100644
--- a/src-vue/src/main.ts
+++ b/src-vue/src/main.ts
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
+import { createI18n } from "vue-i18n";
import App from './App.vue'
import ElementPlus from "element-plus";
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
@@ -10,10 +11,22 @@ import SettingsView from "./views/SettingsView.vue";
import DeveloperView from "./views/DeveloperView.vue";
import RepairView from "./views/RepairView.vue";
import {createRouter, createWebHashHistory} from "vue-router";
+import en from "./i18n/lang/en.json";
+import fr from "./i18n/lang/fr.json";
+import de from "./i18n/lang/de.json";
const app = createApp(App);
+// internationalization
+export const i18n = createI18n({
+ locale: 'en',
+ fallbackLocale: 'en',
+ messages: {
+ en, fr, de
+ }
+});
+app.use(i18n);
// styles
import 'element-plus/theme-chalk/index.css';
diff --git a/src-vue/src/plugins/modules/pull_requests.ts b/src-vue/src/plugins/modules/pull_requests.ts
index 573856ad..ff9ec322 100644
--- a/src-vue/src/plugins/modules/pull_requests.ts
+++ b/src-vue/src/plugins/modules/pull_requests.ts
@@ -1,8 +1,8 @@
-import { ElNotification } from "element-plus";
-import { invoke } from "@tauri-apps/api";
+import { invoke, shell } from "@tauri-apps/api";
import { PullsApiResponseElement } from "../../../../src-tauri/bindings/PullsApiResponseElement";
import { PullRequestType } from '../../../../src-tauri/bindings/PullRequestType';
import { store } from "../store";
+import { showErrorNotification, showNotification } from "../../utils/ui";
interface PullRequestStoreState {
searchValue: string,
@@ -20,11 +20,11 @@ export const pullRequestModule = {
await invoke<PullsApiResponseElement[]>("get_pull_requests_wrapper", { installType: pull_request_type })
.then((message) => {
switch (pull_request_type) {
- case "MODS":
+ case "Mods":
state.pull_requests_mods = message;
break;
- case "LAUNCHER":
+ case "Launcher":
state.pull_requests_launcher = message;
break;
@@ -33,42 +33,35 @@ export const pullRequestModule = {
}
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
+ async downloadLauncherPR(state: PullRequestStoreState, pull_request: PullsApiResponseElement) {
+ await invoke<string>("get_launcher_download_link", { pullRequest: pull_request })
+ .then((url) => {
+ // Open URL in default HTTPS handler (i.e. default browser)
+ shell.open(url);
+ })
+ .catch((error) => {
+ showErrorNotification(error);
+ });
+ },
+ async downloadModsPR(state: PullRequestStoreState, pull_request: PullsApiResponseElement) {
+ let url = `https://github.com/${pull_request.head.repo.full_name}/archive/refs/heads/${pull_request.head.ref}.zip`
+ shell.open(url);
+ },
async installLauncherPR(state: PullRequestStoreState, pull_request: PullsApiResponseElement) {
// Send notification telling the user to wait for the process to finish
- const notification = ElNotification({
- title: `Installing launcher PR ${pull_request.number}`,
- message: 'Please wait',
- duration: 0,
- type: 'info',
- position: 'bottom-right'
- });
+ const notification = showNotification(`Installing launcher PR ${pull_request.number}`, 'Please wait', 'info', 0);
await invoke("apply_launcher_pr", { pullRequest: pull_request, gameInstallPath: store.state.game_path })
.then((message) => {
console.log(message);
// Show user notification if mod install completed.
- ElNotification({
- title: `Done`,
- message: `Installed ${pull_request.number}: "${pull_request.title}"`,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(`Done`, `Installed ${pull_request.number}: "${pull_request.title}"`);
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
})
.finally(() => {
// Clear old notification
@@ -77,32 +70,20 @@ export const pullRequestModule = {
},
async installModsPR(state: PullRequestStoreState, pull_request: PullsApiResponseElement) {
// Send notification telling the user to wait for the process to finish
- const notification = ElNotification({
- title: `Installing mods PR ${pull_request.number}`,
- message: 'Please wait',
- duration: 0,
- type: 'info',
- position: 'bottom-right'
- });
+ const notification = showNotification(`Installing mods PR ${pull_request.number}`, 'Please wait', 'info', 0);
await invoke("apply_mods_pr", { pullRequest: pull_request, gameInstallPath: store.state.game_path })
.then((message) => {
// Show user notification if mod install completed.
- ElNotification({
- title: `Done`,
- message: `Installed ${pull_request.number}: "${pull_request.title}"\nMake sure to launch via batch file or by specifying correct profile!`,
- type: 'success',
- duration: 7_000, // in ms
- position: 'bottom-right'
- });
+ showNotification(
+ `Done`,
+ `Installed ${pull_request.number}: "${pull_request.title}"\nMake sure to launch via batch file or by specifying correct profile!`,
+ 'success',
+ 7000
+ );
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
})
.finally(() => {
// Clear old notification
diff --git a/src-vue/src/plugins/store.ts b/src-vue/src/plugins/store.ts
index 8671d12b..00b8f35a 100644
--- a/src-vue/src/plugins/store.ts
+++ b/src-vue/src/plugins/store.ts
@@ -6,7 +6,7 @@ import { invoke } from "@tauri-apps/api";
import { GameInstall } from "../utils/GameInstall";
import { ReleaseCanal } from "../utils/ReleaseCanal";
import { FlightCoreVersion } from "../../../src-tauri/bindings/FlightCoreVersion";
-import { ElNotification, NotificationHandle } from 'element-plus';
+import { NotificationHandle } from 'element-plus';
import { NorthstarState } from '../utils/NorthstarState';
import { appDir } from '@tauri-apps/api/path';
import { open } from '@tauri-apps/api/dialog';
@@ -16,8 +16,9 @@ import { ReleaseInfo } from "../../../src-tauri/bindings/ReleaseInfo";
import { ThunderstoreMod } from "../../../src-tauri/bindings/ThunderstoreMod";
import { NorthstarMod } from "../../../src-tauri/bindings/NorthstarMod";
import { searchModule } from './modules/search';
+import { i18n } from '../main';
import { pullRequestModule } from './modules/pull_requests';
-import { PullsApiResponseElement } from "../../../src-tauri/bindings/PullsApiResponseElement";
+import { showErrorNotification, showNotification } from '../utils/ui';
const persistentStore = new Store('flight-core-settings.json');
@@ -51,6 +52,7 @@ export interface FlightCoreStore {
let notification_handle: NotificationHandle;
+
export const store = createStore<FlightCoreStore>({
modules: {
search: searchModule,
@@ -87,13 +89,23 @@ export const store = createStore<FlightCoreStore>({
checkNorthstarUpdates(state) {
_get_northstar_version_number(state);
},
- toggleDeveloperMode(state) {
+ async toggleDebugMode(_state) {
+ let menu_bar_handle = document.querySelector('#fc_menu-bar');
+ if (menu_bar_handle !== null) {
+ menu_bar_handle.classList.toggle('developer_build');
+ }
+ },
+ async toggleDeveloperMode(state) {
state.developer_mode = !state.developer_mode;
// Reset tab when closing dev mode.
if (!state.developer_mode) {
store.commit('updateCurrentTab', Tabs.PLAY);
}
+
+ // Save dev mode state in persistent store
+ await persistentStore.set('dev_mode', state.developer_mode);
+ await persistentStore.save();
},
initialize(state) {
_initializeApp(state);
@@ -122,12 +134,10 @@ export const store = createStore<FlightCoreStore>({
let is_valid_titanfall2_install = await invoke("verify_install_location", { gamePath: selected }) as boolean;
if (is_valid_titanfall2_install) {
state.game_path = selected;
- ElNotification({
- title: 'New game folder',
- message: "Game folder was successfully updated.",
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(
+ i18n.global.tc('notification.game_folder.new.title'),
+ i18n.global.tc('notification.game_folder.new.text')
+ );
try {
notification_handle.close();
}
@@ -143,18 +153,17 @@ export const store = createStore<FlightCoreStore>({
// Save change in persistent store
await persistentStore.set('game-install', { value: game_install });
+ await persistentStore.save(); // explicit save to disk
// Check for Northstar install
store.commit('checkNorthstarUpdates');
}
else {
// Not valid Titanfall2 install
- ElNotification({
- title: 'Wrong folder',
- message: "Selected folder is not a valid Titanfall2 install.",
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(
+ i18n.global.tc('notification.game_folder.wrong.text'),
+ i18n.global.tc('notification.game_folder.wrong.title')
+ );
}
}
},
@@ -222,12 +231,7 @@ export const store = createStore<FlightCoreStore>({
})
.catch((error) => {
console.error(error);
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
break;
@@ -236,6 +240,23 @@ export const store = createStore<FlightCoreStore>({
break;
}
},
+ async launchGameSteam(state: any, no_checks = false) {
+ let game_install = {
+ game_path: state.game_path,
+ install_type: state.install_type
+ } as GameInstall;
+
+ await invoke("launch_northstar_steam_caller", { gameInstall: game_install, bypassChecks: no_checks })
+ .then((message) => {
+ showNotification('Success');
+ })
+ .catch((error) => {
+ console.error(error);
+ showErrorNotification(error);
+ });
+
+ return;
+ },
async fetchReleaseNotes(state: FlightCoreStore) {
if (state.releaseNotes.length !== 0) return;
state.releaseNotes = await invoke("get_northstar_release_notes");
@@ -291,12 +312,7 @@ export const store = createStore<FlightCoreStore>({
})
.catch((error) => {
console.error(error);
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
async toggleReleaseCandidate(state: FlightCoreStore) {
@@ -307,17 +323,16 @@ export const store = createStore<FlightCoreStore>({
// Save change in persistent store
await persistentStore.set('northstar-release-canal', { value: state.northstar_release_canal });
+ await persistentStore.save(); // explicit save to disk
// Update current state so that update check etc can be performed
store.commit("checkNorthstarUpdates");
// Display notification to highlight change
- ElNotification({
- title: `${state.northstar_release_canal}`,
- message: `Switched release channel to: "${state.northstar_release_canal}"`,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(
+ i18n.global.tc(`channels.names.${state.northstar_release_canal}`),
+ i18n.global.tc('channels.release.switch.text', {canal: state.northstar_release_canal}),
+ );
}
}
});
@@ -327,18 +342,18 @@ export const store = createStore<FlightCoreStore>({
* It invokes all Rust methods that are needed to initialize UI.
*/
async function _initializeApp(state: any) {
- // Enable dev mode directly if application is in debug mode
- if (await invoke("is_debug_mode")) {
- state.developer_mode = true;
-
- // Make menubar striped if debug build
- let menu_bar_handle = document.querySelector('#fc_menu-bar');
- console.log(menu_bar_handle);
- if (menu_bar_handle !== null) {
- menu_bar_handle.classList.toggle('developer_build');
- }
- } else {
- // Disable context menu in release build.
+ // Display dev view if dev mode was previously enabled.
+ const devModeEnabled: boolean = await persistentStore.get('dev_mode') ?? false;
+ const debugModeEnabled: boolean = await invoke("is_debug_mode");
+ if (devModeEnabled) {
+ store.commit('toggleDeveloperMode');
+ }
+ if (debugModeEnabled) {
+ store.commit('toggleDebugMode');
+ }
+
+ // Disable context menu in release build.
+ if (!debugModeEnabled) {
document.addEventListener('contextmenu', event => event.preventDefault());
}
@@ -391,13 +406,12 @@ async function _initializeApp(state: any) {
.catch((err) => {
// Gamepath not found or other error
console.error(err);
- notification_handle = ElNotification({
- title: 'Titanfall2 not found!',
- message: "Please manually select install location",
- type: 'error',
- position: 'bottom-right',
- duration: 0 // Duration `0` means the notification will not auto-vanish
- });
+ notification_handle = showNotification(
+ i18n.global.tc('notification.game_folder.not_found.title'),
+ i18n.global.tc('notification.game_folder.not_found.text'),
+ 'error',
+ 0 // Duration `0` means the notification will not auto-vanish
+ );
});
}
@@ -407,6 +421,7 @@ async function _initializeApp(state: any) {
// Save change in persistent store
await persistentStore.set('game-install', { value: typedResult });
+ await persistentStore.save(); // explicit save to disk
// Update UI store
state.game_path = typedResult.game_path;
@@ -433,13 +448,12 @@ async function _checkForFlightCoreUpdates(state: FlightCoreStore) {
if (flightcore_is_outdated) {
let newest_flightcore_version = await invoke("get_newest_flightcore_version") as FlightCoreVersion;
- ElNotification({
- title: 'FlightCore outdated!',
- message: `Please update FlightCore.\nRunning outdated version ${state.flightcore_version}.\nNewest is ${newest_flightcore_version.tag_name}!`,
- type: 'warning',
- position: 'bottom-right',
- duration: 0 // Duration `0` means the notification will not auto-vanish
- });
+ showNotification(
+ i18n.global.tc('notification.flightcore_outdated.title'),
+ i18n.global.tc('notification.flightcore_outdated.text', {oldVersion: state.flightcore_version, newVersion: newest_flightcore_version.tag_name}),
+ 'warning',
+ 0 // Duration `0` means the notification will not auto-vanish
+ );
}
}
@@ -455,6 +469,11 @@ function _initializeListeners(state: any) {
listen("northstar-running-ping", function (evt: TauriEvent<any>) {
state.northstar_is_running = evt.payload as boolean;
});
+
+ listen("northstar-statistics", function (evt: TauriEvent<{Ok: [number, number]}>) {
+ state.player_count = evt.payload.Ok[0];
+ state.server_count = evt.payload.Ok[1];
+ });
}
/**
diff --git a/src-vue/src/style.css b/src-vue/src/style.css
index 4401ac96..0366b0ed 100644
--- a/src-vue/src/style.css
+++ b/src-vue/src/style.css
@@ -41,3 +41,8 @@ body {
height: 100%;
color: white;
}
+
+.noModMessage {
+ color: white;
+ margin: 30px 15px;
+}
diff --git a/src-vue/src/utils/SortOptions.d.ts b/src-vue/src/utils/SortOptions.d.ts
index 6bdd0a4a..b6f180d2 100644
--- a/src-vue/src/utils/SortOptions.d.ts
+++ b/src-vue/src/utils/SortOptions.d.ts
@@ -1,8 +1,8 @@
export enum SortOptions {
- NAME_ASC = 'By name (A to Z)',
- NAME_DESC = 'By name (Z to A)',
- DATE_ASC = 'By date (from oldest)',
- DATE_DESC = 'By date (from newest)',
- MOST_DOWNLOADED = "Most downloaded",
- TOP_RATED = "Top rated"
+ NAME_ASC = 'name_asc',
+ NAME_DESC = 'name_desc',
+ DATE_ASC = 'date_asc',
+ DATE_DESC = 'date_desc',
+ MOST_DOWNLOADED = 'most_downloaded',
+ TOP_RATED = 'top_rated'
}
diff --git a/src-vue/src/utils/ui.ts b/src-vue/src/utils/ui.ts
new file mode 100644
index 00000000..b84d7666
--- /dev/null
+++ b/src-vue/src/utils/ui.ts
@@ -0,0 +1,29 @@
+import { ElNotification, NotificationHandle } from "element-plus";
+import { i18n } from "../main";
+
+/**
+ * Displays content to the user in the form of a notification appearing on screen bottom right.
+ **/
+function showNotification(
+ title: string,
+ message: string = '',
+ type: 'success' | 'warning' | 'error' | 'info' = 'success',
+ duration: number = 4500
+): NotificationHandle {
+ return ElNotification({
+ title, message, type, duration,
+ position: 'bottom-right',
+ });
+}
+
+/**
+ * Helper method displaying an error message to the user.
+ **/
+function showErrorNotification(
+ error: string,
+ title: string = i18n.global.tc('generic.error')
+): NotificationHandle {
+ return showNotification(title, error, 'error');
+}
+
+export {showNotification, showErrorNotification};
diff --git a/src-vue/src/views/ChangelogView.vue b/src-vue/src/views/ChangelogView.vue
index 9335220e..e68beded 100644
--- a/src-vue/src/views/ChangelogView.vue
+++ b/src-vue/src/views/ChangelogView.vue
@@ -46,7 +46,7 @@ export default defineComponent({
let content: string = releaseBody.replaceAll(/\@(\S+)/g, `<a target="_blank" href="https://github.com/$1">@$1</a>`);
// PR's links formatting
- content = content.replaceAll(/\[\#(\S+)\]\(([^)]+)\)/g, `<a target="_blank" href="$2">#$1</a>`);
+ content = content.replaceAll(/\[(\S*)\#(\S+)\]\(([^)]+)\)/g, `<a target="_blank" href="$3">$1#$2</a>`);
return marked.parse(content, {breaks: true});
},
diff --git a/src-vue/src/views/DeveloperView.vue b/src-vue/src/views/DeveloperView.vue
index 52117b8d..7e11bd11 100644
--- a/src-vue/src/views/DeveloperView.vue
+++ b/src-vue/src/views/DeveloperView.vue
@@ -27,12 +27,8 @@
Launch Northstar (bypass all checks)
</el-button>
- <h3>Mod install:</h3>
-
- <el-input v-model="mod_to_install_field_string" placeholder="Please input Thunderstore dependency string (example: AuthorName-ModName-1.2.3)" clearable />
-
- <el-button type="primary" @click="installMod">
- Install mod
+ <el-button type="primary" @click="launchGameViaSteam">
+ Launch Northstar via Steam
</el-button>
<h3>Repair:</h3>
@@ -44,6 +40,48 @@
<h3>Testing</h3>
<pull-requests-selector />
+
+ <h3>Mod install:</h3>
+
+ <el-input v-model="mod_to_install_field_string" placeholder="Please input Thunderstore dependency string (example: AuthorName-ModName-1.2.3)" clearable />
+
+ <el-button type="primary" @click="installMod">
+ Install mod
+ </el-button>
+
+ <h3>Release management</h3>
+
+ <el-button type="primary" @click="getTags">
+ Get tags
+ </el-button>
+
+ <el-select v-model="firstTag" class="m-2" placeholder="First tag">
+ <el-option
+ v-for="item in ns_release_tags"
+ :key="item.value"
+ :label="item.label"
+ :value="item"
+ />
+ </el-select>
+ <el-select v-model="secondTag" class="m-2" placeholder="Second tag">
+ <el-option
+ v-for="item in ns_release_tags"
+ :key="item.value"
+ :label="item.label"
+ :value="item"
+ />
+ </el-select>
+
+ <el-button type="primary" @click="compareTags">
+ Compare Tags
+ </el-button>
+
+ <el-input
+ v-model="release_notes_text"
+ type="textarea"
+ :rows="5"
+ placeholder="Output"
+ />
</el-scrollbar>
</div>
</template>
@@ -51,9 +89,10 @@
<script lang="ts">
import { defineComponent } from "vue";
import { invoke } from "@tauri-apps/api";
-import { ElNotification } from "element-plus";
import { GameInstall } from "../utils/GameInstall";
+import { TagWrapper } from "../../../src-tauri/bindings/TagWrapper";
import PullRequestsSelector from "../components/PullRequestsSelector.vue";
+import { showErrorNotification, showNotification } from "../utils/ui";
export default defineComponent({
name: "DeveloperView",
@@ -63,44 +102,54 @@ export default defineComponent({
data() {
return {
mod_to_install_field_string : "",
+ release_notes_text : "",
+ first_tag: { label: '', value: {name: ''} },
+ second_tag: { label: '', value: {name: ''} },
+ ns_release_tags: [] as TagWrapper[],
}
},
+ computed: {
+ firstTag: {
+ get(): TagWrapper {
+ return this.first_tag;
+ },
+ set(value: TagWrapper) {
+ this.first_tag = value;
+ }
+ },
+ secondTag: {
+ get(): TagWrapper {
+ return this.second_tag;
+ },
+ set(value: TagWrapper) {
+ this.second_tag = value;
+ }
+ },
+ },
methods: {
disableDevMode() {
this.$store.commit('toggleDeveloperMode');
},
async crashApplication() {
await invoke("force_panic");
- ElNotification({
- title: 'Error',
- message: "Never should have been able to get here!",
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification("Never should have been able to get here!");
},
async checkLinuxCompatibility() {
await invoke("linux_checks")
.then(() => {
- ElNotification({
- title: 'Linux compatible',
- message: 'All checks passed',
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification('Linux compatible', 'All checks passed');
})
.catch((error) => {
- ElNotification({
- title: 'Not linux compatible',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showNotification('Not Linux compatible', error, 'error');
console.error(error);
});
},
async launchGameWithoutChecks() {
this.$store.commit('launchGame', true);
},
+ async launchGameViaSteam() {
+ this.$store.commit('launchGameSteam', true);
+ },
async getInstalledMods() {
let game_install = {
game_path: this.$store.state.game_path,
@@ -112,20 +161,10 @@ export default defineComponent({
console.log(message);
// Just a visual indicator that it worked
- ElNotification({
- title: 'Success',
- message: "Success",
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification('Success');
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
async installMod() {
@@ -134,22 +173,32 @@ export default defineComponent({
install_type: this.$store.state.install_type
} as GameInstall;
let mod_to_install = this.mod_to_install_field_string;
- await invoke("install_mod_caller", { gameInstall: game_install, thunderstoreModString: mod_to_install }).then((message) => {
+ await invoke<string>("install_mod_caller", { gameInstall: game_install, thunderstoreModString: mod_to_install }).then((message) => {
// Show user notification if mod install completed.
- ElNotification({
- title: `Installed ${mod_to_install}`,
- message: message as string,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(`Installed ${mod_to_install}`, message);
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
+ });
+ },
+ async getTags() {
+ await invoke<TagWrapper[]>("get_list_of_tags")
+ .then((message) => {
+ this.ns_release_tags = message;
+ showNotification("Done", "Fetched tags");
+ })
+ .catch((error) => {
+ showErrorNotification(error);
+ });
+ },
+ async compareTags() {
+ await invoke<string>("compare_tags", {firstTag: this.firstTag.value, secondTag: this.secondTag.value})
+ .then((message) => {
+ this.release_notes_text = message;
+ showNotification("Done", "Generated release notes");
+ })
+ .catch((error) => {
+ showErrorNotification(error);
});
},
}
diff --git a/src-vue/src/views/PlayView.vue b/src-vue/src/views/PlayView.vue
index beca6724..76f4f328 100644
--- a/src-vue/src/views/PlayView.vue
+++ b/src-vue/src/views/PlayView.vue
@@ -1,12 +1,13 @@
<script lang="ts">
-import { ElNotification } from 'element-plus';
import { Tabs } from "../utils/Tabs";
import PlayButton from '../components/PlayButton.vue';
import { defineComponent } from "vue";
+import InstallProgressBar from "../components/InstallProgressBar.vue";
export default defineComponent({
components: {
- PlayButton
+ PlayButton,
+ InstallProgressBar
},
computed: {
northstarIsRunning(): boolean {
@@ -34,37 +35,40 @@ export default defineComponent({
<div class="fc_launch__container">
<div class="fc_title">Northstar</div>
<div class="fc_northstar__version__container">
- {{ northstarVersion === '' ? 'Unknown version' : `v${northstarVersion}` }}
+ {{ northstarVersion === '' ? $t('play.unknown_version') : `v${northstarVersion}` }}
<div v-if="northstarVersion !== ''" class="fc_changelog__link" @click="showChangelogPage">
- (see patch notes)
+ ({{ $t('play.see_patch_notes') }})
</div>
<div v-if="playerCount >= 0" class="fc-stats__container">
- {{ playerCount }} players,
- {{ serverCount }} servers
+ {{ playerCount }} {{ $t('play.players') }},
+ {{ serverCount }} {{ $t('play.servers') }}
</div>
<div v-else="playerCount >= 0" class="fc-stats__container">
- Unable to load playercount
+ {{ $t('play.unable_to_load_playercount') }}
</div>
</div>
- <div>
+
+ <!-- Align play button and services state container -->
+ <div style="display: flex">
<PlayButton />
<div v-if="$store.state.developer_mode" id="fc_services__status">
<div>
- <div class="fc_version__line">Northstar is running: </div>
+ <div class="fc_version__line">{{ $t('play.northstar_running') }}</div>
<div class="fc_version__line fc_version__line__boolean"> {{ northstarIsRunning }}</div>
</div>
<div>
- <div class="fc_version__line">Origin is running: </div>
+ <div class="fc_version__line">{{ $t('play.origin_running') }}</div>
<div class="fc_version__line fc_version__line__boolean">{{ $store.state.origin_is_running }}</div>
</div>
</div>
</div>
+ <InstallProgressBar />
</div>
</template>
<style scoped>
.fc_launch__container {
- margin: 50px;
+ margin: 50px 50px 30px 50px;
position: fixed;
bottom: 0;
}
@@ -102,13 +106,9 @@ export default defineComponent({
border-color: var(--el-color-primary);
}
-
#fc_services__status {
- display: inline-block;
- position: fixed;
- padding: 10px 20px;
color: #e8edef;
- bottom: 43px;
+ align-self: end;
}
.fc_version__line {
diff --git a/src-vue/src/views/RepairView.vue b/src-vue/src/views/RepairView.vue
index e7cd479a..c11518d0 100644
--- a/src-vue/src/views/RepairView.vue
+++ b/src-vue/src/views/RepairView.vue
@@ -1,30 +1,30 @@
<template>
<div class="fc-container">
<el-scrollbar>
- <el-alert title="Info" type="info" :closable="false" show-icon>
- This window contains various functionality to repair common issues with Northstar and FlightCore.
+ <el-alert :title="$t('generic.informationShort')" type="info" :closable="false" show-icon>
+ {{ $t('settings.repair.window.warning') }}
</el-alert>
- <h1>Repair</h1>
+ <h1>{{ $t('settings.repair.title') }}</h1>
<h2>Northstar</h2>
<el-button type="primary" @click="disableAllModsButCore">
- Disable all but core mods
+ {{ $t('settings.repair.window.disable_all_but_core') }}
</el-button>
<el-button type="primary" @click="forceInstallNorthstar">
- Force reinstall Northstar
+ {{ $t('settings.repair.window.force_reinstall_ns') }}
</el-button>
<h2>FlightCore</h2>
<el-button type="primary" @click="cleanUpDownloadFolder">
- Force delete temp download folder
+ {{ $t('settings.repair.window.force_delete_temp_dl') }}
</el-button>
<el-button type="primary" @click="clearFlightCorePersistentStore">
- Delete FlightCore persistent store
+ {{ $t('settings.repair.window.delete_persistent_store') }}
</el-button>
</el-scrollbar>
</div>
@@ -32,15 +32,22 @@
<script lang="ts">
import { defineComponent } from "vue";
-import { ElNotification } from "element-plus";
import { GameInstall } from "../utils/GameInstall";
+import { InstallProgress } from "../../../src-tauri/bindings/InstallProgress";
import { invoke } from "@tauri-apps/api";
import { ReleaseCanal } from "../utils/ReleaseCanal";
import { Store } from 'tauri-plugin-store-api';
+import { showErrorNotification, showNotification } from "../utils/ui";
+import { appWindow } from "@tauri-apps/api/window";
const persistentStore = new Store('flight-core-settings.json');
export default defineComponent({
name: "RepairView",
+ computed: {
+ lang(): string {
+ return this.$root!.$i18n.locale;
+ }
+ },
methods: {
async disableAllModsButCore() {
let game_install = {
@@ -49,20 +56,10 @@ export default defineComponent({
} as GameInstall;
await invoke("disable_all_but_core", { gameInstall: game_install })
.then((message) => {
- ElNotification({
- title: 'Success',
- message: "Disabled all mods but core",
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(this.$t('generic.success'), this.$t('settings.repair.window.disable_all_but_core_success'));
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
async forceInstallNorthstar() {
@@ -72,33 +69,32 @@ export default defineComponent({
} as GameInstall;
// Send notification telling the user to wait for the process to finish
- const notification = ElNotification({
- title: 'Force reinstalling Northstar',
- message: 'Please wait',
- duration: 0,
- type: 'info',
- position: 'bottom-right'
- });
+ const notification = showNotification(
+ this.$t('settings.repair.window.reinstall_title'),
+ this.$t('settings.repair.window.reinstall_text'),
+ 'info',
+ 0
+ );
let install_northstar_result = invoke("install_northstar_caller", { gamePath: game_install.game_path, northstarPackageName: ReleaseCanal.RELEASE });
+
+ appWindow.listen<InstallProgress>(
+ 'northstar-install-download-progress',
+ ({ event, payload }) => {
+ let typed_payload = payload;
+ console.log("current_downloaded:", typed_payload.current_downloaded);
+ console.log("total_size: ", typed_payload.total_size);
+ console.log("state: ", typed_payload.state);
+ }
+ );
await install_northstar_result
.then((message) => {
// Send notification
- ElNotification({
- title: `Done`,
- message: `Successfully reinstalled Northstar`,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(this.$t('generic.done'), this.$t('settings.repair.window.reinstall_success'));
this.$store.commit('checkNorthstarUpdates');
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
console.error(error);
})
.finally(() => {
@@ -113,20 +109,10 @@ export default defineComponent({
} as GameInstall;
await invoke("clean_up_download_folder_caller", { gameInstall: game_install, force: true }).then((message) => {
// Show user notification if task completed.
- ElNotification({
- title: `Done`,
- message: `Done`,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(this.$t('generic.done'), this.$t('generic.done'));
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
async clearFlightCorePersistentStore() {
@@ -135,6 +121,13 @@ export default defineComponent({
// ...and save
await persistentStore.save();
},
+ },
+ watch: {
+ // Lang value is propagated to repair view after it's mounted, so we need to watch
+ // its value, and update window title accordingly.
+ lang(newv: string) {
+ appWindow.setTitle( this.$t('settings.repair.window.title') );
+ }
}
});
</script>
diff --git a/src-vue/src/views/SettingsView.vue b/src-vue/src/views/SettingsView.vue
index c93d69ff..772a4c5c 100644
--- a/src-vue/src/views/SettingsView.vue
+++ b/src-vue/src/views/SettingsView.vue
@@ -3,53 +3,68 @@
<el-scrollbar>
<div class="fc_settings__container">
<!-- Game folder location -->
- <h3>Manage installation</h3>
- <el-input
- v-model="$store.state.game_path"
- class="w-50 m-2"
- placeholder="Choose installation folder"
- @click="updateGamePath"
- >
- <template #prepend>
- <el-button icon="Folder" @click="updateGamePath"/>
- </template>
- </el-input>
+ <div class="fc_parameter__panel">
+ <h3>{{ $t('settings.manage_install') }}</h3>
+ <el-input
+ v-model="$store.state.game_path"
+ :placeholder="$t('settings.choose_folder')"
+ @click="updateGamePath"
+ >
+ <template #prepend>
+ <el-button icon="Folder" @click="updateGamePath"/>
+ </template>
+ </el-input>
+ </div>
<!-- Thunderstore mods per page configuration -->
<div class="fc_parameter__panel">
- <h3>Number of Thunderstore mods per page</h3>
+ <h3>{{ $t('settings.nb_ts_mods_per_page') }}</h3>
<h6>
- This has an impact on display performances when browsing Thunderstore mods.<br>
- Set this value to 0 to disable pagination.
+ {{ $t('settings.nb_ts_mods_per_page_desc1') }}<br>
+ {{ $t('settings.nb_ts_mods_per_page_desc2') }}
</h6>
- <el-input
- v-model="modsPerPage"
+ <el-input
+ v-model="modsPerPage"
type="number"
>
<template #append>
- <el-button @click="modsPerPage = 20">Reset to default</el-button>
+ <el-button @click="modsPerPage = 20">
+ {{ $t('settings.nb_ts_mods_reset') }}
+ </el-button>
</template>
</el-input>
</div>
- <h3>Repair</h3>
- <el-button type="primary" @click="openRepairWindow">
- Open Repair window
- </el-button>
+ <!-- Interface localization -->
+ <div class="fc_parameter__panel">
+ <h3>{{ $t('settings.language') }}</h3>
+ <language-selector/>
+ </div>
+
+ <!-- Repair window -->
+ <div class="fc_parameter__panel">
+ <h3>{{ $t('settings.repair.title') }}</h3>
+ <el-button type="primary" @click="openRepairWindow">
+ {{ $t('settings.repair.open_window') }}
+ </el-button>
+ </div>
+
+ <!-- About section -->
+ <div class="fc_parameter__panel">
+ <h3>{{ $t('settings.about') }}</h3>
+ <div class="fc_northstar__version" @click="activateDeveloperMode">
+ {{ $t('settings.flightcore_version') }} {{ flightcoreVersion === '' ? 'Unknown version' : `${flightcoreVersion}` }}
+ </div>
+ </div>
- <h3>About:</h3>
- <div class="fc_northstar__version" @click="activateDeveloperMode">
- FlightCore Version: {{ flightcoreVersion === '' ? 'Unknown version' : `${flightcoreVersion}` }}
+ <!-- Testing section -->
+ <div class="fc_parameter__panel">
+ <h3>{{ $t('settings.testing') }}</h3>
+ <span>
+ {{ $t('settings.enable_test_channels') }}
+ <el-switch v-model="enableReleasesSwitch"></el-switch>
+ </span>
</div>
- <br />
- <br />
- UI design inspired by <el-link :underline="false" target="_blank" href="https://github.com/TFORevive/tforevive_launcher/" type="primary">TFORevive Launcher</el-link> (not yet public)
-
- <h3>Testing:</h3>
- <span>
- Enable testing release channels
- <el-switch v-model="enableReleasesSwitch"></el-switch>
- </span>
</div>
</el-scrollbar>
</div>
@@ -57,14 +72,18 @@
<script lang="ts">
import { defineComponent } from "vue";
-import { ElNotification } from 'element-plus';
import { invoke } from "@tauri-apps/api";
import { ReleaseCanal } from "../utils/ReleaseCanal";
import { Store } from 'tauri-plugin-store-api';
+import { showErrorNotification, showNotification } from "../utils/ui";
+import LanguageSelector from "../components/LanguageSelector.vue";
const persistentStore = new Store('flight-core-settings.json');
export default defineComponent({
name: "SettingsView",
+ components: {
+ LanguageSelector
+ },
data() {
return {
developerModeClicks: 0
@@ -78,9 +97,10 @@ export default defineComponent({
get(): boolean {
return this.$store.state.enableReleasesSwitch;
},
- set(value: boolean): void {
+ async set(value: boolean): Promise<void> {
this.$store.state.enableReleasesSwitch = value;
persistentStore.set('northstar-releases-switching', { value });
+ await persistentStore.save(); // explicit save to disk
// When disabling switch, we switch release canal to stable release, to avoid users being
// stuck with release candidate after disabling release switching.
@@ -93,23 +113,23 @@ export default defineComponent({
get(): number {
return this.$store.state.mods_per_page;
},
- set(value: number) {
+ async set(value: number) {
this.$store.state.mods_per_page = value;
persistentStore.set('thunderstore-mods-per-page', { value });
+ await persistentStore.save(); // explicit save to disk
}
}
},
methods: {
activateDeveloperMode() {
this.developerModeClicks += 1;
- if (this.developerModeClicks >= 6) {
- this.$store.state.developer_mode = true;
- ElNotification({
- title: 'Watch out!',
- message: 'Developer mode enabled.',
- type: 'info',
- position: 'bottom-right'
- });
+ if (this.developerModeClicks >= 6 && !this.$store.state.developer_mode) {
+ this.$store.commit('toggleDeveloperMode');
+ showNotification(
+ this.$t('settings.dev_mode_enabled_title'),
+ this.$t('settings.dev_mode_enabled_text'),
+ 'info'
+ );
this.developerModeClicks = 0;
}
},
@@ -120,12 +140,7 @@ export default defineComponent({
await invoke("open_repair_window")
.then((message) => { })
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
});
},
},
@@ -154,7 +169,7 @@ h3:first-of-type {
font-weight: unset;
}
-.el-input {
+.el-input, .el-select {
width: 50%;
}
@@ -165,7 +180,7 @@ h3:first-of-type {
/* Parameter panel styles */
.fc_parameter__panel {
- margin: 30px 0;
+ margin-bottom: 30px;
}
.fc_parameter__panel h3 {
diff --git a/src-vue/src/views/mods/LocalModsView.vue b/src-vue/src/views/mods/LocalModsView.vue
index ed801b7a..3fb90bdc 100644
--- a/src-vue/src/views/mods/LocalModsView.vue
+++ b/src-vue/src/views/mods/LocalModsView.vue
@@ -1,38 +1,44 @@
<template>
- <el-scrollbar>
- <div>
- <p v-if="mods.length === 0">No mods were found.</p>
- <el-card v-else shadow="hover" v-for="mod in mods" v-bind:key="mod.name">
- <el-switch style="--el-switch-on-color: #13ce66; --el-switch-off-color: #8957e5" v-model="mod.enabled"
- :before-change="() => updateWhichModsEnabled(mod)" :loading="global_load_indicator" />
- <el-popconfirm
- title="Are you sure to delete this mod?"
- @confirm="deleteMod(mod)"
- >
- <template #reference>
- <el-button type="danger">Delete</el-button>
- </template>
- </el-popconfirm>
- {{ mod.name }}
- <span v-if="mod.version != null">(v{{ mod.version }})</span>
- <img
- v-if="mod.thunderstore_mod_string != null"
- title="This Northstar mod is part of a Thunderstore mod"
- src="/src/assets/thunderstore-icon.png"
- class="image"
- height="16"
- />
- </el-card>
- </div>
+ <!-- Message displayed if no mod matched searched words -->
+ <div v-if="mods.length === 0" class="noModMessage">
+ {{ $t('mods.local.no_mods') }}
+ </div>
+
+ <el-scrollbar v-else>
+ <el-card shadow="hover" v-for="mod in mods" v-bind:key="mod.name">
+ <el-switch style="--el-switch-on-color: #13ce66; --el-switch-off-color: #8957e5" v-model="mod.enabled"
+ :before-change="() => updateWhichModsEnabled(mod)" :loading="global_load_indicator" />
+ <el-popconfirm
+ :title="$t('mods.local.delete_confirm')"
+ :confirm-button-text="$t('generic.yes')"
+ :cancel-button-text="$t('generic.no')"
+ @confirm="deleteMod(mod)"
+ >
+ <template #reference>
+ <el-button type="danger">
+ {{ $t('mods.local.delete') }}
+ </el-button>
+ </template>
+ </el-popconfirm>
+ {{ mod.name }}
+ <span v-if="mod.version != null">(v{{ mod.version }})</span>
+ <img
+ v-if="mod.thunderstore_mod_string != null"
+ :title="$t('mods.local.part_of_ts_mod')"
+ src="/src/assets/thunderstore-icon.png"
+ class="image"
+ height="16"
+ />
+ </el-card>
</el-scrollbar>
</template>
<script lang="ts">
import { invoke } from '@tauri-apps/api';
-import { ElNotification } from 'element-plus';
import { defineComponent } from 'vue';
import { GameInstall } from '../../utils/GameInstall';
import { NorthstarMod } from "../../../../src-tauri/bindings/NorthstarMod";
+import { showErrorNotification, showNotification } from '../../utils/ui';
export default defineComponent({
name: 'LocalModsView',
@@ -79,12 +85,7 @@ export default defineComponent({
})
}
catch (error) {
- ElNotification({
- title: 'Error',
- message: `${error}`,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(`${error}`);
this.global_load_indicator = false;
return false;
}
@@ -100,19 +101,10 @@ export default defineComponent({
await invoke("delete_northstar_mod", { gameInstall: game_install, nsmodName: mod.name })
.then((message) => {
// Just a visual indicator that it worked
- ElNotification({
- title: `Success deleting ${mod.name}`,
- type: 'success',
- position: 'bottom-right'
- });
+ showNotification(this.$t('mods.local.success_deleting', {modName: mod.name}));
})
.catch((error) => {
- ElNotification({
- title: 'Error',
- message: error,
- type: 'error',
- position: 'bottom-right'
- });
+ showErrorNotification(error);
})
.finally(() => {
this.$store.commit('loadInstalledMods');
diff --git a/src-vue/src/views/mods/ThunderstoreModsView.vue b/src-vue/src/views/mods/ThunderstoreModsView.vue
index 19809f3e..5a7270df 100644
--- a/src-vue/src/views/mods/ThunderstoreModsView.vue
+++ b/src-vue/src/views/mods/ThunderstoreModsView.vue
@@ -3,11 +3,17 @@
<div v-if="mods.length === 0" class="fc__changelog__container">
<el-progress :show-text="false" :percentage="50" :indeterminate="true" />
</div>
+
+ <!-- Message displayed if no mod matched searched words -->
+ <div v-else-if="filteredMods.length === 0" class="noModMessage">
+ {{ $t('mods.online.no_match') }}<br/>
+ {{ $t('mods.online.try_another_search') }}
+ </div>
+
<el-scrollbar v-else class="container" ref="scrollbar">
<div class="card-container">
- <div class="pagination_container">
+ <div class="pagination_container" v-if="shouldDisplayPagination">
<el-pagination
- v-if="shouldDisplayPagination"
:currentPage="currentPageIndex + 1"
layout="prev, pager, next"
:page-size="modsPerPage"
@@ -16,12 +22,6 @@
/>
</div>
- <!-- Message displayed if no mod matched searched words -->
- <div v-if="filteredMods.length === 0" class="modMessage">
- No matching mod has been found.<br/>
- Try another search!
- </div>
-
<!-- Mod cards -->
<thunderstore-mod-card v-for="mod of currentPageMods" v-bind:key="mod.name" :mod="mod" />
</div>
@@ -199,11 +199,6 @@ export default defineComponent({
margin: 0 0 0 10px !important;
}
-.modMessage {
- color: white;
- margin: 20px 5px;
-}
-
.card-container {
margin: 0 auto;
}