Compare commits

..
23 Commits
Author SHA1 Message Date
Lncvrt efad434211 ok final fix for windows i hope lmao 2025-11-05 13:31:14 -07:00
Lncvrt 0122652c25 oops lmao 2025-11-05 13:22:37 -07:00
Lncvrt f0d635ebe6 Hopefully fix windows issues 2025-11-05 13:18:18 -07:00
Lncvrt 847c8d2a3e Fix linux issue 2025-11-05 12:44:10 -07:00
Lncvrt f30e2d0ca5 1.2.0 2025-11-05 12:35:33 -07:00
Lncvrt 3b7a9b2d66 Center all text in downloads menu 2025-11-05 12:31:53 -07:00
Lncvrt 3d848ce380 Add cutoff to downloads thing 2025-11-05 12:25:57 -07:00
Lncvrt 0b2e26470d add speed for download 2025-11-05 12:21:42 -07:00
Lncvrt 129259cb74 Add ETA 2025-11-05 12:12:53 -07:00
Lncvrt 1dfeb0be93 Make this visually better 2025-11-05 12:07:29 -07:00
Lncvrt 6677e22ab6 Improvements to download menu 2025-11-05 11:52:04 -07:00
Lncvrt 8c00a3e553 Bad code is indeed bad it took a long time to fix 2025-11-05 11:42:01 -07:00
Lncvrt 818c2ddc20 Bad code is bad 2025-11-05 11:24:02 -07:00
Lncvrt a971616858 Finish info popup 2025-11-05 11:03:13 -07:00
Lncvrt 242a130957 Add size for when downloaded 2025-11-05 10:39:42 -07:00
Lncvrt 9f4f3a8556 Improve release date info 2025-11-05 10:13:35 -07:00
Lncvrt 59c308e7dd 4px difference makes a big difference 2025-11-04 21:55:18 -07:00
Lncvrt ceb1a5ee47 Allow resizing window 2025-11-04 21:51:14 -07:00
Lncvrt 7d5a3177e4 Add version info box 2025-11-04 21:34:10 -07:00
Lncvrt 6005fa9348 Add themes 2025-11-03 13:52:25 -07:00
Lncvrt 6734b47d71 Fix versions url 2025-11-03 11:40:42 -07:00
Lncvrt fdcaf6a137 Fix 2025-11-03 11:22:31 -07:00
Lncvrt a3aa1915d4 1.1.2 2025-11-03 11:20:07 -07:00
23 changed files with 645 additions and 372 deletions

No files matched your search

+38 -35
View File
@@ -14,22 +14,23 @@
"@tauri-apps/plugin-notification": "2.3.3", "@tauri-apps/plugin-notification": "2.3.3",
"@tauri-apps/plugin-opener": "2.5.2", "@tauri-apps/plugin-opener": "2.5.2",
"@tauri-apps/plugin-os": "2.3.2", "@tauri-apps/plugin-os": "2.3.2",
"axios": "1.13.1", "axios": "1.13.2",
"date-fns": "4.1.0", "date-fns": "4.1.0",
"next": "16.0.1", "next": "16.0.1",
"pretty-bytes": "7.1.0",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0", "react-dom": "19.2.0",
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "3.3.1", "@eslint/eslintrc": "3.3.1",
"@tailwindcss/postcss": "4.1.16", "@tailwindcss/postcss": "4.1.16",
"@tauri-apps/cli": "2.9.2", "@tauri-apps/cli": "2.9.3",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/node": "24.10.0", "@types/node": "24.10.0",
"@types/react": "19.2.2", "@types/react": "19.2.2",
"@types/react-dom": "19.2.2", "@types/react-dom": "19.2.2",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"eslint": "9.39.0", "eslint": "9.39.1",
"eslint-config-next": "16.0.1", "eslint-config-next": "16.0.1",
"tailwindcss": "4.1.16", "tailwindcss": "4.1.16",
"typescript": "5.9.3", "typescript": "5.9.3",
@@ -71,9 +72,9 @@
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
"@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="], "@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], "@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="],
"@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.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
@@ -89,7 +90,7 @@
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.39.0", "", {}, "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw=="], "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
@@ -235,29 +236,29 @@
"@tauri-apps/api": ["@tauri-apps/api@2.9.0", "", {}, "sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw=="], "@tauri-apps/api": ["@tauri-apps/api@2.9.0", "", {}, "sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw=="],
"@tauri-apps/cli": ["@tauri-apps/cli@2.9.2", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.9.2", "@tauri-apps/cli-darwin-x64": "2.9.2", "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.2", "@tauri-apps/cli-linux-arm64-gnu": "2.9.2", "@tauri-apps/cli-linux-arm64-musl": "2.9.2", "@tauri-apps/cli-linux-riscv64-gnu": "2.9.2", "@tauri-apps/cli-linux-x64-gnu": "2.9.2", "@tauri-apps/cli-linux-x64-musl": "2.9.2", "@tauri-apps/cli-win32-arm64-msvc": "2.9.2", "@tauri-apps/cli-win32-ia32-msvc": "2.9.2", "@tauri-apps/cli-win32-x64-msvc": "2.9.2" }, "bin": { "tauri": "tauri.js" } }, "sha512-aGzdVgxQW6WQ7e5nydPZ/30u8HvltHjO3Ytzf1wOxX1N5Yj2TsjKWRb/AWJlB95Huml3k3c/b6s0ijAvlSo9xw=="], "@tauri-apps/cli": ["@tauri-apps/cli@2.9.3", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.9.3", "@tauri-apps/cli-darwin-x64": "2.9.3", "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.3", "@tauri-apps/cli-linux-arm64-gnu": "2.9.3", "@tauri-apps/cli-linux-arm64-musl": "2.9.3", "@tauri-apps/cli-linux-riscv64-gnu": "2.9.3", "@tauri-apps/cli-linux-x64-gnu": "2.9.3", "@tauri-apps/cli-linux-x64-musl": "2.9.3", "@tauri-apps/cli-win32-arm64-msvc": "2.9.3", "@tauri-apps/cli-win32-ia32-msvc": "2.9.3", "@tauri-apps/cli-win32-x64-msvc": "2.9.3" }, "bin": { "tauri": "tauri.js" } }, "sha512-BQ7iLUXTQcyG1PpzLWeVSmBCedYDpnA/6Cm/kRFGtqjTf/eVUlyYO5S2ee07tLum3nWwDBWTGFZeruO8yEukfA=="],
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.9.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-g1OtCXydOZFYRUEAyGYdJ2lLaE3l5jk8o+Bro8y2WOLwBLtbWjBoJIVobOKFanfjG/Xr8H/UA+umEVILPhMc2A=="], "@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.9.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W8FQXZXQmQ0Fmj9UJXNrm2mLdIaLLriKVY7o/FzmizyIKTPIvHjfZALTNybbpTQRbJvKoGHLrW1DNzAWVDWJYg=="],
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.9.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-nHHIY33noUmMOyFwAJz0xQyrYIXU+bae8MNos4TGsTo491YWAF2uzr6iW+Bq0N530xDcbe7EyRvDHgK43RmmVw=="], "@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.9.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-zDwu40rlshijt3TU6aRvzPUyVpapsx1sNfOlreDMTaMelQLHl6YoQzSRpLHYwrHrhimxyX2uDqnKIiuGel0Lhg=="],
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.9.2", "", { "os": "linux", "cpu": "arm" }, "sha512-Dq17LBdSuzf+fWOKMIyiSao+Fcq4FiQwYYlx3Nk8oafDINc8sVBjC5gv2xp18KzYhk9teSWfmDpD1sj+D3t7uw=="], "@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.9.3", "", { "os": "linux", "cpu": "arm" }, "sha512-+Oc2OfcTRwYtW93VJqd/HOk77buORwC9IToj/qsEvM7bTMq6Kda4alpZprzwrCHYANSw+zD8PgjJdljTpe4p+g=="],
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.9.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Pxj5k29Rxj9xEht4gdE744t5HLXTwBojkjYDXXyJ3mE+BEg9hFX5WkStg7OkyZwH60u8NSkDSMpo7MJTH9srmA=="], "@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.9.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-59GqU/J1n9wFyAtleoQOaU0oVIo+kwQynEw4meFDoKRXszKGor6lTsbsS3r0QKLSPbc0o/yYGJhqqCtkYjb/eg=="],
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.9.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-mx82BuD4q3Yj5Zw+LXveZgPaDCnmH2At2LosX1siK77kaD5Ap5FF+FN0V4y+3cwq+Hcrk9AhEUPbHqoNOx1R2g=="], "@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.9.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-fzvG+jEn5/iYGNH6Z2IRMheYFC4pJdXa19BR9fFm6Bdn2cuajRLDKdUcEME/DCtwqclphXtFZTrT4oezY5vI/A=="],
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.9.2", "", { "os": "linux", "cpu": "none" }, "sha512-Ypm1nnr7k+ECC1+JfDcnxROHt6BX8t/4GplxBvdY68BDXtIcBbdhPWDos7MK+3bDmoaA0WSJbW+DUjpfSkyKgw=="], "@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.9.3", "", { "os": "linux", "cpu": "none" }, "sha512-qV8DZXI/fZwawk6T3Th1g6smiNC2KeQTk7XFgKvqZ6btC01z3UTsQmNGvI602zwm3Ld1TBZb4+rEWu2QmQimmw=="],
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.9.2", "", { "os": "linux", "cpu": "x64" }, "sha512-tg85cGIM9PWwsbQg8m3uah3SfoNapgUr4vhWtkqgeTDZOjQuQ2duTwCH4UiM7acBpbZHNzvRrxSFpv0U53TqQQ=="], "@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.9.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tquyEONCNRfqEBWEe4eAHnxFN5yY5lFkCuD4w79XLIovUxVftQ684+xLp7zkhntkt4y20SMj2AgJa/+MOlx4Kg=="],
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.9.2", "", { "os": "linux", "cpu": "x64" }, "sha512-xW8qaz9bcwR35W2gIg7fKG9e1Z34idOsGpD2zIPgxlJyF314B/1qie50hbOqt5AbbXHR4iRpxKE4kA2grqMmkg=="], "@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.9.3", "", { "os": "linux", "cpu": "x64" }, "sha512-v2cBIB/6ji8DL+aiL5QUykU3ZO8OoJGyx50/qv2HQVzkf85KdaYSis3D/oVRemN/pcDz+vyCnnL3XnzFnDl4JQ=="],
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.9.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-A1PshB8oHdY7zYOPlLD7Om7/aD9sOUVREd765ElIzYDtptWcALwOP9jb22Wi01vDTqxf98E4ZGIcG2gxr4FhiA=="], "@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.9.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZGvBy7nvrHPbE0HeKp/ioaiw8bNgAHxWnb7JRZ4/G0A+oFj0SeSFxl9k5uU6FKnM7bHM23Gd1oeaDex9g5Fceg=="],
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.9.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-AuCi0Vnc4qkXRLCC58das0u45SmXAjqcOjqF324CBKa1Z7jjNJESm0Sc2oc2G2q6f2eAbAfi34s2iJNaJU1hlQ=="], "@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.9.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-UsgIwOnpCoY9NK9/65QiwgmWVIE80LE7SwRYVblGtmlY9RYfsYvpbItwsovA/AcHMTiO+OCvS/q9yLeqS3m6Sg=="],
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.2", "", { "os": "win32", "cpu": "x64" }, "sha512-kDoejyfvME/mLkR4VofQnmVPTt/smJvoXuE3xgTbUwcUQKqawM8EyQvxOHQosaJYfQphHi7G0ya8UZo3PlDZig=="], "@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.3", "", { "os": "win32", "cpu": "x64" }, "sha512-fmw7NrrHE5m49idCvJAx9T9bsupjdJ0a3p3DPCNCZRGANU6R1tA1L+KTlVuUtdAldX2NqU/9UPo2SCslYKgJHQ=="],
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ=="], "@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ=="],
@@ -285,25 +286,25 @@
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="], "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/type-utils": "8.46.2", "@typescript-eslint/utils": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.2", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.3", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/type-utils": "8.46.3", "@typescript-eslint/utils": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.3", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.3", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.2", "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.3", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.3", "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2" } }, "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3" } }, "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.3", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.46.2", "", {}, "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.46.3", "", {}, "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.2", "@typescript-eslint/tsconfig-utils": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.3", "@typescript-eslint/tsconfig-utils": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w=="], "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" } }, "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg=="],
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
@@ -381,13 +382,13 @@
"axe-core": ["axe-core@4.11.0", "", {}, "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ=="], "axe-core": ["axe-core@4.11.0", "", {}, "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ=="],
"axios": ["axios@1.13.1", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw=="], "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.23", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
@@ -451,7 +452,7 @@
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.244", "", {}, "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw=="], "electron-to-chromium": ["electron-to-chromium@1.5.245", "", {}, "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
@@ -477,7 +478,7 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.39.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.0", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg=="], "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="],
"eslint-config-next": ["eslint-config-next@16.0.1", "", { "dependencies": { "@next/eslint-plugin-next": "16.0.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-wNuHw5gNOxwLUvpg0cu6IL0crrVC9hAwdS/7UwleNkwyaMiWIOAwf8yzXVqBBzL3c9A7jVRngJxjoSpPP1aEhg=="], "eslint-config-next": ["eslint-config-next@16.0.1", "", { "dependencies": { "@next/eslint-plugin-next": "16.0.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-wNuHw5gNOxwLUvpg0cu6IL0crrVC9hAwdS/7UwleNkwyaMiWIOAwf8yzXVqBBzL3c9A7jVRngJxjoSpPP1aEhg=="],
@@ -779,6 +780,8 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"pretty-bytes": ["pretty-bytes@7.1.0", "", {}, "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw=="],
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
@@ -891,7 +894,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.46.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.2", "@typescript-eslint/parser": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg=="], "typescript-eslint": ["typescript-eslint@8.46.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.3", "@typescript-eslint/parser": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA=="],
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
@@ -927,9 +930,9 @@
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@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.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="],
"@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.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+11 -10
View File
@@ -1,7 +1,7 @@
{ {
"name": "lncvrt-games-launcher", "name": "lncvrt-games-launcher",
"private": true, "private": true,
"version": "1.1.1", "version": "1.2.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -11,25 +11,26 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "7.1.0",
"@fortawesome/free-brands-svg-icons": "7.1.0",
"@fortawesome/free-solid-svg-icons": "7.1.0",
"@fortawesome/react-fontawesome": "3.1.0",
"@tauri-apps/api": "2.9.0", "@tauri-apps/api": "2.9.0",
"@tauri-apps/plugin-dialog": "2.4.2", "@tauri-apps/plugin-dialog": "2.4.2",
"@tauri-apps/plugin-fs": "2.4.4", "@tauri-apps/plugin-fs": "2.4.4",
"@tauri-apps/plugin-notification": "2.3.3", "@tauri-apps/plugin-notification": "2.3.3",
"@tauri-apps/plugin-opener": "2.5.2", "@tauri-apps/plugin-opener": "2.5.2",
"@tauri-apps/plugin-os": "2.3.2", "@tauri-apps/plugin-os": "2.3.2",
"axios": "1.13.1", "axios": "1.13.2",
"date-fns": "4.1.0", "date-fns": "4.1.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.1", "next": "16.0.1",
"@fortawesome/fontawesome-svg-core": "7.1.0", "pretty-bytes": "7.1.0",
"@fortawesome/free-brands-svg-icons": "7.1.0", "react": "19.2.0",
"@fortawesome/free-solid-svg-icons": "7.1.0", "react-dom": "19.2.0"
"@fortawesome/react-fontawesome": "3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "4.1.16", "@tailwindcss/postcss": "4.1.16",
"@tauri-apps/cli": "2.9.2", "@tauri-apps/cli": "2.9.3",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/react": "19.2.2", "@types/react": "19.2.2",
"@types/react-dom": "19.2.2", "@types/react-dom": "19.2.2",
@@ -37,7 +38,7 @@
"@types/node": "24.10.0", "@types/node": "24.10.0",
"tailwindcss": "4.1.16", "tailwindcss": "4.1.16",
"typescript": "5.9.3", "typescript": "5.9.3",
"eslint": "9.39.0", "eslint": "9.39.1",
"eslint-config-next": "16.0.1", "eslint-config-next": "16.0.1",
"@eslint/eslintrc": "3.3.1" "@eslint/eslintrc": "3.3.1"
} }
+2 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "lncvrt-games-launcher" name = "lncvrt-games-launcher"
version = "1.1.1" version = "1.2.0"
authors = ["Lncvrt"] authors = ["Lncvrt"]
edition = "2024" edition = "2024"
@@ -27,6 +27,7 @@ libc = "0.2.177"
tauri-plugin-dialog = "2.4.2" tauri-plugin-dialog = "2.4.2"
tauri-plugin-notification = "2.3.3" tauri-plugin-notification = "2.3.3"
sysinfo = "0.37.2" sysinfo = "0.37.2"
sha2 = "0.10.9"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
shlex = "1.3.0" shlex = "1.3.0"
+105 -61
View File
@@ -1,4 +1,8 @@
use futures_util::stream::StreamExt; use futures_util::stream::StreamExt;
use sha2::{Digest, Sha512};
use std::fs;
use std::path::Path;
use std::time::Instant;
use std::{ use std::{
fs::{File, create_dir_all}, fs::{File, create_dir_all},
io::{BufReader, copy}, io::{BufReader, copy},
@@ -15,7 +19,7 @@ use tokio::{io::AsyncWriteExt, time::timeout};
use zip::ZipArchive; use zip::ZipArchive;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use std::{fs, os::unix::fs::PermissionsExt}; use std::{os::unix::fs::PermissionsExt};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use tauri_plugin_decorum::WebviewWindowExt; use tauri_plugin_decorum::WebviewWindowExt;
@@ -60,9 +64,47 @@ async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
} }
} }
#[tauri::command]
fn folder_size(app: AppHandle, version: String) -> String {
let path = app
.path()
.app_local_data_dir()
.unwrap()
.join("game")
.join(&version);
fn inner(path: &Path) -> u64 {
let mut size = 0;
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.flatten() {
if let Ok(metadata) = entry.metadata() {
if metadata.is_file() {
size += metadata.len();
} else if metadata.is_dir() {
size += inner(&entry.path());
}
}
}
}
size
}
let p = Path::new(&path);
if p.exists() && p.is_dir() {
inner(p).to_string()
} else {
"-1".to_string()
}
}
#[allow(unused_variables)] #[allow(unused_variables)]
#[tauri::command] #[tauri::command]
async fn download(app: AppHandle, url: String, name: String, executable: String) -> String { async fn download(
app: AppHandle,
url: String,
name: String,
executable: String,
hash: String,
) -> String {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let resp = match client.get(&url).send().await { let resp = match client.get(&url).send().await {
Ok(r) => r, Ok(r) => r,
@@ -90,7 +132,11 @@ async fn download(app: AppHandle, url: String, name: String, executable: String)
let _ = tokio::fs::remove_dir_all(&game_path.join(name.clone())).await; let _ = tokio::fs::remove_dir_all(&game_path.join(name.clone())).await;
} }
let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await; let _ = tokio::fs::create_dir_all(&game_path.join(&name)).await;
let mut file = tokio::fs::File::create(download_part_path).await.unwrap(); let mut file = tokio::fs::File::create(download_part_path.clone())
.await
.unwrap();
let start = Instant::now();
while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await { while let Ok(Some(chunk_result)) = timeout(Duration::from_secs(5), stream.next()).await {
let chunk = match chunk_result { let chunk = match chunk_result {
@@ -107,25 +153,50 @@ async fn download(app: AppHandle, url: String, name: String, executable: String)
downloaded += chunk.len() as u64; downloaded += chunk.len() as u64;
let progress = if total_size > 0 { let progress = if total_size > 0 {
(downloaded * 100 / total_size) as u8 (downloaded as f64 / total_size as f64) * 100.0
} else { } else {
0 0.0
}; };
app.emit("download-progress", format!("{}:{}", &name, progress)) let elapsed_secs = start.elapsed().as_secs_f64();
.unwrap(); let speed = downloaded as f64 / elapsed_secs;
let eta_secs = if total_size > downloaded {
(total_size - downloaded) as f64 / speed
} else {
0.0
};
app.emit(
"download-progress",
format!(
"{}:{:.8}:{}:{}:{:.2}",
&name, progress, downloaded, speed, eta_secs
),
)
.unwrap();
} }
if total_size > 0 && downloaded < total_size { if total_size > 0 && downloaded < total_size {
return "-1".to_string(); return "-1".to_string();
} }
tokio::fs::rename( app.emit("download-hash-checking", format!("{}", &name))
downloads_path.join(format!("{}.part", name)), .unwrap();
download_zip_path.clone(), let mut hasher = Sha512::new();
) hasher.update(&tokio::fs::read(download_part_path.clone()).await.unwrap());
.await let download_hash = format!("{:x}", hasher.finalize());
.unwrap(); if hash != download_hash {
tokio::fs::remove_file(download_part_path.clone())
.await
.unwrap();
return "-1".to_string();
}
app.emit("download-finishing", format!("{}", &name))
.unwrap();
tokio::fs::rename(download_part_path.clone(), download_zip_path.clone())
.await
.unwrap();
let unzip_res = unzip_to_dir(download_zip_path.clone(), game_path.join(&name)).await; let unzip_res = unzip_to_dir(download_zip_path.clone(), game_path.join(&name)).await;
tokio::fs::remove_file(download_zip_path.clone()) tokio::fs::remove_file(download_zip_path.clone())
.await .await
@@ -167,7 +238,7 @@ async fn download(app: AppHandle, url: String, name: String, executable: String)
#[allow(unused_variables)] #[allow(unused_variables)]
#[tauri::command] #[tauri::command]
fn launch_game(app: AppHandle, name: String, executable: String, wine: bool, wine_command: String) { fn launch_game(app: AppHandle, name: String, executable: String) {
let game_folder = app let game_folder = app
.path() .path()
.app_local_data_dir() .app_local_data_dir()
@@ -183,54 +254,26 @@ fn launch_game(app: AppHandle, name: String, executable: String, wine: bool, win
.show(|_| {}); .show(|_| {});
return; return;
} }
let result = if wine && platform() == "linux" { if is_running_by_path(&game_path) {
#[cfg(target_os = "linux")] app.dialog()
{ .message(format!("The version {} is already running.", name))
let wine_cmd_to_use = .kind(MessageDialogKind::Error)
wine_command.replace("%path%", &format!("\"{}\"", game_path.to_string_lossy())); .title("Game already running")
.show(|_| {});
return;
}
let parts = shlex::split(&wine_cmd_to_use).expect("failed to split command"); if platform() == "macos" {
let exe = &parts[0]; Command::new("open")
let args = &parts[1..]; .arg(&game_path)
.current_dir(&game_folder)
Command::new(exe) .spawn()
.args(args) .unwrap();
.current_dir(&game_folder)
.spawn()
}
#[cfg(not(target_os = "linux"))]
{
Err(std::io::Error::new(std::io::ErrorKind::Other, "not linux"))
}
} else { } else {
if is_running_by_path(&game_path) { Command::new(&game_path)
app.dialog() .current_dir(&game_folder)
.message(format!("The version {} is already running.", name)) .spawn()
.kind(MessageDialogKind::Error) .unwrap();
.title("Game already running")
.show(|_| {});
return;
}
if platform() == "macos" {
Command::new("open")
.arg(&game_path)
.current_dir(&game_folder)
.spawn()
} else {
Command::new(&game_path).current_dir(&game_folder).spawn()
}
};
match result {
Ok(_) => println!("Game launched successfully."),
Err(e) => {
app.dialog()
.message(format!("Failed to load game:\n{}\n\nTry reinstalling the game or make a support request in the Community link on the sidebar.", e))
.kind(MessageDialogKind::Error)
.title("Failed to launch game")
.show(|_| {});
}
} }
} }
@@ -297,7 +340,8 @@ pub fn run() {
download, download,
launch_game, launch_game,
uninstall_version, uninstall_version,
open_folder open_folder,
folder_size
]) ])
.setup(|app| { .setup(|app| {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher", "productName": "Lncvrt Games Launcher",
"version": "1.1.1", "version": "1.2.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
@@ -15,8 +15,8 @@
"title": "Lncvrt Games Launcher", "title": "Lncvrt Games Launcher",
"width": 1000, "width": 1000,
"height": 600, "height": 600,
"resizable": false, "minWidth": 875,
"maximizable": false "minHeight": 500
} }
], ],
"security": { "security": {
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher", "productName": "Lncvrt Games Launcher",
"version": "1.1.1", "version": "1.2.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
@@ -15,8 +15,8 @@
"title": "Lncvrt Games Launcher", "title": "Lncvrt Games Launcher",
"width": 1000, "width": 1000,
"height": 600, "height": 600,
"resizable": false, "minWidth": 875,
"maximizable": false "minHeight": 500
} }
], ],
"security": { "security": {
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher", "productName": "Lncvrt Games Launcher",
"version": "1.1.1", "version": "1.2.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
@@ -16,8 +16,8 @@
"title": "Lncvrt Games Launcher", "title": "Lncvrt Games Launcher",
"width": 1000, "width": 1000,
"height": 600, "height": 600,
"resizable": false, "minWidth": 875,
"maximizable": false, "minHeight": 500,
"titleBarStyle": "Overlay", "titleBarStyle": "Overlay",
"hiddenTitle": true "hiddenTitle": true
} }
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Lncvrt Games Launcher", "productName": "Lncvrt Games Launcher",
"version": "1.1.1", "version": "1.2.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
@@ -16,8 +16,8 @@
"title": "Lncvrt Games Launcher", "title": "Lncvrt Games Launcher",
"width": 1000, "width": 1000,
"height": 632, "height": 632,
"resizable": false, "minWidth": 875,
"maximizable": false, "minHeight": 500,
"titleBarStyle": "Overlay", "titleBarStyle": "Overlay",
"hiddenTitle": true, "hiddenTitle": true,
"decorations": false, "decorations": false,
+1
View File
@@ -34,6 +34,7 @@ type GlobalCtxType = {
installed: number installed: number
total: number total: number
} | null } | null
viewingInfoFromDownloads: boolean
} }
const GlobalCtx = createContext<GlobalCtxType | null>(null) const GlobalCtx = createContext<GlobalCtxType | null>(null)
+74 -10
View File
@@ -1,7 +1,67 @@
@import "tailwindcss"; @import "tailwindcss";
body { body {
@apply bg-[#0f0f0f] text-white select-none; @apply bg-(--col0) text-white select-none;
}
.dark-theme {
--col0: rgb(8, 8, 8);
--col1: rgb(16, 16, 16);
--col2: rgb(32, 32, 32);
--col3: rgb(48, 48, 48);
--col4: rgb(64, 64, 64);
--col5: rgb(80, 80, 80);
--col6: rgb(96, 96, 96);
--col7: rgb(112, 112, 112);
--col8: rgb(128, 128, 128);
}
.red-theme {
--col0: rgb(32, 8, 8);
--col1: rgb(40, 16, 16);
--col2: rgb(56, 32, 32);
--col3: rgb(72, 48, 48);
--col4: rgb(88, 64, 64);
--col5: rgb(104, 80, 80);
--col6: rgb(120, 96, 96);
--col7: rgb(136, 112, 112);
--col8: rgb(152, 128, 128);
}
.green-theme {
--col0: rgb(8, 32, 8);
--col1: rgb(16, 40, 16);
--col2: rgb(32, 56, 32);
--col3: rgb(48, 72, 48);
--col4: rgb(64, 88, 64);
--col5: rgb(80, 104, 80);
--col6: rgb(96, 120, 96);
--col7: rgb(112, 136, 112);
--col8: rgb(128, 152, 128);
}
.blue-theme {
--col0: rgb(8, 8, 32);
--col1: rgb(16, 16, 40);
--col2: rgb(32, 32, 56);
--col3: rgb(48, 48, 72);
--col4: rgb(64, 64, 88);
--col5: rgb(80, 80, 104);
--col6: rgb(96, 96, 120);
--col7: rgb(112, 112, 136);
--col8: rgb(128, 128, 152);
}
.purple-theme {
--col0: rgb(20, 8, 32);
--col1: rgb(28, 16, 40);
--col2: rgb(44, 32, 56);
--col3: rgb(60, 48, 72);
--col4: rgb(76, 64, 88);
--col5: rgb(92, 80, 104);
--col6: rgb(108, 96, 120);
--col7: rgb(124, 112, 136);
--col8: rgb(140, 128, 152);
} }
.button { .button {
@@ -17,11 +77,11 @@ body {
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
@apply bg-[#1f1f1f] rounded-lg; @apply bg-(--col2) rounded-lg;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
@apply bg-[#555] w-1 rounded-lg active:bg-[#888]; @apply bg-(--col4) w-1 rounded-lg active:bg-(--col5);
} }
@keyframes fadeIn { @keyframes fadeIn {
@@ -51,15 +111,15 @@ body {
} }
.popup-box { .popup-box {
@apply relative w-[60vw] h-[80vh] rounded-lg bg-[#161616] border border-[#323232] flex flex-col p-6; @apply relative w-[600px] h-[480px] rounded-lg bg-(--col1) border border-(--col3) flex flex-col p-6;
} }
.popup-content { .popup-content {
@apply flex-1 overflow-auto bg-[#242424] border border-[#484848] rounded-lg mt-4; @apply flex-1 overflow-auto bg-(--col2) border border-(--col4) rounded-lg mt-4;
} }
.popup-entry { .popup-entry {
@apply relative h-fit bg-[#323232] m-2 p-2 rounded-lg border border-[#646464]; @apply relative h-fit bg-(--col3) m-2 p-2 rounded-lg border border-(--col5);
} }
.popup-entry button { .popup-entry button {
@@ -67,7 +127,7 @@ body {
} }
.close-button { .close-button {
@apply flex justify-center items-center absolute bg-[#323232] hover:bg-[#484848] text-2xl cursor-pointer text-gray-300 hover:text-white h-12 w-12 p-3 rounded-xl left-2 top-2 transition-colors border border-[#484848] hover:border-[#646464]; @apply flex justify-center items-center absolute bg-(--col3) hover:bg-(--col4) text-2xl cursor-pointer text-gray-300 hover:text-white h-12 w-12 p-3 rounded-xl left-2 top-2 transition-colors border border-(--col4) hover:border-(--col5);
} }
*:focus { *:focus {
@@ -75,13 +135,17 @@ body {
} }
.input-field { .input-field {
@apply border-2 border-[#484848] rounded-md bg-[#242424] p-2 px-4 focus:border-blue-600 transition-colors; @apply border-2 border-(--col4) rounded-md bg-(--col2) p-2 px-4 focus:border-blue-600 transition-colors;
} }
.entry-info-item { .entry-info-item {
@apply flex flex-row items-center gap-1 bg-[rgb(16,16,16)] text-gray-300 py-1 px-2 rounded-lg w-fit text-[16px]; @apply flex flex-row items-center gap-1 bg-(--col1) border border-(--col3) text-gray-300 py-1 px-2 rounded-lg w-fit text-[16px] transition-colors cursor-pointer;
} }
.downloads-entry:hover .entry-info-item { .downloads-entry:hover .entry-info-item {
@apply bg-[rgb(32,32,32)]; @apply bg-(--col2) border-(--col4);
}
.popup-content .entry-info-item:hover {
@apply bg-(--col3) border-(--col5);
} }
+2 -2
View File
@@ -5,11 +5,11 @@
} }
.downloads-scroll { .downloads-scroll {
@apply h-[515px] bg-[#161616] border border-[#242424] rounded-lg overflow-y-auto w-full; @apply bg-(--col1) border border-(--col3) rounded-lg overflow-y-auto w-full;
} }
.downloads-entry { .downloads-entry {
@apply flex justify-between items-center m-2 p-4 rounded-lg text-gray-200 text-lg transition-colors cursor-default bg-[#242424] hover:bg-[#323232] border border-[#484848] hover:border-[#565656]; @apply flex justify-between items-center m-2 p-4 rounded-lg text-gray-200 text-lg transition-colors cursor-default bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5);
} }
.downloads-entry p.score { .downloads-entry p.score {
+17
View File
@@ -0,0 +1,17 @@
type ProgressBarProps = {
progress: number
className?: string
}
export default function ProgressBar ({ progress, className }: ProgressBarProps) {
return (
<div
className={`w-full bg-(--col1) border border-(--col5) rounded-full h-4 overflow-hidden ${className}`}
>
<div
className='bg-(--col8) border-r border-r-(--col5) h-full'
style={{ width: `${progress}%` }}
/>
</div>
)
}
+1 -1
View File
@@ -5,7 +5,7 @@
} }
.setting-checkbox { .setting-checkbox {
@apply appearance-none w-full h-full border-2 border-[#484848] rounded-md bg-[#242424] transition-colors duration-200 cursor-pointer; @apply appearance-none w-full h-full border-2 border-(--col4) rounded-md bg-(--col2) transition-colors duration-200 cursor-pointer;
} }
.setting-checkbox:checked { .setting-checkbox:checked {
+5 -5
View File
@@ -1,16 +1,16 @@
@import "tailwindcss"; @import "tailwindcss";
.sidebar { .sidebar {
@apply fixed top-0 left-0 w-60 h-screen bg-[#161616] flex flex-col border-e border-[#242424] z-1; @apply fixed top-0 left-0 w-60 h-screen bg-(--col1) flex flex-col border-e border-(--col2) z-1;
} }
.sidebar-downloads { .sidebar-downloads {
@apply text-[#bdbdbd] fixed bottom-3 left-2 bg-[#242424] rounded-lg border border-[#323232] w-55 p-4 cursor-pointer transition-colors duration-[0.25s]; @apply text-[#bdbdbd] fixed bottom-3 left-2 bg-(--col2) rounded-lg border border-(--col3) w-55 p-4 cursor-pointer transition-colors duration-[0.25s];
} }
.sidebar-downloads:hover { .sidebar-downloads:hover {
@apply text-white; @apply text-white;
@apply bg-[#323232] border-[#484848]; @apply bg-(--col3) border-(--col4);
} }
.logo { .logo {
@@ -26,7 +26,7 @@
} }
.link.active { .link.active {
@apply bg-[#242424] border-[#323232]; @apply bg-(--col2) border-(--col3);
} }
.link.active, .link.active,
@@ -35,5 +35,5 @@
} }
.link.active:hover { .link.active:hover {
@apply bg-[#323232] border-[#484848]; @apply bg-(--col3) border-(--col4);
} }
+1 -4
View File
@@ -34,7 +34,7 @@ export default function Sidebar () {
return ( return (
<aside className='sidebar'> <aside className='sidebar'>
<div <div
className='dragarea' data-tauri-drag-region
style={{ style={{
height: '30px', height: '30px',
width: 'calc(var(--spacing) * 60)', width: 'calc(var(--spacing) * 60)',
@@ -46,9 +46,6 @@ export default function Sidebar () {
display: platform() == 'macos' ? 'block' : 'none', display: platform() == 'macos' ? 'block' : 'none',
pointerEvents: 'auto' pointerEvents: 'auto'
}} }}
onMouseDown={() => {
getCurrentWindow().startDragging()
}}
></div> ></div>
<div className='logo'> <div className='logo'>
<Image <Image
+117
View File
@@ -0,0 +1,117 @@
'use client'
import {
faCheck,
faCode,
faHardDrive,
faShieldHalved,
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { format } from 'date-fns'
import { useGlobal } from '../GlobalProvider'
import { invoke } from '@tauri-apps/api/core'
import { useEffect, useState } from 'react'
import prettyBytes from 'pretty-bytes'
export default function VersionInfo () {
const {
getVersionGame,
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
viewingInfoFromDownloads
} = useGlobal()
if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion)
const gameInfo = getVersionGame(versionInfo?.game)
const [versionSize, setVersionSize] = useState<number | null>(null)
useEffect(() => {
invoke<string>('folder_size', {
version: managingVersion
}).then(size => {
setVersionSize(parseInt(size, 10))
})
}, [managingVersion, setVersionSize])
return (
<>
<p className='text-xl text-center'>
Viewing info for{' '}
{getVersionGame(getVersionInfo(managingVersion)?.game)?.name} v
{getVersionInfo(managingVersion)?.versionName}
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div className='entry-info-item' hidden={viewingInfoFromDownloads}>
<p>
Installed{' '}
{format(
new Date(
downloadedVersionsConfig.timestamps[managingVersion] ?? 0
),
'MM/dd/yyyy'
)}
</p>
</div>
<div
className='entry-info-item'
hidden={!versionInfo || versionInfo.releaseDate == 0}
>
<p>
Released{' '}
{format(
new Date(
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
),
'MM/dd/yyyy'
)}
</p>
</div>
<div className='entry-info-item' hidden={!gameInfo?.official}>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
<div className='entry-info-item' hidden={gameInfo?.official}>
<FontAwesomeIcon
icon={gameInfo?.verified ? faShieldHalved : faWarning}
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
/>
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
</div>
<div className='entry-info-item' hidden={gameInfo?.official}>
<FontAwesomeIcon icon={faCode} color='lightgray' />
<p>Developer: {gameInfo?.developer}</p>
</div>
<div
className='entry-info-item'
hidden={viewingInfoFromDownloads || versionSize === null}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size on disk:{' '}
{versionSize !== null
? prettyBytes(versionSize, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
: 'Loading...'}
</p>
</div>
<div className='entry-info-item' hidden={!versionInfo}>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size when downloaded (zipped):{' '}
{versionInfo
? prettyBytes(versionInfo.size, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
: 'Loading...'}
</p>
</div>
</div>
</>
)
}
+20 -46
View File
@@ -1,13 +1,12 @@
'use client' 'use client'
import { useEffect } from 'react' import { useEffect } from 'react'
import { platform } from '@tauri-apps/plugin-os'
import '../Installs.css' import '../Installs.css'
import { format } from 'date-fns' import { format } from 'date-fns'
import { invoke } from '@tauri-apps/api/core' import { invoke } from '@tauri-apps/api/core'
import { message } from '@tauri-apps/plugin-dialog'
import { useGlobal } from '../GlobalProvider' import { useGlobal } from '../GlobalProvider'
import { useSearchParams } from 'next/navigation' import { useSearchParams } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os'
export default function Installs () { export default function Installs () {
const { const {
@@ -50,7 +49,13 @@ export default function Installs () {
</button> </button>
</div> </div>
<div className='downloads-container'> <div className='downloads-container'>
<div className='downloads-scroll'> <div
className={`downloads-scroll ${
platform() === 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{downloadedVersionsConfig && downloadedVersionsConfig.list.length ? ( {downloadedVersionsConfig && downloadedVersionsConfig.list.length ? (
downloadedVersionsConfig.list downloadedVersionsConfig.list
.filter(v => { .filter(v => {
@@ -58,14 +63,6 @@ export default function Installs () {
if (!info) return false if (!info) return false
return info.game === Number(params.get('id') || 0) return info.game === Number(params.get('id') || 0)
}) })
.sort((a, b) => {
const infoA = getVersionInfo(a)
const infoB = getVersionInfo(b)
if (!infoA && !infoB) return 0
if (!infoA) return 1
if (!infoB) return -1
return infoB.place - infoA.place
})
.map((entry, i) => ( .map((entry, i) => (
<div key={i} className='downloads-entry'> <div key={i} className='downloads-entry'>
<div className='flex flex-col'> <div className='flex flex-col'>
@@ -84,6 +81,17 @@ export default function Installs () {
</div> </div>
</div> </div>
<div className='flex flex-row items-center gap-2'> <div className='flex flex-row items-center gap-2'>
<button
className='button'
onClick={async () => {
setManagingVersion(entry)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
>
View Info
</button>
<button <button
className='button' className='button'
onClick={async () => { onClick={async () => {
@@ -100,43 +108,9 @@ export default function Installs () {
onClick={async () => { onClick={async () => {
const verInfo = getVersionInfo(entry) const verInfo = getVersionInfo(entry)
if (verInfo == undefined) return if (verInfo == undefined) return
let plat = platform()
let willUseWine = false
let cfg = null
while (normalConfig != null) {
cfg = normalConfig
break
}
if (plat === 'macos' || plat === 'linux') {
if (
!verInfo.platforms.includes(plat) &&
verInfo.platforms.includes('windows')
) {
if (
cfg != null &&
!cfg.settings.useWineOnUnixWhenNeeded
) {
await message(
'Wine support is disabled in settings and this version requires wine',
{
title: 'Wine is needed to load this version',
kind: 'error'
}
)
return
}
plat = 'windows'
willUseWine = true
}
}
invoke('launch_game', { invoke('launch_game', {
name: verInfo.id, name: verInfo.id,
executable: executable: verInfo.executable
verInfo.executables[
verInfo.platforms.indexOf(plat)
],
wine: willUseWine,
wineCommand: cfg?.settings.wineOnUnixCommand
}) })
}} }}
> >
+168 -138
View File
@@ -4,7 +4,6 @@ import { useCallback, useEffect, useState } from 'react'
import Sidebar from './componets/Sidebar' import Sidebar from './componets/Sidebar'
import './Globals.css' import './Globals.css'
import { DownloadProgress } from './types/DownloadProgress' import { DownloadProgress } from './types/DownloadProgress'
import { arch, platform } from '@tauri-apps/plugin-os'
import { invoke } from '@tauri-apps/api/core' import { invoke } from '@tauri-apps/api/core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { import {
@@ -13,6 +12,7 @@ import {
faChevronLeft, faChevronLeft,
faCode, faCode,
faDownload, faDownload,
faInfo,
faRemove, faRemove,
faShieldHalved, faShieldHalved,
faWarning, faWarning,
@@ -40,6 +40,10 @@ import { GameVersion } from './types/GameVersion'
import { Game } from './types/Game' import { Game } from './types/Game'
import { listen } from '@tauri-apps/api/event' import { listen } from '@tauri-apps/api/event'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { arch, platform } from '@tauri-apps/plugin-os'
import VersionInfo from './componets/VersionInfo'
import prettyBytes from 'pretty-bytes'
import ProgressBar from './componets/ProgressBar'
const roboto = Roboto({ const roboto = Roboto({
subsets: ['latin'] subsets: ['latin']
@@ -70,10 +74,18 @@ export default function RootLayout ({
[] []
) )
const [managingVersion, setManagingVersion] = useState<string | null>(null) const [managingVersion, setManagingVersion] = useState<string | null>(null)
const [viewingInfoFromDownloads, setViewingInfoFromDownloads] =
useState<boolean>(false)
const [selectedGame, setSelectedGame] = useState<number | null>(null) const [selectedGame, setSelectedGame] = useState<number | null>(null)
function handleOverlayClick (e: React.MouseEvent<HTMLDivElement>) { function handleOverlayClick (e: React.MouseEvent<HTMLDivElement>) {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) {
if (viewingInfoFromDownloads) {
setPopupMode(0)
setViewingInfoFromDownloads(false)
setManagingVersion(null)
setSelectedGame(null)
}
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
} }
@@ -103,13 +115,45 @@ export default function RootLayout ({
let unlistenUninstalled: (() => void) | null = null let unlistenUninstalled: (() => void) | null = null
listen<string>('download-progress', event => { listen<string>('download-progress', event => {
const [versionName, progStr] = event.payload.split(':') const [versionName, progStr, totalSizeStr, speedStr, etaSecsStr] =
event.payload.split(':')
const prog = Number(progStr) const prog = Number(progStr)
const progBytes = Number(totalSizeStr)
const speed = Number(speedStr)
const etaSecs = Number(etaSecsStr)
setDownloadProgress(prev => { setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === versionName) const i = prev.findIndex(d => d.version === versionName)
if (i === -1) return prev if (i === -1) return prev
const copy = [...prev] const copy = [...prev]
copy[i] = { ...copy[i], progress: prog } copy[i] = {
...copy[i],
progress: prog,
progressBytes: progBytes,
speed,
etaSecs
}
return copy
})
}).then(f => (unlistenProgress = f))
listen<string>('download-hash-checking', event => {
const versionName = event.payload
setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === versionName)
if (i === -1) return prev
const copy = [...prev]
copy[i] = { ...copy[i], hash_checking: true }
return copy
})
}).then(f => (unlistenProgress = f))
listen<string>('download-finishing', event => {
const versionName = event.payload
setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === versionName)
if (i === -1) return prev
const copy = [...prev]
copy[i] = { ...copy[i], hash_checking: false, finishing: true }
return copy return copy
}) })
}).then(f => (unlistenProgress = f)) }).then(f => (unlistenProgress = f))
@@ -162,7 +206,7 @@ export default function RootLayout ({
setLoadingText('Downloading version list...') setLoadingText('Downloading version list...')
try { try {
const res = await axios.get( const res = await axios.get(
'https://games.lncvrt.xyz/api/launcher/versions' `https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
) )
setServerVersionList(res.data) setServerVersionList(res.data)
} catch { } catch {
@@ -193,92 +237,18 @@ export default function RootLayout ({
function getSpecialVersionsList (game?: number): GameVersion[] { function getSpecialVersionsList (game?: number): GameVersion[] {
if (!normalConfig || !serverVersionList) return [] if (!normalConfig || !serverVersionList) return []
const useWine = normalConfig.settings.useWineOnUnixWhenNeeded
const p = platform()
const a = arch()
return serverVersionList.versions return serverVersionList.versions
.filter(v => !downloadedVersionsConfig?.list.includes(v.id)) .filter(v => !downloadedVersionsConfig?.list.includes(v.id))
.filter(v => { .filter(v => {
if (game && v.game != game) return false if (game && v.game != game) return false
if (p === 'macos' || p === 'linux') { return true
if (useWine) {
return (
v.platforms.includes('windows-x86') ||
v.platforms.includes('windows-x64') ||
v.platforms.includes(p)
)
}
return v.platforms.includes(p)
}
if (p === 'windows') {
if (a === 'x86_64')
return (
v.platforms.includes('windows-x86') ||
v.platforms.includes('windows-x64')
)
if (a === 'aarch64') return v.platforms.includes('windows-arm64')
}
return false
}) })
.sort((a, b) => { .sort((a, b) => {
if (b.game !== a.game) return a.game - b.game if (b.game !== a.game) return a.game - b.game
return b.place - a.place return 0
}) })
} }
function getDownloadLink (version: GameVersion): string | undefined {
const p = platform()
const a = arch()
const findUrl = (plat: string) => {
const i = version.platforms.indexOf(plat)
return i >= 0 ? version.downloadUrls[i] : undefined
}
if (p === 'windows') {
if (a === 'x86_64')
return findUrl('windows-x64') || findUrl('windows-x86')
if (a === 'aarch64') return findUrl('windows-arm64')
}
if (p === 'macos' || p === 'linux') {
if (normalConfig?.settings.useWineOnUnixWhenNeeded) {
return findUrl('windows-x86') || findUrl('windows-x64') || findUrl(p)
}
return findUrl(p)
}
return undefined
}
function getExecutableName (version: GameVersion): string | undefined {
const p = platform()
const a = arch()
const findUrl = (plat: string) => {
const i = version.platforms.indexOf(plat)
return i >= 0 ? version.executables[i] : undefined
}
if (p === 'windows') {
if (a === 'x86_64')
return findUrl('windows-x64') || findUrl('windows-x86')
if (a === 'aarch64') return findUrl('windows-arm64')
}
if (p === 'macos' || p === 'linux') {
if (normalConfig?.settings.useWineOnUnixWhenNeeded) {
return findUrl('windows-x86') || findUrl('windows-x64') || findUrl(p)
}
return findUrl(p)
}
return undefined
}
function getVersionInfo (id: string | undefined): GameVersion | undefined { function getVersionInfo (id: string | undefined): GameVersion | undefined {
if (!id) return undefined if (!id) return undefined
return serverVersionList?.versions.find(v => v.id === id) return serverVersionList?.versions.find(v => v.id === id)
@@ -310,7 +280,8 @@ export default function RootLayout ({
setSelectedVersionList([]) setSelectedVersionList([])
const newDownloads = list.map( const newDownloads = list.map(
version => new DownloadProgress(version, 0, false, true) version =>
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0)
) )
setDownloadProgress(newDownloads) setDownloadProgress(newDownloads)
@@ -330,29 +301,16 @@ export default function RootLayout ({
) )
return return
} }
const downloadLink = getDownloadLink(info)
if (!downloadLink) {
setDownloadProgress(prev =>
prev.filter(d => d.version !== download.version)
)
return
}
const executableName = getExecutableName(info)
if (!executableName) {
setDownloadProgress(prev =>
prev.filter(d => d.version !== download.version)
)
return
}
setDownloadProgress(prev => setDownloadProgress(prev =>
prev.map(d => prev.map(d =>
d.version === download.version ? { ...d, queued: false } : d d.version === download.version ? { ...d, queued: false } : d
) )
) )
const res = await invoke<string>('download', { const res = await invoke<string>('download', {
url: downloadLink, url: info.downloadUrl,
name: info.id, name: info.id,
executable: executableName executable: info.executable,
hash: info.sha512sum
}) })
if (res == '1') { if (res == '1') {
setDownloadProgress(prev => setDownloadProgress(prev =>
@@ -394,46 +352,47 @@ export default function RootLayout ({
} | null { } | null {
if (!downloadedVersionsConfig || !serverVersionList) return null if (!downloadedVersionsConfig || !serverVersionList) return null
const p = platform()
const a = arch()
const installed = downloadedVersionsConfig.list.filter( const installed = downloadedVersionsConfig.list.filter(
v => getVersionGame(getVersionInfo(v)?.game)?.id === gameId v => getVersionGame(getVersionInfo(v)?.game)?.id === gameId
).length ).length
const total = serverVersionList.versions const total = serverVersionList.versions.filter(
.filter(v => { v => getVersionGame(v?.game)?.id === gameId
if (p === 'macos' || p === 'linux') { ).length
if (normalConfig?.settings.useWineOnUnixWhenNeeded) {
return (
v.platforms.includes('windows-x86') ||
v.platforms.includes('windows-x64') ||
v.platforms.includes(p)
)
}
return v.platforms.includes(p)
}
if (p === 'windows') {
if (a === 'x86_64')
return (
v.platforms.includes('windows-x86') ||
v.platforms.includes('windows-x64')
)
if (a === 'aarch64') return v.platforms.includes('windows-arm64')
}
return false
})
.filter(v => getVersionGame(v?.game)?.id === gameId).length
return { installed, total } return { installed, total }
} }
function formatEtaSmart (seconds: number) {
if (seconds < 60) return `${Math.floor(seconds)}s`
if (seconds < 3600)
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`
if (seconds < 86400) {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
return `${h}h ${m}m`
}
const d = Math.floor(seconds / 86400)
const h = Math.floor((seconds % 86400) / 3600)
return `${d}d ${h}h`
}
return ( return (
<> <>
<html lang='en' className={roboto.className}> <html lang='en' className={roboto.className}>
<body> <body
className={
normalConfig?.settings.theme === 1
? 'red-theme'
: normalConfig?.settings.theme === 2
? 'green-theme'
: normalConfig?.settings.theme === 3
? 'blue-theme'
: normalConfig?.settings.theme === 4
? 'purple-theme'
: 'dark-theme'
}
>
{loading ? ( {loading ? (
<div className='w-screen h-screen flex items-center justify-center'> <div className='w-screen h-screen flex items-center justify-center'>
{outdated ? ( {outdated ? (
@@ -479,7 +438,8 @@ export default function RootLayout ({
getVersionGame, getVersionGame,
getListOfGames, getListOfGames,
setSelectedGame, setSelectedGame,
getVersionsAmountData getVersionsAmountData,
viewingInfoFromDownloads
}} }}
> >
<div <div
@@ -489,6 +449,9 @@ export default function RootLayout ({
if (popupMode == 0 && selectedGame) { if (popupMode == 0 && selectedGame) {
setSelectedGame(null) setSelectedGame(null)
setSelectedVersionList([]) setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else { } else {
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
@@ -498,7 +461,7 @@ export default function RootLayout ({
> >
<Sidebar /> <Sidebar />
<div <div
className='relative z-2 ml-[239px] w-[761px] border-b border-[#242424] h-[33px] bg-[#161616]' className='relative z-2 ml-[239px] w-[calc(100vw-239px)] border-b border-(--col3) h-[33px] bg-(--col1)'
style={{ style={{
display: platform() === 'windows' ? 'block' : 'none' display: platform() === 'windows' ? 'block' : 'none'
}} }}
@@ -522,6 +485,9 @@ export default function RootLayout ({
) { ) {
setSelectedGame(null) setSelectedGame(null)
setSelectedVersionList([]) setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else { } else {
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
@@ -530,7 +496,10 @@ export default function RootLayout ({
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={ icon={
popupMode == 0 && selectedGame && pathname === '/' (popupMode == 0 &&
selectedGame &&
pathname === '/') ||
viewingInfoFromDownloads
? faChevronLeft ? faChevronLeft
: faXmark : faXmark
} }
@@ -546,11 +515,16 @@ export default function RootLayout ({
(v, i) => ( (v, i) => (
<div key={i} className='popup-entry'> <div key={i} className='popup-entry'>
<p className='text-2xl'> <p className='text-2xl'>
{getVersionGame(v.game)?.name} v {getVersionGame(v.game)?.cutOff == null
{v.versionName} ? getVersionGame(v.game)?.name
: getVersionGame(v.game)?.name.substring(
0,
getVersionGame(v.game)?.cutOff ?? 0
) + '...'}{' '}
v{v.versionName}
</p> </p>
<button <button
className='button right-2 bottom-2' className='button right-22 bottom-1.5'
onClick={() => { onClick={() => {
setSelectedVersionList(prev => setSelectedVersionList(prev =>
prev.includes(v.id) prev.includes(v.id)
@@ -570,6 +544,16 @@ export default function RootLayout ({
</> </>
)} )}
</button> </button>
<button
className='button right-1.5 bottom-1.5'
onClick={() => {
setManagingVersion(v.id)
setViewingInfoFromDownloads(true)
setPopupMode(3)
}}
>
<FontAwesomeIcon icon={faInfo} /> Info
</button>
</div> </div>
) )
)} )}
@@ -657,7 +641,7 @@ export default function RootLayout ({
key={i} key={i}
className='popup-entry flex flex-col justify-between' className='popup-entry flex flex-col justify-between'
> >
<p className='text-2xl'> <p className='text-2xl text-center'>
{ {
getVersionGame( getVersionGame(
getVersionInfo(v.version)?.game getVersionInfo(v.version)?.game
@@ -669,7 +653,7 @@ export default function RootLayout ({
{v.failed ? ( {v.failed ? (
<> <>
<div className='flex items-center'> <div className='flex items-center'>
<span className='text-red-500'> <span className='text-red-500 inline-block w-full text-center'>
Download failed Download failed
</span> </span>
<button <button
@@ -687,13 +671,51 @@ export default function RootLayout ({
</div> </div>
</> </>
) : v.queued ? ( ) : v.queued ? (
<span className='text-yellow-500'> <span className='text-yellow-500 inline-block w-full text-center'>
Queued… Queued…
</span> </span>
) : ( ) : v.queued ? (
<span> <span className='text-yellow-500 inline-block w-full text-center'>
Downloading: {v.progress}% done Queued…
</span> </span>
) : v.hash_checking ? (
<span className='text-blue-500 inline-block w-full text-center'>
Checking hash...
</span>
) : v.finishing ? (
<span className='text-green-500 inline-block w-full text-center'>
Finishing...
</span>
) : (
<div className='flex flex-col gap-1 w-full'>
<span className='text-center'>
Downloaded{' '}
{prettyBytes(v.progressBytes, {
minimumFractionDigits: 1,
maximumFractionDigits: 1
})}{' '}
of{' '}
{prettyBytes(
getVersionInfo(v.version)?.size ??
0,
{
minimumFractionDigits: 1,
maximumFractionDigits: 1
}
)}{' '}
(ETA: {formatEtaSmart(v.etaSecs)}{' '}
&bull; Speed:{' '}
{prettyBytes(v.speed, {
minimumFractionDigits: 1,
maximumFractionDigits: 1
})}
/s)
</span>
<ProgressBar
progress={v.progress}
className='w-full'
/>
</div>
)} )}
</div> </div>
</div> </div>
@@ -741,6 +763,14 @@ export default function RootLayout ({
No version selected No version selected
</p> </p>
) )
) : popupMode === 3 ? (
managingVersion && downloadedVersionsConfig ? (
<VersionInfo />
) : (
<p className='text-xl text-center'>
No version selected
</p>
)
) : null} ) : null}
{popupMode == 0 && {popupMode == 0 &&
selectedGame && selectedGame &&
+8 -1
View File
@@ -10,6 +10,7 @@ import {
faShieldHalved, faShieldHalved,
faWarning faWarning
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
export default function Installs () { export default function Installs () {
const { const {
@@ -49,7 +50,13 @@ export default function Installs () {
</button> </button>
</div> </div>
<div className='downloads-container'> <div className='downloads-container'>
<div className='downloads-scroll'> <div
className={`downloads-scroll ${
platform() === 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{downloadedVersionsConfig && downloadedVersionsConfig.list.length ? ( {downloadedVersionsConfig && downloadedVersionsConfig.list.length ? (
getListOfGames() getListOfGames()
.sort((a, b) => { .sort((a, b) => {
+52 -39
View File
@@ -3,29 +3,25 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Setting } from '../componets/Setting' import { Setting } from '../componets/Setting'
import { writeNormalConfig } from '../util/BazookaManager' import { writeNormalConfig } from '../util/BazookaManager'
import { platform } from '@tauri-apps/plugin-os'
import { useGlobal } from '../GlobalProvider' import { useGlobal } from '../GlobalProvider'
export default function Settings () { export default function Settings () {
const [allowNotifications, setAllowNotifications] = useState(false) const [allowNotifications, setAllowNotifications] = useState(false)
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] = const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
useState(false) useState(false)
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false) const [theme, setTheme] = useState(0)
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
const [loaded, setLoaded] = useState(false) const [loaded, setLoaded] = useState(false)
const { normalConfig, setNormalConfig } = useGlobal() const { normalConfig, setNormalConfig } = useGlobal()
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {
while (normalConfig != null) { while (normalConfig != null) {
setUseWineOnUnixWhenNeeded(
normalConfig.settings.useWineOnUnixWhenNeeded
)
setWineOnUnixCommand(normalConfig.settings.wineOnUnixCommand)
setAllowNotifications(normalConfig.settings.allowNotifications) setAllowNotifications(normalConfig.settings.allowNotifications)
setAlwaysShowGamesInSidebar( setAlwaysShowGamesInSidebar(
normalConfig.settings.alwaysShowGamesInSidebar normalConfig.settings.alwaysShowGamesInSidebar
) )
setTheme(normalConfig.settings.theme)
setLoaded(true) setLoaded(true)
break break
} }
@@ -36,15 +32,27 @@ export default function Settings () {
<> <>
<p className='text-3xl ml-4 mt-4'>Settings</p> <p className='text-3xl ml-4 mt-4'>Settings</p>
{loaded && ( {loaded && (
<div className='ml-4 mt-4 bg-[#161616] border border-[#242424] rounded-lg p-4 w-fit h-fit'> <div className='ml-4 mt-4 bg-(--col1) border border-(--col3) rounded-lg p-4 w-fit h-fit'>
<Setting <Setting
label='Allow sending notifications' label='Allow sending notifications'
value={allowNotifications} value={allowNotifications}
onChange={async () => { onChange={async () => {
while (normalConfig != null) { while (normalConfig != null) {
setAllowNotifications(!allowNotifications) setAllowNotifications(!allowNotifications)
normalConfig.settings.allowNotifications = !allowNotifications setNormalConfig({
await writeNormalConfig(normalConfig) ...normalConfig,
settings: {
...normalConfig.settings,
allowNotifications: !allowNotifications
}
})
writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
allowNotifications: !allowNotifications
}
})
break break
} }
}} }}
@@ -73,35 +81,40 @@ export default function Settings () {
} }
}} }}
/> />
<Setting <div>
label='Use wine to launch Berry Dash when needed' <label className='text-lg'>Theme:</label>
value={useWineOnUnixWhenNeeded} <select
onChange={async () => { className='ml-2 bg-(--col2) border border-(--col4) rounded-md'
while (normalConfig != null) { value={theme}
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded) onChange={async e => {
normalConfig.settings.useWineOnUnixWhenNeeded = const newTheme = parseInt(e.target.value)
!useWineOnUnixWhenNeeded while (normalConfig != null) {
await writeNormalConfig(normalConfig) setTheme(newTheme)
break setNormalConfig({
} ...normalConfig,
}} settings: {
className={platform() == 'linux' ? '' : 'hidden'} ...normalConfig.settings,
/> theme: newTheme
<input }
type='text' })
value={wineOnUnixCommand} writeNormalConfig({
onChange={async e => { ...normalConfig,
while (normalConfig != null) { settings: {
setWineOnUnixCommand(e.target.value) ...normalConfig.settings,
normalConfig.settings.wineOnUnixCommand = e.target.value theme: newTheme
await writeNormalConfig(normalConfig) }
break })
} break
}} }
className={`input-field ${ }}
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden' >
}`} <option value={0}>Dark (default)</option>
></input> <option value={1}>Red</option>
<option value={2}>Green</option>
<option value={3}>Blue</option>
<option value={4}>Purple</option>
</select>
</div>
</div> </div>
)} )}
</> </>
+6 -1
View File
@@ -2,7 +2,12 @@ export class DownloadProgress {
constructor( constructor(
public version: string, public version: string,
public progress: number, public progress: number,
public progressBytes: number,
public failed: boolean, public failed: boolean,
public queued: boolean public queued: boolean,
public hash_checking: boolean,
public finishing: boolean,
public speed: number,
public etaSecs: number
) { } ) { }
} }
+4 -4
View File
@@ -2,9 +2,9 @@ export interface GameVersion {
id: string id: string
versionName: string versionName: string
releaseDate: number releaseDate: number
downloadUrls: string[]
platforms: string[]
executables: string[]
game: number game: number
place: number downloadUrl: string
executable: string
sha512sum: string
size: number
} }
+1 -2
View File
@@ -2,7 +2,6 @@ export class SettingsType {
constructor( constructor(
public allowNotifications: boolean = true, public allowNotifications: boolean = true,
public alwaysShowGamesInSidebar: boolean = true, public alwaysShowGamesInSidebar: boolean = true,
public useWineOnUnixWhenNeeded: boolean = false, public theme: number = 0
public wineOnUnixCommand: string = 'wine %path%'
) { } ) { }
} }