Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f46d9c67b8
|
||
|
|
132616dc29
|
||
|
|
8b22fdb648
|
||
|
|
dc55c7549d
|
||
|
|
9d9c6147c5
|
||
|
|
48c8365b7c
|
||
|
|
8884d08c7c
|
||
|
|
b516a9cc43
|
||
|
|
6b7b2b198a
|
||
|
|
fbf87dceba
|
||
|
|
9ae28690a7
|
||
|
|
f83cc118fe
|
||
|
|
19788a8a3c
|
||
|
|
e066dd0681
|
||
|
|
29b3c3c58b
|
||
|
|
a8390216d0
|
||
|
|
513f1a0c16
|
||
|
|
e7e624f042
|
||
|
|
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
|
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
NEXT_PUBLIC_PUBLIC_SIGNING_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUNvd0JRWURLMlZ3QXlFQVJiK1N5c0VQemRYaGN4OWtyMy9rY3NNNURheDZRS1Q0VXZ3bW9PbWd2UHc9Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=
|
||||||
@@ -30,9 +30,6 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
|
||||||
.env*
|
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
|
|||||||
@@ -5,36 +5,36 @@
|
|||||||
"": {
|
"": {
|
||||||
"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.10.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",
|
||||||
|
"@tauri-apps/plugin-http": "2.5.7",
|
||||||
"@tauri-apps/plugin-notification": "2.3.3",
|
"@tauri-apps/plugin-notification": "2.3.3",
|
||||||
"@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",
|
|
||||||
"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",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "3.3.3",
|
"@eslint/eslintrc": "3.3.5",
|
||||||
"@tailwindcss/postcss": "4.1.18",
|
"@tailwindcss/postcss": "4.2.1",
|
||||||
"@tauri-apps/cli": "2.10.0",
|
"@tauri-apps/cli": "2.10.1",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.2.0",
|
"@types/node": "25.3.5",
|
||||||
"@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.4",
|
||||||
"eslint-config-next": "16.1.6",
|
"eslint-config-next": "16.1.6",
|
||||||
"tailwindcss": "4.1.18",
|
"tailwindcss": "4.2.1",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
"@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/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.29.0", "", { "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-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ=="],
|
"@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=="],
|
||||||
|
|
||||||
@@ -84,29 +84,29 @@
|
|||||||
|
|
||||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||||
|
|
||||||
"@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
|
"@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="],
|
||||||
|
|
||||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
|
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
|
||||||
|
|
||||||
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
|
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
|
||||||
|
|
||||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="],
|
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="],
|
||||||
|
|
||||||
"@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="],
|
"@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="],
|
||||||
|
|
||||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||||
|
|
||||||
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
|
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||||
|
|
||||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||||
|
|
||||||
@@ -210,61 +210,61 @@
|
|||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="],
|
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="],
|
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="],
|
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="],
|
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="],
|
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="],
|
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="],
|
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="],
|
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="],
|
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="],
|
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="],
|
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="],
|
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="],
|
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
|
||||||
|
|
||||||
"@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.2.1", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "postcss": "^8.5.6", "tailwindcss": "4.2.1" } }, "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw=="],
|
||||||
|
|
||||||
"@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
|
"@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
|
||||||
|
|
||||||
"@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": ["@tauri-apps/cli@2.10.1", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.10.1", "@tauri-apps/cli-darwin-x64": "2.10.1", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", "@tauri-apps/cli-linux-arm64-musl": "2.10.1", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-musl": "2.10.1", "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", "@tauri-apps/cli-win32-x64-msvc": "2.10.1" }, "bin": { "tauri": "tauri.js" } }, "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g=="],
|
||||||
|
|
||||||
"@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-arm64": ["@tauri-apps/cli-darwin-arm64@2.10.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ=="],
|
||||||
|
|
||||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q=="],
|
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw=="],
|
||||||
|
|
||||||
"@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-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.10.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w=="],
|
||||||
|
|
||||||
"@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-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA=="],
|
||||||
|
|
||||||
"@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-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg=="],
|
||||||
|
|
||||||
"@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-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.10.1", "", { "os": "linux", "cpu": "none" }, "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw=="],
|
||||||
|
|
||||||
"@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-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw=="],
|
||||||
|
|
||||||
"@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-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ=="],
|
||||||
|
|
||||||
"@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-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.10.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg=="],
|
||||||
|
|
||||||
"@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-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.10.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw=="],
|
||||||
|
|
||||||
"@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/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.10.1", "", { "os": "win32", "cpu": "x64" }, "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -272,6 +272,8 @@
|
|||||||
|
|
||||||
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.4.5", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-dVxWWGE6VrOxC7/jlhyE+ON/Cc2REJlM35R3PJX3UvFw2XwYhLGQVAIyrehenDdKjotipjYEVc4YjOl3qq90fA=="],
|
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.4.5", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-dVxWWGE6VrOxC7/jlhyE+ON/Cc2REJlM35R3PJX3UvFw2XwYhLGQVAIyrehenDdKjotipjYEVc4YjOl3qq90fA=="],
|
||||||
|
|
||||||
|
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.5.7", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-+F2lEH/c9b0zSsOXKq+5hZNcd9F4IIKCK1T17RqMwpCmVnx2aoqY8yIBccCd25HTYUb3j6NPVbRax/m00hKG8A=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-notification": ["@tauri-apps/plugin-notification@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg=="],
|
"@tauri-apps/plugin-notification": ["@tauri-apps/plugin-notification@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ=="],
|
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ=="],
|
||||||
@@ -290,31 +292,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.2.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="],
|
"@types/node": ["@types/node@25.3.5", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg=="],
|
||||||
|
|
||||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.54.0", "", {}, "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA=="],
|
"@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA=="],
|
||||||
|
|
||||||
"@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.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -354,11 +356,11 @@
|
|||||||
|
|
||||||
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
@@ -386,19 +388,15 @@
|
|||||||
|
|
||||||
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
||||||
|
|
||||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
|
||||||
|
|
||||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||||
|
|
||||||
"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=="],
|
|
||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||||
|
|
||||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="],
|
||||||
|
|
||||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
@@ -414,7 +412,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.30001767", "", {}, "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
@@ -424,8 +422,6 @@
|
|||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
|
||||||
|
|
||||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||||
|
|
||||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||||
@@ -452,19 +448,17 @@
|
|||||||
|
|
||||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||||
|
|
||||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||||
|
|
||||||
"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.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="],
|
||||||
|
|
||||||
"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.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="],
|
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
@@ -486,7 +480,7 @@
|
|||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
"eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="],
|
"eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="],
|
||||||
|
|
||||||
"eslint-config-next": ["eslint-config-next@16.1.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.1.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA=="],
|
"eslint-config-next": ["eslint-config-next@16.1.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.1.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA=="],
|
||||||
|
|
||||||
@@ -538,14 +532,10 @@
|
|||||||
|
|
||||||
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
||||||
|
|
||||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
"flatted": ["flatted@3.3.4", "", {}, "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA=="],
|
||||||
|
|
||||||
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
|
|
||||||
|
|
||||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||||
|
|
||||||
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
|
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
|
||||||
@@ -562,7 +552,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.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w=="],
|
"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=="],
|
||||||
|
|
||||||
@@ -684,29 +674,29 @@
|
|||||||
|
|
||||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
||||||
|
|
||||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
|
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
|
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
|
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
|
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
|
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
|
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
|
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
|
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
|
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
|
||||||
|
|
||||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||||
|
|
||||||
@@ -724,11 +714,7 @@
|
|||||||
|
|
||||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||||
|
|
||||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
"minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
||||||
|
|
||||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
||||||
|
|
||||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||||
|
|
||||||
@@ -742,7 +728,9 @@
|
|||||||
|
|
||||||
"next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
|
"next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
|
||||||
|
|
||||||
"node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
|
"node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="],
|
||||||
|
|
||||||
|
"node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="],
|
||||||
|
|
||||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
@@ -782,7 +770,7 @@
|
|||||||
|
|
||||||
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|
||||||
@@ -790,8 +778,6 @@
|
|||||||
|
|
||||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
@@ -874,7 +860,7 @@
|
|||||||
|
|
||||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
|
||||||
|
|
||||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||||
|
|
||||||
@@ -900,11 +886,11 @@
|
|||||||
|
|
||||||
"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.56.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/parser": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
|
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
|
||||||
|
|
||||||
@@ -950,9 +936,11 @@
|
|||||||
|
|
||||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||||
|
|
||||||
"@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@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
@@ -962,18 +950,20 @@
|
|||||||
|
|
||||||
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||||
|
|
||||||
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="],
|
||||||
|
|
||||||
"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@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,8 @@ const eslintConfig = defineConfig([
|
|||||||
'.next/**',
|
'.next/**',
|
||||||
'out/**',
|
'out/**',
|
||||||
'build/**',
|
'build/**',
|
||||||
'next-env.d.ts'
|
'next-env.d.ts',
|
||||||
|
'src-tauri/**'
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lncvrt-games-launcher",
|
"name": "lncvrt-games-launcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.3",
|
"version": "1.6.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -11,36 +11,36 @@
|
|||||||
"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.10.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",
|
||||||
|
"@tauri-apps/plugin-http": "2.5.7",
|
||||||
"@tauri-apps/plugin-notification": "2.3.3",
|
"@tauri-apps/plugin-notification": "2.3.3",
|
||||||
"@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",
|
|
||||||
"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",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "3.3.3",
|
"@eslint/eslintrc": "3.3.5",
|
||||||
"@tailwindcss/postcss": "4.1.18",
|
"@tailwindcss/postcss": "4.2.1",
|
||||||
"@tauri-apps/cli": "2.10.0",
|
"@tauri-apps/cli": "2.10.1",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/node": "25.2.0",
|
"@types/node": "25.3.5",
|
||||||
"@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.4",
|
||||||
"eslint-config-next": "16.1.6",
|
"eslint-config-next": "16.1.6",
|
||||||
"tailwindcss": "4.1.18",
|
"tailwindcss": "4.2.1",
|
||||||
"typescript": "5.9.3"
|
"typescript": "5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lncvrt-games-launcher"
|
name = "lncvrt-games-launcher"
|
||||||
version = "1.5.3"
|
version = "1.6.5"
|
||||||
authors = ["Lncvrt"]
|
authors = ["Lncvrt"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -9,32 +9,36 @@ name = "lncvrt_games_launcher_lib"
|
|||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.5.4", features = [] }
|
tauri-build = { version = "2.5.6", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2.10.1", features = ["macos-private-api"] }
|
tauri = { version = "2.10.3", 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.50.0"
|
||||||
futures-util = { version = "0.3.31", features = ["io"] }
|
futures-util = { version = "0.3.32", 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.2.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.3"
|
||||||
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.4"
|
||||||
|
openssl = { version = "0.10.75", features = ["v110", "vendored"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
tauri-plugin-http = "2.5.7"
|
||||||
|
|
||||||
[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"
|
||||||
|
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
"core:window:allow-is-maximized",
|
"core:window:allow-is-maximized",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-toggle-maximize",
|
"core:window:allow-toggle-maximize",
|
||||||
|
"core:window:allow-request-user-attention",
|
||||||
|
"core:window:allow-set-progress-bar",
|
||||||
"decorum:allow-show-snap-overlay",
|
"decorum:allow-show-snap-overlay",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
"dialog:default",
|
"dialog:default",
|
||||||
@@ -32,6 +34,26 @@
|
|||||||
"allow": [
|
"allow": [
|
||||||
{ "path": "$APPLOCALDATA/game/**" }
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "opener:allow-open-path",
|
||||||
|
"allow": [
|
||||||
|
{ "path": "$APPLOCALDATA/game/**" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "http:default",
|
||||||
|
"allow": [
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/launcher/latest" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/launcher/versions" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/launcher/download" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/account" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/score" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/berry" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/coin" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/legacy" },
|
||||||
|
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/total" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 248 KiB |
@@ -1,4 +1,7 @@
|
|||||||
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
|
use openssl::pkey::PKey;
|
||||||
|
use openssl::sign::Verifier;
|
||||||
use sha2::{Digest, Sha512};
|
use sha2::{Digest, Sha512};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -12,9 +15,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,14 +24,16 @@ 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();
|
||||||
let target = path.canonicalize().ok();
|
let target = match path.canonicalize() {
|
||||||
if target.is_none() {
|
Ok(t) => t,
|
||||||
return false;
|
Err(_) => return false,
|
||||||
}
|
};
|
||||||
let target = target.unwrap();
|
|
||||||
sys.processes().values().any(|proc| {
|
sys.processes().values().any(|proc| {
|
||||||
proc.exe()
|
proc.exe()
|
||||||
.and_then(|exe| exe.canonicalize().ok())
|
.and_then(|exe| exe.canonicalize().ok())
|
||||||
@@ -37,23 +41,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))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +97,10 @@ async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn folder_size(app: AppHandle, version: String) -> String {
|
fn folder_size(app: AppHandle, version: String) -> String {
|
||||||
let path = app
|
let path = match app.path().app_local_data_dir() {
|
||||||
.path()
|
Ok(p) => p.join("game").join(&version),
|
||||||
.app_local_data_dir()
|
Err(_) => return "-1".to_string(),
|
||||||
.unwrap()
|
};
|
||||||
.join("game")
|
|
||||||
.join(&version);
|
|
||||||
fn inner(path: &Path) -> u64 {
|
fn inner(path: &Path) -> u64 {
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
if let Ok(entries) = fs::read_dir(path) {
|
if let Ok(entries) = fs::read_dir(path) {
|
||||||
@@ -108,48 +134,117 @@ async fn download(
|
|||||||
executable: String,
|
executable: String,
|
||||||
hash: String,
|
hash: String,
|
||||||
) -> String {
|
) -> String {
|
||||||
|
println!("[download] Starting download for '{}' from '{}'", name, url);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let resp = match client.get(&url).send().await {
|
let resp = match client.get(&url).send().await {
|
||||||
Ok(r) => r,
|
Ok(r) => {
|
||||||
Err(_) => {
|
println!("[download] HTTP request succeeded, status: {}", r.status());
|
||||||
|
r
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("[download] ERROR: HTTP request failed: {:?}", e);
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let total_size = resp.content_length().unwrap_or(0);
|
let total_size = resp.content_length().unwrap_or(0);
|
||||||
|
println!(
|
||||||
|
"[download] Content-Length: {} bytes ({:.2} MB)",
|
||||||
|
total_size,
|
||||||
|
total_size as f64 / 1_048_576.0
|
||||||
|
);
|
||||||
|
let _ = app.emit("download-size", format!("{}:{}", &name, &total_size));
|
||||||
|
|
||||||
let mut downloaded: u64 = 0;
|
let mut downloaded: u64 = 0;
|
||||||
let mut stream = resp.bytes_stream();
|
let mut stream = resp.bytes_stream();
|
||||||
|
|
||||||
let downloads_path = app.path().app_local_data_dir().unwrap().join("downloads");
|
let downloads_path = match app.path().app_local_data_dir() {
|
||||||
let game_path = app.path().app_local_data_dir().unwrap().join("game");
|
Ok(p) => {
|
||||||
|
let path = p.join("downloads");
|
||||||
let download_part_path = downloads_path.join(format!("{}.part", name));
|
println!("[download] Downloads path: {:?}", path);
|
||||||
let download_zip_path = downloads_path.join(format!("{}.zip", name));
|
path
|
||||||
|
|
||||||
if download_part_path.exists() {
|
|
||||||
let _ = tokio::fs::remove_file(&download_part_path).await;
|
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
let _ = tokio::fs::create_dir_all(&downloads_path).await;
|
println!(
|
||||||
if let Ok(true) = tokio::fs::try_exists(&game_path.join(name.clone())).await {
|
"[download] ERROR: Failed to resolve app_local_data_dir: {}",
|
||||||
let _ = tokio::fs::remove_dir_all(&game_path.join(name.clone())).await;
|
e
|
||||||
|
);
|
||||||
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
};
|
||||||
let mut file = tokio::fs::File::create(download_part_path.clone())
|
let game_path = match app.path().app_local_data_dir() {
|
||||||
.await
|
Ok(p) => {
|
||||||
.unwrap();
|
let path = p.join("game");
|
||||||
|
println!("[download] Game path: {:?}", path);
|
||||||
let start = Instant::now();
|
path
|
||||||
|
}
|
||||||
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
|
Err(e) => {
|
||||||
let chunk = match chunk_result {
|
println!("[download] ERROR: Failed to resolve game path: {}", e);
|
||||||
Ok(c) => c,
|
|
||||||
Err(_) => {
|
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(_) = file.write_all(&chunk).await {
|
let download_part_path = downloads_path.join(format!("{}.part", name));
|
||||||
|
let download_zip_path = downloads_path.join(format!("{}.zip", name));
|
||||||
|
println!("[download] Part file: {:?}", download_part_path);
|
||||||
|
println!("[download] Zip file: {:?}", download_zip_path);
|
||||||
|
|
||||||
|
if download_part_path.exists() {
|
||||||
|
println!("[download] Stale .part file found, removing...");
|
||||||
|
let _ = tokio::fs::remove_file(&download_part_path).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[download] Creating downloads dir (if needed): {:?}",
|
||||||
|
downloads_path
|
||||||
|
);
|
||||||
|
let _ = tokio::fs::create_dir_all(&downloads_path).await;
|
||||||
|
|
||||||
|
if let Ok(true) = tokio::fs::try_exists(&game_path.join(name.clone())).await {
|
||||||
|
println!(
|
||||||
|
"[download] Existing game dir found, removing: {:?}",
|
||||||
|
game_path.join(&name)
|
||||||
|
);
|
||||||
|
let _ = tokio::fs::remove_dir_all(&game_path.join(name.clone())).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("[download] Creating game dir: {:?}", game_path.join(&name));
|
||||||
|
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
|
||||||
|
|
||||||
|
let mut file = match tokio::fs::File::create(download_part_path.clone()).await {
|
||||||
|
Ok(f) => {
|
||||||
|
println!("[download] Created .part file successfully");
|
||||||
|
f
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("[download] ERROR: Failed to create .part file: {}", e);
|
||||||
|
return "-1".to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut chunk_count: u64 = 0;
|
||||||
|
|
||||||
|
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
|
||||||
|
let chunk = match chunk_result {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"[download] ERROR: Failed to read chunk #{}: {}",
|
||||||
|
chunk_count, e
|
||||||
|
);
|
||||||
|
return "-1".to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
chunk_count += 1;
|
||||||
|
|
||||||
|
if let Err(e) = file.write_all(&chunk).await {
|
||||||
|
println!(
|
||||||
|
"[download] ERROR: Failed to write chunk #{} to disk: {}",
|
||||||
|
chunk_count, e
|
||||||
|
);
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,87 +264,116 @@ async fn download(
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
app.emit(
|
if chunk_count % 50 == 0 || downloaded == total_size {
|
||||||
|
println!(
|
||||||
|
"[download] [{:.1}%] {:.2} MB / {:.2} MB | Speed: {:.1} KB/s | ETA: {:.1}s | Chunks: {}",
|
||||||
|
progress,
|
||||||
|
downloaded as f64 / 1_048_576.0,
|
||||||
|
total_size as f64 / 1_048_576.0,
|
||||||
|
speed / 1024.0,
|
||||||
|
eta_secs,
|
||||||
|
chunk_count,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = app.emit(
|
||||||
"download-progress",
|
"download-progress",
|
||||||
format!(
|
format!(
|
||||||
"{}:{:.8}:{}:{}:{:.2}",
|
"{}:{:.8}:{}:{}:{:.2}",
|
||||||
&name, progress, downloaded, speed, eta_secs
|
&name, progress, downloaded, speed, eta_secs
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if total_size > 0 && downloaded < total_size {
|
if total_size > 0 && downloaded < total_size {
|
||||||
|
println!(
|
||||||
|
"[download] ERROR: Download incomplete — got {} of {} bytes ({:.1}% complete)",
|
||||||
|
downloaded,
|
||||||
|
total_size,
|
||||||
|
(downloaded as f64 / total_size as f64) * 100.0
|
||||||
|
);
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.emit("download-hash-checking", format!("{}", &name))
|
println!(
|
||||||
.unwrap();
|
"[download] Download complete: {} bytes in {:.2}s",
|
||||||
|
downloaded,
|
||||||
|
start.elapsed().as_secs_f64()
|
||||||
|
);
|
||||||
|
println!("[download] Verifying SHA-512 hash...");
|
||||||
|
let _ = app.emit("download-hash-checking", format!("{}", &name));
|
||||||
|
|
||||||
let download_hash = {
|
let download_hash = {
|
||||||
let mut file = tokio::fs::File::open(download_part_path.clone())
|
let mut file = match tokio::fs::File::open(download_part_path.clone()).await {
|
||||||
.await
|
Ok(f) => f,
|
||||||
.unwrap();
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"[download] ERROR: Failed to open .part file for hashing: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return "-1".to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
let mut hasher = Sha512::new();
|
let mut hasher = Sha512::new();
|
||||||
|
let mut bytes_hashed: u64 = 0;
|
||||||
{
|
{
|
||||||
let mut buffer = [0; 8192];
|
let mut buffer = [0; 8192];
|
||||||
loop {
|
loop {
|
||||||
let bytes_read = file.read(&mut buffer).await.unwrap();
|
let bytes_read = match file.read(&mut buffer).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"[download] ERROR: Failed to read file during hashing: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return "-1".to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hasher.update(&buffer[..bytes_read]);
|
hasher.update(&buffer[..bytes_read]);
|
||||||
|
bytes_hashed += bytes_read as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(file);
|
drop(file);
|
||||||
|
println!("[download] Hashed {} bytes", bytes_hashed);
|
||||||
format!("{:x}", hasher.finalize())
|
format!("{:x}", hasher.finalize())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("[download] Expected hash: {}", hash);
|
||||||
|
println!("[download] Computed hash: {}", download_hash);
|
||||||
|
|
||||||
if hash != download_hash {
|
if hash != download_hash {
|
||||||
tokio::fs::remove_file(download_part_path.clone())
|
println!("[download] ERROR: Hash mismatch! File may be corrupt or tampered with.");
|
||||||
.await
|
let _ = tokio::fs::remove_file(download_part_path.clone()).await;
|
||||||
.unwrap();
|
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
app.emit("download-finishing", format!("{}", &name))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
tokio::fs::rename(download_part_path.clone(), download_zip_path.clone())
|
println!("[download] Hash verified OK");
|
||||||
.await
|
let _ = app.emit("download-finishing", format!("{}", &name));
|
||||||
.unwrap();
|
|
||||||
|
println!("[download] Renaming .part -> .zip: {:?}", download_zip_path);
|
||||||
|
if let Err(e) = tokio::fs::rename(download_part_path.clone(), download_zip_path.clone()).await {
|
||||||
|
println!("[download] ERROR: Failed to rename .part to .zip: {}", e);
|
||||||
|
return "-1".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("[download] Unzipping to: {:?}", game_path.join(&name));
|
||||||
let unzip_res = unzip_to_dir(download_zip_path.clone(), game_path.join(&name)).await;
|
let unzip_res = unzip_to_dir(download_zip_path.clone(), game_path.join(&name)).await;
|
||||||
tokio::fs::remove_file(download_zip_path.clone())
|
|
||||||
.await
|
println!("[download] Removing zip file: {:?}", download_zip_path);
|
||||||
.unwrap();
|
let _ = tokio::fs::remove_file(download_zip_path.clone()).await;
|
||||||
|
|
||||||
if unzip_res == "-1" {
|
if unzip_res == "-1" {
|
||||||
|
println!("[download] ERROR: Unzip failed for '{}'", name);
|
||||||
return "-1".to_string();
|
return "-1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
println!(
|
||||||
{
|
"[download] SUCCESS: '{}' downloaded, verified, and extracted",
|
||||||
use std::{fs, os::unix::fs::PermissionsExt};
|
name
|
||||||
|
);
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,12 +387,10 @@ fn launch_game(
|
|||||||
use_wine: bool,
|
use_wine: bool,
|
||||||
wine_command: String,
|
wine_command: String,
|
||||||
) {
|
) {
|
||||||
let game_folder = app
|
let game_folder = match app.path().app_local_data_dir() {
|
||||||
.path()
|
Ok(p) => p.join("game").join(&name),
|
||||||
.app_local_data_dir()
|
Err(_) => return,
|
||||||
.unwrap()
|
};
|
||||||
.join("game")
|
|
||||||
.join(&name);
|
|
||||||
if !game_folder.exists() {
|
if !game_folder.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -278,6 +400,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!(
|
||||||
@@ -297,67 +422,84 @@ fn launch_game(
|
|||||||
let quoted_path = format!("\"{}\"", exe_path.to_string_lossy());
|
let quoted_path = format!("\"{}\"", exe_path.to_string_lossy());
|
||||||
let cmd = wine_command.replace("%path%", "ed_path);
|
let cmd = wine_command.replace("%path%", "ed_path);
|
||||||
|
|
||||||
Command::new("bash")
|
if let Err(_) = Command::new("bash")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(cmd)
|
.arg(cmd)
|
||||||
.current_dir(&game_folder)
|
.current_dir(&game_folder)
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
{
|
||||||
|
eprintln!("Failed to launch game with Wine");
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if platform() == "macos" {
|
if platform() == "macos" {
|
||||||
Command::new("open")
|
if let Err(_) = Command::new("open")
|
||||||
.arg(&executable)
|
.arg(&executable)
|
||||||
.current_dir(&game_folder)
|
.current_dir(&game_folder)
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
{
|
||||||
|
eprintln!("Failed to launch game on macOS");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Command::new(&exe_path)
|
if let Err(_) = Command::new(&exe_path).current_dir(&game_folder).spawn() {
|
||||||
.current_dir(&game_folder)
|
eprintln!("Failed to launch game");
|
||||||
.spawn()
|
}
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn open_folder(app: AppHandle, name: String) {
|
fn verify_signature(body: String, signature: String, public_key: String) -> bool {
|
||||||
let game_path = app
|
let Ok(pem) = general_purpose::STANDARD.decode(public_key) else {
|
||||||
.path()
|
return false;
|
||||||
.app_local_data_dir()
|
};
|
||||||
.unwrap()
|
let Ok(pubkey) = PKey::public_key_from_pem(&pem) else {
|
||||||
.join("game")
|
return false;
|
||||||
.join(&name);
|
};
|
||||||
if game_path.exists() {
|
let Ok(sig) = general_purpose::STANDARD.decode(signature) else {
|
||||||
app.opener()
|
return false;
|
||||||
.open_path(game_path.to_string_lossy(), None::<&str>)
|
};
|
||||||
.unwrap();
|
|
||||||
} else {
|
let mut verifier = match Verifier::new_without_digest(&pubkey) {
|
||||||
app.dialog()
|
Ok(v) => v,
|
||||||
.message(format!(
|
Err(_) => return false,
|
||||||
"Game folder \"{}\" not found.",
|
};
|
||||||
game_path.display()
|
|
||||||
))
|
verifier
|
||||||
.kind(MessageDialogKind::Error)
|
.verify_oneshot(&sig, body.as_bytes())
|
||||||
.title("Folder not found")
|
.unwrap_or(false)
|
||||||
.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_http::init())
|
||||||
|
.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())
|
||||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||||
let _ = app
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
.get_webview_window("main")
|
let _ = window.set_focus();
|
||||||
.expect("no main window")
|
}
|
||||||
.set_focus();
|
|
||||||
}))
|
}))
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.plugin(tauri_plugin_fs::init())
|
.plugin(tauri_plugin_fs::init())
|
||||||
@@ -367,14 +509,17 @@ pub fn run() {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
download,
|
download,
|
||||||
launch_game,
|
launch_game,
|
||||||
open_folder,
|
folder_size,
|
||||||
folder_size
|
verify_signature
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
let main_window = app.get_webview_window("main").unwrap();
|
if let Some(main_window) = app.get_webview_window("main") {
|
||||||
main_window.create_overlay_titlebar().unwrap();
|
if let Err(e) = main_window.create_overlay_titlebar() {
|
||||||
|
eprintln!("Failed to create overlay titlebar: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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.3",
|
"version": "1.6.5",
|
||||||
"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.3",
|
"version": "1.6.5",
|
||||||
"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.3",
|
"version": "1.6.5",
|
||||||
"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.3",
|
"version": "1.6.5",
|
||||||
"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,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 select-text'
|
|
||||||
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,338 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import { BirdColor } from '@/types/BerryDash/BirdColor'
|
||||||
|
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'
|
||||||
|
import { fetch } from '@tauri-apps/plugin-http'
|
||||||
|
import { verifySignature } from '@/lib/Util'
|
||||||
|
|
||||||
|
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 response = await fetch(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/account?username='
|
||||||
|
)
|
||||||
|
const signature = response.headers.get('x-signature') ?? ''
|
||||||
|
const data = await response.json()
|
||||||
|
if (
|
||||||
|
(await verifySignature(JSON.stringify(data), signature)) &&
|
||||||
|
data.success
|
||||||
|
) {
|
||||||
|
let accounts = 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 console.log('Failed')
|
||||||
|
} else {
|
||||||
|
const response = await fetch(
|
||||||
|
'https://games.lncvrt.xyz/api/berrydash/leaderboard/' +
|
||||||
|
(selected == 0
|
||||||
|
? 'score'
|
||||||
|
: selected == 1
|
||||||
|
? 'berry?berry=' + selectedBerryOption
|
||||||
|
: selected == 2
|
||||||
|
? 'coin'
|
||||||
|
: selected == 5
|
||||||
|
? 'legacy'
|
||||||
|
: 'total')
|
||||||
|
)
|
||||||
|
const signature = response.headers.get('x-signature') ?? ''
|
||||||
|
const data = await response.json()
|
||||||
|
if (
|
||||||
|
(await verifySignature(JSON.stringify(data), signature)) &&
|
||||||
|
data.success
|
||||||
|
)
|
||||||
|
setEntries(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.lncvrt.xyz/api/berrydash/render-icon?icon=${
|
||||||
|
item.icon == 1
|
||||||
|
? GetIconForUser(item.id)
|
||||||
|
: item.icon
|
||||||
|
}&overlay=${item.overlay}&birdR=${
|
||||||
|
item.birdColor[0]
|
||||||
|
}&birdG=${item.birdColor[1]}&birdB=${
|
||||||
|
item.birdColor[2]
|
||||||
|
}&overlayR=${item.overlayColor[0]}&overlayG=${
|
||||||
|
item.overlayColor[1]
|
||||||
|
}&overlayB=${item.overlayColor[2]}`
|
||||||
|
: `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true`
|
||||||
|
}
|
||||||
|
width={48}
|
||||||
|
height={48}
|
||||||
|
alt=''
|
||||||
|
className='pointer-events-none scale-x-[-1]'
|
||||||
|
/>
|
||||||
|
<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;
|
||||||
|
}
|
||||||
@@ -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,30 +113,40 @@ 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)
|
||||||
|
.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 === id && info.category === Number(key)
|
||||||
|
}).length
|
||||||
|
|
||||||
|
return count >= 1
|
||||||
|
})
|
||||||
|
.map(([key, value]) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={crypto.randomUUID()}
|
key={key}
|
||||||
className={`downloads-entry ${
|
className={'downloads-entry'}
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
title={'Click to view category'}
|
||||||
? ''
|
onClick={() => setCategory(Number(key))}
|
||||||
: 'cursor-pointer'
|
|
||||||
}`}
|
|
||||||
title={
|
|
||||||
normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
? ''
|
|
||||||
: 'Click to view category'
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (normalConfig?.settings.useLegacyInteractButtons) return
|
|
||||||
setCategory(Number(key))
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className='h-18 w-screen relative'>
|
<div className='h-18 w-screen relative'>
|
||||||
<p className='text-2xl'>{value}</p>
|
<p className='text-2xl'>{value}</p>
|
||||||
@@ -112,28 +165,21 @@ 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
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
return (
|
return (
|
||||||
info.game === id && info.category == Number(key)
|
info.game === id &&
|
||||||
|
info.category == Number(key)
|
||||||
)
|
)
|
||||||
}).length ?? 0
|
}).length ?? 0
|
||||||
return `${count} install${count === 1 ? '' : 's'}`
|
return `${count} install${count === 1 ? '' : 's'}`
|
||||||
})()}
|
})()}
|
||||||
</p>
|
</p>
|
||||||
</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>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -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
|
||||||
)
|
)
|
||||||
@@ -169,18 +215,73 @@ export default function Installs () {
|
|||||||
.map(entry => (
|
.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
|
||||||
|
className='entry-info-item'
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
hidden={
|
||||||
|
!needsRevisionUpdate(
|
||||||
|
getVersionInfo(entry)?.lastRevision,
|
||||||
|
entry
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
|
||||||
|
<p>Needs revision update!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2 absolute right-0 bottom-0'>
|
|
||||||
<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={
|
|
||||||
!normalConfig?.settings.useLegacyInteractButtons
|
|
||||||
}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setManagingVersion(entry)
|
|
||||||
setPopupMode(2)
|
|
||||||
setShowPopup(true)
|
|
||||||
setFadeOut(false)
|
|
||||||
}}
|
|
||||||
title='Click to manage this version install'
|
|
||||||
>
|
|
||||||
Manage
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
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>
|
||||||
|
|||||||
@@ -1,52 +1,48 @@
|
|||||||
'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 { 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'
|
||||||
|
import { fetch } from '@tauri-apps/plugin-http'
|
||||||
|
import { verifySignature } from '@/lib/Util'
|
||||||
|
import {
|
||||||
|
getCurrentWindow,
|
||||||
|
ProgressBarStatus,
|
||||||
|
UserAttentionType
|
||||||
|
} from '@tauri-apps/api/window'
|
||||||
|
|
||||||
const roboto = Roboto({
|
const roboto = Roboto({
|
||||||
subsets: ['latin']
|
subsets: ['latin']
|
||||||
})
|
})
|
||||||
@@ -60,6 +56,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 +73,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 +95,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 +114,21 @@ export default function RootLayout ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionInfo (id: string | undefined): GameVersion | undefined {
|
const getVersionInfo = useCallback(
|
||||||
|
(id: string | undefined): GameVersion | undefined => {
|
||||||
if (!id) return undefined
|
if (!id) return undefined
|
||||||
return serverVersionList?.versions.find(v => v.id === id)
|
return serverVersionList?.versions.find(v => v.id === id)
|
||||||
}
|
},
|
||||||
|
[serverVersionList]
|
||||||
|
)
|
||||||
|
|
||||||
function getGameInfo (game: number | undefined): Game | undefined {
|
const getGameInfo = useCallback(
|
||||||
|
(game: number | undefined): Game | undefined => {
|
||||||
if (!game) return undefined
|
if (!game) return undefined
|
||||||
return serverVersionList?.games.find(g => g.id === game)
|
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 +153,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,39 +171,38 @@ 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
|
||||||
|
|
||||||
listen<string>('download-progress', event => {
|
listen<string>('download-size', event => {
|
||||||
|
const [displayName, sizeStr] = event.payload.split(':')
|
||||||
|
const size = Number(sizeStr)
|
||||||
|
setDownloadProgress(prev => {
|
||||||
|
const i = prev.findIndex(d => d.version === displayName)
|
||||||
|
if (i === -1) return prev
|
||||||
|
const copy = [...prev]
|
||||||
|
copy[i] = {
|
||||||
|
...copy[i],
|
||||||
|
size
|
||||||
|
}
|
||||||
|
return copy
|
||||||
|
})
|
||||||
|
}).then(f => (unlistenProgress = f))
|
||||||
|
|
||||||
|
listen<string>('download-progress', async event => {
|
||||||
const [displayName, progStr, totalSizeStr, speedStr, etaSecsStr] =
|
const [displayName, progStr, totalSizeStr, speedStr, etaSecsStr] =
|
||||||
event.payload.split(':')
|
event.payload.split(':')
|
||||||
const prog = Number(progStr)
|
const prog = Number(progStr)
|
||||||
@@ -214,6 +222,10 @@ export default function RootLayout ({
|
|||||||
}
|
}
|
||||||
return copy
|
return copy
|
||||||
})
|
})
|
||||||
|
getCurrentWindow().setProgressBar({
|
||||||
|
status: ProgressBarStatus.Normal,
|
||||||
|
progress: Math.floor(prog)
|
||||||
|
})
|
||||||
}).then(f => (unlistenProgress = f))
|
}).then(f => (unlistenProgress = f))
|
||||||
|
|
||||||
listen<string>('download-hash-checking', event => {
|
listen<string>('download-hash-checking', event => {
|
||||||
@@ -241,38 +253,50 @@ export default function RootLayout ({
|
|||||||
return () => {
|
return () => {
|
||||||
unlistenProgress?.()
|
unlistenProgress?.()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [downloadProgress])
|
||||||
|
|
||||||
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 fetch(
|
||||||
'https://games.lncvrt.xyz/api/launcher/latest'
|
'https://games.lncvrt.xyz/api/launcher/latest'
|
||||||
)
|
)
|
||||||
if (response.data !== client) {
|
const signature = response.headers.get('x-signature') ?? ''
|
||||||
|
const data = await response.text()
|
||||||
|
if (await verifySignature(data, signature)) {
|
||||||
|
if (data !== client) {
|
||||||
setOutdated(true)
|
setOutdated(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setLoadingText('Failed to check latest version.')
|
||||||
|
return
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setLoadingText('Failed to check latest version.')
|
setLoadingText('Failed to check latest version.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLoadingText('Downloading version list...')
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(
|
const response = await fetch(
|
||||||
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
|
||||||
)
|
)
|
||||||
setServerVersionList(res.data)
|
const signature = response.headers.get('x-signature') ?? ''
|
||||||
|
const data = await response.json()
|
||||||
|
if (await verifySignature(JSON.stringify(data), signature)) {
|
||||||
|
setServerVersionList(data)
|
||||||
|
} else {
|
||||||
|
setLoadingText('Failed to download versions list.')
|
||||||
|
return
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
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,78 +309,84 @@ export default function RootLayout ({
|
|||||||
})()
|
})()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
const downloadVersions = useCallback(
|
||||||
if (process.env.NODE_ENV !== 'production') return
|
async (list: string[]): Promise<void> => {
|
||||||
const handler = (e: MouseEvent) => e.preventDefault()
|
|
||||||
document.addEventListener('contextmenu', handler)
|
|
||||||
return () => document.removeEventListener('contextmenu', handler)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
async function downloadVersions (
|
|
||||||
list: string[],
|
|
||||||
currentConfig: VersionsConfig
|
|
||||||
): Promise<void> {
|
|
||||||
if (list.length === 0) return
|
if (list.length === 0) return
|
||||||
setSelectedVersionList([])
|
setSelectedVersionList([])
|
||||||
|
|
||||||
const newDownloads = list.map(
|
const newVersions = list.filter(
|
||||||
version =>
|
version =>
|
||||||
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
|
!downloadQueue.includes(version) &&
|
||||||
|
!downloadProgress.some(d => d.version === version)
|
||||||
)
|
)
|
||||||
|
|
||||||
setDownloadProgress(newDownloads)
|
if (newVersions.length === 0) return
|
||||||
|
|
||||||
|
const newDownloads = newVersions.map(
|
||||||
|
version =>
|
||||||
|
new DownloadProgress(
|
||||||
|
version,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
setDownloadProgress(prev => [...prev, ...newDownloads])
|
||||||
|
setDownloadQueue(prev => [...prev, ...newVersions])
|
||||||
|
},
|
||||||
|
[downloadQueue, downloadProgress]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isProcessingQueue || downloadQueue.length === 0) return
|
||||||
|
|
||||||
|
const processNextDownload = async () => {
|
||||||
|
setIsProcessingQueue(true)
|
||||||
|
|
||||||
|
const versionId = downloadQueue[0]
|
||||||
|
const info = getVersionInfo(versionId)
|
||||||
|
|
||||||
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 {
|
const downloadInfoRequest = await fetch(
|
||||||
await axios.get(
|
'https://games.lncvrt.xyz/api/launcher/download?gameId=' +
|
||||||
'https://games.lncvrt.xyz/api/launcher/download?id=' + info.id
|
info.id +
|
||||||
|
'&downloadId=' +
|
||||||
|
info.download
|
||||||
)
|
)
|
||||||
} catch {}
|
const signature = downloadInfoRequest.headers.get('x-signature') ?? ''
|
||||||
|
const data = await downloadInfoRequest.json()
|
||||||
const res = await invoke<string>('download', {
|
if (
|
||||||
url: info.downloadUrl,
|
!(await verifySignature(JSON.stringify(data), signature)) ||
|
||||||
name: info.id,
|
!data.success
|
||||||
executable: info.executable,
|
) {
|
||||||
hash: info.sha512sum
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res === '1') {
|
|
||||||
setDownloadProgress(prev =>
|
|
||||||
prev.filter(d => d.version !== download.version)
|
|
||||||
)
|
|
||||||
const date = Date.now()
|
|
||||||
const newConfig = {
|
|
||||||
...currentConfig,
|
|
||||||
list: { ...currentConfig.list, [download.version]: date }
|
|
||||||
}
|
|
||||||
setDownloadedVersionsConfig(newConfig)
|
|
||||||
writeVersionsConfig(newConfig)
|
|
||||||
} 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
|
||||||
)
|
)
|
||||||
@@ -366,45 +396,122 @@ export default function RootLayout ({
|
|||||||
'Download Failed',
|
'Download Failed',
|
||||||
`The download for version ${info.displayName} has failed.`
|
`The download for version ${info.displayName} has failed.`
|
||||||
)
|
)
|
||||||
|
await getCurrentWindow().requestUserAttention(
|
||||||
|
UserAttentionType.Critical
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await invoke<string>('download', {
|
||||||
|
url: data.data.url,
|
||||||
|
name: info.id,
|
||||||
|
executable: info.executable,
|
||||||
|
hash: data.data.hash
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res === '1') {
|
||||||
|
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
|
||||||
|
setDownloadedVersionsConfig(prev => {
|
||||||
|
if (!prev) return prev
|
||||||
|
|
||||||
|
const updated = {
|
||||||
|
...prev,
|
||||||
|
list: {
|
||||||
|
...prev.list,
|
||||||
|
[versionId]: Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeVersionsConfig(updated)
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setDownloadProgress(prev =>
|
||||||
|
prev.map(d =>
|
||||||
|
d.version === versionId
|
||||||
|
? { ...d, queued: false, failed: true, progress: 0 }
|
||||||
|
: d
|
||||||
|
)
|
||||||
|
)
|
||||||
if (normalConfig?.settings.allowNotifications)
|
if (normalConfig?.settings.allowNotifications)
|
||||||
await notifyUser('Downloads Finished', 'All downloads have finished.')
|
await notifyUser(
|
||||||
|
'Download Failed',
|
||||||
setFadeOut(true)
|
`The download for version ${info.displayName} has failed.`
|
||||||
setTimeout(() => setShowPopup(false), 200)
|
)
|
||||||
|
await getCurrentWindow().requestUserAttention(
|
||||||
|
UserAttentionType.Critical
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDownloadQueue(prev => prev.slice(1))
|
||||||
|
setIsProcessingQueue(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
processNextDownload()
|
||||||
|
}, [
|
||||||
|
downloadQueue,
|
||||||
|
isProcessingQueue,
|
||||||
|
getVersionInfo,
|
||||||
|
getGameInfo,
|
||||||
|
normalConfig
|
||||||
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
downloadQueue.length === 0 &&
|
||||||
|
downloadProgress.length === 0 &&
|
||||||
|
!isProcessingQueue &&
|
||||||
|
previousQueueLength.current > 0 &&
|
||||||
|
normalConfig?.settings.allowNotifications
|
||||||
|
) {
|
||||||
|
notifyUser('Downloads Finished', 'All downloads have finished.')
|
||||||
|
setTimeout(() => closePopup(), 0)
|
||||||
|
;(async () => {
|
||||||
|
await getCurrentWindow().setProgressBar({
|
||||||
|
status: ProgressBarStatus.None,
|
||||||
|
progress: 0
|
||||||
|
})
|
||||||
|
await getCurrentWindow().requestUserAttention(
|
||||||
|
UserAttentionType.Informational
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
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 (
|
||||||
<>
|
<>
|
||||||
@@ -414,18 +521,25 @@ 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'>
|
<>
|
||||||
|
<div
|
||||||
|
className='relative z-2 w-screen border-b border-b-(--col3) h-8.25 bg-(--col1)'
|
||||||
|
hidden={platformName != 'windows'}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`w-screen ${
|
||||||
|
platformName == 'windows'
|
||||||
|
? 'h-[calc(100vh-64px)]'
|
||||||
|
: 'h-screen'
|
||||||
|
} flex items-center justify-center`}
|
||||||
|
>
|
||||||
{outdated ? (
|
{outdated ? (
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
<p className='text-8xl mb-4'>Outdated Launcher!</p>
|
||||||
@@ -445,6 +559,7 @@ export default function RootLayout ({
|
|||||||
<p className='text-7xl text-center'>{loadingText}</p>
|
<p className='text-7xl text-center'>{loadingText}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<GlobalProvider
|
<GlobalProvider
|
||||||
value={{
|
value={{
|
||||||
@@ -471,7 +586,16 @@ export default function RootLayout ({
|
|||||||
setSelectedGame,
|
setSelectedGame,
|
||||||
getVersionsAmountData,
|
getVersionsAmountData,
|
||||||
viewingInfoFromDownloads,
|
viewingInfoFromDownloads,
|
||||||
version
|
version,
|
||||||
|
downloadVersions,
|
||||||
|
category,
|
||||||
|
setCategory,
|
||||||
|
downloadQueue,
|
||||||
|
setDownloadQueue,
|
||||||
|
closePopup,
|
||||||
|
getSpecialVersionsList,
|
||||||
|
selectedGame,
|
||||||
|
setViewingInfoFromDownloads
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -482,10 +606,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>
|
||||||
@@ -524,369 +646,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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import { useEffect } 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>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -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,7 +45,7 @@ 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,
|
||||||
@@ -59,15 +54,13 @@ export default function 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,7 +68,7 @@ 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,
|
||||||
@@ -84,54 +77,36 @@ export default function 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,8 +144,8 @@ 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,
|
||||||
@@ -169,23 +154,19 @@ export default function Settings () {
|
|||||||
theme: newTheme
|
theme: newTheme
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
writeNormalConfig({
|
await 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>
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 1.2 MiB |
@@ -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(v.size, {
|
||||||
|
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,209 @@
|
|||||||
|
'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'
|
||||||
|
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 } 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 (
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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'
|
||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
|
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`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifySignature = async (body: string, signature: string) => {
|
||||||
|
try {
|
||||||
|
const result = await invoke<boolean>('verify_signature', {
|
||||||
|
body: body ?? '',
|
||||||
|
signature: signature ?? '',
|
||||||
|
publicKey: process.env.NEXT_PUBLIC_PUBLIC_SIGNING_KEY ?? ''
|
||||||
|
})
|
||||||
|
return Boolean(result)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type BirdColor = [number, number, number]
|
||||||
@@ -8,6 +8,7 @@ export class DownloadProgress {
|
|||||||
public hash_checking: boolean,
|
public hash_checking: boolean,
|
||||||
public finishing: boolean,
|
public finishing: boolean,
|
||||||
public speed: number,
|
public speed: number,
|
||||||
|
public size: number,
|
||||||
public etaSecs: number
|
public etaSecs: number
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,11 @@ export interface GameVersion {
|
|||||||
displayName: string
|
displayName: string
|
||||||
releaseDate: number
|
releaseDate: number
|
||||||
game: number
|
game: number
|
||||||
downloadUrl: string
|
|
||||||
executable: string
|
|
||||||
sha512sum: string
|
|
||||||
size: number
|
|
||||||
place: number
|
place: number
|
||||||
changelog: string
|
changelog: string
|
||||||
wine: number | undefined
|
wine: number | undefined
|
||||||
category: number
|
category: number
|
||||||
lastRevision: number
|
lastRevision: number
|
||||||
|
download: number
|
||||||
|
executable: string
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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"]
|
||||||
}
|
}
|
||||||