Compare commits

...
26 Commits
Author SHA1 Message Date
Lncvrt 2777e6dac0 1.5.0 2026-01-07 19:56:34 -07:00
Lncvrt a551d5fb52 Change the colors of popup close button 2026-01-07 19:39:15 -07:00
Lncvrt 08915ad2ae Remove this case, I want to eventually make it close the popup but I haven't gotten that to work for some reason 2026-01-07 19:36:36 -07:00
Lncvrt 1dd75f8ccf Improve download versions/games button size 2026-01-07 19:14:54 -07:00
Lncvrt 3dc24ab3d7 Use proper way of detecting when a user hovers for games/installs page 2026-01-07 19:01:58 -07:00
Lncvrt a3e9067ba0 Change rounding for popup close button 2026-01-07 19:01:39 -07:00
Lncvrt a0a763ae57 Remove the manage button and make it a right-click instead. And also change the button height/width. Along with more re-arranging in layout.tsx 2026-01-07 18:29:17 -07:00
Lncvrt c56a903d55 Moove stuff around to make more sense 2026-01-07 18:14:33 -07:00
Lncvrt c07828f244 Improve the logic for uninstalling a version, it was incredibly bad and stupid 2026-01-07 18:10:14 -07:00
Lncvrt cf4f1c15ea Launching works as intended now 2026-01-07 18:07:11 -07:00
Lncvrt 76797c6120 Bring back this, either works or doesn't on linux and windows. I will have to test 2026-01-07 17:45:07 -07:00
Lncvrt 36ed3e0095 Rename this to make more sense 2026-01-07 17:43:26 -07:00
Lncvrt e0ba25da77 Improve parts of launch/download logic 2026-01-07 17:29:14 -07:00
Lncvrt e89b0fac8a Fix a memory leak that doesn't fix until another version is downloaded (can take up to 400mb of ram) 2026-01-07 17:28:42 -07:00
Lncvrt fa256dd004 Update dependencies 2026-01-07 17:28:01 -07:00
Lncvrt b5c6598722 Well that's stupid but it's fixed now 2026-01-07 15:09:55 -07:00
Lncvrt db111aaa2e Fix stupidness here 2026-01-07 15:01:44 -07:00
Lncvrt c1ac567204 1.4.0 2026-01-01 02:25:44 -07:00
Lncvrt 1e11525c8e New sidebar style 2026-01-01 02:24:53 -07:00
Lncvrt b46c52e042 Update reqwest 2026-01-01 01:59:50 -07:00
Lncvrt 848511769e Add changelog + update dependencies 2026-01-01 01:55:57 -07:00
Lncvrt 376078d7f4 Improve location of add / info buttons 2025-12-27 21:04:51 -07:00
Lncvrt cb630a9d57 Fix a bug allowing you to do something that isn't normally allowed in popup (it doesn't matter much but it's a bug stil) 2025-12-27 20:42:46 -07:00
Lncvrt 6ec7e8df82 Make it so you can select versions to download while another version is being downloaded 2025-12-27 20:38:25 -07:00
Lncvrt 1e93ef4d07 Add disabled theme for btntheme1-3 2025-12-27 20:37:23 -07:00
Lncvrt af4d634ed9 Add mint theme 2025-12-27 20:36:46 -07:00
21 changed files with 393 additions and 333 deletions

No files matched your search

+17 -17
View File
@@ -80,7 +80,7 @@
"@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=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], "@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=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
@@ -294,25 +294,25 @@
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.50.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.50.1", "@typescript-eslint/type-utils": "8.50.1", "@typescript-eslint/utils": "8.50.1", "@typescript-eslint/visitor-keys": "8.50.1", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.50.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.51.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/type-utils": "8.51.0", "@typescript-eslint/utils": "8.51.0", "@typescript-eslint/visitor-keys": "8.51.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.2.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.51.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.50.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.50.1", "@typescript-eslint/types": "8.50.1", "@typescript-eslint/typescript-estree": "8.50.1", "@typescript-eslint/visitor-keys": "8.50.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.51.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/types": "8.51.0", "@typescript-eslint/typescript-estree": "8.51.0", "@typescript-eslint/visitor-keys": "8.51.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.50.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.50.1", "@typescript-eslint/types": "^8.50.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.51.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.51.0", "@typescript-eslint/types": "^8.51.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.50.1", "", { "dependencies": { "@typescript-eslint/types": "8.50.1", "@typescript-eslint/visitor-keys": "8.50.1" } }, "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.51.0", "", { "dependencies": { "@typescript-eslint/types": "8.51.0", "@typescript-eslint/visitor-keys": "8.51.0" } }, "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.50.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.51.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.50.1", "", { "dependencies": { "@typescript-eslint/types": "8.50.1", "@typescript-eslint/typescript-estree": "8.50.1", "@typescript-eslint/utils": "8.50.1", "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-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.51.0", "", { "dependencies": { "@typescript-eslint/types": "8.51.0", "@typescript-eslint/typescript-estree": "8.51.0", "@typescript-eslint/utils": "8.51.0", "debug": "^4.3.4", "ts-api-utils": "^2.2.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.50.1", "", {}, "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.51.0", "", {}, "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.50.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.50.1", "@typescript-eslint/tsconfig-utils": "8.50.1", "@typescript-eslint/types": "8.50.1", "@typescript-eslint/visitor-keys": "8.50.1", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.51.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.51.0", "@typescript-eslint/tsconfig-utils": "8.51.0", "@typescript-eslint/types": "8.51.0", "@typescript-eslint/visitor-keys": "8.51.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.2.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.50.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.50.1", "@typescript-eslint/types": "8.50.1", "@typescript-eslint/typescript-estree": "8.50.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.51.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/types": "8.51.0", "@typescript-eslint/typescript-estree": "8.51.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.50.1", "", { "dependencies": { "@typescript-eslint/types": "8.50.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ=="], "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.51.0", "", { "dependencies": { "@typescript-eslint/types": "8.51.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg=="],
"@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=="],
@@ -412,7 +412,7 @@
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001761", "", {}, "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g=="], "caniuse-lite": ["caniuse-lite@1.0.30001762", "", {}, "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -510,7 +510,7 @@
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
@@ -882,7 +882,7 @@
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "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.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], "ts-api-utils": ["ts-api-utils@2.3.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg=="],
"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=="], "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=="],
@@ -900,7 +900,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.50.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.50.1", "@typescript-eslint/parser": "8.50.1", "@typescript-eslint/typescript-estree": "8.50.1", "@typescript-eslint/utils": "8.50.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ=="], "typescript-eslint": ["typescript-eslint@8.51.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.51.0", "@typescript-eslint/parser": "8.51.0", "@typescript-eslint/typescript-estree": "8.51.0", "@typescript-eslint/utils": "8.51.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA=="],
"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=="],
@@ -928,7 +928,7 @@
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], "zod": ["zod@4.3.4", "", {}, "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A=="],
"zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="],
@@ -942,7 +942,7 @@
"@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=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], "@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=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+5 -5
View File
@@ -1,7 +1,7 @@
{ {
"name": "lncvrt-games-launcher", "name": "lncvrt-games-launcher",
"private": true, "private": true,
"version": "1.3.1", "version": "1.5.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -30,17 +30,17 @@
"react-dom": "19.2.3" "react-dom": "19.2.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "3.3.3",
"@tailwindcss/postcss": "4.1.18", "@tailwindcss/postcss": "4.1.18",
"@tauri-apps/cli": "2.9.6", "@tauri-apps/cli": "2.9.6",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/node": "25.0.3",
"@types/react": "19.2.7", "@types/react": "19.2.7",
"@types/react-dom": "19.2.3", "@types/react-dom": "19.2.3",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"@types/node": "25.0.3",
"tailwindcss": "4.1.18",
"typescript": "5.9.3",
"eslint": "9.39.2", "eslint": "9.39.2",
"eslint-config-next": "16.1.1", "eslint-config-next": "16.1.1",
"@eslint/eslintrc": "3.3.3" "tailwindcss": "4.1.18",
"typescript": "5.9.3"
} }
} }
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "lncvrt-games-launcher" name = "lncvrt-games-launcher"
version = "1.3.1" version = "1.5.0"
authors = ["Lncvrt"] authors = ["Lncvrt"]
edition = "2024" edition = "2024"
@@ -15,15 +15,15 @@ tauri-build = { version = "2.5.3", features = [] }
tauri = { version = "2.9.5", features = ["macos-private-api"] } tauri = { version = "2.9.5", features = ["macos-private-api"] }
tauri-plugin-opener = "2.5.2" tauri-plugin-opener = "2.5.2"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.147" serde_json = "1.0.149"
tauri-plugin-os = "2.3.2" tauri-plugin-os = "2.3.2"
reqwest = { version = "0.12.28", default-features = false, features = ["stream", "rustls-tls"] } reqwest = { version = "0.13.1", default-features = false, features = ["stream", "rustls"] }
tokio = "1.48.0" tokio = "1.49.0"
futures-util = { version = "0.3.31", features = ["io"] } futures-util = { version = "0.3.31", features = ["io"] }
tauri-plugin-decorum = "1.1.1" tauri-plugin-decorum = "1.1.1"
tauri-plugin-fs = "2.4.4" tauri-plugin-fs = "2.4.4"
zip = "7.0.0" zip = "7.0.0"
libc = "0.2.178" libc = "0.2.179"
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"
+9 -3
View File
@@ -21,11 +21,17 @@
"core:window:allow-toggle-maximize", "core:window:allow-toggle-maximize",
"decorum:allow-show-snap-overlay", "decorum:allow-show-snap-overlay",
"fs:default", "fs:default",
"fs:allow-applocaldata-read",
"fs:allow-applocaldata-write",
"dialog:default", "dialog:default",
"notification:default", "notification:default",
"clipboard-manager:default", "clipboard-manager:default",
"clipboard-manager:allow-write-text" "clipboard-manager:allow-write-text",
"fs:allow-applocaldata-read",
"fs:allow-applocaldata-write",
{
"identifier": "fs:scope",
"allow": [
{ "path": "$APPLOCALDATA/game/**" }
]
}
] ]
} }
+55 -61
View File
@@ -15,22 +15,25 @@ use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_dialog::{DialogExt, MessageDialogKind}; use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
use tauri_plugin_opener::OpenerExt; use tauri_plugin_opener::OpenerExt;
use tauri_plugin_os::platform; use tauri_plugin_os::platform;
use tokio::io::AsyncReadExt;
use tokio::{io::AsyncWriteExt, time::timeout}; use tokio::{io::AsyncWriteExt, time::timeout};
use zip::ZipArchive; use zip::ZipArchive;
#[cfg(target_os = "linux")]
use std::os::unix::fs::PermissionsExt;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use tauri_plugin_decorum::WebviewWindowExt; use tauri_plugin_decorum::WebviewWindowExt;
fn is_running_by_path(path: &PathBuf) -> bool { #[allow(unused)]
fn is_running_by_path(path: &Path) -> bool {
let sys = System::new_all(); let sys = System::new_all();
sys.processes().values().any(|proc| { let target = path.canonicalize().ok();
if let Some(exe) = proc.exe() { if target.is_none() {
exe == path return false;
} else {
false
} }
let target = target.unwrap();
sys.processes().values().any(|proc| {
proc.exe()
.and_then(|exe| exe.canonicalize().ok())
.map_or(false, |exe| exe == target)
}) })
} }
@@ -182,9 +185,26 @@ async fn download(
app.emit("download-hash-checking", format!("{}", &name)) app.emit("download-hash-checking", format!("{}", &name))
.unwrap(); .unwrap();
let download_hash = {
let mut file = tokio::fs::File::open(download_part_path.clone())
.await
.unwrap();
let mut hasher = Sha512::new(); let mut hasher = Sha512::new();
hasher.update(&tokio::fs::read(download_part_path.clone()).await.unwrap()); {
let download_hash = format!("{:x}", hasher.finalize()); let mut buffer = [0; 8192];
loop {
let bytes_read = file.read(&mut buffer).await.unwrap();
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
}
drop(file);
format!("{:x}", hasher.finalize())
};
if hash != download_hash { if hash != download_hash {
tokio::fs::remove_file(download_part_path.clone()) tokio::fs::remove_file(download_part_path.clone())
.await .await
@@ -205,19 +225,14 @@ async fn download(
return "-1".to_string(); return "-1".to_string();
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "macos"))]
{ {
let executable_path = game_path.join(&name).join(&executable); use std::{fs, os::unix::fs::PermissionsExt};
let mut perms = fs::metadata(&executable_path).unwrap().permissions();
perms.set_mode(0o755);
fs::set_permissions(executable_path, perms).unwrap();
}
#[cfg(target_os = "macos")]
{
use std::fs;
use std::os::unix::fs::PermissionsExt;
let macos_app_path = game_path let executable_path = if cfg!(target_os = "linux") {
game_path.join(&name).join(&executable)
} else {
game_path
.join(&name) .join(&name)
.join(&executable) .join(&executable)
.join("Contents") .join("Contents")
@@ -227,80 +242,60 @@ async fn download(
.chars() .chars()
.take(executable.chars().count() - 4) .take(executable.chars().count() - 4)
.collect::<String>(), .collect::<String>(),
); )
};
let mut perms = fs::metadata(&macos_app_path).unwrap().permissions(); let mut perms = fs::metadata(&executable_path).unwrap().permissions();
perms.set_mode(0o755); perms.set_mode(0o755);
fs::set_permissions(&macos_app_path, perms).unwrap(); fs::set_permissions(&executable_path, perms).unwrap();
} }
return "1".to_string(); return "1".to_string();
} }
#[allow(unused_variables)] #[allow(unused_variables)]
#[tauri::command] #[tauri::command]
fn launch_game(app: AppHandle, name: String, executable: String) { fn launch_game(app: AppHandle, name: String, executable: String, display_name: String) {
let game_folder = app let game_folder = app
.path() .path()
.app_local_data_dir() .app_local_data_dir()
.unwrap() .unwrap()
.join("game") .join("game")
.join(&name); .join(&name);
let game_path = game_folder.join(&executable); if !game_folder.exists() {
if !game_path.exists() {
app.dialog()
.message(
format!(
"Executable \"{}\" not found.\n\nTry reinstalling the game or make a support request in the Community link on the sidebar.",
game_path.display().to_string()
)
)
.kind(MessageDialogKind::Error)
.title("Game not found")
.show(|_| {});
return; return;
} }
if is_running_by_path(&game_path) {
//if already running on macos, it'll auto take the user to that proccess
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
if is_running_by_path(&game_folder.join(&executable)) {
app.dialog() app.dialog()
.message(format!("The version {} is already running.", name)) .message(format!(
"{} is already running, if this doesn't seem true, try to kill the proccess.",
display_name
))
.kind(MessageDialogKind::Error) .kind(MessageDialogKind::Error)
.title("Game already running") .title("Game already running")
.show(|_| {}); .show(|_| {});
return; return;
} }
}
if platform() == "macos" { if platform() == "macos" {
Command::new("open") Command::new("open")
.arg(&game_path) .arg(&executable)
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn()
.unwrap(); .unwrap();
} else { } else {
Command::new(&game_path) Command::new(&game_folder.join(&executable))
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn()
.unwrap(); .unwrap();
} }
} }
#[tauri::command]
async fn uninstall_version(app: AppHandle, name: String) {
let game_path = app
.path()
.app_local_data_dir()
.unwrap()
.join("game")
.join(&name);
if game_path.exists() {
if let Err(_) = tokio::fs::remove_dir_all(&game_path).await {
app.emit("version-failed", &name).unwrap();
} else {
app.emit("version-uninstalled", &name).unwrap();
}
} else {
app.emit("version-uninstalled", &name).unwrap();
}
}
#[tauri::command] #[tauri::command]
async fn open_folder(app: AppHandle, name: String) { async fn open_folder(app: AppHandle, name: String) {
let game_path = app let game_path = app
@@ -345,7 +340,6 @@ pub fn run() {
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
download, download,
launch_game, launch_game,
uninstall_version,
open_folder, open_folder,
folder_size folder_size
]) ])
+1 -1
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.3.1", "version": "1.5.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
+1 -1
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.3.1", "version": "1.5.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
+1 -1
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.3.1", "version": "1.5.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
+1 -1
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.3.1", "version": "1.5.0",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev", "beforeDevCommand": "next dev",
+1 -1
View File
@@ -28,7 +28,7 @@ type GlobalCtxType = {
setManagingVersion: (v: string | null) => void setManagingVersion: (v: string | null) => void
setSelectedGame: (v: number | null) => void setSelectedGame: (v: number | null) => void
getVersionInfo: (id: string | undefined) => GameVersion | undefined getVersionInfo: (id: string | undefined) => GameVersion | undefined
getVersionGame: (id: number | undefined) => Game | undefined getGameInfo: (id: number | undefined) => Game | undefined
getListOfGames: () => Game[] getListOfGames: () => Game[]
getVersionsAmountData: (gameId: number) => { getVersionsAmountData: (gameId: number) => {
installed: number installed: number
+17 -5
View File
@@ -64,20 +64,32 @@ body {
--col8: rgb(140, 128, 152); --col8: rgb(140, 128, 152);
} }
.mint-theme {
--col0: rgb(8, 32, 16);
--col1: rgb(16, 48, 32);
--col2: rgb(32, 56, 40);
--col3: rgb(48, 72, 48);
--col4: rgb(64, 88, 66);
--col5: rgb(80, 104, 65);
--col6: rgb(96, 120, 80);
--col7: rgb(112, 136, 85);
--col8: rgb(128, 152, 95);
}
.button { .button {
@apply rounded-md cursor-pointer text-[16px] py-1.5 px-3 transition-colors border; @apply rounded-md cursor-pointer text-[16px] py-1 px-2 transition-colors border;
} }
.btntheme1 { .btntheme1 {
@apply bg-(--col2) border-(--col4) hover:bg-(--col4) hover:border-(--col6); @apply bg-(--col2) border-(--col4) hover:bg-(--col4) hover:border-(--col6) disabled:text-gray-300 disabled:bg-(--col1) disabled:border-(--col5) disabled:hover:text-gray-100 disabled:hover:bg-(--col2) disabled:hover:border-(--col6);
} }
.btntheme2 { .btntheme2 {
@apply bg-(--col3) border-(--col5) hover:bg-(--col5) hover:border-(--col7); @apply bg-(--col3) border-(--col5) hover:bg-(--col5) hover:border-(--col7) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col6) disabled:hover:text-gray-100 disabled:hover:bg-(--col3) disabled:hover:border-(--col7);
} }
.btntheme3 { .btntheme3 {
@apply bg-(--col4) border-(--col6) hover:bg-(--col6) hover:border-(--col8); @apply bg-(--col4) border-(--col6) hover:bg-(--col6) hover:border-(--col8) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col7) disabled:hover:text-gray-100 disabled:hover:bg-(--col4) disabled:hover:border-(--col8);
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
@@ -135,7 +147,7 @@ body {
} }
.close-button { .close-button {
@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); @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;
} }
*:focus { *:focus {
+10
View File
@@ -11,3 +11,13 @@
.downloads-entry { .downloads-entry {
@apply flex justify-between items-center m-2 p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5); @apply flex justify-between items-center m-2 p-2 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5);
} }
.downloads-entry .entry-info-item,
.downloads-entry .button {
@apply bg-(--col3) border-(--col5) hover:bg-(--col5) hover:border-(--col7) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col6) disabled:hover:text-gray-100 disabled:hover:bg-(--col3) disabled:hover:border-(--col7);
}
.downloads-entry:hover .entry-info-item,
.downloads-entry:hover .button {
@apply bg-(--col4) border-(--col6) hover:bg-(--col6) hover:border-(--col8) disabled:text-gray-300 disabled:bg-(--col2) disabled:border-(--col7) disabled:hover:text-gray-100 disabled:hover:bg-(--col4) disabled:hover:border-(--col8);
}
+1 -1
View File
@@ -14,7 +14,7 @@
} }
.logo { .logo {
@apply flex items-center pl-1 h-14 w-60; @apply flex items-center pl-2 h-10 w-60 pt-2;
} }
.nav-links { .nav-links {
+8 -5
View File
@@ -17,6 +17,11 @@ import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import { usePathname, useSearchParams } from 'next/navigation' import { usePathname, useSearchParams } from 'next/navigation'
import { getCurrentWindow } from '@tauri-apps/api/window' import { getCurrentWindow } from '@tauri-apps/api/window'
import { Lexend } from 'next/font/google'
const lexend = Lexend({
subsets: ['latin']
})
export default function Sidebar () { export default function Sidebar () {
const { const {
@@ -63,13 +68,12 @@ export default function Sidebar () {
} }
}} }}
> >
<Image draggable={false} src={Icon} width={48} height={48} alt='' /> <Image draggable={false} src={Icon} width={36} height={36} alt='' />
<p className='ml-1 text-[17px] whitespace-nowrap'> <p className={`ml-1 text-[16px] whitespace-nowrap ${lexend.className}`}>
Lncvrt Games Launcher Lncvrt Games Launcher
</p> </p>
</div> </div>
<div className='overflow-auto'> <nav className='nav-links overflow-auto pt-2'>
<nav className='nav-links'>
<Link <Link
draggable={false} draggable={false}
href='/' href='/'
@@ -120,7 +124,6 @@ export default function Sidebar () {
<FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community <FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community
</button> </button>
</nav> </nav>
</div>
<div <div
className='sidebar-downloads' className='sidebar-downloads'
hidden={downloadProgress.length == 0} hidden={downloadProgress.length == 0}
+32
View File
@@ -0,0 +1,32 @@
'use client'
import { useGlobal } from '../GlobalProvider'
export default function VersionChangelog () {
const {
getGameInfo,
getVersionInfo,
managingVersion,
downloadedVersionsConfig
} = useGlobal()
if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion)
const gameInfo = getGameInfo(versionInfo?.game)
return (
<>
<p className='text-xl text-center'>
Viewing changelog for {gameInfo?.name} v{versionInfo?.versionName}
</p>
<div className='popup-content text-center p-2'>
<span
className='whitespace-pre-wrap'
dangerouslySetInnerHTML={{
__html: versionInfo?.changelog ? atob(versionInfo.changelog) : ''
}}
/>
</div>
</>
)
}
+14 -6
View File
@@ -1,6 +1,7 @@
'use client' 'use client'
import { import {
faArrowUpRightFromSquare,
faCheck, faCheck,
faCode, faCode,
faHardDrive, faHardDrive,
@@ -16,16 +17,17 @@ import prettyBytes from 'pretty-bytes'
export default function VersionInfo () { export default function VersionInfo () {
const { const {
getVersionGame, getGameInfo,
getVersionInfo, getVersionInfo,
managingVersion, managingVersion,
downloadedVersionsConfig, downloadedVersionsConfig,
viewingInfoFromDownloads viewingInfoFromDownloads,
setPopupMode
} = useGlobal() } = useGlobal()
if (!managingVersion || !downloadedVersionsConfig) return <></> if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion) const versionInfo = getVersionInfo(managingVersion)
const gameInfo = getVersionGame(versionInfo?.game) const gameInfo = getGameInfo(versionInfo?.game)
const [versionSize, setVersionSize] = useState<number | null>(null) const [versionSize, setVersionSize] = useState<number | null>(null)
useEffect(() => { useEffect(() => {
@@ -39,9 +41,7 @@ export default function VersionInfo () {
return ( return (
<> <>
<p className='text-xl text-center'> <p className='text-xl text-center'>
Viewing info for{' '} Viewing info for {gameInfo?.name} v{versionInfo?.versionName}
{getVersionGame(getVersionInfo(managingVersion)?.game)?.name} v
{getVersionInfo(managingVersion)?.versionName}
</p> </p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'> <div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div <div
@@ -117,6 +117,14 @@ export default function VersionInfo () {
: 'Loading...'} : 'Loading...'}
</p> </p>
</div> </div>
<div
className='entry-info-item btntheme2'
onClick={() => setPopupMode(4)}
hidden={!versionInfo?.changelog}
>
<p>View Changelog</p>
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
</div> </div>
</> </>
) )
+30 -28
View File
@@ -10,7 +10,6 @@ import { platform } from '@tauri-apps/plugin-os'
export default function Installs () { export default function Installs () {
const { const {
downloadProgress,
showPopup, showPopup,
setShowPopup, setShowPopup,
setPopupMode, setPopupMode,
@@ -20,12 +19,11 @@ export default function Installs () {
normalConfig, normalConfig,
setManagingVersion, setManagingVersion,
getVersionInfo, getVersionInfo,
getVersionGame, getGameInfo,
setSelectedGame setSelectedGame
} = useGlobal() } = useGlobal()
const params = useSearchParams() const params = useSearchParams()
const [hoveredIds, setHoveredIds] = useState<string[]>([])
useEffect(() => { useEffect(() => {
if (!showPopup) return if (!showPopup) return
@@ -37,14 +35,13 @@ export default function Installs () {
<div className='flex justify-between items-center mb-4'> <div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Installs</p> <p className='text-3xl'>Installs</p>
<button <button
className='button btntheme1 text-3xl' className='button btntheme1'
onClick={() => { onClick={() => {
setSelectedGame(Number(params.get('id') || 0)) setSelectedGame(Number(params.get('id') || 0))
setPopupMode(0) setPopupMode(0)
setShowPopup(true) setShowPopup(true)
setFadeOut(false) setFadeOut(false)
}} }}
disabled={downloadProgress.length != 0}
title='Click to download more versions of this game.' title='Click to download more versions of this game.'
> >
Download versions Download versions
@@ -87,31 +84,36 @@ export default function Installs () {
title={ title={
normalConfig?.settings.useLegacyInteractButtons normalConfig?.settings.useLegacyInteractButtons
? '' ? ''
: 'Click to launch game' : 'Click to launch game. Right-click to manage this version install'
} }
onClick={async () => { onClick={async () => {
if (normalConfig?.settings.useLegacyInteractButtons) return if (normalConfig?.settings.useLegacyInteractButtons) return
const verInfo = getVersionInfo(entry) const verInfo = getVersionInfo(entry)
if (verInfo == undefined) return if (verInfo == undefined) return
const gameInfo = getGameInfo(verInfo.game)
if (gameInfo == undefined) return
invoke('launch_game', { invoke('launch_game', {
name: verInfo.id, name: verInfo.id,
executable: verInfo.executable executable: verInfo.executable,
displayName: `${gameInfo.name} v${verInfo.versionName}`
}) })
}} }}
onMouseEnter={() => setHoveredIds(prev => [...prev, entry])} onContextMenu={e => {
onMouseLeave={() => e.preventDefault()
setHoveredIds(prev => prev.filter(i => i !== i)) setManagingVersion(entry)
} setPopupMode(2)
setShowPopup(true)
setFadeOut(false)
}}
> >
<div className='flex flex-col'> <div className='h-18 w-screen relative'>
<p className='text-2xl'> <p className='text-2xl'>
{getVersionGame(getVersionInfo(entry)?.game)?.name} v {getGameInfo(getVersionInfo(entry)?.game)?.name} v
{getVersionInfo(entry)?.versionName} {getVersionInfo(entry)?.versionName}
</p> </p>
<div <div
className={`entry-info-item mt-2 ${ className='entry-info-item absolute left-0 bottom-0'
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
title='The date the game was installed in MM/dd/yyyy format' title='The date the game was installed in MM/dd/yyyy format'
> >
<p> <p>
@@ -122,12 +124,10 @@ export default function Installs () {
)} )}
</p> </p>
</div> </div>
</div>
<div className='flex flex-row items-center gap-2 mt-auto'> <div className='flex gap-2 absolute right-0 bottom-0'>
<button <button
className={`button ${ className='button'
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
onClick={e => { onClick={e => {
e.stopPropagation() e.stopPropagation()
setManagingVersion(entry) setManagingVersion(entry)
@@ -140,9 +140,10 @@ export default function Installs () {
View Info View Info
</button> </button>
<button <button
className={`button ${ className='button'
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2' hidden={
}`} !normalConfig?.settings.useLegacyInteractButtons
}
onClick={e => { onClick={e => {
e.stopPropagation() e.stopPropagation()
setManagingVersion(entry) setManagingVersion(entry)
@@ -155,9 +156,7 @@ export default function Installs () {
Manage Manage
</button> </button>
<button <button
className={`button ${ className='button'
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
onClick={e => { onClick={e => {
e.stopPropagation() e.stopPropagation()
const verInfo = getVersionInfo(entry) const verInfo = getVersionInfo(entry)
@@ -167,13 +166,16 @@ export default function Installs () {
executable: verInfo.executable executable: verInfo.executable
}) })
}} }}
hidden={!normalConfig?.settings.useLegacyInteractButtons} hidden={
!normalConfig?.settings.useLegacyInteractButtons
}
title='Click to launch game' title='Click to launch game'
> >
Launch Launch
</button> </button>
</div> </div>
</div> </div>
</div>
)) ))
) : ( ) : (
<div className='flex justify-center items-center h-full'> <div className='flex justify-center items-center h-full'>
+170 -162
View File
@@ -44,6 +44,8 @@ import {
isPermissionGranted, isPermissionGranted,
requestPermission requestPermission
} from '@tauri-apps/plugin-notification' } from '@tauri-apps/plugin-notification'
import VersionChangelog from './componets/VersionChangelog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
const roboto = Roboto({ const roboto = Roboto({
subsets: ['latin'] subsets: ['latin']
@@ -79,24 +81,100 @@ export default function RootLayout ({
useState<boolean>(false) useState<boolean>(false)
const [selectedGame, setSelectedGame] = useState<number | null>(null) const [selectedGame, setSelectedGame] = useState<number | null>(null)
function handleOverlayClick (e: React.MouseEvent<HTMLDivElement>) { const pathname = usePathname()
if (e.target === e.currentTarget) {
if (viewingInfoFromDownloads) { function getSpecialVersionsList (game?: number): GameVersion[] {
setPopupMode(0) if (!normalConfig || !serverVersionList) return []
setViewingInfoFromDownloads(false)
setManagingVersion(null) return serverVersionList.versions
setSelectedGame(null) .filter(v => !downloadedVersionsConfig?.list.includes(v.id))
.filter(v => {
if (game && v.game != game) return false
if (downloadProgress.length != 0) {
return !downloadProgress.some(d => d.version === v.id)
} }
return true
})
.sort((a, b) => {
if (b.game !== a.game) return a.game - b.game
return 0
})
}
function getVersionInfo (id: string | undefined): GameVersion | undefined {
if (!id) return undefined
return serverVersionList?.versions.find(v => v.id === id)
}
function getGameInfo (game: number | undefined): Game | undefined {
if (!game) return undefined
return serverVersionList?.games.find(g => g.id === game)
}
function getListOfGames (): Game[] {
if (!downloadedVersionsConfig?.list) return []
const gamesMap = new Map<number, Game>()
downloadedVersionsConfig.list.forEach(i => {
const version = getVersionInfo(i)
if (!version) return
const game = getGameInfo(version.game)
if (!game) return
gamesMap.set(game.id, game)
})
return Array.from(gamesMap.values())
}
function getVersionsAmountData (gameId: number): {
installed: number
total: number
} | null {
if (!downloadedVersionsConfig || !serverVersionList) return null
const installed = downloadedVersionsConfig.list.filter(
v => getGameInfo(getVersionInfo(v)?.game)?.id === gameId
).length
const total = serverVersionList.versions.filter(
v => getGameInfo(v?.game)?.id === gameId
).length
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`
}
function closePopup () {
if (popupMode == 0 && selectedGame && pathname === '/') {
setSelectedGame(null)
setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else if (popupMode == 4) {
setPopupMode(3)
} else {
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
} }
} }
const pathname = usePathname()
useEffect(() => { useEffect(() => {
let unlistenProgress: (() => void) | null = null let unlistenProgress: (() => void) | null = null
let unlistenUninstalled: (() => void) | null = null
listen<string>('download-progress', event => { listen<string>('download-progress', event => {
const [versionName, progStr, totalSizeStr, speedStr, etaSecsStr] = const [versionName, progStr, totalSizeStr, speedStr, etaSecsStr] =
@@ -142,30 +220,8 @@ export default function RootLayout ({
}) })
}).then(f => (unlistenProgress = f)) }).then(f => (unlistenProgress = f))
listen<string>('version-uninstalled', event => {
const versionName = event.payload
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = prev.list.filter(v => v !== versionName)
const updatedTimestamps = Object.fromEntries(
Object.entries(prev.timestamps).filter(([k]) => k !== versionName)
)
const updatedConfig = {
...prev,
list: updatedList,
timestamps: updatedTimestamps
}
writeVersionsConfig(updatedConfig)
setManagingVersion(null)
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
return updatedConfig
})
}).then(f => (unlistenUninstalled = f))
return () => { return () => {
unlistenProgress?.() unlistenProgress?.()
unlistenUninstalled?.()
} }
}, []) }, [])
@@ -218,47 +274,6 @@ export default function RootLayout ({
return () => document.removeEventListener('contextmenu', handler) return () => document.removeEventListener('contextmenu', handler)
}, []) }, [])
function getSpecialVersionsList (game?: number): GameVersion[] {
if (!normalConfig || !serverVersionList) return []
return serverVersionList.versions
.filter(v => !downloadedVersionsConfig?.list.includes(v.id))
.filter(v => {
if (game && v.game != game) return false
return true
})
.sort((a, b) => {
if (b.game !== a.game) return a.game - b.game
return 0
})
}
function getVersionInfo (id: string | undefined): GameVersion | undefined {
if (!id) return undefined
return serverVersionList?.versions.find(v => v.id === id)
}
function getVersionGame (game: number | undefined): Game | undefined {
if (!game) return undefined
return serverVersionList?.games.find(g => g.id === game)
}
function getListOfGames (): Game[] {
if (!downloadedVersionsConfig?.list) return []
const gamesMap = new Map<number, Game>()
downloadedVersionsConfig.list.forEach(i => {
const version = getVersionInfo(i)
if (!version) return
const game = getVersionGame(version.game)
if (!game) return
gamesMap.set(game.id, game)
})
return Array.from(gamesMap.values())
}
async function downloadVersions (): Promise<void> { async function downloadVersions (): Promise<void> {
const list = selectedVersionList const list = selectedVersionList
setSelectedVersionList([]) setSelectedVersionList([])
@@ -278,7 +293,7 @@ export default function RootLayout ({
) )
return return
} }
const gameInfo = getVersionGame(info.game) const gameInfo = getGameInfo(info.game)
if (!gameInfo) { if (!gameInfo) {
setDownloadProgress(prev => setDownloadProgress(prev =>
prev.filter(d => d.version !== download.version) prev.filter(d => d.version !== download.version)
@@ -332,37 +347,6 @@ export default function RootLayout ({
await notifyUser('Downloads Finished', 'All downloads have finished.') await notifyUser('Downloads Finished', 'All downloads have finished.')
} }
function getVersionsAmountData (gameId: number): {
installed: number
total: number
} | null {
if (!downloadedVersionsConfig || !serverVersionList) return null
const installed = downloadedVersionsConfig.list.filter(
v => getVersionGame(getVersionInfo(v)?.game)?.id === gameId
).length
const total = serverVersionList.versions.filter(
v => getVersionGame(v?.game)?.id === gameId
).length
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}>
@@ -376,6 +360,8 @@ export default function RootLayout ({
? 'blue-theme' ? 'blue-theme'
: normalConfig?.settings.theme === 4 : normalConfig?.settings.theme === 4
? 'purple-theme' ? 'purple-theme'
: normalConfig?.settings.theme === 5
? 'mint-theme'
: 'dark-theme' : 'dark-theme'
} }
> >
@@ -421,7 +407,7 @@ export default function RootLayout ({
managingVersion, managingVersion,
setManagingVersion, setManagingVersion,
getVersionInfo, getVersionInfo,
getVersionGame, getGameInfo,
getListOfGames, getListOfGames,
setSelectedGame, setSelectedGame,
getVersionsAmountData, getVersionsAmountData,
@@ -432,18 +418,7 @@ export default function RootLayout ({
<div <div
tabIndex={0} tabIndex={0}
onKeyDown={e => { onKeyDown={e => {
if (showPopup && e.key === 'Escape') { if (showPopup && e.key === 'Escape') closePopup()
if (popupMode == 0 && selectedGame) {
setSelectedGame(null)
setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0)
} else {
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
}
}
}} }}
> >
<Sidebar /> <Sidebar />
@@ -459,34 +434,31 @@ export default function RootLayout ({
{showPopup && ( {showPopup && (
<div <div
className={`popup-overlay ${fadeOut ? 'fade-out' : ''}`} className={`popup-overlay ${fadeOut ? 'fade-out' : ''}`}
onClick={handleOverlayClick} onClick={(e: React.MouseEvent<HTMLDivElement>) => {
> if (e.target === e.currentTarget) {
<div className='popup-box'> if (viewingInfoFromDownloads) {
<button
className='close-button'
onClick={() => {
if (
popupMode == 0 &&
selectedGame &&
pathname === '/'
) {
setSelectedGame(null)
setSelectedVersionList([])
} else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false)
setPopupMode(0) setPopupMode(0)
} else { setViewingInfoFromDownloads(false)
setManagingVersion(null)
setSelectedGame(null)
}
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
} }
}} }}
>
<div className='popup-box'>
<button
className='close-button btntheme1'
onClick={() => closePopup()}
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={ icon={
(popupMode == 0 && (popupMode == 0 &&
selectedGame && selectedGame &&
pathname === '/') || pathname === '/') ||
viewingInfoFromDownloads viewingInfoFromDownloads ||
popupMode == 4
? faChevronLeft ? faChevronLeft
: faXmark : faXmark
} }
@@ -505,16 +477,16 @@ export default function RootLayout ({
<p <p
className={`text-2xl truncate ${ className={`text-2xl truncate ${
selectedVersionList.includes(v.id) selectedVersionList.includes(v.id)
? 'max-w-80.5' ? 'max-w-84.5'
: 'max-w-87.5' : 'max-w-91.5'
}`} }`}
> >
{getVersionGame(v.game)?.name} v {getGameInfo(v.game)?.name} v
{v.versionName} {v.versionName}
</p> </p>
</div> </div>
<button <button
className='button btntheme3 right-22 bottom-1.5' className='button btntheme3 right-20.75 bottom-1.75'
onClick={() => { onClick={() => {
setSelectedVersionList(prev => setSelectedVersionList(prev =>
prev.includes(v.id) prev.includes(v.id)
@@ -540,7 +512,7 @@ export default function RootLayout ({
)} )}
</button> </button>
<button <button
className='button btntheme3 right-1.5 bottom-1.5' className='button btntheme3 right-1.5 bottom-1.75'
onClick={() => { onClick={() => {
setManagingVersion(v.id) setManagingVersion(v.id)
setViewingInfoFromDownloads(true) setViewingInfoFromDownloads(true)
@@ -642,21 +614,15 @@ export default function RootLayout ({
<> <>
<p className='text-xl text-center'>Downloads</p> <p className='text-xl text-center'>Downloads</p>
<div className='popup-content'> <div className='popup-content'>
{downloadProgress.length === 0 ? ( {downloadProgress.map((v, i) => (
<p className='text-center mt-6'>
No more downloads!
</p>
) : (
downloadProgress.map((v, i) => (
<div <div
key={i} key={i}
className='popup-entry flex flex-col justify-between' className='popup-entry flex flex-col justify-between'
> >
<p className='text-2xl text-center'> <p className='text-2xl text-center'>
{ {
getVersionGame( getGameInfo(getVersionInfo(v.version)?.game)
getVersionInfo(v.version)?.game ?.name
)?.name
}{' '} }{' '}
v{getVersionInfo(v.version)?.versionName} v{getVersionInfo(v.version)?.versionName}
</p> </p>
@@ -708,15 +674,14 @@ export default function RootLayout ({
})}{' '} })}{' '}
of{' '} of{' '}
{prettyBytes( {prettyBytes(
getVersionInfo(v.version)?.size ?? getVersionInfo(v.version)?.size ?? 0,
0,
{ {
minimumFractionDigits: 1, minimumFractionDigits: 1,
maximumFractionDigits: 1 maximumFractionDigits: 1
} }
)}{' '} )}{' '}
(ETA: {formatEtaSmart(v.etaSecs)}{' '} (ETA: {formatEtaSmart(v.etaSecs)} &bull;
&bull; Speed:{' '} Speed:{' '}
{prettyBytes(v.speed, { {prettyBytes(v.speed, {
minimumFractionDigits: 1, minimumFractionDigits: 1,
maximumFractionDigits: 1 maximumFractionDigits: 1
@@ -731,8 +696,7 @@ export default function RootLayout ({
)} )}
</div> </div>
</div> </div>
)) ))}
)}
</div> </div>
</> </>
) : popupMode === 2 ? ( ) : popupMode === 2 ? (
@@ -741,7 +705,7 @@ export default function RootLayout ({
<p className='text-xl text-center'> <p className='text-xl text-center'>
Manage{' '} Manage{' '}
{ {
getVersionGame( getGameInfo(
getVersionInfo(managingVersion)?.game getVersionInfo(managingVersion)?.game
)?.name )?.name
}{' '} }{' '}
@@ -751,11 +715,42 @@ export default function RootLayout ({
<button <button
className='button btntheme2' className='button btntheme2'
disabled={downloadProgress.length != 0} disabled={downloadProgress.length != 0}
onClick={() => onClick={async () => {
invoke('uninstall_version', { closePopup()
name: managingVersion
}) setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = prev.list.filter(
v => v !== managingVersion
)
const updatedTimestamps =
Object.fromEntries(
Object.entries(prev.timestamps).filter(
([k]) => k !== managingVersion
)
)
const updatedConfig = {
...prev,
list: updatedList,
timestamps: updatedTimestamps
} }
writeVersionsConfig(updatedConfig)
setManagingVersion(null)
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
}}
title='Click to uninstall this game. This will NOT remove any progress or any save files.' title='Click to uninstall this game. This will NOT remove any progress or any save files.'
> >
Uninstall Uninstall
@@ -786,6 +781,14 @@ export default function RootLayout ({
No version selected No version selected
</p> </p>
) )
) : popupMode === 4 ? (
managingVersion && downloadedVersionsConfig ? (
<VersionChangelog />
) : (
<p className='text-xl text-center'>
No version selected
</p>
)
) : null} ) : null}
{popupMode == 0 && {popupMode == 0 &&
selectedGame && selectedGame &&
@@ -798,11 +801,16 @@ export default function RootLayout ({
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
downloadVersions() downloadVersions()
}} }}
title={`Click to download ${ disabled={downloadProgress.length != 0}
title={
downloadProgress.length != 0
? "You cannot download the versions as you have another download that hasn't completed."
: `Click to download ${
selectedVersionList.length selectedVersionList.length
} version${ } version${
selectedVersionList.length == 1 ? '' : 's' selectedVersionList.length == 1 ? '' : 's'
} of ${getVersionGame(selectedGame)?.name}`} } of ${getGameInfo(selectedGame)?.name}`
}
> >
Download {selectedVersionList.length} version Download {selectedVersionList.length} version
{selectedVersionList.length == 1 ? '' : 's'} {selectedVersionList.length == 1 ? '' : 's'}
+11 -28
View File
@@ -15,7 +15,6 @@ import { useRouter } from 'next/navigation'
export default function Installs () { export default function Installs () {
const { const {
downloadProgress,
showPopup, showPopup,
setShowPopup, setShowPopup,
setPopupMode, setPopupMode,
@@ -29,7 +28,6 @@ export default function Installs () {
} = useGlobal() } = useGlobal()
const router = useRouter() const router = useRouter()
const [hoveredIds, setHoveredIds] = useState<number[]>([])
useEffect(() => { useEffect(() => {
if (!showPopup) return if (!showPopup) return
@@ -41,14 +39,13 @@ export default function Installs () {
<div className='flex justify-between items-center mb-4'> <div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Games</p> <p className='text-3xl'>Games</p>
<button <button
className='button btntheme1 text-3xl' className='button btntheme1'
onClick={() => { onClick={() => {
setSelectedGame(null) setSelectedGame(null)
setPopupMode(0) setPopupMode(0)
setShowPopup(true) setShowPopup(true)
setFadeOut(false) setFadeOut(false)
}} }}
disabled={downloadProgress.length != 0}
title='Click to download more games.' title='Click to download more games.'
> >
Download game Download game
@@ -84,18 +81,13 @@ export default function Installs () {
if (normalConfig?.settings.useLegacyInteractButtons) return if (normalConfig?.settings.useLegacyInteractButtons) return
router.push('/game?id=' + i.id) router.push('/game?id=' + i.id)
}} }}
onMouseEnter={() => setHoveredIds(prev => [...prev, i.id])}
onMouseLeave={() =>
setHoveredIds(prev => prev.filter(e => e !== i.id))
}
> >
<div className='flex flex-col'> <div className='h-18 w-screen relative'>
<p className='text-2xl'>{i.name}</p> <p className='text-2xl'>{i.name}</p>
<div className='flex gap-2 mt-2'>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div <div
className={`entry-info-item ${ className='entry-info-item'
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
title='The amount of versions installed of this game in installed/installable format.' title='The amount of versions installed of this game in installed/installable format.'
> >
<p> <p>
@@ -108,9 +100,7 @@ export default function Installs () {
</p> </p>
</div> </div>
<div <div
className={`entry-info-item ${ className='entry-info-item'
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
hidden={!i.official} hidden={!i.official}
title='This game is official.' title='This game is official.'
> >
@@ -118,9 +108,7 @@ export default function Installs () {
<p>Official</p> <p>Official</p>
</div> </div>
<div <div
className={`entry-info-item ${ className='entry-info-item'
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
hidden={i.official} hidden={i.official}
title={ title={
i.verified i.verified
@@ -135,17 +123,12 @@ export default function Installs () {
<p>{i.verified ? 'Verified' : 'Unverified'}</p> <p>{i.verified ? 'Verified' : 'Unverified'}</p>
</div> </div>
</div> </div>
</div>
<div <Link
className='flex flex-row items-center gap-2 mt-auto' className='button absolute right-0 bottom-0'
href={'/game?id=' + i.id}
hidden={!normalConfig?.settings.useLegacyInteractButtons} hidden={!normalConfig?.settings.useLegacyInteractButtons}
title='Click to view game installs' title='Click to view game installs'
>
<Link
className={`button ${
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
href={'/game?id=' + i.id}
> >
Installs Installs
</Link> </Link>
+3 -2
View File
@@ -90,7 +90,7 @@ export default function Settings () {
title="This setting will make it so when you are on a page like this, the games won't disappear." title="This setting will make it so when you are on a page like this, the games won't disappear."
/> />
<Setting <Setting
label='Show Installs/Launch Buttons' label='Show Installs/Launch/Manage Buttons'
value={useLegacyInteractButtons} value={useLegacyInteractButtons}
onChange={async () => { onChange={async () => {
while (normalConfig != null) { while (normalConfig != null) {
@@ -112,7 +112,7 @@ export default function Settings () {
break break
} }
}} }}
title='Enable the legacy method of using the installs/launch buttons. In the future this setting may be removed so try and get used to the new method.' title='Enable the legacy method of using the installs/launch/manage buttons. In the future this setting may be removed so try and get used to the new method.'
/> />
<div title='The theme you want the launcher to use.'> <div title='The theme you want the launcher to use.'>
<label className='text-lg'>Theme:</label> <label className='text-lg'>Theme:</label>
@@ -146,6 +146,7 @@ export default function Settings () {
<option value={2}>Green</option> <option value={2}>Green</option>
<option value={3}>Blue</option> <option value={3}>Blue</option>
<option value={4}>Purple</option> <option value={4}>Purple</option>
<option value={5}>Mint</option>
</select> </select>
</div> </div>
</div> </div>
+1
View File
@@ -8,4 +8,5 @@ export interface GameVersion {
sha512sum: string sha512sum: string
size: number size: number
place: number place: number
changelog: string
} }