Compare commits

...
30 Commits
Author SHA1 Message Date
Lncvrt c69060a360 1.8.1 2026-03-27 12:47:27 -07:00
Lncvrt f2b0358605 Big smart 2026-03-27 12:45:58 -07:00
Lncvrt 57441a8872 Close popup & notify on download finish 2026-03-27 12:45:29 -07:00
Lncvrt df522122ac Remove debug logging and fix hashing 2026-03-27 12:40:25 -07:00
Lncvrt f4e16c12e5 Fix overscroll 2026-03-27 12:28:33 -07:00
Lncvrt e2d98307c0 Optimize builds 2026-03-27 12:27:05 -07:00
Lncvrt 387bd54ce2 Rename "unix wine" to "linux wine" 2026-03-24 22:50:09 -07:00
Lncvrt e7a61872b7 Switch to Tauris json store management for settings 2026-03-24 22:45:28 -07:00
Lncvrt ac66c78dbf Fix this not using Rust for sending requests 2026-03-24 21:31:52 -07:00
Lncvrt d9302319fa Fix sorting issue 2026-03-24 20:15:41 -07:00
Lncvrt 5206a7f25d Fix downloads 2026-03-24 19:55:00 -07:00
Lncvrt 3ee371998a Should fix linux/macos BIE support? 2026-03-24 19:01:44 -07:00
Lncvrt 5733c21fbb 1.8.0 2026-03-24 18:38:28 -07:00
Lncvrt 61b9d07fc6 Fix number formatting 2026-03-24 18:34:56 -07:00
Lncvrt 59cd625238 Finish mod manager & Count downloads for mods 2026-03-24 18:33:06 -07:00
Lncvrt 6bf2733cec Show that no mods are available to download/installed 2026-03-24 18:08:08 -07:00
Lncvrt 60f9f95be4 Finish mod installing and uninstalling 2026-03-24 18:01:24 -07:00
Lncvrt ea1c05d2d9 Add view mod info mode 2026-03-24 17:11:12 -07:00
Lncvrt 49c03e82b1 Pre-work on adding view mod info popup 2026-03-24 16:29:15 -07:00
Lncvrt 0c52169533 Add a refresh button 2026-03-24 16:12:44 -07:00
Lncvrt bf6da20fd8 Improvements here 2026-03-24 16:08:24 -07:00
Lncvrt c1f87580c7 Early WIP version of the mod manager 2026-03-24 15:57:33 -07:00
Lncvrt dd7d2b96e3 Fix potential issues here 2026-03-13 18:34:58 -07:00
Lncvrt 874f0627f7 Add downloading mod manager 2026-03-13 18:31:52 -07:00
Lncvrt e8dec1a1b2 Add types to downloads, will be for modding stuff 2026-03-13 18:11:58 -07:00
Lncvrt e1f0ce1290 wtf 2026-03-13 17:46:16 -07:00
Lncvrt bc5ea81170 Make remove button how it should be 2026-03-13 17:40:15 -07:00
Lncvrt 71b998f4bc Fix outdated page 2026-03-13 17:31:09 -07:00
Lncvrt cbac50753f Built in modding support wip 2026-03-13 17:10:11 -07:00
Lncvrt fb65348ba3 Fix this logic broken in 1.6.0 2026-03-12 23:20:29 -07:00
34 changed files with 1061 additions and 668 deletions

No files matched your search

+82 -79
View File
@@ -8,7 +8,7 @@
"@fortawesome/fontawesome-svg-core": "7.2.0",
"@fortawesome/free-brands-svg-icons": "7.2.0",
"@fortawesome/free-solid-svg-icons": "7.2.0",
"@fortawesome/react-fontawesome": "3.2.0",
"@fortawesome/react-fontawesome": "3.3.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
"@tauri-apps/plugin-dialog": "2.6.0",
@@ -17,24 +17,25 @@
"@tauri-apps/plugin-notification": "2.3.3",
"@tauri-apps/plugin-opener": "2.5.3",
"@tauri-apps/plugin-os": "2.3.2",
"@tauri-apps/plugin-store": "2.4.2",
"@tauri-apps/plugin-window-state": "2.4.1",
"next": "16.1.6",
"next": "16.2.1",
"pretty-bytes": "7.1.0",
"react": "19.2.4",
"react-dom": "19.2.4",
},
"devDependencies": {
"@eslint/eslintrc": "3.3.5",
"@tailwindcss/postcss": "4.2.1",
"@tailwindcss/postcss": "4.2.2",
"@tauri-apps/cli": "2.10.1",
"@types/crypto-js": "4.2.2",
"@types/node": "25.4.0",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"crypto-js": "4.2.0",
"eslint": "9.39.4",
"eslint-config-next": "16.1.6",
"tailwindcss": "4.2.1",
"eslint-config-next": "16.2.1",
"tailwindcss": "4.2.2",
"typescript": "5.9.3",
},
},
@@ -64,9 +65,9 @@
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
"@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
"@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
"@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
@@ -74,11 +75,11 @@
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
"@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
@@ -106,7 +107,7 @@
"@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.2.0", "", { "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~6 || ~7", "react": "^18.0.0 || ^19.0.0" } }, "sha512-E9Gu1hqd6JussVO26EC4WqRZssXMnQr2ol7ZNWkkFOH8jZUaxDJ9Z9WF9wIVkC+kJGXUdY3tlffpDwEKfgQrQw=="],
"@fortawesome/react-fontawesome": ["@fortawesome/react-fontawesome@3.3.0", "", { "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~6 || ~7", "react": "^18.0.0 || ^19.0.0" } }, "sha512-EHmHeTf8WgO29sdY3iX/7ekE3gNUdlc2RW6mm/FzELlHFKfTrA9S4MlyquRR+RRCRCn8+jXfLFpLGB2l7wCWyw=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -178,25 +179,25 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="],
"@next/env": ["@next/env@16.2.1", "", {}, "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg=="],
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.1.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ=="],
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.1", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -210,35 +211,35 @@
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
"@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/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
"@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": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="],
"@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-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="],
"@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-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="],
"@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-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="],
"@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-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="],
"@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-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "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-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="],
"@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=="],
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", "tailwindcss": "4.2.2" } }, "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ=="],
"@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
@@ -280,6 +281,8 @@
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
"@tauri-apps/plugin-store": ["@tauri-apps/plugin-store@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-0ClHS50Oq9HEvLPhNzTNFxbWVOqoAp3dRvtewQBeqfIQ0z5m3JRnOISIn2ZVPCrQC0MyGyhTS9DWhHjpigQE7A=="],
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -292,31 +295,31 @@
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
"@types/node": ["@types/node@25.4.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw=="],
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@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=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/type-utils": "8.57.2", "@typescript-eslint/utils": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.2", "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" } }, "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2" } }, "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "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-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2", "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-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.57.0", "", {}, "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.57.2", "", {}, "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "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-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.2", "@typescript-eslint/tsconfig-utils": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "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-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw=="],
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
@@ -396,7 +399,7 @@
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.10", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ=="],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
@@ -412,7 +415,7 @@
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001781", "", {}, "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -454,11 +457,11 @@
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="],
"electron-to-chromium": ["electron-to-chromium@1.5.322", "", {}, "sha512-vFU34OcrvMcH66T+dYC3G4nURmgfDVewMIu6Q2urXpumAPSMmzvcn04KVVV8Opikq8Vs5nUbO/8laNhNRqSzYw=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
"enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
"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=="],
@@ -466,7 +469,7 @@
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-iterator-helpers": ["es-iterator-helpers@1.2.2", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.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", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" } }, "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w=="],
"es-iterator-helpers": ["es-iterator-helpers@1.3.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.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", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0", "safe-array-concat": "^1.1.3" } }, "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
@@ -482,7 +485,7 @@
"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.2.1", "", { "dependencies": { "@next/eslint-plugin-next": "16.2.1", "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-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg=="],
"eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
@@ -532,7 +535,7 @@
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.4.1", "", {}, "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="],
"flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
@@ -552,7 +555,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-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="],
"get-tsconfig": ["get-tsconfig@4.13.7", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
@@ -674,29 +677,29 @@
"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.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": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
"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-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
@@ -726,7 +729,7 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"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.2.1", "", { "dependencies": { "@next/env": "16.2.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.1", "@next/swc-darwin-x64": "16.2.1", "@next/swc-linux-arm64-gnu": "16.2.1", "@next/swc-linux-arm64-musl": "16.2.1", "@next/swc-linux-x64-gnu": "16.2.1", "@next/swc-linux-x64-musl": "16.2.1", "@next/swc-win32-arm64-msvc": "16.2.1", "@next/swc-win32-x64-msvc": "16.2.1", "sharp": "^0.34.5" }, "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-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q=="],
"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=="],
@@ -766,7 +769,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
@@ -860,15 +863,15 @@
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
"tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
"tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
@@ -886,7 +889,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.57.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.0", "@typescript-eslint/parser": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA=="],
"typescript-eslint": ["typescript-eslint@8.57.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A=="],
"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=="],
@@ -922,11 +925,11 @@
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
@@ -956,13 +959,13 @@
"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.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"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.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"@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": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
}
+8 -7
View File
@@ -1,7 +1,7 @@
{
"name": "lncvrt-games-launcher",
"private": true,
"version": "1.7.0",
"version": "1.8.1",
"type": "module",
"scripts": {
"dev": "next dev",
@@ -14,7 +14,7 @@
"@fortawesome/fontawesome-svg-core": "7.2.0",
"@fortawesome/free-brands-svg-icons": "7.2.0",
"@fortawesome/free-solid-svg-icons": "7.2.0",
"@fortawesome/react-fontawesome": "3.2.0",
"@fortawesome/react-fontawesome": "3.3.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
"@tauri-apps/plugin-dialog": "2.6.0",
@@ -23,24 +23,25 @@
"@tauri-apps/plugin-notification": "2.3.3",
"@tauri-apps/plugin-opener": "2.5.3",
"@tauri-apps/plugin-os": "2.3.2",
"@tauri-apps/plugin-store": "2.4.2",
"@tauri-apps/plugin-window-state": "2.4.1",
"next": "16.1.6",
"next": "16.2.1",
"pretty-bytes": "7.1.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
"devDependencies": {
"@eslint/eslintrc": "3.3.5",
"@tailwindcss/postcss": "4.2.1",
"@tailwindcss/postcss": "4.2.2",
"@tauri-apps/cli": "2.10.1",
"@types/crypto-js": "4.2.2",
"@types/node": "25.4.0",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"crypto-js": "4.2.0",
"eslint": "9.39.4",
"eslint-config-next": "16.1.6",
"tailwindcss": "4.2.1",
"eslint-config-next": "16.2.1",
"tailwindcss": "4.2.2",
"typescript": "5.9.3"
}
}
+15 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "lncvrt-games-launcher"
version = "1.7.0"
version = "1.8.1"
authors = ["Lncvrt"]
edition = "2024"
@@ -22,17 +22,21 @@ tokio = "1.50.0"
futures-util = { version = "0.3.32", features = ["io"] }
tauri-plugin-decorum = "1.1.1"
tauri-plugin-fs = "2.4.5"
zip = "8.2.0"
zip = "8.4.0"
tauri-plugin-dialog = "2.6.0"
tauri-plugin-notification = "2.3.3"
sysinfo = "0.38.4"
sha2 = "0.10.9"
sha2 = "0.11.0"
tauri-plugin-clipboard-manager = "2.3.2"
tauri-plugin-prevent-default = "4.0.4"
openssl = { version = "0.10.75", features = ["v110", "vendored"] }
openssl = { version = "0.10.76", features = ["v110", "vendored"] }
base64 = "0.22.1"
tauri-plugin-http = "2.5.7"
dashmap = "6.1.0"
tauri-plugin-store = "2.4.2"
hex = "0.4.3"
[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies]
sysinfo = "0.38.4"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = "2.4.0"
@@ -40,3 +44,9 @@ tauri-plugin-window-state = "2.4.1"
[target.'cfg(target_os = "windows")'.dependencies]
tauri-plugin-decorum = "1.1.1"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
+9 -11
View File
@@ -2,9 +2,7 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": [
"main"
],
"windows": ["main"],
"permissions": [
"core:default",
"opener:default",
@@ -26,19 +24,18 @@
"notification:default",
"clipboard-manager:default",
"clipboard-manager:allow-write-text",
"store:default",
"store:allow-load",
"store:allow-save",
"fs:allow-applocaldata-read",
"fs:allow-applocaldata-write",
{
"identifier": "fs:scope",
"allow": [
{ "path": "$APPLOCALDATA/game/**" }
]
"allow": [{ "path": "$APPLOCALDATA/game/**" }]
},
{
"identifier": "opener:allow-open-path",
"allow": [
{ "path": "$APPLOCALDATA/game/**" }
]
"allow": [{ "path": "$APPLOCALDATA/game/**" }]
},
{
"identifier": "http:default",
@@ -51,8 +48,9 @@
{ "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" }
{ "url": "https://games.lncvrt.xyz/api/berrydash/leaderboard/total" },
{ "url": "https://games.lncvrt.xyz/api/launcher/mods" }
]
}
]
}
}
+53 -128
View File
@@ -15,7 +15,6 @@ use std::{
process::Command,
time::Duration,
};
use sysinfo::System;
use tauri::window::{ProgressBarState, ProgressBarStatus};
use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_os::platform;
@@ -25,6 +24,9 @@ use tokio::{io::AsyncWriteExt, time::timeout};
use zip::ZipArchive;
use zip::result::ZipError;
#[cfg(any(target_os = "windows", target_os = "linux"))]
use sysinfo::System;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
@@ -38,7 +40,7 @@ fn is_cancelled(name: &str) -> bool {
cancel_map().get(name).map(|v| *v).unwrap_or(false)
}
#[allow(unused)]
#[cfg(any(target_os = "windows", target_os = "linux"))]
fn is_running_by_path(path: &Path) -> bool {
let sys = System::new_all();
let target = match path.canonicalize() {
@@ -143,34 +145,28 @@ fn folder_size(app: AppHandle, version: String) -> String {
}
}
#[allow(unused_variables)]
#[tauri::command]
async fn download(
app: AppHandle,
url: String,
name: String,
executable: String,
hash: String,
download_type: i8,
mod_id: String,
) -> String {
println!("[download] Starting download for '{}' from '{}'", name, url);
let _ = app.emit("download-start", format!("{}", name));
let window = app.get_webview_window("main").expect("main window missing");
let downloads_path = match app.path().app_local_data_dir() {
Ok(p) => p.join("downloads"),
Err(e) => {
println!(
"[download] ERROR: Failed to resolve app_local_data_dir: {}",
e
);
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
};
let game_path = match app.path().app_local_data_dir() {
Ok(p) => p.join("game"),
Err(e) => {
println!("[download] ERROR: Failed to resolve game path: {}", e);
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
@@ -178,18 +174,12 @@ async fn download(
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);
let _ = tokio::fs::create_dir_all(&downloads_path).await;
let resume_from: u64 = match tokio::fs::metadata(&download_part_path).await {
Ok(meta) => {
let size = meta.len();
println!(
"[download] Found existing .part file ({} bytes), will attempt to resume",
size
);
size
}
Err(_) => 0,
@@ -199,16 +189,11 @@ async fn download(
let mut request = client.get(&url);
if resume_from > 0 {
request = request.header("Range", format!("bytes={}-", resume_from));
println!("[download] Requesting range: bytes={}-", resume_from);
}
let resp = match request.send().await {
Ok(r) => {
println!("[download] HTTP request succeeded, status: {}", r.status());
r
}
Err(e) => {
println!("[download] ERROR: HTTP request failed: {:?}", e);
Ok(r) => r,
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
@@ -217,19 +202,11 @@ async fn download(
let status = resp.status();
let actual_resume_from = if resume_from > 0 && status == reqwest::StatusCode::OK {
println!(
"[download] Server does not support range requests (got 200), restarting from scratch"
);
let _ = tokio::fs::remove_file(&download_part_path).await;
0
} else if resume_from > 0 && status == reqwest::StatusCode::PARTIAL_CONTENT {
println!("[download] Server accepted range request (206 Partial Content)");
resume_from
} else if resume_from > 0 {
println!(
"[download] Unexpected status {} while resuming, restarting",
status
);
let _ = tokio::fs::remove_file(&download_part_path).await;
0
} else {
@@ -238,13 +215,6 @@ async fn download(
let remaining_size = resp.content_length().unwrap_or(0);
let total_size = actual_resume_from + remaining_size;
println!(
"[download] Resume offset: {} bytes | Remaining: {} bytes | Total: {} bytes ({:.2} MB)",
actual_resume_from,
remaining_size,
total_size,
total_size as f64 / 1_048_576.0
);
let _ = app.emit("download-size", format!("{}:{}", &name, &total_size));
let mut file = if actual_resume_from > 0 {
@@ -253,36 +223,23 @@ async fn download(
.open(&download_part_path)
.await
{
Ok(f) => {
println!("[download] Opened .part file in append mode for resume");
f
}
Err(e) => {
println!(
"[download] ERROR: Failed to open .part file for append: {}",
e
);
Ok(f) => f,
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
}
} else {
if let Ok(true) = tokio::fs::try_exists(&game_path.join(&name)).await {
println!(
"[download] Existing game dir found, removing: {:?}",
game_path.join(&name)
);
let _ = tokio::fs::remove_dir_all(&game_path.join(&name)).await;
if download_type == 0 {
if let Ok(true) = tokio::fs::try_exists(&game_path.join(&name)).await {
let _ = tokio::fs::remove_dir_all(&game_path.join(&name)).await;
}
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
}
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
match tokio::fs::File::create(&download_part_path).await {
Ok(f) => {
println!("[download] Created new .part file");
f
}
Err(e) => {
println!("[download] ERROR: Failed to create .part file: {}", e);
Ok(f) => f,
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
@@ -297,7 +254,6 @@ async fn download(
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
if is_cancelled(&name) {
println!("[download] Cancelled '{}' during chunk loop", name);
cancel_map().remove(&name);
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
@@ -308,11 +264,7 @@ async fn download(
let chunk = match chunk_result {
Ok(c) => c,
Err(e) => {
println!(
"[download] ERROR: Failed to read chunk #{}: {}",
chunk_count, e
);
Err(_) => {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
@@ -320,11 +272,7 @@ async fn download(
chunk_count += 1;
if let Err(e) = file.write_all(&chunk).await {
println!(
"[download] ERROR: Failed to write chunk #{} to disk: {}",
chunk_count, e
);
if let Err(_) = file.write_all(&chunk).await {
let _ = &app.emit("download-stop", format!("{}", name));
return "-1".to_string();
}
@@ -351,15 +299,6 @@ async fn download(
};
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 _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::Normal),
progress: Some(progress as u64),
@@ -376,12 +315,6 @@ async fn download(
}
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
);
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
progress: Some(0),
@@ -389,36 +322,21 @@ async fn download(
return "-1".to_string();
}
println!(
"[download] Download complete: {} bytes total in {:.2}s (this session)",
downloaded,
start.elapsed().as_secs_f64()
);
println!("[download] Verifying SHA-512 hash...");
let _ = app.emit("download-hash-checking", format!("{}", &name));
let download_hash = {
let mut file = match tokio::fs::File::open(&download_part_path).await {
Ok(f) => f,
Err(e) => {
println!(
"[download] ERROR: Failed to open .part file for hashing: {}",
e
);
Err(_) => {
return "-1".to_string();
}
};
let mut hasher = Sha512::new();
let mut bytes_hashed: u64 = 0;
let mut buffer = [0u8; 8192];
loop {
let bytes_read = match file.read(&mut buffer).await {
Ok(n) => n,
Err(e) => {
println!(
"[download] ERROR: Failed to read file during hashing: {}",
e
);
Err(_) => {
return "-1".to_string();
}
};
@@ -426,18 +344,12 @@ async fn download(
break;
}
hasher.update(&buffer[..bytes_read]);
bytes_hashed += bytes_read as u64;
}
drop(file);
println!("[download] Hashed {} bytes", bytes_hashed);
format!("{:x}", hasher.finalize())
hex::encode(hasher.finalize())
};
println!("[download] Expected hash: {}", hash);
println!("[download] Computed hash: {}", download_hash);
if hash != download_hash {
println!("[download] ERROR: Hash mismatch! Deleting corrupt .part file.");
let _ = tokio::fs::remove_file(&download_part_path).await;
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
@@ -446,10 +358,7 @@ async fn download(
return "-1".to_string();
}
println!("[download] Hash verified OK");
println!("[download] Renaming .part -> .zip: {:?}", download_zip_path);
if let Err(e) = tokio::fs::rename(&download_part_path, &download_zip_path).await {
println!("[download] ERROR: Failed to rename .part to .zip: {}", e);
if let Err(_) = tokio::fs::rename(&download_part_path, &download_zip_path).await {
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
progress: Some(0),
@@ -459,20 +368,25 @@ async fn download(
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
println!("[download] Unzipping to: {:?}", game_path.join(&name));
let unzip_res = unzip_to_dir(
app.clone(),
download_zip_path.clone(),
game_path.join(&name),
if download_type == 2 {
game_path
.join(&name)
.join("BepInEx")
.join("plugins")
.join(&mod_id)
} else {
game_path.join(&name)
},
name.clone(),
)
.await;
println!("[download] Removing zip file: {:?}", download_zip_path);
let _ = tokio::fs::remove_file(&download_zip_path).await;
if unzip_res == "-1" {
println!("[download] ERROR: Unzip failed for '{}'", name);
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
progress: Some(0),
@@ -480,10 +394,6 @@ async fn download(
return "-1".to_string();
}
println!(
"[download] SUCCESS: '{}' downloaded, verified, and extracted",
&name
);
let _ = window.set_progress_bar(ProgressBarState {
status: Some(ProgressBarStatus::None),
progress: Some(0),
@@ -549,7 +459,12 @@ fn launch_game(
}
}
if platform() == "macos" {
let bepinex_folder = match app.path().app_local_data_dir() {
Ok(p) => p.join("game").join(&name).join("BepInEx"),
Err(_) => return,
};
if platform() == "macos" && !bepinex_folder.exists() {
if let Err(_) = Command::new("open")
.arg(&executable)
.current_dir(&game_folder)
@@ -558,8 +473,18 @@ fn launch_game(
eprintln!("Failed to launch game on macOS");
}
} else {
if let Err(_) = Command::new(&exe_path).current_dir(&game_folder).spawn() {
eprintln!("Failed to launch game");
if (platform() == "macos" || platform() == "linux") && bepinex_folder.exists() {
if let Err(_) = Command::new("./run_bepinex.sh")
.arg(&executable)
.current_dir(&game_folder)
.spawn()
{
eprintln!("Failed to launch game");
}
} else {
if let Err(_) = Command::new(&exe_path).current_dir(&game_folder).spawn() {
eprintln!("Failed to launch game");
}
}
}
}
@@ -588,7 +513,6 @@ fn verify_signature(body: String, signature: String, public_key: String) -> bool
#[tauri::command]
async fn cancel_download(name: String) {
println!("[cancel_download] Cancellation requested for '{}'", name);
cancel_map().insert(name.clone(), true);
}
@@ -596,6 +520,7 @@ async fn cancel_download(name: String) {
pub fn run() {
#[allow(unused_variables)]
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_http::init())
.plugin(
tauri_plugin_prevent_default::Builder::new()
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.7.0",
"version": "1.8.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.7.0",
"version": "1.8.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.7.0",
"version": "1.8.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher",
"version": "1.7.0",
"version": "1.8.1",
"identifier": "xyz.lncvrt.games-launcher",
"build": {
"beforeDevCommand": "next dev --webpack",
+20 -4
View File
@@ -9,10 +9,11 @@ import {
} 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'
import { Mod } from '@/types/Mod'
import { Store } from '@tauri-apps/plugin-store'
type GlobalCtxType = {
serverVersionList: ServerVersionsResponse | null
@@ -28,8 +29,6 @@ type GlobalCtxType = {
setFadeOut: Dispatch<SetStateAction<boolean>>
downloadedVersionsConfig: VersionsConfig | null
setDownloadedVersionsConfig: Dispatch<SetStateAction<VersionsConfig | null>>
normalConfig: NormalConfig | null
setNormalConfig: Dispatch<SetStateAction<NormalConfig | null>>
managingVersion: string | null
setManagingVersion: Dispatch<SetStateAction<string | null>>
setSelectedGame: Dispatch<SetStateAction<number | null>>
@@ -42,7 +41,16 @@ type GlobalCtxType = {
} | null
viewingInfoFromDownloads: boolean
version: string | null
downloadVersions: (list: string[]) => Promise<void>
downloadVersions: (
list: Array<{
id: string
type: 0 | 1 | 2
modDownload?: number
gameId?: number
modId?: number
modVersion?: string
}>
) => Promise<void>
category: number
setCategory: Dispatch<SetStateAction<number>>
downloadQueue: string[]
@@ -51,6 +59,14 @@ type GlobalCtxType = {
getSpecialVersionsList(game?: number | undefined): GameVersion[]
selectedGame: number | null
setViewingInfoFromDownloads: Dispatch<SetStateAction<boolean>>
showModInfo: Mod | null
setShowModInfo: Dispatch<SetStateAction<Mod | null>>
settings: Store | null
notificationsAllowed: boolean
sidebarAlwaysShowGames: boolean
linuxUseWine: boolean
linuxWineCommand: string
theme: string
}
const GlobalCtx = createContext<GlobalCtxType | null>(null)
+6 -2
View File
@@ -1,5 +1,9 @@
@import "tailwindcss";
html {
overscroll-behavior: none;
}
body {
@apply bg-(--col0) text-white select-none;
}
@@ -130,8 +134,8 @@ body {
@apply absolute;
}
.close-button {
@apply flex justify-center items-center absolute text-2xl text-gray-200 hover:text-white cursor-pointer h-12 w-12 p-3 rounded-lg left-2 top-2 transition-colors border;
.popup-top-button {
@apply flex justify-center items-center absolute text-2xl text-gray-200 hover:text-white cursor-pointer h-12 w-12 p-3 rounded-lg top-2 transition-colors border;
}
*:focus {
+3 -3
View File
@@ -298,14 +298,14 @@ export default function BerryDashLeaderboards () {
: selected === 2
? 'Coins'
: 'Score'}
: {item.value.toLocaleString('en-US')}
: {item.value.toLocaleString()}
</p>
) : isAccount ? (
<p>
{selected === 3 ? 'Level' : 'XP'}:{' '}
{selected === 3
? calculateLevel(item.xp).toLocaleString('en-US')
: item.xp.toLocaleString('en-US')}
? calculateLevel(item.xp).toLocaleString()
: item.xp.toLocaleString()}
</p>
) : null}
</div>
+55 -22
View File
@@ -21,7 +21,6 @@ export default function Installs () {
setFadeOut,
setSelectedVersionList,
downloadedVersionsConfig,
normalConfig,
setManagingVersion,
getVersionInfo,
getGameInfo,
@@ -30,7 +29,10 @@ export default function Installs () {
category,
setCategory,
setDownloadedVersionsConfig,
downloadVersions
downloadVersions,
downloadProgress,
linuxUseWine,
linuxWineCommand
} = useGlobal()
const params = useSearchParams()
@@ -42,7 +44,7 @@ export default function Installs () {
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
}, [normalConfig, setSelectedVersionList, showPopup])
}, [setSelectedVersionList, showPopup])
if (!id || !game) return <p>Invalid game</p>
@@ -128,11 +130,7 @@ export default function Installs () {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return info.game === id && info.category === Number(key)
@@ -167,8 +165,7 @@ export default function Installs () {
if (
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings
.useWineOnUnixWhenNeeded
!linuxUseWine
)
return false
return (
@@ -193,17 +190,13 @@ export default function Installs () {
.sort((a, b) => {
const infoA = getVersionInfo(a)
const infoB = getVersionInfo(b)
if (!infoA || !infoB) return -1
if (!infoA || !infoB) return 0
return infoB.place - infoA.place
})
.filter(v => {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return (
info.game === id &&
@@ -276,7 +269,12 @@ export default function Installs () {
//reinstall
setSelectedVersionList([entry])
downloadVersions([entry])
downloadVersions([
{
id: entry,
type: 0
}
])
} else {
openFolder(entry)
}
@@ -293,9 +291,9 @@ export default function Installs () {
useWine: !!(
platform() == 'linux' &&
verInfo.wine &&
normalConfig?.settings.useWineOnUnixWhenNeeded
linuxUseWine
),
wineCommand: normalConfig?.settings.wineOnUnixCommand
wineCommand: linuxWineCommand
})
}}
onContextMenu={e => {
@@ -315,7 +313,6 @@ export default function Installs () {
<div
className='entry-info-item'
title='The date the game was installed.'
onClick={e => e.stopPropagation()}
>
<p>
Installed{' '}
@@ -336,14 +333,12 @@ export default function Installs () {
entry
)
}
onClick={e => e.stopPropagation()}
>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Uses wine</p>
</div>
<div
className='entry-info-item'
onClick={e => e.stopPropagation()}
hidden={
!needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
@@ -355,6 +350,44 @@ export default function Installs () {
<p>Needs revision update!</p>
</div>
</div>
<button
className='absolute right-0 bottom-0 button'
title='Click to manage mods for this game!'
onClick={async e => {
e.stopPropagation()
if (
!(await exists('game/' + entry + '/BepInEx', {
baseDir: BaseDirectory.AppLocalData
}))
) {
if (
(await ask(
"You don't have BepInEx (the mod loader for Unity Games), would you like to install it now? It is about 1MB in size. If you choose yes, it will download the recommended BepInEx version for " +
getVersionInfo(entry)?.displayName +
'.',
{ title: 'BepInEx not found!', kind: 'error' }
)) &&
!downloadProgress.find(d => d.version == entry)
) {
downloadVersions([
{
id: entry,
type: 1
}
])
}
return
}
setManagingVersion(entry)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
hidden={!getVersionInfo(entry)?.modSupportDownload}
>
Mod Manager
</button>
</div>
</div>
))
+251 -80
View File
@@ -1,19 +1,14 @@
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import Sidebar from '@/componets/Sidebar'
import './Globals.css'
import { useCallback, useEffect, useRef, useState } from 'react'
import Sidebar from '@/components/Sidebar'
import { DownloadProgress } from '@/types/DownloadProgress'
import { invoke } from '@tauri-apps/api/core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronLeft, faXmark } from '@fortawesome/free-solid-svg-icons'
import {
readNormalConfig,
readVersionsConfig,
writeVersionsConfig
} from '@/lib/BazookaManager'
import { readVersionsConfig, writeVersionsConfig } from '@/lib/BazookaManager'
import { VersionsConfig } from '@/types/VersionsConfig'
import { NormalConfig } from '@/types/NormalConfig'
import { app } from '@tauri-apps/api'
import { openUrl } from '@tauri-apps/plugin-opener'
import { GlobalProvider } from './GlobalProvider'
@@ -24,21 +19,29 @@ import { Game } from '@/types/Game'
import { listen } from '@tauri-apps/api/event'
import { usePathname } from 'next/navigation'
import { arch, platform } from '@tauri-apps/plugin-os'
import { notifyUser } from '@/lib/Notifications'
import {
isPermissionGranted,
requestPermission
} from '@tauri-apps/plugin-notification'
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 {
BaseDirectory,
exists,
readTextFile,
remove
} from '@tauri-apps/plugin-fs'
import { fetch } from '@tauri-apps/plugin-http'
import { verifySignature } from '@/lib/Util'
import { getCurrentWindow, UserAttentionType } from '@tauri-apps/api/window'
import VersionsDownloadPopup from '@/components/popups/VersionsDownload'
import GamesDownloadPopup from '@/components/popups/GamesDownload'
import DownloadsPopup from '@/components/popups/Downloads'
import ManageVersionPopup from '@/components/popups/ManageVersion'
import ModDownloadsPopup from '@/components/popups/ModDownloads'
import { Mod } from '@/types/Mod'
import { load, Store } from '@tauri-apps/plugin-store'
import { notifyUser } from '@/lib/Notifications'
const roboto = Roboto({
subsets: ['latin']
})
@@ -60,7 +63,6 @@ export default function RootLayout ({
const [downloadedVersionsConfig, setDownloadedVersionsConfig] =
useState<VersionsConfig | null>(null)
const [normalConfig, setNormalConfig] = useState<NormalConfig | null>(null)
const [showPopup, setShowPopup] = useState(false)
const [popupMode, setPopupMode] = useState<null | number>(null)
@@ -77,24 +79,31 @@ export default function RootLayout ({
const [selectedGame, setSelectedGame] = useState<number | null>(null)
const [category, setCategory] = useState<number>(-1)
const [showModInfo, setShowModInfo] = useState<Mod | null>(null)
const pathname = usePathname()
const revisionCheck = useRef(false)
const [settings, setSettings] = useState<Store | null>(null)
const [notificationsAllowed, setNotificationsAllowed] =
useState<boolean>(false)
const [sidebarAlwaysShowGames, setSidebarAlwaysShowGames] =
useState<boolean>(false)
const [linuxUseWine, setLinuxUseWine] = useState<boolean>(false)
const [linuxWineCommand, setLinuxWineCommand] =
useState<string>('wine %path%')
const [theme, setTheme] = useState<string>('dark')
function getSpecialVersionsList (game?: number): GameVersion[] {
if (!normalConfig || !serverVersionList) return []
if (!serverVersionList) return []
return serverVersionList.versions
.filter(
v => !Object.keys(downloadedVersionsConfig?.list ?? []).includes(v.id)
)
.filter(v => {
if (
platformName == 'linux' &&
v.wine &&
!normalConfig.settings.useWineOnUnixWhenNeeded
)
return false
if (platformName == 'linux' && v.wine && linuxUseWine) return false
if (game && v.game != game) return false
if (category != -1 && v.category != category) return false
@@ -147,8 +156,7 @@ export default function RootLayout ({
} | null {
if (!downloadedVersionsConfig || !serverVersionList) return null
const allowWine =
platformName !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
const allowWine = platformName !== 'linux' || linuxUseWine
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
const info = getVersionInfo(v)
@@ -173,11 +181,13 @@ export default function RootLayout ({
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else if (popupMode == 3 && showModInfo) {
setShowModInfo(null)
} else {
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
}
}, [popupMode, selectedGame, pathname, viewingInfoFromDownloads])
}, [popupMode, selectedGame, pathname, viewingInfoFromDownloads, showModInfo])
useEffect(() => {
const unlisteners: (() => void)[] = []
@@ -348,10 +358,85 @@ export default function RootLayout ({
setLoadingText('Failed to download versions list.')
return
}
const normalConfig = await readNormalConfig()
const settingsLocal = await load('settings.json', {
autoSave: true,
defaults: {
version: client,
notificationsAllowed: true,
sidebarAlwaysShowGames: true,
linuxUseWine: false,
linuxWineCommand: 'wine %path%',
theme: 'dark'
}
})
settingsLocal.set('version', client)
const oldSettingsOptions = {
baseDir: BaseDirectory.AppLocalData
}
if (await exists('config.json', oldSettingsOptions)) {
const config = await readTextFile('config.json', oldSettingsOptions)
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
}
if (raw.settings.allowNotifications)
settingsLocal?.set(
'notificationsAllowed',
Boolean(raw.settings.allowNotifications)
)
if (raw.settings.alwaysShowGamesInSidebar)
settingsLocal?.set(
'sidebarAlwaysShowGames',
Boolean(raw.settings.alwaysShowGamesInSidebar)
)
if (raw.settings.useWineOnUnixWhenNeeded)
settingsLocal?.set(
'linuxUseWine',
Boolean(raw.settings.useWineOnUnixWhenNeeded)
)
if (raw.settings.wineOnUnixCommand)
settingsLocal?.set(
'linuxWineCommand',
String(raw.settings.wineOnUnixCommand)
)
if (raw.settings.theme) {
const tempTheme = (() => {
switch (Number(raw.settings.theme)) {
case 1:
return 'red'
case 2:
return 'blue'
case 3:
return 'purple'
default:
return 'dark'
}
})()
settingsLocal?.set('theme', tempTheme)
}
await remove('config.json', oldSettingsOptions)
}
const versionsConfig = await readVersionsConfig()
setDownloadedVersionsConfig(versionsConfig)
setNormalConfig(normalConfig)
setSettings(settingsLocal)
setLoading(false)
if (!(await isPermissionGranted())) {
@@ -361,22 +446,31 @@ export default function RootLayout ({
}, [])
const downloadVersions = useCallback(
async (list: string[]): Promise<void> => {
async (
list: Array<{
id: string
type: 0 | 1 | 2
modDownload?: number
gameId?: number
modId?: number
modVersion?: string
}>
): Promise<void> => {
if (list.length === 0) return
setSelectedVersionList([])
const newVersions = list.filter(
version =>
!downloadQueue.includes(version) &&
!downloadProgress.some(d => d.version === version)
item =>
!downloadQueue.includes(item.id) &&
!downloadProgress.some(d => d.version === item.id)
)
if (newVersions.length === 0) return
const newDownloads = newVersions.map(
version =>
item =>
new DownloadProgress(
version,
item.id,
0,
0,
false,
@@ -393,12 +487,17 @@ export default function RootLayout ({
0,
null,
null,
null
null,
item.type,
item.modDownload ?? null,
item.gameId ?? null,
item.modId ?? null,
item.modVersion ?? null
)
)
setDownloadProgress(prev => [...prev, ...newDownloads])
setDownloadQueue(prev => [...prev, ...newVersions])
setDownloadQueue(prev => [...prev, ...newVersions.map(v => v.id)])
},
[downloadQueue, downloadProgress]
)
@@ -410,6 +509,8 @@ export default function RootLayout ({
setIsProcessingQueue(true)
const versionId = downloadQueue[0]
const downloadInfo = downloadProgress.find(d => d.version == versionId)
if (!downloadInfo) return
const info = getVersionInfo(versionId)
if (!info) {
@@ -432,10 +533,14 @@ export default function RootLayout ({
)
const downloadInfoRequest = await fetch(
'https://games.lncvrt.xyz/api/launcher/download?gameId=' +
info.id +
'&downloadId=' +
info.download
'https://games.lncvrt.xyz/api/launcher/download' +
(downloadInfo.type == 0
? '?versionId=' + info.id + '&downloadId=' + info.download
: '?downloadId=' +
(downloadInfo.type == 1
? info.modSupportDownload
: downloadInfo.modDownload)) +
(downloadInfo.type == 2 ? '&modId=' + downloadInfo.modId : '')
)
const signature = downloadInfoRequest.headers.get('x-signature') ?? ''
const data = await downloadInfoRequest.json()
@@ -450,7 +555,7 @@ export default function RootLayout ({
: d
)
)
if (normalConfig?.settings.allowNotifications)
if (await settings?.get<boolean>('notifications-allowed'))
await notifyUser(
'Download Failed',
`The download for version ${info.displayName} has failed.`
@@ -467,7 +572,7 @@ export default function RootLayout ({
? {
...d,
url: data.data.url,
executable: info.executable,
executable: downloadInfo.type == 0 ? info.executable : null,
hash: data.data.hash
}
: d
@@ -477,26 +582,40 @@ export default function RootLayout ({
const res = await invoke<string>('download', {
url: data.data.url,
name: info.id,
executable: info.executable,
hash: data.data.hash
hash: data.data.hash,
downloadType: downloadInfo.type,
modId: String(downloadInfo.modId ?? '')
})
if (res === '1') {
setDownloadProgress(prev => prev.filter(d => d.version !== versionId))
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
if (downloadInfo.type != 1) {
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updated = {
...prev,
list: {
...prev.list,
[versionId]: Date.now()
}
}
const updated =
downloadInfo.type == 2
? {
...prev,
mods: {
...prev.mods,
[downloadInfo.modGame! + '-' + downloadInfo.modId!]: {
[downloadInfo.modVersion!]: Date.now()
}
}
}
: {
...prev,
list: {
...prev.list,
[versionId]: Date.now()
}
}
writeVersionsConfig(updated)
return updated
})
writeVersionsConfig(updated)
return updated
})
}
} else if (res == '0') {
setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === info.id)
@@ -521,7 +640,7 @@ export default function RootLayout ({
: d
)
)
if (normalConfig?.settings.allowNotifications)
if (await settings?.get<boolean>('notifications-allowed'))
await notifyUser(
'Download Failed',
`The download for version ${info.displayName} has failed.`
@@ -541,7 +660,8 @@ export default function RootLayout ({
isProcessingQueue,
getVersionInfo,
getGameInfo,
normalConfig
downloadProgress,
settings
])
useEffect(() => {
@@ -572,20 +692,62 @@ export default function RootLayout ({
})()
}, [serverVersionList, downloadedVersionsConfig, downloadVersions])
useEffect(() => {
if (
downloadProgress.length === 0 &&
downloadQueue.length === 0 &&
showPopup &&
popupMode === 1
) {
setTimeout(async () => {
if (notificationsAllowed) {
await notifyUser('Downloads Complete', 'All downloads finished!')
}
await getCurrentWindow().requestUserAttention(
UserAttentionType.Informational
)
closePopup()
}, 0)
}
}, [
downloadProgress,
downloadQueue,
showPopup,
popupMode,
closePopup,
notificationsAllowed
])
useEffect(() => {
if (!settings) return
const unlisteners: Promise<() => void>[] = []
function watch<T> (key: string, fn: (v: T | undefined) => void) {
settings!.get<T>(key).then(fn)
unlisteners.push(settings!.onKeyChange<T>(key, fn))
}
watch<string>('theme', v => setTheme(v ?? 'dark'))
watch<boolean>('notificationsAllowed', v =>
setNotificationsAllowed(v ?? true)
)
watch<boolean>('sidebarAlwaysShowGames', v =>
setSidebarAlwaysShowGames(v ?? true)
)
watch<boolean>('linuxUseWine', v => setLinuxUseWine(v ?? false))
watch<string>('linuxWineCommand', v =>
setLinuxWineCommand(v ?? 'wine %path%')
)
return () => {
unlisteners.forEach(u => u.then(fn => fn()))
}
}, [settings])
return (
<>
<html lang='en' className={roboto.className}>
<body
className={
normalConfig?.settings.theme === 1
? 'red-theme'
: normalConfig?.settings.theme === 2
? 'blue-theme'
: normalConfig?.settings.theme === 3
? 'purple-theme'
: 'dark-theme'
}
>
<body className={theme + '-theme'}>
{loading ? (
<>
<div
@@ -601,17 +763,17 @@ export default function RootLayout ({
>
{outdated ? (
<div className='text-center'>
<p className='text-8xl mb-4'>Outdated Launcher!</p>
<p className='text-4xl mb-4'>
<p className='text-6xl mb-4'>Outdated Launcher!</p>
<p className='text-2xl mb-4'>
Please update to the latest version to continue.
</p>
<button
className='button'
className='button btntheme1'
onClick={() =>
openUrl('https://games.lncvrt.xyz/berrydash/download')
openUrl('https://games.lncvrt.xyz/download')
}
>
Download latest version
Download latest version!
</button>
</div>
) : (
@@ -635,8 +797,6 @@ export default function RootLayout ({
setFadeOut,
downloadedVersionsConfig,
setDownloadedVersionsConfig,
normalConfig,
setNormalConfig,
managingVersion,
setManagingVersion,
getVersionInfo,
@@ -654,7 +814,15 @@ export default function RootLayout ({
closePopup,
getSpecialVersionsList,
selectedGame,
setViewingInfoFromDownloads
setViewingInfoFromDownloads,
showModInfo,
setShowModInfo,
settings,
notificationsAllowed,
sidebarAlwaysShowGames,
linuxUseWine,
linuxWineCommand,
theme
}}
>
<div
@@ -676,6 +844,7 @@ export default function RootLayout ({
className={`popup-overlay ${fadeOut ? 'fade-out' : ''}`}
onClick={(e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === e.currentTarget) {
if (showModInfo) setShowModInfo(null)
if (viewingInfoFromDownloads) {
setPopupMode(0)
setViewingInfoFromDownloads(false)
@@ -689,7 +858,7 @@ export default function RootLayout ({
>
<div className='popup-box'>
<button
className='close-button btntheme1'
className='popup-top-button left-2 btntheme1'
onClick={() => closePopup()}
>
<FontAwesomeIcon
@@ -698,7 +867,7 @@ export default function RootLayout ({
selectedGame &&
pathname === '/') ||
viewingInfoFromDownloads ||
popupMode == 4
(popupMode == 3 && showModInfo)
? faChevronLeft
: faXmark
}
@@ -711,7 +880,9 @@ export default function RootLayout ({
) : popupMode === 1 ? (
<DownloadsPopup />
) : popupMode === 2 ? (
<VersionVersionPopup />
<ManageVersionPopup />
) : popupMode === 3 ? (
<ModDownloadsPopup />
) : null}
</div>
</div>
+1 -2
View File
@@ -20,7 +20,6 @@ export default function Installs () {
setFadeOut,
setSelectedVersionList,
downloadedVersionsConfig,
normalConfig,
setSelectedGame,
getListOfGames,
getVersionsAmountData,
@@ -32,7 +31,7 @@ export default function Installs () {
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
}, [normalConfig, setSelectedVersionList, showPopup])
}, [setSelectedVersionList, showPopup])
return (
<div className='mx-4 mt-4'>
+65 -162
View File
@@ -1,181 +1,84 @@
'use client'
import { useEffect, useState } from 'react'
import { Setting } from '@/componets/Setting'
import { writeNormalConfig } from '@/lib/BazookaManager'
import { Setting } from '@/components/Setting'
import { useGlobal } from '@/app/GlobalProvider'
import { copyToClipboard } from '@/lib/Clipboard'
import { platform } from '@tauri-apps/plugin-os'
export default function Settings () {
const [allowNotifications, setAllowNotifications] = useState(false)
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
useState(false)
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
const [theme, setTheme] = useState(0)
const [loaded, setLoaded] = useState(false)
const { normalConfig, setNormalConfig, version } = useGlobal()
useEffect(() => {
;(async () => {
while (normalConfig != null) {
setAllowNotifications(normalConfig.settings.allowNotifications)
setAlwaysShowGamesInSidebar(
normalConfig.settings.alwaysShowGamesInSidebar
)
setUseWineOnUnixWhenNeeded(
normalConfig.settings.useWineOnUnixWhenNeeded
)
setWineOnUnixCommand(normalConfig.settings.wineOnUnixCommand)
setTheme(normalConfig.settings.theme)
setLoaded(true)
break
}
})()
}, [normalConfig])
const {
settings,
version,
notificationsAllowed,
sidebarAlwaysShowGames,
linuxUseWine,
linuxWineCommand,
theme
} = useGlobal()
return (
<>
<p className='text-3xl ml-4 mt-4'>Settings</p>
{loaded && (
<div className='ml-4 mt-4 bg-(--col1) border border-(--col3) rounded-lg p-4 w-fit h-fit'>
<Setting
label='Allow sending notifications'
value={allowNotifications}
onChange={async () => {
if (!normalConfig) return
setAllowNotifications(!allowNotifications)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
allowNotifications: !allowNotifications
}
})
await writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
allowNotifications: !allowNotifications
}
})
}}
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
/>
<Setting
label='Always show games in sidebar'
value={alwaysShowGamesInSidebar}
onChange={async () => {
if (!normalConfig) return
setAlwaysShowGamesInSidebar(!alwaysShowGamesInSidebar)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
}
})
await writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
alwaysShowGamesInSidebar: !alwaysShowGamesInSidebar
}
})
}}
title="This setting will make it so when you are on a page like this, the games won't disappear."
/>
<Setting
label='Use wine when needed to launch games'
value={useWineOnUnixWhenNeeded}
onChange={async () => {
if (!normalConfig) return
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
}
})
await writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
useWineOnUnixWhenNeeded: !useWineOnUnixWhenNeeded
}
})
}}
className={platform() == 'linux' ? '' : 'hidden'}
/>
<p hidden={!(platform() == 'linux' && useWineOnUnixWhenNeeded)}>
Wine Command:
</p>
<input
type='text'
value={wineOnUnixCommand}
<div className='ml-4 mt-4 bg-(--col1) border border-(--col3) rounded-lg p-4 w-fit h-fit'>
<Setting
label='Allow sending notifications'
value={notificationsAllowed}
onChange={async () => {
await settings?.set('notificationsAllowed', !notificationsAllowed)
}}
title='This setting does as you expect, allow the launcher to send notifications for when stuff like downloading is done.'
/>
<Setting
label='Always show games in sidebar'
value={sidebarAlwaysShowGames}
onChange={async () => {
await settings?.set(
'sidebarAlwaysShowGames',
!sidebarAlwaysShowGames
)
}}
title="This setting will make it so when you are on a page like this, the games won't disappear."
/>
<Setting
label='Use wine if needed'
value={linuxUseWine}
onChange={async () => {
await settings?.set('linuxUseWine', !linuxUseWine)
}}
className={platform() == 'linux' ? '' : 'hidden'}
/>
<p hidden={!(platform() == 'linux' && linuxUseWine)}>Wine Command:</p>
<input
type='text'
value={linuxWineCommand}
onChange={async e => {
await settings?.set('linuxWineCommand', e.target.value)
}}
className={`input-field my-1 ${
platform() == 'linux' && linuxWineCommand ? '' : 'hidden'
}`}
></input>
<div title='The theme you want the launcher to use.'>
<label className='text-lg'>Theme:</label>
<select
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
value={theme}
onChange={async e => {
if (!normalConfig) return
setWineOnUnixCommand(e.target.value)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
wineOnUnixCommand: e.target.value
}
})
await writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
wineOnUnixCommand: e.target.value
}
})
await settings?.set('theme', e.target.value)
}}
className={`input-field my-1 ${
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden'
}`}
></input>
<div title='The theme you want the launcher to use.'>
<label className='text-lg'>Theme:</label>
<select
className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
value={theme}
onChange={async e => {
if (!normalConfig) return
const newTheme = parseInt(e.target.value)
setTheme(newTheme)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
theme: newTheme
}
})
await writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
theme: newTheme
}
})
}}
>
<option value={0}>Dark (default)</option>
<option value={1}>Red</option>
<option value={2}>Blue</option>
<option value={3}>Purple</option>
</select>
</div>
>
<option value={'dark'}>Dark (default)</option>
<option value={'red'}>Red</option>
<option value={'blue'}>Blue</option>
<option value={'purple'}>Purple</option>
</select>
</div>
)}
</div>
<p
className='fixed bottom-1.5 right-1.5 rounded-md cursor-pointer px-1 border z-100 transition-colors btntheme1'
onClick={async () => {
await copyToClipboard(`v${version}`, normalConfig)
}}
onClick={async () =>
await copyToClipboard(`v${version}`, notificationsAllowed)
}
title='The current launcher version.'
>
v{version}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -27,7 +27,6 @@ const lexend = Lexend({
export default function Sidebar () {
const {
normalConfig,
getListOfGames,
setShowPopup,
setPopupMode,
@@ -36,7 +35,9 @@ export default function Sidebar () {
downloadedVersionsConfig,
getVersionInfo,
category,
setCategory
setCategory,
sidebarAlwaysShowGames,
linuxUseWine
} = useGlobal()
const pathname = usePathname()
@@ -104,7 +105,7 @@ export default function Sidebar () {
? 'active'
: ''
} ml-auto w-50 ${
normalConfig?.settings.alwaysShowGamesInSidebar ||
sidebarAlwaysShowGames ||
pathname === '/' ||
pathname.startsWith('/game')
? ''
@@ -136,11 +137,7 @@ export default function Sidebar () {
const info = getVersionInfo(v)
if (!info) return false
if (
platform() == 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
if (platform() == 'linux' && info.wine && !linuxUseWine)
return false
return info.game === i.id && info.category === Number(key)
@@ -153,15 +150,15 @@ export default function Sidebar () {
key={`${i.id}-${key}`}
draggable={false}
className={`link ${
(pathname === '/game' &&
((pathname === '/game' &&
Number(params.get('id') || 0) == i.id) ||
(i.id == 1 &&
pathname === '/game/berrydash/leaderboards' &&
category == Number(key))
(i.id == 1 &&
pathname === '/game/berrydash/leaderboards')) &&
category == Number(key)
? 'active'
: ''
} ml-auto w-47.5 ${
normalConfig?.settings.alwaysShowGamesInSidebar ||
sidebarAlwaysShowGames ||
pathname === '/' ||
pathname.startsWith('/game')
? ''
@@ -3,7 +3,12 @@ import prettyBytes from 'pretty-bytes'
import ProgressBar from '../ProgressBar'
import { formatEtaSmart } from '@/lib/Util'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCancel, faPause, faPlay } from '@fortawesome/free-solid-svg-icons'
import {
faCancel,
faPause,
faPlay,
faTrash
} from '@fortawesome/free-solid-svg-icons'
import { invoke } from '@tauri-apps/api/core'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { notifyUser } from '@/lib/Notifications'
@@ -16,8 +21,8 @@ export default function DownloadsPopup () {
setDownloadProgress,
downloadQueue,
setDownloadQueue,
normalConfig,
setDownloadedVersionsConfig
setDownloadedVersionsConfig,
notificationsAllowed
} = useGlobal()
return (
@@ -53,28 +58,42 @@ export default function DownloadsPopup () {
const res = await invoke<string>('download', {
url: v.url,
name: v.version,
executable: v.executable,
hash: v.hash
hash: v.hash,
downloadType: v.type,
modId: String(v.modId ?? '')
})
if (res === '1') {
setDownloadProgress(prev =>
prev.filter(d => d.version !== v.version)
)
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
if (v.type != 1) {
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updated = {
...prev,
list: {
...prev.list,
[v.version]: Date.now()
}
}
const updated =
v.type == 2
? {
...prev,
mods: {
...prev.mods,
[v.modGame! + '-' + v.modId!]: {
[v.modVersion!]: Date.now()
}
}
}
: {
...prev,
list: {
...prev.list,
[v.version]: Date.now()
}
}
writeVersionsConfig(updated)
return updated
})
writeVersionsConfig(updated)
return updated
})
}
} else if (res == '0') {
setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === v.version)
@@ -104,7 +123,7 @@ export default function DownloadsPopup () {
: d
)
)
if (normalConfig?.settings.allowNotifications)
if (notificationsAllowed)
await notifyUser(
'Download Failed',
`The download for version ${
@@ -150,8 +169,27 @@ export default function DownloadsPopup () {
>
<FontAwesomeIcon icon={faCancel} className='w-6 h-6' />
</div>
<div
className='cursor-pointer bg-(--col5) hover:bg-(--col7) border border-(--col7) hover:border-(--col9) transition-colors w-8 h-8 flex items-center justify-center rounded-full'
hidden={!v.failed && !v.queued}
onClick={() => {
setDownloadQueue(prev =>
prev.filter(id => id !== v.version)
)
setDownloadProgress(prev =>
prev.filter(d => d.version !== v.version)
)
}}
>
<FontAwesomeIcon icon={faTrash} className='w-6 h-6' />
</div>
</div>
<p className='text-2xl text-center'>
{v.type == 1
? 'Mod Manager for '
: v.type == 2
? 'Mod for '
: null}{' '}
{getVersionInfo(v.version)?.displayName}
</p>
<div className='mt-6.25 flex items-center justify-between'>
@@ -168,20 +206,6 @@ export default function DownloadsPopup () {
? '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 ? (
<span className='text-blue-300 inline-block w-full text-center'>
@@ -18,7 +18,7 @@ import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
export default function VersionVersionPopup () {
export default function ManageVersionPopup () {
const {
getGameInfo,
getVersionInfo,
@@ -196,7 +196,12 @@ export default function VersionVersionPopup () {
//reinstall
setSelectedVersionList([managingVersion])
downloadVersions([managingVersion])
downloadVersions([
{
id: managingVersion,
type: 0
}
])
}}
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}
+381
View File
@@ -0,0 +1,381 @@
'use client'
import { useGlobal } from '@/app/GlobalProvider'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { verifySignature } from '@/lib/Util'
import { Mod } from '@/types/Mod'
import {
faArrowUpRightFromSquare,
faClock,
faCodeBranch,
faDownload,
faGlobe,
faPeopleGroup,
faRefresh,
faTrash
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { message } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { fetch } from '@tauri-apps/plugin-http'
import { arch, platform } from '@tauri-apps/plugin-os'
import { useEffect, useState } from 'react'
export default function ModDownloadsPopup () {
const {
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
showModInfo,
setShowModInfo,
downloadVersions,
downloadProgress,
setDownloadedVersionsConfig
} = useGlobal()
const [mods, setMods] = useState<Mod[] | 0 | 1>(0)
const [tab, setTab] = useState<number>(0)
useEffect(() => {
if (!managingVersion) return
;(async () => {
const response = await fetch(
`https://games.lncvrt.xyz/api/launcher/mods?platform=${platform()}&arch=${arch()}&version=${managingVersion}`
)
const signature = response.headers.get('x-signature') ?? ''
const data = await response.json()
if (await verifySignature(JSON.stringify(data), signature)) {
setMods(data as Mod[])
} else {
setMods(1)
}
})()
}, [managingVersion])
if (!managingVersion) return <></>
const versionInfo = getVersionInfo(managingVersion)
return (
<>
<button
className='popup-top-button btntheme1 right-2'
onClick={async () => {
setMods(0)
const response = await fetch(
`https://games.lncvrt.xyz/api/launcher/mods?platform=${platform()}&arch=${arch()}&version=${managingVersion}`
)
const signature = response.headers.get('x-signature') ?? ''
const data = await response.json()
if (await verifySignature(JSON.stringify(data), signature)) {
setMods(data as Mod[])
} else {
setMods(1)
}
}}
hidden={typeof mods == 'number' || showModInfo != null}
>
<FontAwesomeIcon icon={faRefresh} />
</button>
<p className='text-xl text-center'>
{!showModInfo
? versionInfo?.displayName + ' Mod Manager'
: showModInfo.name +
' by ' +
showModInfo.creators[0] +
(showModInfo.creators.length > 1
? ' + ' + (showModInfo.creators.length - 1) + ' more'
: '')}
</p>
{typeof mods != 'number' && !showModInfo ? (
<div className='flex flex-row justify-center items-center -mb-2 mt-0.5 gap-1.5'>
<button
className={`button ${tab == 0 ? 'btntheme3' : 'btntheme2'}`}
onClick={() => setTab(0)}
>
<FontAwesomeIcon icon={faDownload} />
Installed
</button>
<button
className={`button ${tab == 1 ? 'btntheme3' : 'btntheme2'}`}
onClick={() => setTab(1)}
>
<FontAwesomeIcon icon={faGlobe} />
Download
</button>
</div>
) : null}
<div className='popup-content h-full'>
{typeof mods == 'number' ||
(!showModInfo &&
mods.filter(v =>
tab == 0
? Object.keys(downloadedVersionsConfig?.mods ?? []).includes(
String(versionInfo?.game + '-' + v.id)
)
: !Object.keys(downloadedVersionsConfig?.mods ?? []).includes(
String(versionInfo?.game + '-' + v.id)
)
).length == 0) ? (
<p className='text-2xl flex justify-center items-center h-full'>
{typeof mods == 'number'
? mods == 0
? 'Loading'
: 'Failed to load mods'
: tab == 0
? 'No mods installed'
: 'No mods available to download'}
</p>
) : !showModInfo ? (
<div className='flex flex-col items-center justify-center gap-2 p-2'>
{mods
.filter(v =>
tab == 0
? Object.keys(downloadedVersionsConfig?.mods ?? []).includes(
String(versionInfo?.game + '-' + v.id)
)
: !Object.keys(downloadedVersionsConfig?.mods ?? []).includes(
String(versionInfo?.game + '-' + v.id)
)
)
.map(v => {
const localVersion =
tab == 0
? Object.keys(
downloadedVersionsConfig?.mods?.[
versionInfo?.game + '-' + v.id
] ?? {}
)[0]
: null
return (
<div
key={v.id}
className='bg-(--col3) border border-(--col5) rounded-lg w-full h-16 flex flex-row'
>
<div className='flex flex-col justify-center h-full px-3 w-fit'>
<p>
<span className='text-lg'>{v.name}</span>{' '}
<span
className={
tab != 0 || v.latestVersion == localVersion
? 'text-green-300'
: 'text-red-300'
}
>
v{tab == 0 ? localVersion : v.latestVersion}
</span>
<span
className='text-green-300'
hidden={tab != 0 || v.latestVersion == localVersion}
>
{' '}
(available: v{v.latestVersion})
</span>
</p>
<p className='text-yellow-200'>
Made by {v.creators[0]}
{v.creators.length > 1
? ' + ' + (v.creators.length - 1) + ' more'
: null}
</p>
</div>
<div className='flex flex-row items-center h-full gap-2 px-3 ml-auto'>
<p className='text-green-400' hidden={tab == 0}>
<FontAwesomeIcon icon={faDownload} />{' '}
{v.downloads.toLocaleString()}
</p>
<button
className='button btntheme3'
onClick={async () => {
const path =
'game/' +
versionInfo?.id +
'/BepInEx/plugins/' +
v.id
if (
await exists(path, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove(path, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedMods = Object.fromEntries(
Object.entries(prev.mods).filter(
([k]) => k !== versionInfo?.game + '-' + v.id
)
)
const updatedConfig = {
...prev,
mods: updatedMods
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
downloadVersions([
{
id: managingVersion,
type: 2,
modDownload: v.latestDownload,
gameId: versionInfo?.game,
modId: v.id,
modVersion: v.latestVersion
}
])
}}
hidden={tab != 0 || v.latestVersion == localVersion}
disabled={downloadProgress.length != 0}
>
Update
</button>
<button
className='button btntheme3'
onClick={() => setShowModInfo(v)}
>
{tab == 0 ? 'View' : 'Get'}
</button>
</div>
</div>
)
})}
</div>
) : (
<div className='flex flex-col h-full w-full items-center'>
<div className='flex flex-col p-2 h-fit w-fit bg-(--col3) border border-(--col5) rounded-lg m-2 items-center'>
<p>
<FontAwesomeIcon icon={faDownload} className='text-green-400' />{' '}
Downloads {showModInfo.downloads.toLocaleString()}
</p>
<p>
<FontAwesomeIcon icon={faClock} /> Released{' '}
{new Intl.DateTimeFormat(undefined).format(
showModInfo.released * 1000
)}
</p>
<p>
<FontAwesomeIcon icon={faClock} /> Updated{' '}
{new Intl.DateTimeFormat(undefined).format(
showModInfo.updated * 1000
)}
</p>
<p>
<FontAwesomeIcon icon={faCodeBranch} /> Version{' '}
{showModInfo.latestVersion}
</p>
</div>
<div className='flex flex-col p-2 bg-(--col3) border border-(--col5) rounded-lg h-full w-[calc(100%-16px)]'>
{showModInfo.description
? atob(showModInfo.description)
: '(No description added)'}
</div>
<div className='flex flex-row h-fit w-full gap-2 justify-center my-2'>
{Object.keys(downloadedVersionsConfig?.mods ?? []).includes(
String(versionInfo?.game + '-' + showModInfo.id)
) ? (
<button
className='button btntheme2 w-fit'
disabled={downloadProgress.length != 0}
onClick={async () => {
const path =
'game/' +
versionInfo?.id +
'/BepInEx/plugins/' +
showModInfo.id
if (
await exists(path, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove(path, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedMods = Object.fromEntries(
Object.entries(prev.mods).filter(
([k]) =>
k !== versionInfo?.game + '-' + showModInfo.id
)
)
const updatedConfig = {
...prev,
mods: updatedMods
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
}}
>
<FontAwesomeIcon icon={faTrash} className='text-red-400' />{' '}
Uninstall
</button>
) : (
<button
className='button btntheme2 w-fit'
onClick={() => {
downloadVersions([
{
id: managingVersion,
type: 2,
modDownload: showModInfo.latestDownload,
gameId: versionInfo?.game,
modId: showModInfo.id,
modVersion: showModInfo.latestVersion
}
])
}}
disabled={downloadProgress.length != 0}
>
<FontAwesomeIcon
icon={faDownload}
className='text-green-400'
/>{' '}
Install
</button>
)}
<button
className='button btntheme2 w-fit'
onClick={async () => {
await message(
!showModInfo.changelog
? '(No changelog provided)'
: atob(showModInfo?.changelog ?? ''),
{
title: 'Changelog for ' + showModInfo.name,
kind: 'info'
}
)
}}
>
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
color='lightgray'
/>{' '}
View changelog
</button>
<button
className='button btntheme2 w-fit'
hidden={showModInfo.creators.length < 2}
onClick={async () => {
if (!versionInfo) return
await message(showModInfo.creators.join(', '), {
title: 'Creators for ' + showModInfo.name,
kind: 'info'
})
}}
>
<FontAwesomeIcon icon={faPeopleGroup} color='lightgray' /> View
all creators
</button>
</div>
</div>
)}
</div>
</>
)
}
@@ -15,6 +15,7 @@ export default function VersionsDownloadPopup () {
downloadVersions,
getGameInfo
} = useGlobal()
if (!selectedGame) return <></>
return (
@@ -78,7 +79,12 @@ export default function VersionsDownloadPopup () {
className='button btntheme1 w-fit mt-2 -mb-4'
onClick={() => {
if (downloadedVersionsConfig) {
downloadVersions(selectedVersionList)
downloadVersions(
selectedVersionList.map(versionId => ({
id: versionId,
type: 0
}))
)
setPopupMode(1)
}
}}
-75
View File
@@ -1,5 +1,4 @@
import { app } from '@tauri-apps/api'
import { NormalConfig } from '@/types/NormalConfig'
import {
BaseDirectory,
create,
@@ -10,80 +9,6 @@ import {
} from '@tauri-apps/plugin-fs'
import { VersionsConfig } from '@/types/VersionsConfig'
export async function readNormalConfig (): Promise<NormalConfig> {
const version = await app.getVersion()
try {
const options = {
baseDir: BaseDirectory.AppLocalData
}
const doesFolderExist = await exists('', options)
const doesConfigExist = await exists('config.json', options)
if (!doesFolderExist || !doesConfigExist) {
if (!doesFolderExist) {
await mkdir('', options)
}
const file = await create('config.json', options)
await file.write(
new TextEncoder().encode(
JSON.stringify(new NormalConfig(version), null, 2)
)
)
await file.close()
return new NormalConfig(version)
}
const config = await readTextFile('config.json', options)
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 {
return new NormalConfig(version)
}
}
export async function writeNormalConfig (data: NormalConfig) {
const options = {
baseDir: BaseDirectory.AppLocalData
}
const doesFolderExist = await exists('', options)
const doesConfigExist = await exists('config.json', options)
if (!doesFolderExist || !doesConfigExist) {
if (!doesFolderExist) {
await mkdir('', options)
}
const file = await create('config.json', options)
await file.write(new TextEncoder().encode(JSON.stringify(data, null, 2)))
await file.close()
} else {
await writeFile(
'config.json',
new TextEncoder().encode(JSON.stringify(data, null, 2)),
options
)
}
}
export async function readVersionsConfig (): Promise<VersionsConfig> {
const version = await app.getVersion()
try {
+4 -3
View File
@@ -1,10 +1,11 @@
import { NormalConfig } from '@/types/NormalConfig'
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
import { notifyUser } from './Notifications'
export async function copyToClipboard (
text: string,
normalConfig: NormalConfig | null
notificationsAllowed: boolean
) {
if (normalConfig?.settings.allowNotifications)
await writeText(text)
if (notificationsAllowed)
await notifyUser('Copied', 'Text "' + text + '" copied to clipboard')
}
+6 -1
View File
@@ -17,6 +17,11 @@ export class DownloadProgress {
public unzipped: number,
public url: string | null,
public executable: string | null,
public hash: string | null
public hash: string | null,
public type: 0 | 1 | 2,
public modDownload: number | null,
public modGame: number | null,
public modId: number | null,
public modVersion: string | null
) {}
}
+2 -1
View File
@@ -8,6 +8,7 @@ export interface GameVersion {
wine: number | undefined
category: number
lastRevision: number
download: number
download: number | undefined
executable: string
modSupportDownload: number | undefined
}
+12
View File
@@ -0,0 +1,12 @@
export interface Mod {
id: number
name: string
description: string | null
creators: string[]
downloads: number
released: number
updated: number
latestVersion: string
latestDownload: number
changelog: string | null
}
-21
View File
@@ -1,21 +0,0 @@
import { SettingsType } from './SettingsType'
type NormalConfigData = {
version: string
settings?: Partial<SettingsType>
}
export class NormalConfig {
constructor (
public version: string,
public settings: SettingsType = new SettingsType()
) {}
static import (data: NormalConfigData) {
const cfg = new NormalConfig(data.version)
if (data.settings) {
cfg.settings = { ...cfg.settings, ...data.settings }
}
return cfg
}
}
-9
View File
@@ -1,9 +0,0 @@
export class SettingsType {
constructor (
public allowNotifications: boolean = true,
public alwaysShowGamesInSidebar: boolean = true,
public useWineOnUnixWhenNeeded: boolean = false,
public wineOnUnixCommand: string = 'wine %path%',
public theme: number = 0
) {}
}
+4 -1
View File
@@ -1,17 +1,20 @@
export type VersionsConfigData = {
version: string
list: Record<string, number>
mods: Record<string, Record<string, number>>
}
export class VersionsConfig {
constructor (
public version: string,
public list: Record<string, number> = {}
public list: Record<string, number> = {},
public mods: Record<string, Record<string, number>> = {}
) {}
static import (data: VersionsConfigData) {
const cfg = new VersionsConfig(data.version)
cfg.list = { ...data.list }
cfg.mods = { ...data.mods }
return cfg
}
}