Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
654405e016
|
||
|
|
c461a0a459
|
||
|
|
16ce04ba7a
|
||
|
|
1700d127c3
|
||
|
|
7c47b32c68
|
||
|
|
56781b5d1a
|
||
|
|
6ce7c2a8ae
|
||
|
|
15dd9ad013
|
||
|
|
217222a22b
|
||
|
|
39da88a856
|
||
|
|
29c90d2f06
|
||
|
|
467dd1b0a1
|
||
|
|
a1a69c9ac5
|
||
|
|
0163e09b02
|
||
|
|
fbb4b15971
|
||
|
|
77933a2298
|
||
|
|
7209a36752
|
||
|
|
f00b78996a
|
||
|
|
38779c1715
|
||
|
|
fa67d26fe9
|
||
|
|
81d47ea71d
|
||
|
|
70433b76ab
|
||
|
|
105913503c
|
||
|
|
2ad54f976b
|
||
|
|
d8d59febbb
|
||
|
|
a618c76292
|
||
|
|
118fad49d8
|
||
|
|
62c0f84d14
|
||
|
|
4a96911885
|
||
|
|
51cd3c3620
|
||
|
|
dfbfac33d3
|
||
|
|
03ec5a220a
|
||
|
|
abebc0bb08
|
||
|
|
3c7487f20f
|
||
|
|
97231c6b38
|
||
|
|
a93e4ab521
|
||
|
|
e7c48b182f
|
||
|
|
16dd3fd613
|
||
|
|
c91303d194
|
||
|
|
a76e81789b
|
||
|
|
c909973aab
|
||
|
|
28f694e4e7
|
||
|
|
508e4d1640
|
||
|
|
41a66eaf86
|
||
|
|
ac4b76fa46
|
||
|
|
2e02275c29
|
||
|
|
dbaac6a6a6
|
||
|
|
68525f161e
|
||
|
|
d386454e91
|
||
|
|
6e05aac871
|
||
|
|
fbe3feec9d
|
||
|
|
1c540ad35b
|
||
|
|
4ec597967a
|
||
|
|
be4b8fc3ee
|
||
|
|
d9fa967ade
|
No files matched your search
@@ -5,11 +5,11 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "lncvrt-games-launcher",
|
"name": "lncvrt-games-launcher",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
"@fortawesome/fontawesome-svg-core": "7.2.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "7.1.0",
|
"@fortawesome/free-brands-svg-icons": "7.2.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "7.1.0",
|
"@fortawesome/free-solid-svg-icons": "7.2.0",
|
||||||
"@fortawesome/react-fontawesome": "3.1.1",
|
"@fortawesome/react-fontawesome": "3.2.0",
|
||||||
"@tauri-apps/api": "2.9.1",
|
"@tauri-apps/api": "2.10.1",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
|
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
|
||||||
"@tauri-apps/plugin-dialog": "2.6.0",
|
"@tauri-apps/plugin-dialog": "2.6.0",
|
||||||
"@tauri-apps/plugin-fs": "2.4.5",
|
"@tauri-apps/plugin-fs": "2.4.5",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"@tauri-apps/plugin-opener": "2.5.3",
|
"@tauri-apps/plugin-opener": "2.5.3",
|
||||||
"@tauri-apps/plugin-os": "2.3.2",
|
"@tauri-apps/plugin-os": "2.3.2",
|
||||||
"@tauri-apps/plugin-window-state": "2.4.1",
|
"@tauri-apps/plugin-window-state": "2.4.1",
|
||||||
"axios": "1.13.4",
|
"axios": "1.13.5",
|
||||||
"next": "16.1.6",
|
"next": "16.1.6",
|
||||||
"pretty-bytes": "7.1.0",
|
"pretty-bytes": "7.1.0",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
@@ -26,10 +26,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "3.3.3",
|
"@eslint/eslintrc": "3.3.3",
|
||||||
"@tailwindcss/postcss": "4.1.18",
|
"@tailwindcss/postcss": "4.1.18",
|
||||||
"@tauri-apps/cli": "2.9.6",
|
"@tauri-apps/cli": "2.10.0",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.1.0",
|
"@types/node": "25.2.3",
|
||||||
"@types/react": "19.2.10",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"eslint": "9.39.2",
|
"eslint": "9.39.2",
|
||||||
@@ -42,13 +42,13 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
"@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
|
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||||
|
|
||||||
"@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="],
|
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
|
||||||
|
|
||||||
"@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="],
|
"@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
||||||
|
|
||||||
"@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="],
|
"@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||||
|
|
||||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
||||||
|
|
||||||
@@ -66,13 +66,13 @@
|
|||||||
|
|
||||||
"@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
|
"@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
|
||||||
|
|
||||||
"@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
|
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
|
||||||
|
|
||||||
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||||
|
|
||||||
"@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="],
|
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||||
|
|
||||||
"@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
|
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||||
|
|
||||||
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
||||||
|
|
||||||
@@ -98,15 +98,15 @@
|
|||||||
|
|
||||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
|
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
|
||||||
|
|
||||||
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.1.0", "", {}, "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA=="],
|
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@7.2.0", "", {}, "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw=="],
|
||||||
|
|
||||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA=="],
|
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q=="],
|
||||||
|
|
||||||
"@fortawesome/free-brands-svg-icons": ["@fortawesome/free-brands-svg-icons@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-9byUd9bgNfthsZAjBl6GxOu1VPHgBuRUP9juI7ZoM98h8xNPTCTagfwUFyYscdZq4Hr7gD1azMfM9s5tIWKZZA=="],
|
"@fortawesome/free-brands-svg-icons": ["@fortawesome/free-brands-svg-icons@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-VNG8xqOip1JuJcC3zsVsKRQ60oXG9+oYNDCosjoU/H9pgYmLTEwWw8pE0jhPz/JWdHeUuK6+NQ3qsM4gIbdbYQ=="],
|
||||||
|
|
||||||
"@fortawesome/free-solid-svg-icons": ["@fortawesome/free-solid-svg-icons@7.1.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" } }, "sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA=="],
|
"@fortawesome/free-solid-svg-icons": ["@fortawesome/free-solid-svg-icons@7.2.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "7.2.0" } }, "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w=="],
|
||||||
|
|
||||||
"@fortawesome/react-fontawesome": ["@fortawesome/react-fontawesome@3.1.1", "", { "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~6 || ~7", "react": "^18.0.0 || ^19.0.0" } }, "sha512-EDllr9hpodc21odmUywHS1alXNiCd4E8sp5GJ5s7wYINz8vSmMiNWpALTiuYODb865YyQ/NlyiN4mbXp7HCNqg=="],
|
"@fortawesome/react-fontawesome": ["@fortawesome/react-fontawesome@3.2.0", "", { "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~6 || ~7", "react": "^18.0.0 || ^19.0.0" } }, "sha512-E9Gu1hqd6JussVO26EC4WqRZssXMnQr2ol7ZNWkkFOH8jZUaxDJ9Z9WF9wIVkC+kJGXUdY3tlffpDwEKfgQrQw=="],
|
||||||
|
|
||||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||||
|
|
||||||
@@ -240,31 +240,31 @@
|
|||||||
|
|
||||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
|
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
|
||||||
|
|
||||||
"@tauri-apps/api": ["@tauri-apps/api@2.9.1", "", {}, "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw=="],
|
"@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
|
||||||
|
|
||||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.9.6", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.9.6", "@tauri-apps/cli-darwin-x64": "2.9.6", "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.6", "@tauri-apps/cli-linux-arm64-gnu": "2.9.6", "@tauri-apps/cli-linux-arm64-musl": "2.9.6", "@tauri-apps/cli-linux-riscv64-gnu": "2.9.6", "@tauri-apps/cli-linux-x64-gnu": "2.9.6", "@tauri-apps/cli-linux-x64-musl": "2.9.6", "@tauri-apps/cli-win32-arm64-msvc": "2.9.6", "@tauri-apps/cli-win32-ia32-msvc": "2.9.6", "@tauri-apps/cli-win32-x64-msvc": "2.9.6" }, "bin": { "tauri": "tauri.js" } }, "sha512-3xDdXL5omQ3sPfBfdC8fCtDKcnyV7OqyzQgfyT5P3+zY6lcPqIYKQBvUasNvppi21RSdfhy44ttvJmftb0PCDw=="],
|
"@tauri-apps/cli": ["@tauri-apps/cli@2.10.0", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.10.0", "@tauri-apps/cli-darwin-x64": "2.10.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0", "@tauri-apps/cli-linux-arm64-gnu": "2.10.0", "@tauri-apps/cli-linux-arm64-musl": "2.10.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.0", "@tauri-apps/cli-linux-x64-gnu": "2.10.0", "@tauri-apps/cli-linux-x64-musl": "2.10.0", "@tauri-apps/cli-win32-arm64-msvc": "2.10.0", "@tauri-apps/cli-win32-ia32-msvc": "2.10.0", "@tauri-apps/cli-win32-x64-msvc": "2.10.0" }, "bin": { "tauri": "tauri.js" } }, "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.9.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gf5no6N9FCk1qMrti4lfwP77JHP5haASZgVbBgpZG7BUepB3fhiLCXGUK8LvuOjP36HivXewjg72LTnPDScnQQ=="],
|
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.10.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.9.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-oWh74WmqbERwwrwcueJyY6HYhgCksUc6NT7WKeXyrlY/FPmNgdyQAgcLuTSkhRFuQ6zh4Np1HZpOqCTpeZBDcw=="],
|
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.9.6", "", { "os": "linux", "cpu": "arm" }, "sha512-/zde3bFroFsNXOHN204DC2qUxAcAanUjVXXSdEGmhwMUZeAQalNj5cz2Qli2elsRjKN/hVbZOJj0gQ5zaYUjSg=="],
|
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.10.0", "", { "os": "linux", "cpu": "arm" }, "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-pvbljdhp9VOo4RnID5ywSxgBs7qiylTPlK56cTk7InR3kYSTJKYMqv/4Q/4rGo/mG8cVppesKIeBMH42fw6wjg=="],
|
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.10.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-02TKUndpodXBCR0oP//6dZWGYcc22Upf2eP27NvC6z0DIqvkBBFziQUcvi2n6SrwTRL0yGgQjkm9K5NIn8s6jw=="],
|
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.10.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.9.6", "", { "os": "linux", "cpu": "none" }, "sha512-fmp1hnulbqzl1GkXl4aTX9fV+ubHw2LqlLH1PE3BxZ11EQk+l/TmiEongjnxF0ie4kV8DQfDNJ1KGiIdWe1GvQ=="],
|
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.10.0", "", { "os": "linux", "cpu": "none" }, "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-vY0le8ad2KaV1PJr+jCd8fUF9VOjwwQP/uBuTJvhvKTloEwxYA/kAjKK9OpIslGA9m/zcnSo74czI6bBrm2sYA=="],
|
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.10.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-TOEuB8YCFZTWVDzsO2yW0+zGcoMiPPwcUgdnW1ODnmgfwccpnihDRoks+ABT1e3fHb1ol8QQWsHSCovb3o2ENQ=="],
|
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.10.0", "", { "os": "linux", "cpu": "x64" }, "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.9.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ujmDGMRc4qRLAnj8nNG26Rlz9klJ0I0jmZs2BPpmNNf0gM/rcVHhqbEkAaHPTBVIrtUdf7bGvQAD2pyIiUrBHQ=="],
|
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.10.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.9.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-S4pT0yAJgFX8QRCyKA1iKjZ9Q/oPjCZf66A/VlG5Yw54Nnr88J1uBpmenINbXxzyhduWrIXBaUbEY1K80ZbpMg=="],
|
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.10.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-ldWuWSSkWbKOPjQMJoYVj9wLHcOniv7diyI5UAJ4XsBdtaFB0pKHQsqw/ItUma0VXGC7vB4E9fZjivmxur60aw=="],
|
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.10.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-clipboard-manager": ["@tauri-apps/plugin-clipboard-manager@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ=="],
|
"@tauri-apps/plugin-clipboard-manager": ["@tauri-apps/plugin-clipboard-manager@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ=="],
|
||||||
|
|
||||||
@@ -290,31 +290,31 @@
|
|||||||
|
|
||||||
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="],
|
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="],
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.54.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/type-utils": "8.54.0", "@typescript-eslint/utils": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.54.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ=="],
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.54.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA=="],
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.55.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw=="],
|
||||||
|
|
||||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.54.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.54.0", "@typescript-eslint/types": "^8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g=="],
|
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.55.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0" } }, "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg=="],
|
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0" } }, "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q=="],
|
||||||
|
|
||||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.54.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw=="],
|
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.55.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q=="],
|
||||||
|
|
||||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA=="],
|
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g=="],
|
||||||
|
|
||||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.54.0", "", {}, "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA=="],
|
"@typescript-eslint/types": ["@typescript-eslint/types@8.55.0", "", {}, "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.54.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.54.0", "@typescript-eslint/tsconfig-utils": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA=="],
|
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.55.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.55.0", "@typescript-eslint/tsconfig-utils": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw=="],
|
||||||
|
|
||||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.54.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA=="],
|
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.55.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow=="],
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA=="],
|
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA=="],
|
||||||
|
|
||||||
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
|
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@
|
|||||||
|
|
||||||
"axe-core": ["axe-core@4.11.1", "", {}, "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A=="],
|
"axe-core": ["axe-core@4.11.1", "", {}, "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A=="],
|
||||||
|
|
||||||
"axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="],
|
"axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="],
|
||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@
|
|||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="],
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
@@ -460,11 +460,11 @@
|
|||||||
|
|
||||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.279", "", {}, "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],
|
||||||
|
|
||||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="],
|
"enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="],
|
||||||
|
|
||||||
"es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
|
"es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
|
||||||
|
|
||||||
@@ -562,7 +562,7 @@
|
|||||||
|
|
||||||
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
||||||
|
|
||||||
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
|
"get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
||||||
@@ -900,7 +900,7 @@
|
|||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
"typescript-eslint": ["typescript-eslint@8.54.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ=="],
|
"typescript-eslint": ["typescript-eslint@8.55.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.55.0", "@typescript-eslint/parser": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw=="],
|
||||||
|
|
||||||
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
|
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
|
||||||
|
|
||||||
@@ -952,7 +952,7 @@
|
|||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
"eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
|
"eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
|
||||||
|
|
||||||
@@ -966,13 +966,13 @@
|
|||||||
|
|
||||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"is-bun-module/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"is-bun-module/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||||
|
|
||||||
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ const eslintConfig = defineConfig([
|
|||||||
'.next/**',
|
'.next/**',
|
||||||
'out/**',
|
'out/**',
|
||||||
'build/**',
|
'build/**',
|
||||||
'next-env.d.ts'
|
'next-env.d.ts',
|
||||||
|
'src-tauri/**'
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
||||||
+10
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lncvrt-games-launcher",
|
"name": "lncvrt-games-launcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
"@fortawesome/fontawesome-svg-core": "7.2.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "7.1.0",
|
"@fortawesome/free-brands-svg-icons": "7.2.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "7.1.0",
|
"@fortawesome/free-solid-svg-icons": "7.2.0",
|
||||||
"@fortawesome/react-fontawesome": "3.1.1",
|
"@fortawesome/react-fontawesome": "3.2.0",
|
||||||
"@tauri-apps/api": "2.9.1",
|
"@tauri-apps/api": "2.10.1",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
|
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
|
||||||
"@tauri-apps/plugin-dialog": "2.6.0",
|
"@tauri-apps/plugin-dialog": "2.6.0",
|
||||||
"@tauri-apps/plugin-fs": "2.4.5",
|
"@tauri-apps/plugin-fs": "2.4.5",
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"@tauri-apps/plugin-opener": "2.5.3",
|
"@tauri-apps/plugin-opener": "2.5.3",
|
||||||
"@tauri-apps/plugin-os": "2.3.2",
|
"@tauri-apps/plugin-os": "2.3.2",
|
||||||
"@tauri-apps/plugin-window-state": "2.4.1",
|
"@tauri-apps/plugin-window-state": "2.4.1",
|
||||||
"axios": "1.13.4",
|
"axios": "1.13.5",
|
||||||
"next": "16.1.6",
|
"next": "16.1.6",
|
||||||
"pretty-bytes": "7.1.0",
|
"pretty-bytes": "7.1.0",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
@@ -32,10 +32,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "3.3.3",
|
"@eslint/eslintrc": "3.3.3",
|
||||||
"@tailwindcss/postcss": "4.1.18",
|
"@tailwindcss/postcss": "4.1.18",
|
||||||
"@tauri-apps/cli": "2.9.6",
|
"@tauri-apps/cli": "2.10.0",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.1.0",
|
"@types/node": "25.2.3",
|
||||||
"@types/react": "19.2.10",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"eslint": "9.39.2",
|
"eslint": "9.39.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lncvrt-games-launcher"
|
name = "lncvrt-games-launcher"
|
||||||
version = "1.5.2"
|
version = "1.6.1"
|
||||||
authors = ["Lncvrt"]
|
authors = ["Lncvrt"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -9,32 +9,33 @@ name = "lncvrt_games_launcher_lib"
|
|||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.5.3", features = [] }
|
tauri-build = { version = "2.5.5", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2.9.5", features = ["macos-private-api"] }
|
tauri = { version = "2.10.2", features = ["macos-private-api"] }
|
||||||
tauri-plugin-opener = "2.5.3"
|
tauri-plugin-opener = "2.5.3"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
tauri-plugin-os = "2.3.2"
|
tauri-plugin-os = "2.3.2"
|
||||||
reqwest = { version = "0.13.1", default-features = false, features = ["stream", "rustls"] }
|
reqwest = { version = "0.13.2", default-features = false, features = ["stream", "rustls"] }
|
||||||
tokio = "1.49.0"
|
tokio = "1.49.0"
|
||||||
futures-util = { version = "0.3.31", features = ["io"] }
|
futures-util = { version = "0.3.31", features = ["io"] }
|
||||||
tauri-plugin-decorum = "1.1.1"
|
tauri-plugin-decorum = "1.1.1"
|
||||||
tauri-plugin-fs = "2.4.5"
|
tauri-plugin-fs = "2.4.5"
|
||||||
zip = "7.2.0"
|
zip = "8.0.0"
|
||||||
libc = "0.2.180"
|
libc = "0.2.182"
|
||||||
tauri-plugin-dialog = "2.6.0"
|
tauri-plugin-dialog = "2.6.0"
|
||||||
tauri-plugin-notification = "2.3.3"
|
tauri-plugin-notification = "2.3.3"
|
||||||
sysinfo = "0.38.0"
|
sysinfo = "0.38.1"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
tauri-plugin-clipboard-manager = "2.3.2"
|
tauri-plugin-clipboard-manager = "2.3.2"
|
||||||
|
tauri-plugin-prevent-default = "4.0.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
shlex = "1.3.0"
|
shlex = "1.3.0"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
tauri-plugin-single-instance = "2.3.7"
|
tauri-plugin-single-instance = "2.4.0"
|
||||||
tauri-plugin-window-state = "2.4.1"
|
tauri-plugin-window-state = "2.4.1"
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +32,12 @@
|
|||||||
"allow": [
|
"allow": [
|
||||||
{ "path": "$APPLOCALDATA/game/**" }
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "opener:allow-open-path",
|
||||||
|
"allow": [
|
||||||
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+52
-61
@@ -12,9 +12,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
use tauri::{AppHandle, Emitter, Manager};
|
use tauri::{AppHandle, Emitter, Manager};
|
||||||
use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
|
|
||||||
use tauri_plugin_opener::OpenerExt;
|
|
||||||
use tauri_plugin_os::platform;
|
use tauri_plugin_os::platform;
|
||||||
|
use tauri_plugin_prevent_default::Flags;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::{io::AsyncWriteExt, time::timeout};
|
use tokio::{io::AsyncWriteExt, time::timeout};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
@@ -22,6 +21,9 @@ use zip::ZipArchive;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use tauri_plugin_decorum::WebviewWindowExt;
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn is_running_by_path(path: &Path) -> bool {
|
fn is_running_by_path(path: &Path) -> bool {
|
||||||
let sys = System::new_all();
|
let sys = System::new_all();
|
||||||
@@ -37,23 +39,47 @@ fn is_running_by_path(path: &Path) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn should_skip(name: &str) -> bool {
|
||||||
|
name.starts_with("__MACOSX/")
|
||||||
|
|| name == "__MACOSX"
|
||||||
|
|| name.ends_with("/.DS_Store")
|
||||||
|
|| name.ends_with(".DS_Store")
|
||||||
|
}
|
||||||
|
|
||||||
async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
||||||
let res = tauri::async_runtime::spawn_blocking(move || {
|
let res = tauri::async_runtime::spawn_blocking(move || {
|
||||||
let file = File::open(zip_path)?;
|
let file = File::open(zip_path)?;
|
||||||
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
||||||
|
|
||||||
for i in 0..archive.len() {
|
for i in 0..archive.len() {
|
||||||
let mut file = archive.by_index(i)?;
|
let mut entry = archive.by_index(i)?;
|
||||||
let outpath = out_dir.join(file.name());
|
let name = entry.name();
|
||||||
|
|
||||||
if file.is_dir() {
|
if should_skip(name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let outpath = out_dir.join(name);
|
||||||
|
|
||||||
|
if entry.is_dir() {
|
||||||
create_dir_all(&outpath)?;
|
create_dir_all(&outpath)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(mode) = entry.unix_mode() {
|
||||||
|
std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode))?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(parent) = outpath.parent() {
|
if let Some(parent) = outpath.parent() {
|
||||||
create_dir_all(parent)?;
|
create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut outfile = File::create(&outpath)?;
|
let mut outfile = File::create(&outpath)?;
|
||||||
copy(&mut file, &mut outfile)?;
|
copy(&mut entry, &mut outfile)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(mode) = entry.unix_mode() {
|
||||||
|
std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,31 +251,6 @@ async fn download(
|
|||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
|
||||||
{
|
|
||||||
use std::{fs, os::unix::fs::PermissionsExt};
|
|
||||||
|
|
||||||
let executable_path = if cfg!(target_os = "linux") {
|
|
||||||
game_path.join(&name).join(&executable)
|
|
||||||
} else {
|
|
||||||
game_path
|
|
||||||
.join(&name)
|
|
||||||
.join(&executable)
|
|
||||||
.join("Contents")
|
|
||||||
.join("MacOS")
|
|
||||||
.join(
|
|
||||||
&executable
|
|
||||||
.chars()
|
|
||||||
.take(executable.chars().count() - 4)
|
|
||||||
.collect::<String>(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut perms = fs::metadata(&executable_path).unwrap().permissions();
|
|
||||||
perms.set_mode(0o755);
|
|
||||||
fs::set_permissions(&executable_path, perms).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
return "1".to_string();
|
return "1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +279,9 @@ fn launch_game(
|
|||||||
//if already running on macos, it'll auto take the user to that proccess
|
//if already running on macos, it'll auto take the user to that proccess
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
|
use tauri_plugin_dialog::DialogExt;
|
||||||
|
use tauri_plugin_dialog::MessageDialogKind;
|
||||||
|
|
||||||
if !use_wine && is_running_by_path(&exe_path) {
|
if !use_wine && is_running_by_path(&exe_path) {
|
||||||
app.dialog()
|
app.dialog()
|
||||||
.message(format!(
|
.message(format!(
|
||||||
@@ -322,34 +326,26 @@ fn launch_game(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn open_folder(app: AppHandle, name: String) {
|
|
||||||
let game_path = app
|
|
||||||
.path()
|
|
||||||
.app_local_data_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("game")
|
|
||||||
.join(&name);
|
|
||||||
if game_path.exists() {
|
|
||||||
app.opener()
|
|
||||||
.open_path(game_path.to_string_lossy(), None::<&str>)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
app.dialog()
|
|
||||||
.message(format!(
|
|
||||||
"Game folder \"{}\" not found.",
|
|
||||||
game_path.display()
|
|
||||||
))
|
|
||||||
.kind(MessageDialogKind::Error)
|
|
||||||
.title("Folder not found")
|
|
||||||
.show(|_| {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(
|
||||||
|
tauri_plugin_prevent_default::Builder::new()
|
||||||
|
.with_flags(
|
||||||
|
Flags::FIND
|
||||||
|
| Flags::CARET_BROWSING
|
||||||
|
| Flags::DEV_TOOLS
|
||||||
|
| Flags::DOWNLOADS
|
||||||
|
| Flags::FOCUS_MOVE
|
||||||
|
| Flags::RELOAD
|
||||||
|
| Flags::SOURCE
|
||||||
|
| Flags::OPEN
|
||||||
|
| Flags::PRINT
|
||||||
|
| Flags::CONTEXT_MENU,
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.plugin(tauri_plugin_window_state::Builder::new().build())
|
.plugin(tauri_plugin_window_state::Builder::new().build())
|
||||||
.plugin(tauri_plugin_clipboard_manager::init())
|
.plugin(tauri_plugin_clipboard_manager::init())
|
||||||
.plugin(tauri_plugin_notification::init())
|
.plugin(tauri_plugin_notification::init())
|
||||||
@@ -364,12 +360,7 @@ pub fn run() {
|
|||||||
.plugin(tauri_plugin_decorum::init())
|
.plugin(tauri_plugin_decorum::init())
|
||||||
.plugin(tauri_plugin_os::init())
|
.plugin(tauri_plugin_os::init())
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![download, launch_game, folder_size])
|
||||||
download,
|
|
||||||
launch_game,
|
|
||||||
open_folder,
|
|
||||||
folder_size
|
|
||||||
])
|
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"targets": ["app"],
|
"targets": ["app"],
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"minimumSystemVersion": "15.0"
|
"minimumSystemVersion": "13.0"
|
||||||
},
|
},
|
||||||
"icon": ["icons/icon.icns"]
|
"icon": ["icons/icon.icns"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Lncvrt Games Launcher",
|
"productName": "Lncvrt Games Launcher",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"identifier": "xyz.lncvrt.games-launcher",
|
"identifier": "xyz.lncvrt.games-launcher",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "next dev",
|
"beforeDevCommand": "next dev --webpack",
|
||||||
"devUrl": "http://localhost:3000",
|
"devUrl": "http://localhost:3000",
|
||||||
"beforeBuildCommand": "next build",
|
"beforeBuildCommand": "next build",
|
||||||
"frontendDist": "../out"
|
"frontendDist": "../out"
|
||||||
|
|||||||
+33
-18
@@ -1,41 +1,56 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { createContext, useContext, ReactNode } from 'react'
|
import {
|
||||||
import { DownloadProgress } from './types/DownloadProgress'
|
createContext,
|
||||||
import { VersionsConfig } from './types/VersionsConfig'
|
useContext,
|
||||||
import { NormalConfig } from './types/NormalConfig'
|
ReactNode,
|
||||||
import { ServerVersionsResponse } from './types/ServerVersionsResponse'
|
Dispatch,
|
||||||
import { GameVersion } from './types/GameVersion'
|
SetStateAction
|
||||||
import { Game } from './types/Game'
|
} from 'react'
|
||||||
|
import { DownloadProgress } from '@/types/DownloadProgress'
|
||||||
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
|
import { ServerVersionsResponse } from '@/types/ServerVersionsResponse'
|
||||||
|
import { GameVersion } from '@/types/GameVersion'
|
||||||
|
import { Game } from '@/types/Game'
|
||||||
|
|
||||||
type GlobalCtxType = {
|
type GlobalCtxType = {
|
||||||
serverVersionList: ServerVersionsResponse | null
|
serverVersionList: ServerVersionsResponse | null
|
||||||
selectedVersionList: string[]
|
selectedVersionList: string[]
|
||||||
setSelectedVersionList: (v: string[]) => void
|
setSelectedVersionList: (value: SetStateAction<string[]>) => void
|
||||||
downloadProgress: DownloadProgress[]
|
downloadProgress: DownloadProgress[]
|
||||||
setDownloadProgress: (v: DownloadProgress[]) => void
|
setDownloadProgress: Dispatch<SetStateAction<DownloadProgress[]>>
|
||||||
showPopup: boolean
|
showPopup: boolean
|
||||||
setShowPopup: (v: boolean) => void
|
setShowPopup: Dispatch<SetStateAction<boolean>>
|
||||||
popupMode: number | null
|
popupMode: number | null
|
||||||
setPopupMode: (v: number | null) => void
|
setPopupMode: Dispatch<SetStateAction<number | null>>
|
||||||
fadeOut: boolean
|
fadeOut: boolean
|
||||||
setFadeOut: (v: boolean) => void
|
setFadeOut: Dispatch<SetStateAction<boolean>>
|
||||||
downloadedVersionsConfig: VersionsConfig | null
|
downloadedVersionsConfig: VersionsConfig | null
|
||||||
setDownloadedVersionsConfig: (v: VersionsConfig | null) => void
|
setDownloadedVersionsConfig: Dispatch<SetStateAction<VersionsConfig | null>>
|
||||||
normalConfig: NormalConfig | null
|
normalConfig: NormalConfig | null
|
||||||
setNormalConfig: (v: NormalConfig | null) => void
|
setNormalConfig: Dispatch<SetStateAction<NormalConfig | null>>
|
||||||
managingVersion: string | null
|
managingVersion: string | null
|
||||||
setManagingVersion: (v: string | null) => void
|
setManagingVersion: Dispatch<SetStateAction<string | null>>
|
||||||
setSelectedGame: (v: number | null) => void
|
setSelectedGame: Dispatch<SetStateAction<number | null>>
|
||||||
getVersionInfo: (id: string | undefined) => GameVersion | undefined
|
getVersionInfo: (id: string | undefined) => GameVersion | undefined
|
||||||
getGameInfo: (id: number | undefined) => Game | undefined
|
getGameInfo: (game: number | undefined) => Game | undefined
|
||||||
getListOfGames: () => Game[]
|
getListOfGames(): Game[]
|
||||||
getVersionsAmountData: (gameId: number) => {
|
getVersionsAmountData: (gameId: number) => {
|
||||||
installed: number
|
installed: number
|
||||||
total: number
|
total: number
|
||||||
} | null
|
} | null
|
||||||
viewingInfoFromDownloads: boolean
|
viewingInfoFromDownloads: boolean
|
||||||
version: string | null
|
version: string | null
|
||||||
|
downloadVersions: (list: string[]) => Promise<void>
|
||||||
|
category: number
|
||||||
|
setCategory: Dispatch<SetStateAction<number>>
|
||||||
|
downloadQueue: string[]
|
||||||
|
setDownloadQueue: Dispatch<SetStateAction<string[]>>
|
||||||
|
closePopup: () => void
|
||||||
|
getSpecialVersionsList(game?: number | undefined): GameVersion[]
|
||||||
|
selectedGame: number | null
|
||||||
|
setViewingInfoFromDownloads: Dispatch<SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
const GlobalCtx = createContext<GlobalCtxType | null>(null)
|
||||||
|
|||||||
@@ -28,18 +28,6 @@ body {
|
|||||||
--col8: rgb(152, 128, 128);
|
--col8: rgb(152, 128, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
.green-theme {
|
|
||||||
--col0: rgb(8, 32, 8);
|
|
||||||
--col1: rgb(16, 40, 16);
|
|
||||||
--col2: rgb(32, 56, 32);
|
|
||||||
--col3: rgb(48, 72, 48);
|
|
||||||
--col4: rgb(64, 88, 64);
|
|
||||||
--col5: rgb(80, 104, 80);
|
|
||||||
--col6: rgb(96, 120, 96);
|
|
||||||
--col7: rgb(112, 136, 112);
|
|
||||||
--col8: rgb(128, 152, 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blue-theme {
|
.blue-theme {
|
||||||
--col0: rgb(8, 8, 32);
|
--col0: rgb(8, 8, 32);
|
||||||
--col1: rgb(16, 16, 40);
|
--col1: rgb(16, 16, 40);
|
||||||
@@ -64,18 +52,6 @@ body {
|
|||||||
--col8: rgb(140, 128, 152);
|
--col8: rgb(140, 128, 152);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mint-theme {
|
|
||||||
--col0: rgb(8, 32, 16);
|
|
||||||
--col1: rgb(16, 48, 32);
|
|
||||||
--col2: rgb(32, 56, 40);
|
|
||||||
--col3: rgb(48, 72, 48);
|
|
||||||
--col4: rgb(64, 88, 66);
|
|
||||||
--col5: rgb(80, 104, 65);
|
|
||||||
--col6: rgb(96, 120, 80);
|
|
||||||
--col7: rgb(112, 136, 85);
|
|
||||||
--col8: rgb(128, 152, 95);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@apply rounded-md cursor-pointer text-[16px] py-1 px-2 transition-colors border;
|
@apply rounded-md cursor-pointer text-[16px] py-1 px-2 transition-colors border;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.downloads-entry {
|
.downloads-entry {
|
||||||
@apply flex justify-between items-center m-2 p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5);
|
@apply flex justify-between items-center m-2 p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
|
||||||
}
|
}
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import './Sidebar.css'
|
|
||||||
import Icon from '@/assets/Icon.png'
|
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import {
|
|
||||||
faCog,
|
|
||||||
faDownload,
|
|
||||||
faGamepad,
|
|
||||||
faHexagonNodes
|
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
|
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
|
||||||
import { useGlobal } from '../GlobalProvider'
|
|
||||||
import Image from 'next/image'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { usePathname, useSearchParams } from 'next/navigation'
|
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
|
||||||
import { Lexend } from 'next/font/google'
|
|
||||||
|
|
||||||
const lexend = Lexend({
|
|
||||||
subsets: ['latin']
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function Sidebar () {
|
|
||||||
const {
|
|
||||||
normalConfig,
|
|
||||||
getListOfGames,
|
|
||||||
setShowPopup,
|
|
||||||
setPopupMode,
|
|
||||||
setFadeOut,
|
|
||||||
downloadProgress
|
|
||||||
} = useGlobal()
|
|
||||||
|
|
||||||
const pathname = usePathname()
|
|
||||||
const params = useSearchParams()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside className='sidebar'>
|
|
||||||
<div
|
|
||||||
className='macos-drag'
|
|
||||||
hidden={platform() != 'macos'}
|
|
||||||
onMouseDown={e => {
|
|
||||||
if (e.buttons === 1) {
|
|
||||||
e.detail === 2
|
|
||||||
? getCurrentWindow().toggleMaximize()
|
|
||||||
: getCurrentWindow().startDragging()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
className='logo'
|
|
||||||
style={{
|
|
||||||
marginTop:
|
|
||||||
platform() == 'windows'
|
|
||||||
? '32px'
|
|
||||||
: platform() == 'macos'
|
|
||||||
? '28px'
|
|
||||||
: ''
|
|
||||||
}}
|
|
||||||
onMouseDown={e => {
|
|
||||||
if (platform() != 'macos') return
|
|
||||||
if (e.buttons === 1) {
|
|
||||||
e.detail === 2
|
|
||||||
? getCurrentWindow().toggleMaximize()
|
|
||||||
: getCurrentWindow().startDragging()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image draggable={false} src={Icon} width={36} height={36} alt='' />
|
|
||||||
<p className={`ml-1 text-[16px] whitespace-nowrap ${lexend.className}`}>
|
|
||||||
Lncvrt Games Launcher
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<nav className='nav-links overflow-auto pt-2'>
|
|
||||||
<Link
|
|
||||||
draggable={false}
|
|
||||||
href='/'
|
|
||||||
className={`link relative flex items-center ${
|
|
||||||
pathname === '/' || pathname === '/game' ? 'active' : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faHexagonNodes} className='mr-2' /> Games
|
|
||||||
</Link>
|
|
||||||
{getListOfGames()
|
|
||||||
.sort((a, b) => {
|
|
||||||
return a.id - b.id
|
|
||||||
})
|
|
||||||
.map(i => (
|
|
||||||
<Link
|
|
||||||
key={i.id}
|
|
||||||
draggable={false}
|
|
||||||
href={'/game?id=' + i.id}
|
|
||||||
className={`link ${
|
|
||||||
pathname === '/game' && Number(params.get('id') || 0) == i.id
|
|
||||||
? 'active'
|
|
||||||
: ''
|
|
||||||
} ml-auto w-50 ${
|
|
||||||
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
|
||||||
pathname === '/' ||
|
|
||||||
pathname === '/game'
|
|
||||||
? ''
|
|
||||||
: 'hidden'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<FontAwesomeIcon icon={faGamepad} className='mr-1' />
|
|
||||||
<span className='truncate max-w-full'>{i.name}</span>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
<Link
|
|
||||||
draggable={false}
|
|
||||||
href='/settings'
|
|
||||||
className={`link ${pathname === '/settings' ? 'active' : ''}`}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faCog} className='mr-1' /> Settings
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
onClick={() => openUrl('https://games.lncvrt.xyz/discord')}
|
|
||||||
className='link mr-auto'
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
<div
|
|
||||||
className='sidebar-downloads'
|
|
||||||
hidden={downloadProgress.length == 0}
|
|
||||||
onClick={() => {
|
|
||||||
setPopupMode(1)
|
|
||||||
setShowPopup(true)
|
|
||||||
setFadeOut(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
<FontAwesomeIcon icon={faDownload} /> Downloads
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useGlobal } from '../GlobalProvider'
|
|
||||||
|
|
||||||
export default function VersionChangelog () {
|
|
||||||
const { getVersionInfo, managingVersion, downloadedVersionsConfig } =
|
|
||||||
useGlobal()
|
|
||||||
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
|
||||||
|
|
||||||
const versionInfo = getVersionInfo(managingVersion)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Viewing changelog for {versionInfo?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='popup-content text-center p-2'>
|
|
||||||
<span
|
|
||||||
className='whitespace-pre-wrap'
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: versionInfo?.changelog ? atob(versionInfo.changelog) : ''
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import {
|
|
||||||
faArrowUpRightFromSquare,
|
|
||||||
faCheck,
|
|
||||||
faCode,
|
|
||||||
faHardDrive,
|
|
||||||
faShieldHalved,
|
|
||||||
faWarning
|
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import { useGlobal } from '../GlobalProvider'
|
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import prettyBytes from 'pretty-bytes'
|
|
||||||
|
|
||||||
export default function VersionInfo () {
|
|
||||||
const {
|
|
||||||
getGameInfo,
|
|
||||||
getVersionInfo,
|
|
||||||
managingVersion,
|
|
||||||
downloadedVersionsConfig,
|
|
||||||
viewingInfoFromDownloads,
|
|
||||||
setPopupMode
|
|
||||||
} = useGlobal()
|
|
||||||
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
|
||||||
|
|
||||||
const versionInfo = getVersionInfo(managingVersion)
|
|
||||||
const gameInfo = getGameInfo(versionInfo?.game)
|
|
||||||
const [versionSize, setVersionSize] = useState<number | null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
invoke<string>('folder_size', {
|
|
||||||
version: managingVersion
|
|
||||||
}).then(size => {
|
|
||||||
setVersionSize(parseInt(size, 10))
|
|
||||||
})
|
|
||||||
}, [managingVersion, setVersionSize])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Viewing info for {versionInfo?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme2'
|
|
||||||
hidden={viewingInfoFromDownloads}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Installed{' '}
|
|
||||||
{new Intl.DateTimeFormat(undefined).format(
|
|
||||||
downloadedVersionsConfig.list[managingVersion] ?? 0
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme2'
|
|
||||||
hidden={!versionInfo || versionInfo.releaseDate == 0}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Released{' '}
|
|
||||||
{new Intl.DateTimeFormat(undefined).format(
|
|
||||||
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
|
|
||||||
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
|
|
||||||
<p>Official</p>
|
|
||||||
</div>
|
|
||||||
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={gameInfo?.verified ? faShieldHalved : faWarning}
|
|
||||||
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
|
|
||||||
/>
|
|
||||||
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme2'
|
|
||||||
hidden={gameInfo?.developer == null}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faCode} color='lightgray' />
|
|
||||||
<p>Developer: {gameInfo?.developer}</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme2'
|
|
||||||
hidden={viewingInfoFromDownloads || versionSize === null}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
|
||||||
<p>
|
|
||||||
Size on disk:{' '}
|
|
||||||
{versionSize !== null
|
|
||||||
? prettyBytes(versionSize, {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2
|
|
||||||
})
|
|
||||||
: 'Loading...'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
|
|
||||||
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
|
||||||
<p>
|
|
||||||
Size when downloaded (zipped):{' '}
|
|
||||||
{versionInfo
|
|
||||||
? prettyBytes(versionInfo.size, {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2
|
|
||||||
})
|
|
||||||
: 'Loading...'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme2'
|
|
||||||
onClick={() => setPopupMode(4)}
|
|
||||||
hidden={!versionInfo?.changelog}
|
|
||||||
>
|
|
||||||
<p>View Changelog</p>
|
|
||||||
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import { BirdColor } from '@/types/BerryDash/BirdColor'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { GetIconForUser } from '@/lib/BerryDash'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import './styles.css'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
|
|
||||||
|
interface BaseEntry {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
value: number
|
||||||
|
icon: number
|
||||||
|
overlay: number
|
||||||
|
birdColor: BirdColor
|
||||||
|
overlayColor: BirdColor
|
||||||
|
customIcon: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stats {
|
||||||
|
highScore: string
|
||||||
|
totalNormalBerries: string
|
||||||
|
totalPoisonBerries: string
|
||||||
|
totalSlowBerries: string
|
||||||
|
totalUltraBerries: string
|
||||||
|
totalSpeedyBerries: string
|
||||||
|
totalCoinBerries: string
|
||||||
|
totalRandomBerries: string
|
||||||
|
totalAntiBerries: string
|
||||||
|
totalGoldenBerries: string
|
||||||
|
coins: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LeaderboardEntry extends BaseEntry {
|
||||||
|
type: 'leaderboard'
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Account extends BaseEntry {
|
||||||
|
type: 'account'
|
||||||
|
stats: Stats
|
||||||
|
xp: bigint
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateXP (
|
||||||
|
normalBerries: bigint,
|
||||||
|
poisonBerries: bigint,
|
||||||
|
slowBerries: bigint,
|
||||||
|
ultraBerries: bigint,
|
||||||
|
speedyBerries: bigint,
|
||||||
|
coinBerries: bigint,
|
||||||
|
randomBerries: bigint,
|
||||||
|
antiBerries: bigint,
|
||||||
|
goldenBerries: bigint
|
||||||
|
): bigint {
|
||||||
|
let totalXp = 0n
|
||||||
|
totalXp += normalBerries
|
||||||
|
totalXp -= poisonBerries
|
||||||
|
totalXp -= slowBerries
|
||||||
|
totalXp += ultraBerries * 5n
|
||||||
|
totalXp += speedyBerries * 10n
|
||||||
|
totalXp += coinBerries * 10n
|
||||||
|
totalXp += randomBerries
|
||||||
|
totalXp -= antiBerries
|
||||||
|
totalXp += goldenBerries * 4n
|
||||||
|
|
||||||
|
if (totalXp < 0n) totalXp = 0n
|
||||||
|
return totalXp
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateLevel (xp: bigint): number {
|
||||||
|
const levelDivisor = 50.0
|
||||||
|
|
||||||
|
const xpNumber = Number(xp)
|
||||||
|
const discriminant = 95 * 95 + levelDivisor * 2 * xpNumber
|
||||||
|
const level = (-95 + Math.sqrt(discriminant)) / levelDivisor
|
||||||
|
return Math.floor(level) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BerryDashLeaderboards () {
|
||||||
|
const [selected, setSelected] = useState<number>(-1)
|
||||||
|
const [selectedBerryOption, setSelectedBerryOption] = useState<number>(0)
|
||||||
|
const [entries, setEntries] = useState<(LeaderboardEntry | Account)[]>([])
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const Refresh = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
if (selected == 3 || selected == 4) {
|
||||||
|
const result = await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/account?username='
|
||||||
|
)
|
||||||
|
if (result.data.success) {
|
||||||
|
let accounts = result.data.data as Account[]
|
||||||
|
|
||||||
|
accounts = accounts.map(acc => {
|
||||||
|
const xp = calculateXP(
|
||||||
|
BigInt(acc.stats.totalNormalBerries),
|
||||||
|
BigInt(acc.stats.totalPoisonBerries),
|
||||||
|
BigInt(acc.stats.totalSlowBerries),
|
||||||
|
BigInt(acc.stats.totalUltraBerries),
|
||||||
|
BigInt(acc.stats.totalSpeedyBerries),
|
||||||
|
BigInt(acc.stats.totalCoinBerries),
|
||||||
|
BigInt(acc.stats.totalRandomBerries),
|
||||||
|
BigInt(acc.stats.totalAntiBerries),
|
||||||
|
BigInt(acc.stats.totalGoldenBerries)
|
||||||
|
)
|
||||||
|
return { ...acc, xp }
|
||||||
|
})
|
||||||
|
|
||||||
|
accounts.sort((a, b) => (b.xp > a.xp ? 1 : b.xp < a.xp ? -1 : 0))
|
||||||
|
|
||||||
|
setEntries(accounts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const result = await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/leaderboard/' +
|
||||||
|
(selected == 0
|
||||||
|
? 'score'
|
||||||
|
: selected == 1
|
||||||
|
? 'berry?berry=' + selectedBerryOption
|
||||||
|
: selected == 2
|
||||||
|
? 'coin'
|
||||||
|
: selected == 5
|
||||||
|
? 'legacy'
|
||||||
|
: 'total')
|
||||||
|
)
|
||||||
|
if (result.data.success) {
|
||||||
|
setEntries(result.data.data as LeaderboardEntry[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setEntries([])
|
||||||
|
}
|
||||||
|
}, [selected, selectedBerryOption])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = 'Lncvrt Games - Berry Dash Leaderboards'
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selected != -1) setTimeout(() => Refresh(), 0)
|
||||||
|
}, [selected, Refresh])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mx-4 mt-4'>
|
||||||
|
<div className='flex justify-between items-center mb-4'>
|
||||||
|
<p className='text-3xl'>Berry Dash Leaderboards</p>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
setEntries([])
|
||||||
|
Refresh()
|
||||||
|
}}
|
||||||
|
title='Click to refresh the leaderboards.'
|
||||||
|
hidden={selected == -1}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
if (selected == -1) router.push('/game?id=1')
|
||||||
|
else {
|
||||||
|
setSelected(-1)
|
||||||
|
setEntries([])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title='Click to go up a level.'
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`box ${
|
||||||
|
platform() == 'windows'
|
||||||
|
? 'h-[calc(100vh-116px)]'
|
||||||
|
: 'h-[calc(100vh-84px)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{selected == -1 ? (
|
||||||
|
<>
|
||||||
|
<p className='text-center mt-2 text-xl'>Select a Leaderboard</p>
|
||||||
|
<div className='flex flex-col gap-2 mt-2 items-center justify-center'>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(0)}
|
||||||
|
>
|
||||||
|
Score Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(1)}
|
||||||
|
>
|
||||||
|
Berry Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(2)}
|
||||||
|
>
|
||||||
|
Coins Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(3)}
|
||||||
|
>
|
||||||
|
Level Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(4)}
|
||||||
|
>
|
||||||
|
Total XP Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(5)}
|
||||||
|
>
|
||||||
|
Legacy Leaderboard
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='leaderboard-button'
|
||||||
|
onClick={() => setSelected(6)}
|
||||||
|
>
|
||||||
|
Total Berries Leaderboard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col gap-2 overflow-y-auto ${
|
||||||
|
selected == 1
|
||||||
|
? platform() == 'windows'
|
||||||
|
? 'h-[calc(100vh-168px)]'
|
||||||
|
: 'h-[calc(100vh-136px)]'
|
||||||
|
: platform() == 'windows'
|
||||||
|
? 'h-[calc(100vh-128px)]'
|
||||||
|
: 'h-[calc(100vh-96px)]'
|
||||||
|
} px-1`}
|
||||||
|
>
|
||||||
|
{entries.map((item, index) => {
|
||||||
|
const isAccount = 'stats' in item
|
||||||
|
const isLeaderboard = 'value' in item && !isAccount
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className='leaderboard-entry flex justify-between items-center'
|
||||||
|
>
|
||||||
|
<div className='flex items-center gap-1'>
|
||||||
|
<Image
|
||||||
|
src={
|
||||||
|
!item.customIcon
|
||||||
|
? `https://games-r2.lncvrt.xyz/game-assets/berrydash/icons/bird_${
|
||||||
|
item.icon === 1
|
||||||
|
? GetIconForUser(item.id)
|
||||||
|
: item.icon
|
||||||
|
}.png`
|
||||||
|
: `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true`
|
||||||
|
}
|
||||||
|
className='pointer-events-none'
|
||||||
|
width={48}
|
||||||
|
height={48}
|
||||||
|
alt=''
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
{item.username} (#{index + 1})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLeaderboard ? (
|
||||||
|
<p>
|
||||||
|
{selected === 1 || selected === 6
|
||||||
|
? 'Berries'
|
||||||
|
: selected === 2
|
||||||
|
? 'Coins'
|
||||||
|
: 'Score'}
|
||||||
|
: {item.value.toLocaleString('en-US')}
|
||||||
|
</p>
|
||||||
|
) : isAccount ? (
|
||||||
|
<p>
|
||||||
|
{selected === 3 ? 'Level' : 'XP'}:{' '}
|
||||||
|
{selected === 3
|
||||||
|
? calculateLevel(item.xp).toLocaleString('en-US')
|
||||||
|
: item.xp.toLocaleString('en-US')}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{selected == 1 && (
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<select
|
||||||
|
value={selectedBerryOption}
|
||||||
|
onChange={e => setSelectedBerryOption(Number(e.target.value))}
|
||||||
|
className='leaderboard-select mt-0.75'
|
||||||
|
>
|
||||||
|
<option value='0'>Normal Berry</option>
|
||||||
|
<option value='1'>Poison Berry</option>
|
||||||
|
<option value='2'>Slow Berry</option>
|
||||||
|
<option value='3'>Ultra Berry</option>
|
||||||
|
<option value='4'>Speedy Berry</option>
|
||||||
|
<option value='5'>Coin Berry</option>
|
||||||
|
<option value='6'>Random Berry</option>
|
||||||
|
<option value='7'>Anti Berry</option>
|
||||||
|
<option value='8'>Golden Berry</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
.box {
|
||||||
|
@apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboard-button,
|
||||||
|
.leaderboard-select {
|
||||||
|
@apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all duration-200 cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboard-entry {
|
||||||
|
@apply rounded-lg text-gray-200 text-lg px-1 py-0.5 transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
|
||||||
|
}
|
||||||
+198
-133
@@ -1,13 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect } from 'react'
|
||||||
import '../Installs.css'
|
import '@/app/Installs.css'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
import { faWarning } from '@fortawesome/free-solid-svg-icons'
|
import { faWarning } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { ask } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
|
import { writeVersionsConfig } from '@/lib/BazookaManager'
|
||||||
|
import { openFolder } from '@/lib/Util'
|
||||||
|
|
||||||
export default function Installs () {
|
export default function Installs () {
|
||||||
const {
|
const {
|
||||||
@@ -22,27 +26,66 @@ export default function Installs () {
|
|||||||
getVersionInfo,
|
getVersionInfo,
|
||||||
getGameInfo,
|
getGameInfo,
|
||||||
setSelectedGame,
|
setSelectedGame,
|
||||||
serverVersionList
|
serverVersionList,
|
||||||
|
category,
|
||||||
|
setCategory,
|
||||||
|
setDownloadedVersionsConfig,
|
||||||
|
downloadVersions
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
const params = useSearchParams()
|
const params = useSearchParams()
|
||||||
const [category, setCategory] = useState<number>(-1)
|
const router = useRouter()
|
||||||
|
|
||||||
const id = Number(params.get('id') || 0)
|
const id = Number(params.get('id') || 0)
|
||||||
if (!id) return <p>Invalid game</p>
|
|
||||||
const game = serverVersionList?.games.find(g => g.id === id)
|
const game = serverVersionList?.games.find(g => g.id === id)
|
||||||
if (!game) return <p>Invalid game</p>
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showPopup) return
|
if (!showPopup) return
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
}, [normalConfig, setSelectedVersionList, showPopup])
|
}, [normalConfig, setSelectedVersionList, showPopup])
|
||||||
|
|
||||||
|
if (!id || !game) return <p>Invalid game</p>
|
||||||
|
|
||||||
|
const needsRevisionUpdate = (
|
||||||
|
lastRevision: number | undefined,
|
||||||
|
version: string
|
||||||
|
) => {
|
||||||
|
if (!lastRevision) return false
|
||||||
|
return (
|
||||||
|
lastRevision > 0 &&
|
||||||
|
(downloadedVersionsConfig == undefined
|
||||||
|
? 0
|
||||||
|
: downloadedVersionsConfig?.list[version]) /
|
||||||
|
1000 <=
|
||||||
|
lastRevision
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mx-4 mt-4'>
|
<div className='mx-4 mt-4'>
|
||||||
<div className='flex justify-between items-center mb-4'>
|
<div className='flex justify-between items-center mb-4'>
|
||||||
<p className='text-3xl'>Installs</p>
|
<p
|
||||||
|
className={`text-3xl truncate ${
|
||||||
|
category != -1
|
||||||
|
? 'w-[calc(100vw-495px)]'
|
||||||
|
: game.id == 1
|
||||||
|
? 'w-[calc(100vw-560px)]'
|
||||||
|
: 'w-[calc(100vw-440px)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{game.name} Installs
|
||||||
|
</p>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1'
|
||||||
|
onClick={() => {
|
||||||
|
router.push('/game/berrydash/leaderboards')
|
||||||
|
}}
|
||||||
|
title='View the leaderboards for this game.'
|
||||||
|
hidden={game.id != 1}
|
||||||
|
>
|
||||||
|
Leaderboards
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className='button btntheme1'
|
className='button btntheme1'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -70,74 +113,77 @@ export default function Installs () {
|
|||||||
<div className='downloads-container'>
|
<div className='downloads-container'>
|
||||||
<div
|
<div
|
||||||
className={`downloads-scroll ${
|
className={`downloads-scroll ${
|
||||||
platform() === 'windows'
|
platform() == 'windows'
|
||||||
? 'h-[calc(100vh-116px)]'
|
? 'h-[calc(100vh-116px)]'
|
||||||
: 'h-[calc(100vh-84px)]'
|
: 'h-[calc(100vh-84px)]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{category == -1 &&
|
{category == -1 &&
|
||||||
Object.entries(game.categoryNames).map(([key, value]) => {
|
Object.entries(game.categoryNames)
|
||||||
return (
|
.sort(([a], [b]) => Number(b) - Number(a))
|
||||||
<div
|
.filter(([key]) => {
|
||||||
key={crypto.randomUUID()}
|
const count = Object.keys(
|
||||||
className={`downloads-entry ${
|
downloadedVersionsConfig?.list ?? {}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
).filter(v => {
|
||||||
? ''
|
const info = getVersionInfo(v)
|
||||||
: 'cursor-pointer'
|
if (!info) return false
|
||||||
}`}
|
|
||||||
title={
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'Click to view category'
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
|
||||||
setCategory(Number(key))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='h-18 w-screen relative'>
|
|
||||||
<p className='text-2xl'>{value}</p>
|
|
||||||
|
|
||||||
<div
|
if (
|
||||||
className='entry-info-item flex absolute left-0 bottom-0'
|
platform() == 'linux' &&
|
||||||
title='The amount of versions installed of this game in installed/installable format.'
|
info.wine &&
|
||||||
onClick={e => e.stopPropagation()}
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
>
|
)
|
||||||
<p>
|
return false
|
||||||
{(() => {
|
|
||||||
const count =
|
return info.game === id && info.category === Number(key)
|
||||||
Object.keys(
|
}).length
|
||||||
downloadedVersionsConfig?.list ?? []
|
|
||||||
).filter(v => {
|
return count >= 1
|
||||||
const info = getVersionInfo(v)
|
})
|
||||||
if (!info) return false
|
.map(([key, value]) => {
|
||||||
if (
|
return (
|
||||||
platform() === 'linux' &&
|
<div
|
||||||
info.wine &&
|
key={key}
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
className={'downloads-entry'}
|
||||||
)
|
title={'Click to view category'}
|
||||||
return false
|
onClick={() => setCategory(Number(key))}
|
||||||
return (
|
>
|
||||||
info.game === id && info.category == Number(key)
|
<div className='h-18 w-screen relative'>
|
||||||
)
|
<p className='text-2xl'>{value}</p>
|
||||||
}).length ?? 0
|
|
||||||
return `${count} install${count === 1 ? '' : 's'}`
|
<div
|
||||||
})()}
|
className='entry-info-item flex absolute left-0 bottom-0'
|
||||||
</p>
|
title='The amount of versions installed of this game in installed/installable format.'
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{(() => {
|
||||||
|
const count =
|
||||||
|
Object.keys(
|
||||||
|
downloadedVersionsConfig?.list ?? []
|
||||||
|
).filter(v => {
|
||||||
|
const info = getVersionInfo(v)
|
||||||
|
if (!info) return false
|
||||||
|
if (
|
||||||
|
platform() == 'linux' &&
|
||||||
|
info.wine &&
|
||||||
|
!normalConfig?.settings
|
||||||
|
.useWineOnUnixWhenNeeded
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
return (
|
||||||
|
info.game === id &&
|
||||||
|
info.category == Number(key)
|
||||||
|
)
|
||||||
|
}).length ?? 0
|
||||||
|
return `${count} install${count === 1 ? '' : 's'}`
|
||||||
|
})()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
className='button absolute right-0 bottom-0'
|
|
||||||
hidden={!normalConfig?.settings.useLegacyInteractButtons}
|
|
||||||
title='Click to view category'
|
|
||||||
onClick={() => setCategory(Number(key))}
|
|
||||||
>
|
|
||||||
Installs
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
|
||||||
{Object.keys(downloadedVersionsConfig?.list ?? []).filter(v => {
|
{Object.keys(downloadedVersionsConfig?.list ?? []).filter(v => {
|
||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
@@ -154,7 +200,7 @@ export default function Installs () {
|
|||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
if (!info) return false
|
if (!info) return false
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
info.wine &&
|
info.wine &&
|
||||||
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -166,21 +212,76 @@ export default function Installs () {
|
|||||||
: info.category == category)
|
: info.category == category)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map((entry, i) => (
|
.map(entry => (
|
||||||
<div
|
<div
|
||||||
key={entry}
|
key={entry}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
title={
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
'Click to launch game. Right-click to manage this version install'
|
||||||
? ''
|
|
||||||
: 'Click to launch game. Right-click to manage this version install'
|
|
||||||
}
|
}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
if (
|
||||||
|
needsRevisionUpdate(
|
||||||
|
getVersionInfo(entry)?.lastRevision,
|
||||||
|
entry
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const answer = await ask(
|
||||||
|
'Before proceeding, if you do not want your installation directory wiped just yet, please backup the files to another directory. When you click "Yes", it will be wiped. Click "No" if you want to open the installation folder instead.',
|
||||||
|
{
|
||||||
|
title: 'Revision Update',
|
||||||
|
kind: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (answer) {
|
||||||
|
const answer2 = await ask(
|
||||||
|
'Are you sure you want to update? If you did not read the last popup, please go back and read it.',
|
||||||
|
{
|
||||||
|
title: 'Revision Update',
|
||||||
|
kind: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!answer2) return
|
||||||
|
|
||||||
|
//open downloads popup
|
||||||
|
setPopupMode(1)
|
||||||
|
setShowPopup(true)
|
||||||
|
setFadeOut(false)
|
||||||
|
|
||||||
|
//uninstall
|
||||||
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
const updatedList = Object.fromEntries(
|
||||||
|
Object.entries(prev.list).filter(
|
||||||
|
([k]) => k !== entry
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const updatedConfig = {
|
||||||
|
...prev,
|
||||||
|
list: updatedList
|
||||||
|
}
|
||||||
|
writeVersionsConfig(updatedConfig)
|
||||||
|
return updatedConfig
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
await exists('game/' + entry, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await remove('game/' + entry, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData,
|
||||||
|
recursive: true
|
||||||
|
})
|
||||||
|
|
||||||
|
//reinstall
|
||||||
|
setSelectedVersionList([entry])
|
||||||
|
downloadVersions([entry])
|
||||||
|
} else {
|
||||||
|
openFolder(entry)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
const verInfo = getVersionInfo(entry)
|
const verInfo = getVersionInfo(entry)
|
||||||
if (verInfo == undefined) return
|
if (verInfo == undefined) return
|
||||||
const gameInfo = getGameInfo(verInfo.game)
|
const gameInfo = getGameInfo(verInfo.game)
|
||||||
@@ -190,7 +291,7 @@ export default function Installs () {
|
|||||||
executable: verInfo.executable,
|
executable: verInfo.executable,
|
||||||
displayName: verInfo.displayName,
|
displayName: verInfo.displayName,
|
||||||
useWine: !!(
|
useWine: !!(
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' &&
|
||||||
verInfo.wine &&
|
verInfo.wine &&
|
||||||
normalConfig?.settings.useWineOnUnixWhenNeeded
|
normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
),
|
),
|
||||||
@@ -199,8 +300,6 @@ export default function Installs () {
|
|||||||
}}
|
}}
|
||||||
onContextMenu={e => {
|
onContextMenu={e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
|
||||||
|
|
||||||
setManagingVersion(entry)
|
setManagingVersion(entry)
|
||||||
setPopupMode(2)
|
setPopupMode(2)
|
||||||
setShowPopup(true)
|
setShowPopup(true)
|
||||||
@@ -209,7 +308,7 @@ export default function Installs () {
|
|||||||
>
|
>
|
||||||
<div className='h-18 w-screen relative'>
|
<div className='h-18 w-screen relative'>
|
||||||
<p className='text-2xl'>
|
<p className='text-2xl'>
|
||||||
{getVersionInfo(entry)?.displayName}
|
{getVersionInfo(entry)?.displayName}{' '}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className='flex gap-2 absolute left-0 bottom-0'>
|
<div className='flex gap-2 absolute left-0 bottom-0'>
|
||||||
@@ -230,8 +329,11 @@ export default function Installs () {
|
|||||||
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
|
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
|
||||||
hidden={
|
hidden={
|
||||||
!(
|
!(
|
||||||
platform() === 'linux' &&
|
platform() == 'linux' && getVersionInfo(entry)?.wine
|
||||||
getVersionInfo(entry)?.wine
|
) ||
|
||||||
|
needsRevisionUpdate(
|
||||||
|
getVersionInfo(entry)?.lastRevision,
|
||||||
|
entry
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
@@ -239,56 +341,19 @@ export default function Installs () {
|
|||||||
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
|
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
|
||||||
<p>Uses wine</p>
|
<p>Uses wine</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div
|
||||||
|
className='entry-info-item'
|
||||||
<div className='flex gap-2 absolute right-0 bottom-0'>
|
onClick={e => e.stopPropagation()}
|
||||||
<button
|
|
||||||
className='button'
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setManagingVersion(entry)
|
|
||||||
setPopupMode(3)
|
|
||||||
setShowPopup(true)
|
|
||||||
setFadeOut(false)
|
|
||||||
}}
|
|
||||||
title='Click to view version info'
|
|
||||||
>
|
|
||||||
View Info
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button'
|
|
||||||
hidden={
|
hidden={
|
||||||
!normalConfig?.settings.useLegacyInteractButtons
|
!needsRevisionUpdate(
|
||||||
|
getVersionInfo(entry)?.lastRevision,
|
||||||
|
entry
|
||||||
|
)
|
||||||
}
|
}
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setManagingVersion(entry)
|
|
||||||
setPopupMode(2)
|
|
||||||
setShowPopup(true)
|
|
||||||
setFadeOut(false)
|
|
||||||
}}
|
|
||||||
title='Click to manage this version install'
|
|
||||||
>
|
>
|
||||||
Manage
|
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
|
||||||
</button>
|
<p>Needs revision update!</p>
|
||||||
<button
|
</div>
|
||||||
className='button'
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
const verInfo = getVersionInfo(entry)
|
|
||||||
if (verInfo == undefined) return
|
|
||||||
invoke('launch_game', {
|
|
||||||
name: verInfo.id,
|
|
||||||
executable: verInfo.executable
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
hidden={
|
|
||||||
!normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
title='Click to launch game'
|
|
||||||
>
|
|
||||||
Launch
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+195
-508
@@ -1,52 +1,42 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import Sidebar from './componets/Sidebar'
|
import Sidebar from '@/componets/Sidebar'
|
||||||
import './Globals.css'
|
import './Globals.css'
|
||||||
import { DownloadProgress } from './types/DownloadProgress'
|
import { DownloadProgress } from '@/types/DownloadProgress'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import {
|
import { faChevronLeft, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||||
faAdd,
|
|
||||||
faCheck,
|
|
||||||
faChevronLeft,
|
|
||||||
faCode,
|
|
||||||
faDownload,
|
|
||||||
faInfo,
|
|
||||||
faRemove,
|
|
||||||
faShieldHalved,
|
|
||||||
faWarning,
|
|
||||||
faXmark
|
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import {
|
import {
|
||||||
readNormalConfig,
|
readNormalConfig,
|
||||||
readVersionsConfig,
|
readVersionsConfig,
|
||||||
writeVersionsConfig
|
writeVersionsConfig
|
||||||
} from './util/BazookaManager'
|
} from '@/lib/BazookaManager'
|
||||||
import { VersionsConfig } from './types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
import { NormalConfig } from './types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
import { GlobalProvider } from './GlobalProvider'
|
import { GlobalProvider } from './GlobalProvider'
|
||||||
import { Roboto } from 'next/font/google'
|
import { Roboto } from 'next/font/google'
|
||||||
import { ServerVersionsResponse } from './types/ServerVersionsResponse'
|
import { ServerVersionsResponse } from '@/types/ServerVersionsResponse'
|
||||||
import { GameVersion } from './types/GameVersion'
|
import { GameVersion } from '@/types/GameVersion'
|
||||||
import { Game } from './types/Game'
|
import { Game } from '@/types/Game'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import { arch, platform } from '@tauri-apps/plugin-os'
|
import { arch, platform } from '@tauri-apps/plugin-os'
|
||||||
import VersionInfo from './componets/VersionInfo'
|
import { notifyUser } from '@/lib/Notifications'
|
||||||
import prettyBytes from 'pretty-bytes'
|
|
||||||
import ProgressBar from './componets/ProgressBar'
|
|
||||||
import { notifyUser } from './util/Notifications'
|
|
||||||
import {
|
import {
|
||||||
isPermissionGranted,
|
isPermissionGranted,
|
||||||
requestPermission
|
requestPermission
|
||||||
} from '@tauri-apps/plugin-notification'
|
} from '@tauri-apps/plugin-notification'
|
||||||
import VersionChangelog from './componets/VersionChangelog'
|
|
||||||
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
|
|
||||||
|
import VersionsDownloadPopup from '@/componets/popups/VersionsDownload'
|
||||||
|
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
|
||||||
|
import DownloadsPopup from '@/componets/popups/Downloads'
|
||||||
|
import VersionVersionPopup from '@/componets/popups/VersionVersion'
|
||||||
|
|
||||||
const roboto = Roboto({
|
const roboto = Roboto({
|
||||||
subsets: ['latin']
|
subsets: ['latin']
|
||||||
})
|
})
|
||||||
@@ -60,6 +50,7 @@ export default function RootLayout ({
|
|||||||
const [loadingText, setLoadingText] = useState('Loading...')
|
const [loadingText, setLoadingText] = useState('Loading...')
|
||||||
const [outdated, setOutdated] = useState(false)
|
const [outdated, setOutdated] = useState(false)
|
||||||
const [version, setVersion] = useState<string | null>(null)
|
const [version, setVersion] = useState<string | null>(null)
|
||||||
|
const [platformName, setPlatformName] = useState<string | null>(null)
|
||||||
|
|
||||||
const [serverVersionList, setServerVersionList] =
|
const [serverVersionList, setServerVersionList] =
|
||||||
useState<null | ServerVersionsResponse>(null)
|
useState<null | ServerVersionsResponse>(null)
|
||||||
@@ -76,13 +67,18 @@ export default function RootLayout ({
|
|||||||
const [downloadProgress, setDownloadProgress] = useState<DownloadProgress[]>(
|
const [downloadProgress, setDownloadProgress] = useState<DownloadProgress[]>(
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
const [downloadQueue, setDownloadQueue] = useState<string[]>([])
|
||||||
|
const [isProcessingQueue, setIsProcessingQueue] = useState<boolean>(false)
|
||||||
const [managingVersion, setManagingVersion] = useState<string | null>(null)
|
const [managingVersion, setManagingVersion] = useState<string | null>(null)
|
||||||
const [viewingInfoFromDownloads, setViewingInfoFromDownloads] =
|
const [viewingInfoFromDownloads, setViewingInfoFromDownloads] =
|
||||||
useState<boolean>(false)
|
useState<boolean>(false)
|
||||||
const [selectedGame, setSelectedGame] = useState<number | null>(null)
|
const [selectedGame, setSelectedGame] = useState<number | null>(null)
|
||||||
|
|
||||||
|
const [category, setCategory] = useState<number>(-1)
|
||||||
|
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const revisionCheck = useRef(false)
|
const revisionCheck = useRef(false)
|
||||||
|
const previousQueueLength = useRef(0)
|
||||||
|
|
||||||
function getSpecialVersionsList (game?: number): GameVersion[] {
|
function getSpecialVersionsList (game?: number): GameVersion[] {
|
||||||
if (!normalConfig || !serverVersionList) return []
|
if (!normalConfig || !serverVersionList) return []
|
||||||
@@ -93,13 +89,14 @@ export default function RootLayout ({
|
|||||||
)
|
)
|
||||||
.filter(v => {
|
.filter(v => {
|
||||||
if (
|
if (
|
||||||
platform() === 'linux' &&
|
platformName == 'linux' &&
|
||||||
v.wine &&
|
v.wine &&
|
||||||
!normalConfig.settings.useWineOnUnixWhenNeeded
|
!normalConfig.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (game && v.game != game) return false
|
if (game && v.game != game) return false
|
||||||
|
if (category != -1 && v.category != category) return false
|
||||||
if (downloadProgress.length != 0) {
|
if (downloadProgress.length != 0) {
|
||||||
return !downloadProgress.some(d => d.version === v.id)
|
return !downloadProgress.some(d => d.version === v.id)
|
||||||
}
|
}
|
||||||
@@ -111,15 +108,21 @@ export default function RootLayout ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionInfo (id: string | undefined): GameVersion | undefined {
|
const getVersionInfo = useCallback(
|
||||||
if (!id) return undefined
|
(id: string | undefined): GameVersion | undefined => {
|
||||||
return serverVersionList?.versions.find(v => v.id === id)
|
if (!id) return undefined
|
||||||
}
|
return serverVersionList?.versions.find(v => v.id === id)
|
||||||
|
},
|
||||||
|
[serverVersionList]
|
||||||
|
)
|
||||||
|
|
||||||
function getGameInfo (game: number | undefined): Game | undefined {
|
const getGameInfo = useCallback(
|
||||||
if (!game) return undefined
|
(game: number | undefined): Game | undefined => {
|
||||||
return serverVersionList?.games.find(g => g.id === game)
|
if (!game) return undefined
|
||||||
}
|
return serverVersionList?.games.find(g => g.id === game)
|
||||||
|
},
|
||||||
|
[serverVersionList]
|
||||||
|
)
|
||||||
|
|
||||||
function getListOfGames (): Game[] {
|
function getListOfGames (): Game[] {
|
||||||
if (!downloadedVersionsConfig?.list) return []
|
if (!downloadedVersionsConfig?.list) return []
|
||||||
@@ -144,7 +147,7 @@ export default function RootLayout ({
|
|||||||
if (!downloadedVersionsConfig || !serverVersionList) return null
|
if (!downloadedVersionsConfig || !serverVersionList) return null
|
||||||
|
|
||||||
const allowWine =
|
const allowWine =
|
||||||
platform() !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
|
platformName !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
|
|
||||||
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
|
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
|
||||||
const info = getVersionInfo(v)
|
const info = getVersionInfo(v)
|
||||||
@@ -162,34 +165,18 @@ export default function RootLayout ({
|
|||||||
return { installed, total }
|
return { installed, total }
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEtaSmart (seconds: number) {
|
const closePopup = useCallback(() => {
|
||||||
if (seconds < 60) return `${Math.floor(seconds)}s`
|
|
||||||
if (seconds < 3600)
|
|
||||||
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`
|
|
||||||
if (seconds < 86400) {
|
|
||||||
const h = Math.floor(seconds / 3600)
|
|
||||||
const m = Math.floor((seconds % 3600) / 60)
|
|
||||||
return `${h}h ${m}m`
|
|
||||||
}
|
|
||||||
const d = Math.floor(seconds / 86400)
|
|
||||||
const h = Math.floor((seconds % 86400) / 3600)
|
|
||||||
return `${d}d ${h}h`
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePopup () {
|
|
||||||
if (popupMode == 0 && selectedGame && pathname === '/') {
|
if (popupMode == 0 && selectedGame && pathname === '/') {
|
||||||
setSelectedGame(null)
|
setSelectedGame(null)
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
} else if (viewingInfoFromDownloads) {
|
} else if (viewingInfoFromDownloads) {
|
||||||
setViewingInfoFromDownloads(false)
|
setViewingInfoFromDownloads(false)
|
||||||
setPopupMode(0)
|
setPopupMode(0)
|
||||||
} else if (popupMode == 4) {
|
|
||||||
setPopupMode(3)
|
|
||||||
} else {
|
} else {
|
||||||
setFadeOut(true)
|
setFadeOut(true)
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
setTimeout(() => setShowPopup(false), 200)
|
||||||
}
|
}
|
||||||
}
|
}, [popupMode, selectedGame, pathname, viewingInfoFromDownloads])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unlistenProgress: (() => void) | null = null
|
let unlistenProgress: (() => void) | null = null
|
||||||
@@ -245,10 +232,10 @@ export default function RootLayout ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
|
setPlatformName(platform())
|
||||||
const client = await app.getVersion()
|
const client = await app.getVersion()
|
||||||
setVersion(client)
|
setVersion(client)
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
setLoadingText('Checking latest version...')
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
'https://games.lncvrt.xyz/api/launcher/latest'
|
'https://games.lncvrt.xyz/api/launcher/latest'
|
||||||
@@ -262,7 +249,6 @@ export default function RootLayout ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLoadingText('Downloading version list...')
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(
|
const res = await axios.get(
|
||||||
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
||||||
@@ -272,7 +258,6 @@ export default function RootLayout ({
|
|||||||
setLoadingText('Failed to download versions list.')
|
setLoadingText('Failed to download versions list.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLoadingText('Loading configs...')
|
|
||||||
const normalConfig = await readNormalConfig()
|
const normalConfig = await readNormalConfig()
|
||||||
const versionsConfig = await readVersionsConfig()
|
const versionsConfig = await readVersionsConfig()
|
||||||
setDownloadedVersionsConfig(versionsConfig)
|
setDownloadedVersionsConfig(versionsConfig)
|
||||||
@@ -285,50 +270,64 @@ export default function RootLayout ({
|
|||||||
})()
|
})()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const downloadVersions = useCallback(
|
||||||
|
async (list: string[]): Promise<void> => {
|
||||||
|
if (list.length === 0) return
|
||||||
|
setSelectedVersionList([])
|
||||||
|
|
||||||
|
const newVersions = list.filter(
|
||||||
|
version =>
|
||||||
|
!downloadQueue.includes(version) &&
|
||||||
|
!downloadProgress.some(d => d.version === version)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (newVersions.length === 0) return
|
||||||
|
|
||||||
|
const newDownloads = newVersions.map(
|
||||||
|
version =>
|
||||||
|
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
setDownloadProgress(prev => [...prev, ...newDownloads])
|
||||||
|
setDownloadQueue(prev => [...prev, ...newVersions])
|
||||||
|
},
|
||||||
|
[downloadQueue, downloadProgress]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (process.env.NODE_ENV !== 'production') return
|
if (isProcessingQueue || downloadQueue.length === 0) return
|
||||||
const handler = (e: MouseEvent) => e.preventDefault()
|
|
||||||
document.addEventListener('contextmenu', handler)
|
|
||||||
return () => document.removeEventListener('contextmenu', handler)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
async function downloadVersions (
|
const processNextDownload = async () => {
|
||||||
list: string[],
|
setIsProcessingQueue(true)
|
||||||
currentConfig: VersionsConfig
|
|
||||||
): Promise<void> {
|
|
||||||
if (list.length === 0) return
|
|
||||||
setSelectedVersionList([])
|
|
||||||
|
|
||||||
const newDownloads = list.map(
|
const versionId = downloadQueue[0]
|
||||||
version =>
|
const info = getVersionInfo(versionId)
|
||||||
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
setDownloadProgress(newDownloads)
|
|
||||||
|
|
||||||
for (const download of newDownloads) {
|
|
||||||
const info = getVersionInfo(download.version)
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
prev.filter(d => d.version !== download.version)
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
)
|
setIsProcessingQueue(false)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const gameInfo = getGameInfo(info.game)
|
const gameInfo = getGameInfo(info.game)
|
||||||
if (!gameInfo) {
|
if (!gameInfo) {
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
prev.filter(d => d.version !== download.version)
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
)
|
setIsProcessingQueue(false)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev =>
|
||||||
prev.map(d =>
|
prev.map(d => (d.version === versionId ? { ...d, queued: false } : d))
|
||||||
d.version === download.version ? { ...d, queued: false } : d
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.get(
|
||||||
|
'https://games.lncvrt.xyz/api/launcher/download?id=' + info.id
|
||||||
|
)
|
||||||
|
} catch {}
|
||||||
|
|
||||||
const res = await invoke<string>('download', {
|
const res = await invoke<string>('download', {
|
||||||
url: info.downloadUrl,
|
url: info.downloadUrl,
|
||||||
name: info.id,
|
name: info.id,
|
||||||
@@ -337,20 +336,25 @@ export default function RootLayout ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (res === '1') {
|
if (res === '1') {
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
prev.filter(d => d.version !== download.version)
|
setDownloadedVersionsConfig(prev => {
|
||||||
)
|
if (!prev) return prev
|
||||||
const date = Date.now()
|
|
||||||
const newConfig = {
|
const updated = {
|
||||||
...currentConfig,
|
...prev,
|
||||||
list: { ...currentConfig.list, [download.version]: date }
|
list: {
|
||||||
}
|
...prev.list,
|
||||||
setDownloadedVersionsConfig(newConfig)
|
[versionId]: Date.now()
|
||||||
writeVersionsConfig(newConfig)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeVersionsConfig(updated)
|
||||||
|
return updated
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
setDownloadProgress(prev =>
|
setDownloadProgress(prev =>
|
||||||
prev.map(d =>
|
prev.map(d =>
|
||||||
d.version === download.version
|
d.version === versionId
|
||||||
? { ...d, queued: false, failed: true, progress: 0 }
|
? { ...d, queued: false, failed: true, progress: 0 }
|
||||||
: d
|
: d
|
||||||
)
|
)
|
||||||
@@ -361,44 +365,67 @@ export default function RootLayout ({
|
|||||||
`The download for version ${info.displayName} has failed.`
|
`The download for version ${info.displayName} has failed.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
|
setIsProcessingQueue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalConfig?.settings.allowNotifications)
|
processNextDownload()
|
||||||
await notifyUser('Downloads Finished', 'All downloads have finished.')
|
}, [
|
||||||
|
downloadQueue,
|
||||||
|
isProcessingQueue,
|
||||||
|
getVersionInfo,
|
||||||
|
getGameInfo,
|
||||||
|
normalConfig
|
||||||
|
])
|
||||||
|
|
||||||
setFadeOut(true)
|
useEffect(() => {
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
if (
|
||||||
}
|
downloadQueue.length === 0 &&
|
||||||
|
downloadProgress.length === 0 &&
|
||||||
|
!isProcessingQueue &&
|
||||||
|
previousQueueLength.current > 0 &&
|
||||||
|
normalConfig?.settings.allowNotifications
|
||||||
|
) {
|
||||||
|
notifyUser('Downloads Finished', 'All downloads have finished.')
|
||||||
|
setTimeout(() => closePopup(), 0)
|
||||||
|
}
|
||||||
|
previousQueueLength.current = downloadQueue.length + downloadProgress.length
|
||||||
|
}, [
|
||||||
|
downloadQueue,
|
||||||
|
downloadProgress,
|
||||||
|
isProcessingQueue,
|
||||||
|
normalConfig,
|
||||||
|
closePopup
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (revisionCheck.current) return
|
if (revisionCheck.current) return
|
||||||
if (!serverVersionList || !downloadedVersionsConfig) return
|
if (!serverVersionList || !downloadedVersionsConfig) return
|
||||||
revisionCheck.current = true
|
revisionCheck.current = true
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const newConfig = {
|
|
||||||
...downloadedVersionsConfig,
|
|
||||||
list: { ...downloadedVersionsConfig.list }
|
|
||||||
}
|
|
||||||
const versionsToSelect: string[] = []
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(
|
for (const [key, value] of Object.entries(
|
||||||
downloadedVersionsConfig.list
|
downloadedVersionsConfig.list
|
||||||
)) {
|
)) {
|
||||||
const verInfo = serverVersionList.versions.find(item => item.id === key)
|
const verInfo = serverVersionList.versions.find(item => item.id === key)
|
||||||
|
|
||||||
if (!verInfo || value / 1000 <= verInfo.lastRevision) {
|
if (
|
||||||
delete newConfig.list[key]
|
!verInfo ||
|
||||||
versionsToSelect.push(key)
|
(verInfo.lastRevision > 0 && value / 1000 <= verInfo.lastRevision)
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
await exists('game/' + key + '/' + verInfo?.executable, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await remove('game/' + key + '/' + verInfo?.executable, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData,
|
||||||
|
recursive: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setDownloadedVersionsConfig(newConfig)
|
|
||||||
writeVersionsConfig(newConfig)
|
|
||||||
setSelectedVersionList(prev => [...prev, ...versionsToSelect])
|
|
||||||
|
|
||||||
await downloadVersions(versionsToSelect, newConfig)
|
|
||||||
})()
|
})()
|
||||||
}, [serverVersionList, downloadedVersionsConfig])
|
}, [serverVersionList, downloadedVersionsConfig, downloadVersions])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -408,37 +435,45 @@ export default function RootLayout ({
|
|||||||
normalConfig?.settings.theme === 1
|
normalConfig?.settings.theme === 1
|
||||||
? 'red-theme'
|
? 'red-theme'
|
||||||
: normalConfig?.settings.theme === 2
|
: normalConfig?.settings.theme === 2
|
||||||
? 'green-theme'
|
|
||||||
: normalConfig?.settings.theme === 3
|
|
||||||
? 'blue-theme'
|
? 'blue-theme'
|
||||||
: normalConfig?.settings.theme === 4
|
: normalConfig?.settings.theme === 3
|
||||||
? 'purple-theme'
|
? 'purple-theme'
|
||||||
: normalConfig?.settings.theme === 5
|
|
||||||
? 'mint-theme'
|
|
||||||
: 'dark-theme'
|
: 'dark-theme'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className='w-screen h-screen flex items-center justify-center'>
|
<>
|
||||||
{outdated ? (
|
<div
|
||||||
<div className='text-center'>
|
className='relative z-2 w-screen border-b border-b-(--col3) h-8.25 bg-(--col1)'
|
||||||
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
hidden={platformName != 'windows'}
|
||||||
<p className='text-4xl mb-4'>
|
/>
|
||||||
Please update to the latest version to continue.
|
<div
|
||||||
</p>
|
className={`w-screen ${
|
||||||
<button
|
platformName == 'windows'
|
||||||
className='button'
|
? 'h-[calc(100vh-64px)]'
|
||||||
onClick={() =>
|
: 'h-screen'
|
||||||
openUrl('https://games.lncvrt.xyz/berrydash/download')
|
} flex items-center justify-center`}
|
||||||
}
|
>
|
||||||
>
|
{outdated ? (
|
||||||
Download latest version
|
<div className='text-center'>
|
||||||
</button>
|
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
||||||
</div>
|
<p className='text-4xl mb-4'>
|
||||||
) : (
|
Please update to the latest version to continue.
|
||||||
<p className='text-7xl text-center'>{loadingText}</p>
|
</p>
|
||||||
)}
|
<button
|
||||||
</div>
|
className='button'
|
||||||
|
onClick={() =>
|
||||||
|
openUrl('https://games.lncvrt.xyz/berrydash/download')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Download latest version
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className='text-7xl text-center'>{loadingText}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<GlobalProvider
|
<GlobalProvider
|
||||||
value={{
|
value={{
|
||||||
@@ -465,7 +500,16 @@ export default function RootLayout ({
|
|||||||
setSelectedGame,
|
setSelectedGame,
|
||||||
getVersionsAmountData,
|
getVersionsAmountData,
|
||||||
viewingInfoFromDownloads,
|
viewingInfoFromDownloads,
|
||||||
version
|
version,
|
||||||
|
downloadVersions,
|
||||||
|
category,
|
||||||
|
setCategory,
|
||||||
|
downloadQueue,
|
||||||
|
setDownloadQueue,
|
||||||
|
closePopup,
|
||||||
|
getSpecialVersionsList,
|
||||||
|
selectedGame,
|
||||||
|
setViewingInfoFromDownloads
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -476,10 +520,8 @@ export default function RootLayout ({
|
|||||||
>
|
>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-(--col3) h-8.25 bg-(--col1)'
|
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-b-(--col3) h-8.25 bg-(--col1)'
|
||||||
style={{
|
hidden={platformName != 'windows'}
|
||||||
display: platform() === 'windows' ? 'block' : 'none'
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<div className='relative z-0'>
|
<div className='relative z-0'>
|
||||||
<main className='ml-60'>{children}</main>
|
<main className='ml-60'>{children}</main>
|
||||||
@@ -518,369 +560,14 @@ export default function RootLayout ({
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{popupMode === 0 && selectedGame ? (
|
{popupMode === 0 && selectedGame ? (
|
||||||
<>
|
<VersionsDownloadPopup />
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Select versions to download
|
|
||||||
</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{getSpecialVersionsList(selectedGame).map(
|
|
||||||
(v, i) => (
|
|
||||||
<div key={i} className='popup-entry'>
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<p
|
|
||||||
className={`text-2xl truncate ${
|
|
||||||
selectedVersionList.includes(v.id)
|
|
||||||
? 'max-w-84.5'
|
|
||||||
: 'max-w-91.5'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{v.displayName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-20.75 bottom-1.75'
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedVersionList(prev =>
|
|
||||||
prev.includes(v.id)
|
|
||||||
? prev.filter(i => i !== v.id)
|
|
||||||
: [...prev, v.id]
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title={
|
|
||||||
selectedVersionList.includes(v.id)
|
|
||||||
? 'This version will be downloaded. Click to remove from the list of versions that will be downloaded.'
|
|
||||||
: 'This version will NOT be downloaded. Click to add from the list of versions that will be downloaded.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{selectedVersionList.includes(v.id) ? (
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faRemove} />{' '}
|
|
||||||
Remove
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faAdd} /> Add
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-1.5 bottom-1.75'
|
|
||||||
onClick={() => {
|
|
||||||
setManagingVersion(v.id)
|
|
||||||
setViewingInfoFromDownloads(true)
|
|
||||||
setPopupMode(3)
|
|
||||||
}}
|
|
||||||
title='Click to view version info'
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faInfo} /> Info
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 0 && !selectedGame ? (
|
) : popupMode === 0 && !selectedGame ? (
|
||||||
<>
|
<GamesDownloadPopup />
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Select a game to download
|
|
||||||
</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{serverVersionList?.games.map((v, i) => (
|
|
||||||
<div key={i} className='popup-entry'>
|
|
||||||
<p className='text-2xl'>{v.name}</p>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
title='The amount of versions installed of this game in installed/installable format.'
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
{(() => {
|
|
||||||
const data = getVersionsAmountData(v.id)
|
|
||||||
if (!data) return 'N/A'
|
|
||||||
return `${data.installed}/${data.total}`
|
|
||||||
})()}{' '}
|
|
||||||
versions installed
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
hidden={!v.official}
|
|
||||||
title='This game is official.'
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faCheck}
|
|
||||||
color='#19c84b'
|
|
||||||
/>
|
|
||||||
<p>Official</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3'
|
|
||||||
hidden={v.official}
|
|
||||||
title={
|
|
||||||
v.verified
|
|
||||||
? 'This game is verified to be safe'
|
|
||||||
: 'This game is NOT verified to be save. Proceed with caution.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={
|
|
||||||
v.verified ? faShieldHalved : faWarning
|
|
||||||
}
|
|
||||||
color={v.verified ? '#19c84b' : '#ffc800'}
|
|
||||||
/>
|
|
||||||
<p>
|
|
||||||
{v.verified ? 'Verified' : 'Unverified'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='entry-info-item btntheme3 mt-2'
|
|
||||||
hidden={v.developer == null}
|
|
||||||
title={`The developer of ${v.name} is ${v.developer}.`}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faCode}
|
|
||||||
color='lightgray'
|
|
||||||
/>
|
|
||||||
<p>Developer: {v.developer}</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 right-2 bottom-2'
|
|
||||||
onClick={() => setSelectedGame(v.id)}
|
|
||||||
title={`Click to download specific versions of the game. You have ${(() => {
|
|
||||||
const data = getVersionsAmountData(v.id)
|
|
||||||
if (!data) return 'N/A'
|
|
||||||
return `${data.installed} of ${data.total}`
|
|
||||||
})()} versions downloaded.`}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<FontAwesomeIcon icon={faDownload} />{' '}
|
|
||||||
Download
|
|
||||||
</>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 1 ? (
|
) : popupMode === 1 ? (
|
||||||
<>
|
<DownloadsPopup />
|
||||||
<p className='text-xl text-center'>Downloads</p>
|
|
||||||
<div className='popup-content'>
|
|
||||||
{downloadProgress.map((v, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className='popup-entry flex flex-col justify-between'
|
|
||||||
>
|
|
||||||
<p className='text-2xl text-center'>
|
|
||||||
{getVersionInfo(v.version)?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='mt-6.25 flex items-center justify-between'>
|
|
||||||
{v.failed ? (
|
|
||||||
<>
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<span className='text-red-500 inline-block w-full text-center'>
|
|
||||||
Download failed
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className='button btntheme3 ml-30 mb-2'
|
|
||||||
onClick={() => {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(
|
|
||||||
d => d.version !== v.version
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title='Click to remove this version from this menu.'
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : v.queued ? (
|
|
||||||
<span className='text-yellow-500 inline-block w-full text-center'>
|
|
||||||
Queued…
|
|
||||||
</span>
|
|
||||||
) : v.queued ? (
|
|
||||||
<span className='text-yellow-500 inline-block w-full text-center'>
|
|
||||||
Queued…
|
|
||||||
</span>
|
|
||||||
) : v.hash_checking ? (
|
|
||||||
<span className='text-blue-500 inline-block w-full text-center'>
|
|
||||||
Checking hash...
|
|
||||||
</span>
|
|
||||||
) : v.finishing ? (
|
|
||||||
<span className='text-green-500 inline-block w-full text-center'>
|
|
||||||
Finishing...
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<div className='flex flex-col gap-1 w-full'>
|
|
||||||
<span className='text-center'>
|
|
||||||
Downloaded{' '}
|
|
||||||
{prettyBytes(v.progressBytes, {
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
})}{' '}
|
|
||||||
of{' '}
|
|
||||||
{prettyBytes(
|
|
||||||
getVersionInfo(v.version)?.size ?? 0,
|
|
||||||
{
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
}
|
|
||||||
)}{' '}
|
|
||||||
(ETA: {formatEtaSmart(v.etaSecs)} •
|
|
||||||
Speed:{' '}
|
|
||||||
{prettyBytes(v.speed, {
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1
|
|
||||||
})}
|
|
||||||
/s)
|
|
||||||
</span>
|
|
||||||
<ProgressBar
|
|
||||||
progress={v.progress}
|
|
||||||
className='w-full'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : popupMode === 2 ? (
|
) : popupMode === 2 ? (
|
||||||
managingVersion ? (
|
<VersionVersionPopup />
|
||||||
<>
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
Manage{' '}
|
|
||||||
{getVersionInfo(managingVersion)?.displayName}
|
|
||||||
</p>
|
|
||||||
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
|
||||||
<button
|
|
||||||
className='button btntheme2'
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
onClick={async () => {
|
|
||||||
closePopup()
|
|
||||||
|
|
||||||
setDownloadedVersionsConfig(prev => {
|
|
||||||
if (!prev) return prev
|
|
||||||
const updatedList = Object.fromEntries(
|
|
||||||
Object.entries(prev.list).filter(
|
|
||||||
([k]) => k !== managingVersion
|
|
||||||
)
|
|
||||||
)
|
|
||||||
const updatedConfig = {
|
|
||||||
...prev,
|
|
||||||
list: updatedList
|
|
||||||
}
|
|
||||||
writeVersionsConfig(updatedConfig)
|
|
||||||
setManagingVersion(null)
|
|
||||||
setFadeOut(true)
|
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
|
||||||
return updatedConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
if (
|
|
||||||
await exists('game/' + managingVersion, {
|
|
||||||
baseDir: BaseDirectory.AppLocalData
|
|
||||||
})
|
|
||||||
)
|
|
||||||
await remove('game/' + managingVersion, {
|
|
||||||
baseDir: BaseDirectory.AppLocalData,
|
|
||||||
recursive: true
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
|
|
||||||
>
|
|
||||||
Uninstall
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button btntheme2'
|
|
||||||
onClick={async () =>
|
|
||||||
invoke('open_folder', {
|
|
||||||
name: managingVersion
|
|
||||||
})
|
|
||||||
}
|
|
||||||
title="Click to browse the game's files."
|
|
||||||
>
|
|
||||||
Open Folder
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
No version selected
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
) : popupMode === 3 ? (
|
|
||||||
managingVersion && downloadedVersionsConfig ? (
|
|
||||||
<VersionInfo />
|
|
||||||
) : (
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
No version selected
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
) : popupMode === 4 ? (
|
|
||||||
managingVersion && downloadedVersionsConfig ? (
|
|
||||||
<VersionChangelog />
|
|
||||||
) : (
|
|
||||||
<p className='text-xl text-center'>
|
|
||||||
No version selected
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
) : null}
|
) : null}
|
||||||
{popupMode == 0 &&
|
|
||||||
selectedGame &&
|
|
||||||
serverVersionList != null && (
|
|
||||||
<div className='flex justify-center'>
|
|
||||||
<button
|
|
||||||
className='button btntheme1 w-fit mt-2 -mb-4'
|
|
||||||
onClick={() => {
|
|
||||||
setFadeOut(true)
|
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
|
||||||
if (downloadedVersionsConfig)
|
|
||||||
downloadVersions(
|
|
||||||
selectedVersionList,
|
|
||||||
downloadedVersionsConfig
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
disabled={downloadProgress.length != 0}
|
|
||||||
title={
|
|
||||||
downloadProgress.length != 0
|
|
||||||
? "You cannot download the versions as you have another download that hasn't completed."
|
|
||||||
: `Click to download ${
|
|
||||||
selectedVersionList.length
|
|
||||||
} version${
|
|
||||||
selectedVersionList.length == 1 ? '' : 's'
|
|
||||||
} of ${getGameInfo(selectedGame)?.name}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Download {selectedVersionList.length} version
|
|
||||||
{selectedVersionList.length == 1 ? '' : 's'}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className='button btntheme1 w-fit mt-2 ml-2 -mb-4'
|
|
||||||
onClick={() => {
|
|
||||||
const allIds = getSpecialVersionsList(
|
|
||||||
selectedGame
|
|
||||||
).map(v => v.id)
|
|
||||||
setSelectedVersionList(prev =>
|
|
||||||
prev.length === allIds.length ? [] : allIds
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
title={
|
|
||||||
selectedVersionList.length ===
|
|
||||||
getSpecialVersionsList(selectedGame).length
|
|
||||||
? 'Click to remove all selected versions for download.'
|
|
||||||
: 'Click to add all selected versions for download.'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{selectedVersionList.length ===
|
|
||||||
getSpecialVersionsList(selectedGame).length
|
|
||||||
? 'Deselect All'
|
|
||||||
: 'Select All'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+7
-24
@@ -1,9 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect } from 'react'
|
||||||
import './Installs.css'
|
import './Installs.css'
|
||||||
import { useGlobal } from './GlobalProvider'
|
import { useGlobal } from './GlobalProvider'
|
||||||
import Link from 'next/link'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import {
|
import {
|
||||||
faCheck,
|
faCheck,
|
||||||
@@ -24,7 +23,8 @@ export default function Installs () {
|
|||||||
normalConfig,
|
normalConfig,
|
||||||
setSelectedGame,
|
setSelectedGame,
|
||||||
getListOfGames,
|
getListOfGames,
|
||||||
getVersionsAmountData
|
getVersionsAmountData,
|
||||||
|
setCategory
|
||||||
} = useGlobal()
|
} = useGlobal()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -54,7 +54,7 @@ export default function Installs () {
|
|||||||
<div className='downloads-container'>
|
<div className='downloads-container'>
|
||||||
<div
|
<div
|
||||||
className={`downloads-scroll ${
|
className={`downloads-scroll ${
|
||||||
platform() === 'windows'
|
platform() == 'windows'
|
||||||
? 'h-[calc(100vh-116px)]'
|
? 'h-[calc(100vh-116px)]'
|
||||||
: 'h-[calc(100vh-84px)]'
|
: 'h-[calc(100vh-84px)]'
|
||||||
}`}
|
}`}
|
||||||
@@ -68,18 +68,10 @@ export default function Installs () {
|
|||||||
.map(i => (
|
.map(i => (
|
||||||
<div
|
<div
|
||||||
key={i.id}
|
key={i.id}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
title={'Click to view game installs'}
|
||||||
? ''
|
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'Click to view game installs'
|
|
||||||
}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
setCategory(-1)
|
||||||
router.push('/game?id=' + i.id)
|
router.push('/game?id=' + i.id)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -127,15 +119,6 @@ export default function Installs () {
|
|||||||
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
|
<p>{i.verified ? 'Verified' : 'Unverified'}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link
|
|
||||||
className='button absolute right-0 bottom-0'
|
|
||||||
href={'/game?id=' + i.id}
|
|
||||||
hidden={!normalConfig?.settings.useLegacyInteractButtons}
|
|
||||||
title='Click to view game installs'
|
|
||||||
>
|
|
||||||
Installs
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
+86
-105
@@ -1,18 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Setting } from '../componets/Setting'
|
import { Setting } from '@/componets/Setting'
|
||||||
import { writeNormalConfig } from '../util/BazookaManager'
|
import { writeNormalConfig } from '@/lib/BazookaManager'
|
||||||
import { useGlobal } from '../GlobalProvider'
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
import { copyToClipboard } from '../util/Clipboard'
|
import { copyToClipboard } from '@/lib/Clipboard'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
|
|
||||||
export default function Settings () {
|
export default function Settings () {
|
||||||
const [allowNotifications, setAllowNotifications] = useState(false)
|
const [allowNotifications, setAllowNotifications] = useState(false)
|
||||||
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
|
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [useLegacyInteractButtons, setUseLegacyInteractButtons] =
|
|
||||||
useState(false)
|
|
||||||
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
||||||
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
|
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
|
||||||
const [theme, setTheme] = useState(0)
|
const [theme, setTheme] = useState(0)
|
||||||
@@ -27,9 +25,6 @@ export default function Settings () {
|
|||||||
setAlwaysShowGamesInSidebar(
|
setAlwaysShowGamesInSidebar(
|
||||||
normalConfig.settings.alwaysShowGamesInSidebar
|
normalConfig.settings.alwaysShowGamesInSidebar
|
||||||
)
|
)
|
||||||
setUseLegacyInteractButtons(
|
|
||||||
normalConfig.settings.useLegacyInteractButtons
|
|
||||||
)
|
|
||||||
setUseWineOnUnixWhenNeeded(
|
setUseWineOnUnixWhenNeeded(
|
||||||
normalConfig.settings.useWineOnUnixWhenNeeded
|
normalConfig.settings.useWineOnUnixWhenNeeded
|
||||||
)
|
)
|
||||||
@@ -50,24 +45,22 @@ export default function Settings () {
|
|||||||
label='Allow sending notifications'
|
label='Allow sending notifications'
|
||||||
value={allowNotifications}
|
value={allowNotifications}
|
||||||
onChange={async () => {
|
onChange={async () => {
|
||||||
while (normalConfig != null) {
|
if (!normalConfig) return
|
||||||
setAllowNotifications(!allowNotifications)
|
setAllowNotifications(!allowNotifications)
|
||||||
setNormalConfig({
|
setNormalConfig({
|
||||||
...normalConfig,
|
...normalConfig,
|
||||||
settings: {
|
settings: {
|
||||||
...normalConfig.settings,
|
...normalConfig.settings,
|
||||||
allowNotifications: !allowNotifications
|
allowNotifications: !allowNotifications
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
writeNormalConfig({
|
await writeNormalConfig({
|
||||||
...normalConfig,
|
...normalConfig,
|
||||||
settings: {
|
settings: {
|
||||||
...normalConfig.settings,
|
...normalConfig.settings,
|
||||||
allowNotifications: !allowNotifications
|
allowNotifications: !allowNotifications
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
|
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
|
||||||
/>
|
/>
|
||||||
@@ -75,63 +68,45 @@ export default function Settings () {
|
|||||||
label='Always show games in sidebar'
|
label='Always show games in sidebar'
|
||||||
value={alwaysShowGamesInSidebar}
|
value={alwaysShowGamesInSidebar}
|
||||||
onChange={async () => {
|
onChange={async () => {
|
||||||
while (normalConfig != null) {
|
if (!normalConfig) return
|
||||||
setAlwaysShowGamesInSidebar(!alwaysShowGamesInSidebar)
|
setAlwaysShowGamesInSidebar(!alwaysShowGamesInSidebar)
|
||||||
setNormalConfig({
|
setNormalConfig({
|
||||||
...normalConfig,
|
...normalConfig,
|
||||||
settings: {
|
settings: {
|
||||||
...normalConfig.settings,
|
...normalConfig.settings,
|
||||||
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
writeNormalConfig({
|
await writeNormalConfig({
|
||||||
...normalConfig,
|
...normalConfig,
|
||||||
settings: {
|
settings: {
|
||||||
...normalConfig.settings,
|
...normalConfig.settings,
|
||||||
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
title="This setting will make it so when you are on a page like this, the games won't disappear."
|
||||||
/>
|
/>
|
||||||
<Setting
|
|
||||||
label='Show Installs/Launch/Manage Buttons'
|
|
||||||
value={useLegacyInteractButtons}
|
|
||||||
onChange={async () => {
|
|
||||||
while (normalConfig != null) {
|
|
||||||
setUseLegacyInteractButtons(!useLegacyInteractButtons)
|
|
||||||
setNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useLegacyInteractButtons: !useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
})
|
|
||||||
writeNormalConfig({
|
|
||||||
...normalConfig,
|
|
||||||
settings: {
|
|
||||||
...normalConfig.settings,
|
|
||||||
useLegacyInteractButtons: !useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title='Enable the legacy method of using the installs/launch/manage buttons. In the future this setting may be removed so try and get used to the new method.'
|
|
||||||
/>
|
|
||||||
<Setting
|
<Setting
|
||||||
label='Use wine when needed to launch games'
|
label='Use wine when needed to launch games'
|
||||||
value={useWineOnUnixWhenNeeded}
|
value={useWineOnUnixWhenNeeded}
|
||||||
onChange={async () => {
|
onChange={async () => {
|
||||||
while (normalConfig != null) {
|
if (!normalConfig) return
|
||||||
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
|
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
|
||||||
normalConfig.settings.useWineOnUnixWhenNeeded =
|
setNormalConfig({
|
||||||
!useWineOnUnixWhenNeeded
|
...normalConfig,
|
||||||
await writeNormalConfig(normalConfig)
|
settings: {
|
||||||
break
|
...normalConfig.settings,
|
||||||
}
|
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await writeNormalConfig({
|
||||||
|
...normalConfig,
|
||||||
|
settings: {
|
||||||
|
...normalConfig.settings,
|
||||||
|
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
|
||||||
|
}
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
className={platform() == 'linux' ? '' : 'hidden'}
|
className={platform() == 'linux' ? '' : 'hidden'}
|
||||||
/>
|
/>
|
||||||
@@ -142,12 +117,22 @@ export default function Settings () {
|
|||||||
type='text'
|
type='text'
|
||||||
value={wineOnUnixCommand}
|
value={wineOnUnixCommand}
|
||||||
onChange={async e => {
|
onChange={async e => {
|
||||||
while (normalConfig != null) {
|
if (!normalConfig) return
|
||||||
setWineOnUnixCommand(e.target.value)
|
setWineOnUnixCommand(e.target.value)
|
||||||
normalConfig.settings.wineOnUnixCommand = e.target.value
|
setNormalConfig({
|
||||||
await writeNormalConfig(normalConfig)
|
...normalConfig,
|
||||||
break
|
settings: {
|
||||||
}
|
...normalConfig.settings,
|
||||||
|
wineOnUnixCommand: e.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await writeNormalConfig({
|
||||||
|
...normalConfig,
|
||||||
|
settings: {
|
||||||
|
...normalConfig.settings,
|
||||||
|
wineOnUnixCommand: e.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
className={`input-field my-1 ${
|
className={`input-field my-1 ${
|
||||||
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden'
|
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden'
|
||||||
@@ -159,33 +144,29 @@ export default function Settings () {
|
|||||||
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
|
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
|
||||||
value={theme}
|
value={theme}
|
||||||
onChange={async e => {
|
onChange={async e => {
|
||||||
|
if (!normalConfig) return
|
||||||
const newTheme = parseInt(e.target.value)
|
const newTheme = parseInt(e.target.value)
|
||||||
while (normalConfig != null) {
|
setTheme(newTheme)
|
||||||
setTheme(newTheme)
|
setNormalConfig({
|
||||||
setNormalConfig({
|
...normalConfig,
|
||||||
...normalConfig,
|
settings: {
|
||||||
settings: {
|
...normalConfig.settings,
|
||||||
...normalConfig.settings,
|
theme: newTheme
|
||||||
theme: newTheme
|
}
|
||||||
}
|
})
|
||||||
})
|
await writeNormalConfig({
|
||||||
writeNormalConfig({
|
...normalConfig,
|
||||||
...normalConfig,
|
settings: {
|
||||||
settings: {
|
...normalConfig.settings,
|
||||||
...normalConfig.settings,
|
theme: newTheme
|
||||||
theme: newTheme
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value={0}>Dark (default)</option>
|
<option value={0}>Dark (default)</option>
|
||||||
<option value={1}>Red</option>
|
<option value={1}>Red</option>
|
||||||
<option value={2}>Green</option>
|
<option value={2}>Blue</option>
|
||||||
<option value={3}>Blue</option>
|
<option value={3}>Purple</option>
|
||||||
<option value={4}>Purple</option>
|
|
||||||
<option value={5}>Mint</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
export interface LeaderboardEntry {
|
|
||||||
username: string
|
|
||||||
userid: bigint
|
|
||||||
value: bigint
|
|
||||||
icon: number
|
|
||||||
overlay: number
|
|
||||||
birdColor: number[]
|
|
||||||
overlayColor: number[]
|
|
||||||
customIcon: string | null
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { LeaderboardEntry } from './LeaderboardEntry'
|
|
||||||
|
|
||||||
export interface LeaderboardResponse {
|
|
||||||
entries: LeaderboardEntry[]
|
|
||||||
customIcons: Record<string, string>
|
|
||||||
}
|
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
|
|||||||
import './Setting.css'
|
import './Setting.css'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
import { faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { SettingProps } from '../types/SettingProps'
|
import { SettingProps } from '@/types/SettingProps'
|
||||||
|
|
||||||
export function Setting ({
|
export function Setting ({
|
||||||
label,
|
label,
|
||||||
@@ -13,10 +13,6 @@
|
|||||||
@apply bg-(--col3) border-(--col4);
|
@apply bg-(--col3) border-(--col4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
|
||||||
@apply flex items-center pl-2 h-10 w-60 pt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-links {
|
.nav-links {
|
||||||
@apply flex flex-col px-4 space-y-1;
|
@apply flex flex-col px-4 space-y-1;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import './Sidebar.css'
|
||||||
|
import Icon from '@/assets/Icon.png'
|
||||||
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import {
|
||||||
|
faCog,
|
||||||
|
faDownload,
|
||||||
|
faGamepad,
|
||||||
|
faHexagonNodes,
|
||||||
|
faLayerGroup
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||||
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
|
import { Lexend } from 'next/font/google'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const lexend = Lexend({
|
||||||
|
subsets: ['latin']
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function Sidebar () {
|
||||||
|
const {
|
||||||
|
normalConfig,
|
||||||
|
getListOfGames,
|
||||||
|
setShowPopup,
|
||||||
|
setPopupMode,
|
||||||
|
setFadeOut,
|
||||||
|
downloadProgress,
|
||||||
|
downloadedVersionsConfig,
|
||||||
|
getVersionInfo,
|
||||||
|
category,
|
||||||
|
setCategory
|
||||||
|
} = useGlobal()
|
||||||
|
|
||||||
|
const pathname = usePathname()
|
||||||
|
const params = useSearchParams()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className='sidebar'>
|
||||||
|
<div
|
||||||
|
className='macos-drag'
|
||||||
|
hidden={platform() != 'macos'}
|
||||||
|
onMouseDown={e => {
|
||||||
|
if (e.buttons === 1) {
|
||||||
|
if (e.detail === 2) {
|
||||||
|
getCurrentWindow().toggleMaximize()
|
||||||
|
} else {
|
||||||
|
getCurrentWindow().startDragging()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
className={`flex items-center h-10 w-60 ${
|
||||||
|
(platform() == 'windows' ? 'pl-1 pt-1' : 'pl-2 pt-2') +
|
||||||
|
(platform() == 'macos' ? ' mt-7' : '')
|
||||||
|
}`}
|
||||||
|
onMouseDown={e => {
|
||||||
|
if (platform() != 'macos') return
|
||||||
|
if (e.buttons === 1) {
|
||||||
|
if (e.detail === 2) {
|
||||||
|
getCurrentWindow().toggleMaximize()
|
||||||
|
} else {
|
||||||
|
getCurrentWindow().startDragging()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image draggable={false} src={Icon} width={36} height={36} alt='' />
|
||||||
|
<p className={`ml-1 text-[16px] whitespace-nowrap ${lexend.className}`}>
|
||||||
|
Lncvrt Games Launcher
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<nav className='nav-links overflow-auto pt-2'>
|
||||||
|
<Link
|
||||||
|
draggable={false}
|
||||||
|
href='/'
|
||||||
|
className={`link relative flex items-center ${
|
||||||
|
pathname === '/' || pathname.startsWith('/game') ? 'active' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faHexagonNodes} className='mr-2' /> Games
|
||||||
|
</Link>
|
||||||
|
{getListOfGames()
|
||||||
|
.sort((a, b) => {
|
||||||
|
return a.id - b.id
|
||||||
|
})
|
||||||
|
.map(i => (
|
||||||
|
<React.Fragment key={i.id}>
|
||||||
|
<div
|
||||||
|
draggable={false}
|
||||||
|
className={`link ${
|
||||||
|
(pathname === '/game' &&
|
||||||
|
Number(params.get('id') || 0) == i.id) ||
|
||||||
|
(i.id == 1 && pathname === '/game/berrydash/leaderboards')
|
||||||
|
? 'active'
|
||||||
|
: ''
|
||||||
|
} ml-auto w-50 ${
|
||||||
|
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
||||||
|
pathname === '/' ||
|
||||||
|
pathname.startsWith('/game')
|
||||||
|
? ''
|
||||||
|
: 'hidden'
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setCategory(-1)
|
||||||
|
router.push('/game?id=' + i.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={
|
||||||
|
Object.entries(i.categoryNames).length > 0
|
||||||
|
? faLayerGroup
|
||||||
|
: faGamepad
|
||||||
|
}
|
||||||
|
className='mr-1'
|
||||||
|
/>
|
||||||
|
<span className='truncate max-w-full'>{i.name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{Object.entries(i.categoryNames)
|
||||||
|
.sort(([a], [b]) => Number(b) - Number(a))
|
||||||
|
.filter(([key]) => {
|
||||||
|
const count = Object.keys(
|
||||||
|
downloadedVersionsConfig?.list ?? {}
|
||||||
|
).filter(v => {
|
||||||
|
const info = getVersionInfo(v)
|
||||||
|
if (!info) return false
|
||||||
|
|
||||||
|
if (
|
||||||
|
platform() == 'linux' &&
|
||||||
|
info.wine &&
|
||||||
|
!normalConfig?.settings.useWineOnUnixWhenNeeded
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return info.game === i.id && info.category === Number(key)
|
||||||
|
}).length
|
||||||
|
|
||||||
|
return count >= 1
|
||||||
|
})
|
||||||
|
.map(([key, value]) => (
|
||||||
|
<div
|
||||||
|
key={`${i.id}-${key}`}
|
||||||
|
draggable={false}
|
||||||
|
className={`link ${
|
||||||
|
(pathname === '/game' &&
|
||||||
|
Number(params.get('id') || 0) == i.id) ||
|
||||||
|
(i.id == 1 &&
|
||||||
|
pathname === '/game/berrydash/leaderboards' &&
|
||||||
|
category == Number(key))
|
||||||
|
? 'active'
|
||||||
|
: ''
|
||||||
|
} ml-auto w-47.5 ${
|
||||||
|
normalConfig?.settings.alwaysShowGamesInSidebar ||
|
||||||
|
pathname === '/' ||
|
||||||
|
pathname.startsWith('/game')
|
||||||
|
? ''
|
||||||
|
: 'hidden'
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setCategory(Number(key))
|
||||||
|
router.push('/game?id=' + i.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<FontAwesomeIcon icon={faGamepad} className='mr-1' />
|
||||||
|
<span className='truncate max-w-full'>{value}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
<Link
|
||||||
|
draggable={false}
|
||||||
|
href='/settings'
|
||||||
|
className={`link ${pathname === '/settings' ? 'active' : ''}`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCog} className='mr-1' /> Settings
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
onClick={() => openUrl('https://games.lncvrt.xyz/discord')}
|
||||||
|
className='link mr-auto'
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<div
|
||||||
|
className='sidebar-downloads'
|
||||||
|
hidden={downloadProgress.length == 0}
|
||||||
|
onClick={() => {
|
||||||
|
setPopupMode(1)
|
||||||
|
setShowPopup(true)
|
||||||
|
setFadeOut(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<FontAwesomeIcon icon={faDownload} /> Downloads
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import prettyBytes from 'pretty-bytes'
|
||||||
|
import ProgressBar from '../ProgressBar'
|
||||||
|
import { formatEtaSmart } from '@/lib/Util'
|
||||||
|
|
||||||
|
export default function DownloadsPopup () {
|
||||||
|
const {
|
||||||
|
downloadProgress,
|
||||||
|
getVersionInfo,
|
||||||
|
setDownloadProgress,
|
||||||
|
downloadQueue,
|
||||||
|
setDownloadQueue
|
||||||
|
} = useGlobal()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Downloads</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{downloadProgress.map((v, i) => {
|
||||||
|
const queuePosition = downloadQueue.indexOf(v.version)
|
||||||
|
return (
|
||||||
|
<div key={i} className='popup-entry flex flex-col justify-between'>
|
||||||
|
<p className='text-2xl text-center'>
|
||||||
|
{getVersionInfo(v.version)?.displayName}
|
||||||
|
</p>
|
||||||
|
<div className='mt-6.25 flex items-center justify-between'>
|
||||||
|
{v.failed || v.queued ? (
|
||||||
|
<div className='flex items-center justify-between w-full'>
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
v.failed ? 'text-red-500' : 'text-yellow-300'
|
||||||
|
} inline-block text-center flex-1`}
|
||||||
|
>
|
||||||
|
{v.failed
|
||||||
|
? 'Download failed'
|
||||||
|
: queuePosition === 0
|
||||||
|
? 'Starting soon...'
|
||||||
|
: `Queued (Position ${queuePosition + 1})`}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 -ml-1.25'
|
||||||
|
onClick={() => {
|
||||||
|
setDownloadQueue(prev =>
|
||||||
|
prev.filter(id => id !== v.version)
|
||||||
|
)
|
||||||
|
setDownloadProgress(prev =>
|
||||||
|
prev.filter(d => d.version !== v.version)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title='Click to remove this version from the download queue.'
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : v.hash_checking || v.finishing ? (
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
v.hash_checking ? 'text-blue-300' : 'text-green-300'
|
||||||
|
} inline-block w-full text-center`}
|
||||||
|
>
|
||||||
|
{v.hash_checking ? 'Verifying file integerty' : 'Finishing'}
|
||||||
|
...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<div className='flex flex-col gap-1 w-full'>
|
||||||
|
<span className='text-center'>
|
||||||
|
Downloaded{' '}
|
||||||
|
{prettyBytes(v.progressBytes, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}{' '}
|
||||||
|
of{' '}
|
||||||
|
{prettyBytes(getVersionInfo(v.version)?.size ?? 0, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}{' '}
|
||||||
|
(ETA: {formatEtaSmart(v.etaSecs)} • Speed:{' '}
|
||||||
|
{prettyBytes(v.speed, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1
|
||||||
|
})}
|
||||||
|
/s)
|
||||||
|
</span>
|
||||||
|
<ProgressBar progress={v.progress} className='w-full' />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import {
|
||||||
|
faCheck,
|
||||||
|
faCode,
|
||||||
|
faDownload,
|
||||||
|
faShieldHalved,
|
||||||
|
faWarning
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
export default function GamesDownloadPopup () {
|
||||||
|
const { serverVersionList, getVersionsAmountData, setSelectedGame } =
|
||||||
|
useGlobal()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Select a game to download</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{serverVersionList?.games
|
||||||
|
.filter(v => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return false
|
||||||
|
if (data.total > 0) return true
|
||||||
|
})
|
||||||
|
.map((v, i) => (
|
||||||
|
<div key={i} className='popup-entry'>
|
||||||
|
<p className='text-2xl'>{v.name}</p>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
title='The amount of versions installed of this game in installed/installable format.'
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{(() => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return 'N/A'
|
||||||
|
return `${data.installed}/${data.total}`
|
||||||
|
})()}{' '}
|
||||||
|
versions installed
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
hidden={!v.official}
|
||||||
|
title='This game is official.'
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
|
||||||
|
<p>Official</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3'
|
||||||
|
hidden={v.official}
|
||||||
|
title={
|
||||||
|
v.verified
|
||||||
|
? 'This game is verified to be safe'
|
||||||
|
: 'This game is NOT verified to be save. Proceed with caution.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={v.verified ? faShieldHalved : faWarning}
|
||||||
|
color={v.verified ? '#19c84b' : '#ffc800'}
|
||||||
|
/>
|
||||||
|
<p>{v.verified ? 'Verified' : 'Unverified'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme3 mt-2'
|
||||||
|
hidden={v.developer == null}
|
||||||
|
title={`The developer of ${v.name} is ${v.developer}.`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCode} color='lightgray' />
|
||||||
|
<p>Developer: {v.developer}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-2 bottom-2'
|
||||||
|
onClick={() => setSelectedGame(v.id)}
|
||||||
|
title={`Click to download specific versions of the game. You have ${(() => {
|
||||||
|
const data = getVersionsAmountData(v.id)
|
||||||
|
if (!data) return 'N/A'
|
||||||
|
return `${data.installed} of ${data.total}`
|
||||||
|
})()} versions downloaded.`}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faDownload} /> Download
|
||||||
|
</>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
faArrowUpRightFromSquare,
|
||||||
|
faCheck,
|
||||||
|
faCode,
|
||||||
|
faHardDrive,
|
||||||
|
faShieldHalved,
|
||||||
|
faWarning
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import prettyBytes from 'pretty-bytes'
|
||||||
|
import { message } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
|
||||||
|
import { writeVersionsConfig } from '@/lib/BazookaManager'
|
||||||
|
import { openFolder } from '@/lib/Util'
|
||||||
|
|
||||||
|
export default function VersionVersionPopup () {
|
||||||
|
const {
|
||||||
|
getGameInfo,
|
||||||
|
getVersionInfo,
|
||||||
|
managingVersion,
|
||||||
|
downloadedVersionsConfig,
|
||||||
|
viewingInfoFromDownloads,
|
||||||
|
setManagingVersion,
|
||||||
|
closePopup,
|
||||||
|
setDownloadedVersionsConfig,
|
||||||
|
setPopupMode,
|
||||||
|
setSelectedVersionList,
|
||||||
|
downloadVersions
|
||||||
|
} = useGlobal()
|
||||||
|
const [versionSize, setVersionSize] = useState<number>(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (viewingInfoFromDownloads) return
|
||||||
|
invoke<string>('folder_size', {
|
||||||
|
version: managingVersion
|
||||||
|
}).then(size => {
|
||||||
|
setVersionSize(Number(size))
|
||||||
|
})
|
||||||
|
}, [managingVersion, setVersionSize, viewingInfoFromDownloads])
|
||||||
|
|
||||||
|
if (!managingVersion || !downloadedVersionsConfig) return <></>
|
||||||
|
|
||||||
|
const versionInfo = getVersionInfo(managingVersion)
|
||||||
|
const gameInfo = getGameInfo(versionInfo?.game)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Viewing {versionInfo?.displayName}</p>
|
||||||
|
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Installed{' '}
|
||||||
|
{new Intl.DateTimeFormat(undefined).format(
|
||||||
|
downloadedVersionsConfig.list[managingVersion] ?? 0
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={!versionInfo || versionInfo.releaseDate == 0}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Released{' '}
|
||||||
|
{new Intl.DateTimeFormat(undefined).format(
|
||||||
|
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
|
||||||
|
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
|
||||||
|
<p>Official</p>
|
||||||
|
</div>
|
||||||
|
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={gameInfo?.verified ? faShieldHalved : faWarning}
|
||||||
|
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
|
||||||
|
/>
|
||||||
|
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={gameInfo?.developer == null}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCode} color='lightgray' />
|
||||||
|
<p>Developer: {gameInfo?.developer}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||||
|
<p>
|
||||||
|
Size on disk:{' '}
|
||||||
|
{versionSize && versionSize > 0
|
||||||
|
? prettyBytes(versionSize, {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2
|
||||||
|
})
|
||||||
|
: 'N/A'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
hidden={!viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
|
||||||
|
<p>
|
||||||
|
Size when downloaded (zipped):{' '}
|
||||||
|
{versionInfo
|
||||||
|
? prettyBytes(versionInfo.size, {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2
|
||||||
|
})
|
||||||
|
: 'Loading...'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
onClick={async () => {
|
||||||
|
if (!versionInfo) return
|
||||||
|
await message(atob(versionInfo.changelog), {
|
||||||
|
title: 'Changelog for ' + versionInfo.displayName,
|
||||||
|
kind: 'info'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
hidden={!versionInfo?.changelog}
|
||||||
|
>
|
||||||
|
<p>View Changelog</p>
|
||||||
|
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
onClick={async () => openFolder(managingVersion)}
|
||||||
|
title="Click to browse the game's files."
|
||||||
|
hidden={viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
Open Folder
|
||||||
|
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
onClick={async () => {
|
||||||
|
closePopup()
|
||||||
|
|
||||||
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
const updatedList = Object.fromEntries(
|
||||||
|
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
|
||||||
|
)
|
||||||
|
const updatedConfig = {
|
||||||
|
...prev,
|
||||||
|
list: updatedList
|
||||||
|
}
|
||||||
|
writeVersionsConfig(updatedConfig)
|
||||||
|
return updatedConfig
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
await exists('game/' + managingVersion, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await remove('game/' + managingVersion, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData,
|
||||||
|
recursive: true
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
|
||||||
|
hidden={viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
Uninstall
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='entry-info-item btntheme2'
|
||||||
|
onClick={async () => {
|
||||||
|
//change popup to downloads
|
||||||
|
setManagingVersion(null)
|
||||||
|
setPopupMode(1)
|
||||||
|
|
||||||
|
//uninstall
|
||||||
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
const updatedList = Object.fromEntries(
|
||||||
|
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
|
||||||
|
)
|
||||||
|
const updatedConfig = {
|
||||||
|
...prev,
|
||||||
|
list: updatedList
|
||||||
|
}
|
||||||
|
writeVersionsConfig(updatedConfig)
|
||||||
|
return updatedConfig
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
await exists('game/' + managingVersion, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await remove('game/' + managingVersion, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData,
|
||||||
|
recursive: true
|
||||||
|
})
|
||||||
|
|
||||||
|
//reinstall
|
||||||
|
setSelectedVersionList([managingVersion])
|
||||||
|
downloadVersions([managingVersion])
|
||||||
|
}}
|
||||||
|
title="Click to reinstall this game. This will NOT remove any progress or any save files. This WILL uninstall any modifications to the game's executable files."
|
||||||
|
hidden={viewingInfoFromDownloads}
|
||||||
|
>
|
||||||
|
Reinstall
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { useGlobal } from '@/app/GlobalProvider'
|
||||||
|
import { faAdd, faInfo, faRemove } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
export default function VersionsDownloadPopup () {
|
||||||
|
const {
|
||||||
|
selectedVersionList,
|
||||||
|
setSelectedVersionList,
|
||||||
|
setManagingVersion,
|
||||||
|
setPopupMode,
|
||||||
|
getSpecialVersionsList,
|
||||||
|
selectedGame,
|
||||||
|
setViewingInfoFromDownloads,
|
||||||
|
downloadedVersionsConfig,
|
||||||
|
downloadVersions,
|
||||||
|
getGameInfo
|
||||||
|
} = useGlobal()
|
||||||
|
if (!selectedGame) return <></>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl text-center'>Select versions to download</p>
|
||||||
|
<div className='popup-content'>
|
||||||
|
{getSpecialVersionsList(selectedGame).map((v, i) => (
|
||||||
|
<div key={i} className='popup-entry'>
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<p
|
||||||
|
className={`text-2xl truncate ${
|
||||||
|
selectedVersionList.includes(v.id)
|
||||||
|
? 'max-w-84.5'
|
||||||
|
: 'max-w-91.5'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{v.displayName}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-20.75 bottom-1.75'
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedVersionList(prev =>
|
||||||
|
prev.includes(v.id)
|
||||||
|
? prev.filter(i => i !== v.id)
|
||||||
|
: [...prev, v.id]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
selectedVersionList.includes(v.id)
|
||||||
|
? 'This version will be downloaded. Click to remove from the list of versions that will be downloaded.'
|
||||||
|
: 'This version will NOT be downloaded. Click to add from the list of versions that will be downloaded.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selectedVersionList.includes(v.id) ? (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faRemove} /> Remove
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faAdd} /> Add
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme3 right-1.5 bottom-1.75'
|
||||||
|
onClick={() => {
|
||||||
|
setManagingVersion(v.id)
|
||||||
|
setViewingInfoFromDownloads(true)
|
||||||
|
setPopupMode(2)
|
||||||
|
}}
|
||||||
|
title='Click to view version info'
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfo} /> Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<button
|
||||||
|
className='button btntheme1 w-fit mt-2 -mb-4'
|
||||||
|
onClick={() => {
|
||||||
|
if (downloadedVersionsConfig) {
|
||||||
|
downloadVersions(selectedVersionList)
|
||||||
|
setPopupMode(1)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={selectedVersionList.length == 0}
|
||||||
|
title={
|
||||||
|
selectedVersionList.length == 0
|
||||||
|
? 'Select at least one version to download'
|
||||||
|
: `Download ${selectedVersionList.length} version${
|
||||||
|
selectedVersionList.length == 1 ? '' : 's'
|
||||||
|
} of ${getGameInfo(selectedGame)?.name}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Download {selectedVersionList.length} version
|
||||||
|
{selectedVersionList.length == 1 ? '' : 's'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='button btntheme1 w-fit mt-2 ml-2 -mb-4'
|
||||||
|
onClick={() => {
|
||||||
|
const allIds = getSpecialVersionsList(selectedGame).map(v => v.id)
|
||||||
|
setSelectedVersionList(prev =>
|
||||||
|
prev.length === allIds.length ? [] : allIds
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
selectedVersionList.length ===
|
||||||
|
getSpecialVersionsList(selectedGame).length
|
||||||
|
? 'Click to remove all selected versions for download.'
|
||||||
|
: 'Click to add all selected versions for download.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{selectedVersionList.length ===
|
||||||
|
getSpecialVersionsList(selectedGame).length
|
||||||
|
? 'Deselect All'
|
||||||
|
: 'Select All'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { app } from '@tauri-apps/api'
|
import { app } from '@tauri-apps/api'
|
||||||
import { NormalConfig } from '../types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import {
|
import {
|
||||||
BaseDirectory,
|
BaseDirectory,
|
||||||
create,
|
create,
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
readTextFile,
|
readTextFile,
|
||||||
writeFile
|
writeFile
|
||||||
} from '@tauri-apps/plugin-fs'
|
} from '@tauri-apps/plugin-fs'
|
||||||
import { VersionsConfig, VersionsConfigData } from '../types/VersionsConfig'
|
import { VersionsConfig } from '@/types/VersionsConfig'
|
||||||
|
|
||||||
export async function readNormalConfig (): Promise<NormalConfig> {
|
export async function readNormalConfig (): Promise<NormalConfig> {
|
||||||
const version = await app.getVersion()
|
const version = await app.getVersion()
|
||||||
@@ -32,7 +32,31 @@ export async function readNormalConfig (): Promise<NormalConfig> {
|
|||||||
return new NormalConfig(version)
|
return new NormalConfig(version)
|
||||||
}
|
}
|
||||||
const config = await readTextFile('config.json', options)
|
const config = await readTextFile('config.json', options)
|
||||||
return NormalConfig.import(JSON.parse(config))
|
const raw = JSON.parse(config)
|
||||||
|
if (
|
||||||
|
raw.settings &&
|
||||||
|
raw.settings.theme &&
|
||||||
|
(raw.version == '1.0.0' ||
|
||||||
|
raw.version == '1.1.0' ||
|
||||||
|
raw.version == '1.1.1' ||
|
||||||
|
raw.version == '1.2.0' ||
|
||||||
|
raw.version == '1.3.0' ||
|
||||||
|
raw.version == '1.3.1' ||
|
||||||
|
raw.version == '1.4.0' ||
|
||||||
|
raw.version == '1.5.0' ||
|
||||||
|
raw.version == '1.5.1' ||
|
||||||
|
raw.version == '1.5.2' ||
|
||||||
|
raw.version == '1.5.3' ||
|
||||||
|
raw.version == '1.5.4')
|
||||||
|
) {
|
||||||
|
const parsed = Number(raw.settings.theme)
|
||||||
|
if (parsed == 3) raw.settings.theme = 2
|
||||||
|
if (parsed == 4) raw.settings.theme = 3
|
||||||
|
else if (parsed != 0 && parsed != 1) raw.settings.theme = 0
|
||||||
|
}
|
||||||
|
raw.version = version
|
||||||
|
writeNormalConfig(raw)
|
||||||
|
return NormalConfig.import(raw)
|
||||||
} catch {
|
} catch {
|
||||||
return new NormalConfig(version)
|
return new NormalConfig(version)
|
||||||
}
|
}
|
||||||
@@ -83,8 +107,7 @@ export async function readVersionsConfig (): Promise<VersionsConfig> {
|
|||||||
}
|
}
|
||||||
const config = await readTextFile('versions.json', options)
|
const config = await readTextFile('versions.json', options)
|
||||||
const raw = JSON.parse(config)
|
const raw = JSON.parse(config)
|
||||||
if (raw.version && raw.list && raw.timestamps) {
|
if (raw.list && raw.timestamps) {
|
||||||
raw.version = version
|
|
||||||
raw.list = raw.timestamps
|
raw.list = raw.timestamps
|
||||||
delete raw.timestamps
|
delete raw.timestamps
|
||||||
|
|
||||||
@@ -94,6 +117,8 @@ export async function readVersionsConfig (): Promise<VersionsConfig> {
|
|||||||
options
|
options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
raw.version = version
|
||||||
|
writeVersionsConfig(raw)
|
||||||
return VersionsConfig.import(raw)
|
return VersionsConfig.import(raw)
|
||||||
} catch {
|
} catch {
|
||||||
return new VersionsConfig(version)
|
return new VersionsConfig(version)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const GetIconForUser = (user: number) => {
|
||||||
|
if (user == 1) return -1
|
||||||
|
else if (user == 2) return -2
|
||||||
|
else if (user == 4) return -3
|
||||||
|
else if (user == 3) return -4
|
||||||
|
else return 1
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NormalConfig } from '../types/NormalConfig'
|
import { NormalConfig } from '@/types/NormalConfig'
|
||||||
import { notifyUser } from './Notifications'
|
import { notifyUser } from './Notifications'
|
||||||
|
|
||||||
export async function copyToClipboard (
|
export async function copyToClipboard (
|
||||||
File renamed without changes.
@@ -0,0 +1,50 @@
|
|||||||
|
import { message } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { BaseDirectory, exists, stat } from '@tauri-apps/plugin-fs'
|
||||||
|
import { openPath } from '@tauri-apps/plugin-opener'
|
||||||
|
import { join, appLocalDataDir } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
|
export const openFolder = async (name: string) => {
|
||||||
|
const relativePath = await join('game', name)
|
||||||
|
const basePath = await appLocalDataDir()
|
||||||
|
const absolutePath = await join(basePath, relativePath)
|
||||||
|
|
||||||
|
const folderExists = await exists(relativePath, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!folderExists) {
|
||||||
|
await message(`Game folder "${absolutePath}" not found.`, {
|
||||||
|
title: 'Folder not found',
|
||||||
|
kind: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderStat = await stat(relativePath, {
|
||||||
|
baseDir: BaseDirectory.AppLocalData
|
||||||
|
})
|
||||||
|
|
||||||
|
if (folderStat.isFile) {
|
||||||
|
await message(`Game folder "${absolutePath}" not found.`, {
|
||||||
|
title: 'Folder not found',
|
||||||
|
kind: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await openPath(absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatEtaSmart = (seconds: number) => {
|
||||||
|
if (seconds < 60) return `${Math.floor(seconds)}s`
|
||||||
|
if (seconds < 3600)
|
||||||
|
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`
|
||||||
|
if (seconds < 86400) {
|
||||||
|
const h = Math.floor(seconds / 3600)
|
||||||
|
const m = Math.floor((seconds % 3600) / 60)
|
||||||
|
return `${h}h ${m}m`
|
||||||
|
}
|
||||||
|
const d = Math.floor(seconds / 86400)
|
||||||
|
const h = Math.floor((seconds % 86400) / 3600)
|
||||||
|
return `${d}d ${h}h`
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type BirdColor = [number, number, number]
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,7 +2,6 @@ export class SettingsType {
|
|||||||
constructor (
|
constructor (
|
||||||
public allowNotifications: boolean = true,
|
public allowNotifications: boolean = true,
|
||||||
public alwaysShowGamesInSidebar: boolean = true,
|
public alwaysShowGamesInSidebar: boolean = true,
|
||||||
public useLegacyInteractButtons: boolean = false,
|
|
||||||
public useWineOnUnixWhenNeeded: boolean = false,
|
public useWineOnUnixWhenNeeded: boolean = false,
|
||||||
public wineOnUnixCommand: string = 'wine %path%',
|
public wineOnUnixCommand: string = 'wine %path%',
|
||||||
public theme: number = 0
|
public theme: number = 0
|
||||||
File renamed without changes.
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "es2024",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
@@ -28,7 +28,9 @@
|
|||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
".next/types/**/*.ts",
|
".next/types/**/*.ts",
|
||||||
".next/dev/types/**/*.ts",
|
".next/dev/types/**/*.ts",
|
||||||
"**/*.mts"
|
"**/*.mts",
|
||||||
|
"next.config.ts",
|
||||||
|
"eslint.config.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user