Compare commits

...
16 Commits
41 changed files with 529 additions and 526 deletions

No files matched your search

+17 -13
View File
@@ -296,25 +296,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.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/type-utils": "8.56.0", "@typescript-eslint/utils": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.55.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.55.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.0", "@typescript-eslint/types": "^8.56.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0" } }, "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0" } }, "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.55.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/utils": "8.56.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.55.0", "", {}, "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.56.0", "", {}, "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.55.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.55.0", "@typescript-eslint/tsconfig-utils": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.0", "@typescript-eslint/tsconfig-utils": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.55.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA=="], "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg=="],
"@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=="],
@@ -414,7 +414,7 @@
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], "caniuse-lite": ["caniuse-lite@1.0.30001770", "", {}, "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw=="],
"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=="],
@@ -742,6 +742,8 @@
"next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
"node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="],
"node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
@@ -900,7 +902,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.55.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.55.0", "@typescript-eslint/parser": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw=="], "typescript-eslint": ["typescript-eslint@8.56.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.0", "@typescript-eslint/parser": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/utils": "8.56.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg=="],
"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=="],
@@ -954,6 +956,8 @@
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
"eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], "eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
@@ -962,7 +966,7 @@
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], "eslint-plugin-react/resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+6 -6
View File
@@ -1,7 +1,7 @@
{ {
"name": "lncvrt-games-launcher", "name": "lncvrt-games-launcher",
"private": true, "private": true,
"version": "1.6.0", "version": "1.6.3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -30,17 +30,17 @@
"react-dom": "19.2.4" "react-dom": "19.2.4"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "3.3.3", "@eslint/eslintrc": "3.3.4",
"@tailwindcss/postcss": "4.1.18", "@tailwindcss/postcss": "4.2.1",
"@tauri-apps/cli": "2.10.0", "@tauri-apps/cli": "2.10.0",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/node": "25.2.3", "@types/node": "25.3.2",
"@types/react": "19.2.14", "@types/react": "19.2.14",
"@types/react-dom": "19.2.3", "@types/react-dom": "19.2.3",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"eslint": "9.39.2", "eslint": "10.0.2",
"eslint-config-next": "16.1.6", "eslint-config-next": "16.1.6",
"tailwindcss": "4.1.18", "tailwindcss": "4.2.1",
"typescript": "5.9.3" "typescript": "5.9.3"
} }
} }
+5 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "lncvrt-games-launcher" name = "lncvrt-games-launcher"
version = "1.6.0" version = "1.6.3"
authors = ["Lncvrt"] authors = ["Lncvrt"]
edition = "2024" edition = "2024"
@@ -19,16 +19,17 @@ serde_json = "1.0.149"
tauri-plugin-os = "2.3.2" tauri-plugin-os = "2.3.2"
reqwest = { version = "0.13.2", default-features = false, features = ["stream", "rustls"] } reqwest = { version = "0.13.2", default-features = false, features = ["stream", "rustls"] }
tokio = "1.49.0" tokio = "1.49.0"
futures-util = { version = "0.3.31", features = ["io"] } futures-util = { version = "0.3.32", features = ["io"] }
tauri-plugin-decorum = "1.1.1" tauri-plugin-decorum = "1.1.1"
tauri-plugin-fs = "2.4.5" tauri-plugin-fs = "2.4.5"
zip = "7.4.0" zip = "8.1.0"
libc = "0.2.182" libc = "0.2.182"
tauri-plugin-dialog = "2.6.0" tauri-plugin-dialog = "2.6.0"
tauri-plugin-notification = "2.3.3" tauri-plugin-notification = "2.3.3"
sysinfo = "0.38.1" sysinfo = "0.38.2"
sha2 = "0.10.9" sha2 = "0.10.9"
tauri-plugin-clipboard-manager = "2.3.2" tauri-plugin-clipboard-manager = "2.3.2"
tauri-plugin-prevent-default = "4.0.4"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
shlex = "1.3.0" shlex = "1.3.0"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 248 KiB

+81 -63
View File
@@ -13,6 +13,7 @@ use std::{
use sysinfo::System; use sysinfo::System;
use tauri::{AppHandle, Emitter, Manager}; use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_os::platform; use tauri_plugin_os::platform;
use tauri_plugin_prevent_default::Flags;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
use tokio::{io::AsyncWriteExt, time::timeout}; use tokio::{io::AsyncWriteExt, time::timeout};
use zip::ZipArchive; use zip::ZipArchive;
@@ -26,11 +27,10 @@ use std::os::unix::fs::PermissionsExt;
#[allow(unused)] #[allow(unused)]
fn is_running_by_path(path: &Path) -> bool { fn is_running_by_path(path: &Path) -> bool {
let sys = System::new_all(); let sys = System::new_all();
let target = path.canonicalize().ok(); let target = match path.canonicalize() {
if target.is_none() { Ok(t) => t,
return false; Err(_) => return false,
} };
let target = target.unwrap();
sys.processes().values().any(|proc| { sys.processes().values().any(|proc| {
proc.exe() proc.exe()
.and_then(|exe| exe.canonicalize().ok()) .and_then(|exe| exe.canonicalize().ok())
@@ -94,12 +94,10 @@ async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
#[tauri::command] #[tauri::command]
fn folder_size(app: AppHandle, version: String) -> String { fn folder_size(app: AppHandle, version: String) -> String {
let path = app let path = match app.path().app_local_data_dir() {
.path() Ok(p) => p.join("game").join(&version),
.app_local_data_dir() Err(_) => return "-1".to_string(),
.unwrap() };
.join("game")
.join(&version);
fn inner(path: &Path) -> u64 { fn inner(path: &Path) -> u64 {
let mut size = 0; let mut size = 0;
if let Ok(entries) = fs::read_dir(path) { if let Ok(entries) = fs::read_dir(path) {
@@ -141,12 +139,19 @@ async fn download(
} }
}; };
let total_size = resp.content_length().unwrap_or(0); let total_size = resp.content_length().unwrap_or(0);
let _ = app.emit("download-size", format!("{}:{}", &name, &total_size));
let mut downloaded: u64 = 0; let mut downloaded: u64 = 0;
let mut stream = resp.bytes_stream(); let mut stream = resp.bytes_stream();
let downloads_path = app.path().app_local_data_dir().unwrap().join("downloads"); let downloads_path = match app.path().app_local_data_dir() {
let game_path = app.path().app_local_data_dir().unwrap().join("game"); Ok(p) => p.join("downloads"),
Err(_) => return "-1".to_string(),
};
let game_path = match app.path().app_local_data_dir() {
Ok(p) => p.join("game"),
Err(_) => return "-1".to_string(),
};
let download_part_path = downloads_path.join(format!("{}.part", name)); let download_part_path = downloads_path.join(format!("{}.part", name));
let download_zip_path = downloads_path.join(format!("{}.zip", name)); let download_zip_path = downloads_path.join(format!("{}.zip", name));
@@ -160,9 +165,10 @@ async fn download(
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.clone()) let mut file = match tokio::fs::File::create(download_part_path.clone()).await {
.await Ok(f) => f,
.unwrap(); Err(_) => return "-1".to_string(),
};
let start = Instant::now(); let start = Instant::now();
@@ -194,32 +200,34 @@ async fn download(
0.0 0.0
}; };
app.emit( let _ = app.emit(
"download-progress", "download-progress",
format!( format!(
"{}:{:.8}:{}:{}:{:.2}", "{}:{:.8}:{}:{}:{:.2}",
&name, progress, downloaded, speed, eta_secs &name, progress, downloaded, speed, eta_secs
), ),
) );
.unwrap();
} }
if total_size > 0 && downloaded < total_size { if total_size > 0 && downloaded < total_size {
return "-1".to_string(); return "-1".to_string();
} }
app.emit("download-hash-checking", format!("{}", &name)) let _ = app.emit("download-hash-checking", format!("{}", &name));
.unwrap();
let download_hash = { let download_hash = {
let mut file = tokio::fs::File::open(download_part_path.clone()) let mut file = match tokio::fs::File::open(download_part_path.clone()).await {
.await Ok(f) => f,
.unwrap(); Err(_) => return "-1".to_string(),
};
let mut hasher = Sha512::new(); let mut hasher = Sha512::new();
{ {
let mut buffer = [0; 8192]; let mut buffer = [0; 8192];
loop { loop {
let bytes_read = file.read(&mut buffer).await.unwrap(); let bytes_read = match file.read(&mut buffer).await {
Ok(n) => n,
Err(_) => return "-1".to_string(),
};
if bytes_read == 0 { if bytes_read == 0 {
break; break;
} }
@@ -231,21 +239,16 @@ async fn download(
}; };
if hash != download_hash { if hash != download_hash {
tokio::fs::remove_file(download_part_path.clone()) let _ = tokio::fs::remove_file(download_part_path.clone()).await;
.await
.unwrap();
return "-1".to_string(); return "-1".to_string();
} }
app.emit("download-finishing", format!("{}", &name)) let _ = app.emit("download-finishing", format!("{}", &name));
.unwrap();
tokio::fs::rename(download_part_path.clone(), download_zip_path.clone()) if let Err(_) = tokio::fs::rename(download_part_path.clone(), download_zip_path.clone()).await {
.await return "-1".to_string();
.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()) let _ = tokio::fs::remove_file(download_zip_path.clone()).await;
.await
.unwrap();
if unzip_res == "-1" { if unzip_res == "-1" {
return "-1".to_string(); return "-1".to_string();
} }
@@ -263,12 +266,10 @@ fn launch_game(
use_wine: bool, use_wine: bool,
wine_command: String, wine_command: String,
) { ) {
let game_folder = app let game_folder = match app.path().app_local_data_dir() {
.path() Ok(p) => p.join("game").join(&name),
.app_local_data_dir() Err(_) => return,
.unwrap() };
.join("game")
.join(&name);
if !game_folder.exists() { if !game_folder.exists() {
return; return;
} }
@@ -278,8 +279,8 @@ fn launch_game(
//if already running on macos, it'll auto take the user to that proccess //if already running on macos, it'll auto take the user to that proccess
#[cfg(any(target_os = "windows", target_os = "linux"))] #[cfg(any(target_os = "windows", target_os = "linux"))]
{ {
use tauri_plugin_dialog::MessageDialogKind;
use tauri_plugin_dialog::DialogExt; use tauri_plugin_dialog::DialogExt;
use tauri_plugin_dialog::MessageDialogKind;
if !use_wine && is_running_by_path(&exe_path) { if !use_wine && is_running_by_path(&exe_path) {
app.dialog() app.dialog()
@@ -300,28 +301,31 @@ fn launch_game(
let quoted_path = format!("\"{}\"", exe_path.to_string_lossy()); let quoted_path = format!("\"{}\"", exe_path.to_string_lossy());
let cmd = wine_command.replace("%path%", &quoted_path); let cmd = wine_command.replace("%path%", &quoted_path);
Command::new("bash") if let Err(_) = Command::new("bash")
.arg("-c") .arg("-c")
.arg(cmd) .arg(cmd)
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn() {
.unwrap(); eprintln!("Failed to launch game with Wine");
}
return; return;
} }
} }
if platform() == "macos" { if platform() == "macos" {
Command::new("open") if let Err(_) = Command::new("open")
.arg(&executable) .arg(&executable)
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn() {
.unwrap(); eprintln!("Failed to launch game on macOS");
}
} else { } else {
Command::new(&exe_path) if let Err(_) = Command::new(&exe_path)
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn() {
.unwrap(); eprintln!("Failed to launch game");
}
} }
} }
@@ -329,30 +333,44 @@ fn launch_game(
pub fn run() { pub fn run() {
#[allow(unused_variables)] #[allow(unused_variables)]
tauri::Builder::default() tauri::Builder::default()
.plugin(
tauri_plugin_prevent_default::Builder::new()
.with_flags(
Flags::FIND
| Flags::CARET_BROWSING
| Flags::DEV_TOOLS
| Flags::DOWNLOADS
| Flags::FOCUS_MOVE
| Flags::RELOAD
| Flags::SOURCE
| Flags::OPEN
| Flags::PRINT
| Flags::CONTEXT_MENU,
)
.build(),
)
.plugin(tauri_plugin_window_state::Builder::new().build()) .plugin(tauri_plugin_window_state::Builder::new().build())
.plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
let _ = app if let Some(window) = app.get_webview_window("main") {
.get_webview_window("main") let _ = window.set_focus();
.expect("no main window") }
.set_focus();
})) }))
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_decorum::init()) .plugin(tauri_plugin_decorum::init())
.plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![download, launch_game, folder_size])
download,
launch_game,
folder_size
])
.setup(|app| { .setup(|app| {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let main_window = app.get_webview_window("main").unwrap(); if let Some(main_window) = app.get_webview_window("main") {
main_window.create_overlay_titlebar().unwrap(); if let Err(e) = main_window.create_overlay_titlebar() {
eprintln!("Failed to create overlay titlebar: {:?}", e);
}
}
} }
Ok(()) Ok(())
}) })
+1 -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.6.0", "version": "1.6.3",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev --webpack", "beforeDevCommand": "next dev --webpack",
+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.6.0", "version": "1.6.3",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev --webpack", "beforeDevCommand": "next dev --webpack",
+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.6.0", "version": "1.6.3",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev --webpack", "beforeDevCommand": "next dev --webpack",
+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.6.0", "version": "1.6.3",
"identifier": "xyz.lncvrt.games-launcher", "identifier": "xyz.lncvrt.games-launcher",
"build": { "build": {
"beforeDevCommand": "next dev --webpack", "beforeDevCommand": "next dev --webpack",
+27 -9
View File
@@ -7,6 +7,7 @@ import { GetIconForUser } from '@/lib/BerryDash'
import Image from 'next/image' import Image from 'next/image'
import './styles.css' import './styles.css'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os'
interface BaseEntry { interface BaseEntry {
id: number id: number
@@ -175,11 +176,17 @@ export default function BerryDashLeaderboards () {
</button> </button>
</div> </div>
</div> </div>
<div className='box'> <div
className={`box ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{selected == -1 ? ( {selected == -1 ? (
<> <>
<p className='text-center mt-4 text-xl'>Select a Leaderboard</p> <p className='text-center mt-2 text-xl'>Select a Leaderboard</p>
<div className='flex flex-col gap-2 mt-4 items-center justify-center'> <div className='flex flex-col gap-2 mt-2 items-center justify-center'>
<button <button
className='leaderboard-button' className='leaderboard-button'
onClick={() => setSelected(0)} onClick={() => setSelected(0)}
@@ -228,7 +235,13 @@ export default function BerryDashLeaderboards () {
<> <>
<div <div
className={`flex flex-col gap-2 overflow-y-auto ${ className={`flex flex-col gap-2 overflow-y-auto ${
selected == 1 ? 'h-[calc(100vh-128px)]' : 'h-[calc(100vh-96px)]' selected == 1
? platform() == 'windows'
? 'h-[calc(100vh-168px)]'
: 'h-[calc(100vh-136px)]'
: platform() == 'windows'
? 'h-[calc(100vh-128px)]'
: 'h-[calc(100vh-96px)]'
} px-1`} } px-1`}
> >
{entries.map((item, index) => { {entries.map((item, index) => {
@@ -244,18 +257,23 @@ export default function BerryDashLeaderboards () {
<Image <Image
src={ src={
!item.customIcon !item.customIcon
? `https://games-r2.lncvrt.xyz/game-assets/berrydash/icons/bird_${ ? `https://games.lncvrt.xyz/api/berrydash/render-icon?icon=${
item.icon === 1 item.icon === 1
? GetIconForUser(item.id) ? GetIconForUser(item.id)
: item.icon : item.icon
}.png` }&overlay=${item.overlay}&birdR=${
item.birdColor[0]
}&birdG=${item.birdColor[1]}&birdB=${
item.birdColor[2]
}&overlayR=${item.overlayColor[0]}&overlayG=${
item.overlayColor[1]
}&overlayB=${item.overlayColor[2]}`
: `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true` : `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true`
} }
className='pointer-events-none'
width={48} width={48}
height={48} height={48}
alt='' alt=''
unoptimized className='pointer-events-none scale-x-[-1]'
/> />
<p> <p>
{item.username} (#{index + 1}) {item.username} (#{index + 1})
@@ -288,7 +306,7 @@ export default function BerryDashLeaderboards () {
<select <select
value={selectedBerryOption} value={selectedBerryOption}
onChange={e => setSelectedBerryOption(Number(e.target.value))} onChange={e => setSelectedBerryOption(Number(e.target.value))}
className='leaderboard-select mt-2' className='leaderboard-select mt-0.75'
> >
<option value='0'>Normal Berry</option> <option value='0'>Normal Berry</option>
<option value='1'>Poison Berry</option> <option value='1'>Poison Berry</option>
@@ -1,12 +1,12 @@
@import "tailwindcss"; @import "tailwindcss";
.box { .box {
@apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1 h-[calc(100vh-84px)]; @apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1;
} }
.leaderboard-button, .leaderboard-button,
.leaderboard-select { .leaderboard-select {
@apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all duration-200; @apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all duration-200 cursor-pointer;
} }
.leaderboard-entry { .leaderboard-entry {
+60 -100
View File
@@ -8,7 +8,7 @@ import { useRouter, useSearchParams } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os' import { platform } from '@tauri-apps/plugin-os'
import { faWarning } from '@fortawesome/free-solid-svg-icons' import { faWarning } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ask, message } from '@tauri-apps/plugin-dialog' import { ask } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs' import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '@/lib/BazookaManager' import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util' import { openFolder } from '@/lib/Util'
@@ -113,7 +113,7 @@ export default function Installs () {
<div className='downloads-container'> <div className='downloads-container'>
<div <div
className={`downloads-scroll ${ className={`downloads-scroll ${
platform() === 'windows' platform() == 'windows'
? 'h-[calc(100vh-116px)]' ? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]' : 'h-[calc(100vh-84px)]'
}`} }`}
@@ -129,7 +129,7 @@ export default function Installs () {
if (!info) return false if (!info) return false
if ( if (
platform() === 'linux' && platform() == 'linux' &&
info.wine && info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded !normalConfig?.settings.useWineOnUnixWhenNeeded
) )
@@ -165,7 +165,7 @@ export default function Installs () {
const info = getVersionInfo(v) const info = getVersionInfo(v)
if (!info) return false if (!info) return false
if ( if (
platform() === 'linux' && platform() == 'linux' &&
info.wine && info.wine &&
!normalConfig?.settings !normalConfig?.settings
.useWineOnUnixWhenNeeded .useWineOnUnixWhenNeeded
@@ -200,7 +200,7 @@ export default function Installs () {
const info = getVersionInfo(v) const info = getVersionInfo(v)
if (!info) return false if (!info) return false
if ( if (
platform() === 'linux' && platform() == 'linux' &&
info.wine && info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded !normalConfig?.settings.useWineOnUnixWhenNeeded
) )
@@ -226,10 +226,60 @@ export default function Installs () {
entry entry
) )
) { ) {
await message( const answer = await ask(
"Can't launch game. Need revision update.", 'Before proceeding, if you do not want your installation directory wiped just yet, please backup the files to another directory. When you click "Yes", it will be wiped. Click "No" if you want to open the installation folder instead.',
{ title: 'Error launching game', kind: 'error' } {
title: 'Revision Update',
kind: 'warning'
}
) )
if (answer) {
const answer2 = await ask(
'Are you sure you want to update? If you did not read the last popup, please go back and read it.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (!answer2) return
//open downloads popup
setPopupMode(1)
setShowPopup(true)
setFadeOut(false)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(
([k]) => k !== entry
)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + entry, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + entry, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([entry])
downloadVersions([entry])
} else {
openFolder(entry)
}
return return
} }
const verInfo = getVersionInfo(entry) const verInfo = getVersionInfo(entry)
@@ -241,7 +291,7 @@ export default function Installs () {
executable: verInfo.executable, executable: verInfo.executable,
displayName: verInfo.displayName, displayName: verInfo.displayName,
useWine: !!( useWine: !!(
platform() === 'linux' && platform() == 'linux' &&
verInfo.wine && verInfo.wine &&
normalConfig?.settings.useWineOnUnixWhenNeeded normalConfig?.settings.useWineOnUnixWhenNeeded
), ),
@@ -279,8 +329,7 @@ export default function Installs () {
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.' title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
hidden={ hidden={
!( !(
platform() === 'linux' && platform() == 'linux' && getVersionInfo(entry)?.wine
getVersionInfo(entry)?.wine
) || ) ||
needsRevisionUpdate( needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision, getVersionInfo(entry)?.lastRevision,
@@ -306,95 +355,6 @@ export default function Installs () {
<p>Needs revision update!</p> <p>Needs revision update!</p>
</div> </div>
</div> </div>
<div className='flex gap-2 absolute right-0 bottom-0'>
<button
className='button'
onClick={e => {
e.stopPropagation()
setManagingVersion(entry)
setPopupMode(3)
setShowPopup(true)
setFadeOut(false)
}}
hidden={needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
entry
)}
title='Click to view version info'
>
View Info
</button>
<button
className='button'
onClick={async e => {
e.stopPropagation()
const answer = await ask(
'Before proceeding, if you do not want your installation directory wiped just yet, please backup the files to another directory. When you click "Yes", it will be wiped. Click "No" if you want to open the installation folder instead.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (answer) {
const answer2 = await ask(
'Are you sure you want to update? If you did not read the last popup, please go back and read it.',
{
title: 'Revision Update',
kind: 'warning'
}
)
if (!answer2) return
//open downloads popup
setPopupMode(1)
setShowPopup(true)
setFadeOut(false)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(
([k]) => k !== entry
)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + entry, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + entry, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([entry])
downloadVersions([entry])
} else {
openFolder(entry)
}
}}
hidden={
!needsRevisionUpdate(
getVersionInfo(entry)?.lastRevision,
entry
)
}
title={'Click to update the game'}
>
Update
</button>
</div>
</div> </div>
</div> </div>
)) ))
+91 -52
View File
@@ -32,11 +32,10 @@ import {
} from '@tauri-apps/plugin-notification' } from '@tauri-apps/plugin-notification'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs' import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import DownloadsPopup from '@/componets/popups/Downloads'
import VersionInfoPopup from '@/componets/popups/VersionInfo'
import ManagingVersionPopup from '@/componets/popups/ManageVersion'
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
import VersionsDownloadPopup from '@/componets/popups/VersionsDownload' import VersionsDownloadPopup from '@/componets/popups/VersionsDownload'
import GamesDownloadPopup from '@/componets/popups/GamesDownload'
import DownloadsPopup from '@/componets/popups/Downloads'
import VersionVersionPopup from '@/componets/popups/VersionVersion'
const roboto = Roboto({ const roboto = Roboto({
subsets: ['latin'] subsets: ['latin']
@@ -51,6 +50,7 @@ export default function RootLayout ({
const [loadingText, setLoadingText] = useState('Loading...') const [loadingText, setLoadingText] = useState('Loading...')
const [outdated, setOutdated] = useState(false) const [outdated, setOutdated] = useState(false)
const [version, setVersion] = useState<string | null>(null) const [version, setVersion] = useState<string | null>(null)
const [platformName, setPlatformName] = useState<string | null>(null)
const [serverVersionList, setServerVersionList] = const [serverVersionList, setServerVersionList] =
useState<null | ServerVersionsResponse>(null) useState<null | ServerVersionsResponse>(null)
@@ -89,7 +89,7 @@ export default function RootLayout ({
) )
.filter(v => { .filter(v => {
if ( if (
platform() === 'linux' && platformName == 'linux' &&
v.wine && v.wine &&
!normalConfig.settings.useWineOnUnixWhenNeeded !normalConfig.settings.useWineOnUnixWhenNeeded
) )
@@ -147,7 +147,7 @@ export default function RootLayout ({
if (!downloadedVersionsConfig || !serverVersionList) return null if (!downloadedVersionsConfig || !serverVersionList) return null
const allowWine = const allowWine =
platform() !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded platformName !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
const installed = Object.keys(downloadedVersionsConfig.list).filter(v => { const installed = Object.keys(downloadedVersionsConfig.list).filter(v => {
const info = getVersionInfo(v) const info = getVersionInfo(v)
@@ -172,8 +172,6 @@ export default function RootLayout ({
} else if (viewingInfoFromDownloads) { } else if (viewingInfoFromDownloads) {
setViewingInfoFromDownloads(false) setViewingInfoFromDownloads(false)
setPopupMode(0) setPopupMode(0)
} else if (popupMode == 4) {
setPopupMode(3)
} else { } else {
setFadeOut(true) setFadeOut(true)
setTimeout(() => setShowPopup(false), 200) setTimeout(() => setShowPopup(false), 200)
@@ -183,6 +181,21 @@ export default function RootLayout ({
useEffect(() => { useEffect(() => {
let unlistenProgress: (() => void) | null = null let unlistenProgress: (() => void) | null = null
listen<string>('download-size', event => {
const [displayName, sizeStr] = event.payload.split(':')
const size = Number(sizeStr)
setDownloadProgress(prev => {
const i = prev.findIndex(d => d.version === displayName)
if (i === -1) return prev
const copy = [...prev]
copy[i] = {
...copy[i],
size
}
return copy
})
}).then(f => (unlistenProgress = f))
listen<string>('download-progress', event => { listen<string>('download-progress', event => {
const [displayName, progStr, totalSizeStr, speedStr, etaSecsStr] = const [displayName, progStr, totalSizeStr, speedStr, etaSecsStr] =
event.payload.split(':') event.payload.split(':')
@@ -234,10 +247,10 @@ export default function RootLayout ({
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {
setPlatformName(platform())
const client = await app.getVersion() const client = await app.getVersion()
setVersion(client) setVersion(client)
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
setLoadingText('Checking latest version...')
try { try {
const response = await axios.get( const response = await axios.get(
'https://games.lncvrt.xyz/api/launcher/latest' 'https://games.lncvrt.xyz/api/launcher/latest'
@@ -251,7 +264,6 @@ export default function RootLayout ({
return return
} }
} }
setLoadingText('Downloading version list...')
try { try {
const res = await axios.get( const res = await axios.get(
`https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}` `https://games.lncvrt.xyz/api/launcher/versions?platform=${platform()}&arch=${arch()}`
@@ -261,7 +273,6 @@ export default function RootLayout ({
setLoadingText('Failed to download versions list.') setLoadingText('Failed to download versions list.')
return return
} }
setLoadingText('Loading configs...')
const normalConfig = await readNormalConfig() const normalConfig = await readNormalConfig()
const versionsConfig = await readVersionsConfig() const versionsConfig = await readVersionsConfig()
setDownloadedVersionsConfig(versionsConfig) setDownloadedVersionsConfig(versionsConfig)
@@ -274,13 +285,6 @@ export default function RootLayout ({
})() })()
}, []) }, [])
useEffect(() => {
if (process.env.NODE_ENV !== 'production') return
const handler = (e: MouseEvent) => e.preventDefault()
document.addEventListener('contextmenu', handler)
return () => document.removeEventListener('contextmenu', handler)
}, [])
const downloadVersions = useCallback( const downloadVersions = useCallback(
async (list: string[]): Promise<void> => { async (list: string[]): Promise<void> => {
if (list.length === 0) return if (list.length === 0) return
@@ -296,7 +300,18 @@ export default function RootLayout ({
const newDownloads = newVersions.map( const newDownloads = newVersions.map(
version => version =>
new DownloadProgress(version, 0, 0, false, true, false, false, 0, 0) new DownloadProgress(
version,
0,
0,
false,
true,
false,
false,
0,
0,
0
)
) )
setDownloadProgress(prev => [...prev, ...newDownloads]) setDownloadProgress(prev => [...prev, ...newDownloads])
@@ -333,17 +348,33 @@ export default function RootLayout ({
prev.map(d => (d.version === versionId ? { ...d, queued: false } : d)) prev.map(d => (d.version === versionId ? { ...d, queued: false } : d))
) )
try { const downloadInfoRequest = await axios.get(
await axios.get( 'https://games.lncvrt.xyz/api/launcher/download?gameId=' +
'https://games.lncvrt.xyz/api/launcher/download?id=' + info.id info.id +
'&downloadId=' +
info.download
)
if (!downloadInfoRequest.data.success) {
setDownloadProgress(prev =>
prev.map(d =>
d.version === versionId
? { ...d, queued: false, failed: true, progress: 0 }
: d
)
) )
} catch {} if (normalConfig?.settings.allowNotifications)
await notifyUser(
'Download Failed',
`The download for version ${info.displayName} has failed.`
)
return
}
const res = await invoke<string>('download', { const res = await invoke<string>('download', {
url: info.downloadUrl, url: downloadInfoRequest.data.data.url,
name: info.id, name: info.id,
executable: info.executable, executable: info.executable,
hash: info.sha512sum hash: downloadInfoRequest.data.data.hash
}) })
if (res === '1') { if (res === '1') {
@@ -453,26 +484,38 @@ export default function RootLayout ({
} }
> >
{loading ? ( {loading ? (
<div className='w-screen h-screen flex items-center justify-center'> <>
{outdated ? ( <div
<div className='text-center'> className='relative z-2 w-screen border-b border-b-(--col3) h-8.25 bg-(--col1)'
<p className='text-8xl mb-4'>Outdated Launcher!</p> hidden={platformName != 'windows'}
<p className='text-4xl mb-4'> />
Please update to the latest version to continue. <div
</p> className={`w-screen ${
<button platformName == 'windows'
className='button' ? 'h-[calc(100vh-64px)]'
onClick={() => : 'h-screen'
openUrl('https://games.lncvrt.xyz/berrydash/download') } flex items-center justify-center`}
} >
> {outdated ? (
Download latest version <div className='text-center'>
</button> <p className='text-8xl mb-4'>Outdated Launcher!</p>
</div> <p className='text-4xl mb-4'>
) : ( Please update to the latest version to continue.
<p className='text-7xl text-center'>{loadingText}</p> </p>
)} <button
</div> className='button'
onClick={() =>
openUrl('https://games.lncvrt.xyz/berrydash/download')
}
>
Download latest version
</button>
</div>
) : (
<p className='text-7xl text-center'>{loadingText}</p>
)}
</div>
</>
) : ( ) : (
<GlobalProvider <GlobalProvider
value={{ value={{
@@ -519,10 +562,8 @@ export default function RootLayout ({
> >
<Sidebar /> <Sidebar />
<div <div
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-(--col3) h-8.25 bg-(--col1)' className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-b-(--col3) h-8.25 bg-(--col1)'
style={{ hidden={platformName != 'windows'}
display: platform() === 'windows' ? 'block' : 'none'
}}
/> />
<div className='relative z-0'> <div className='relative z-0'>
<main className='ml-60'>{children}</main> <main className='ml-60'>{children}</main>
@@ -567,9 +608,7 @@ export default function RootLayout ({
) : popupMode === 1 ? ( ) : popupMode === 1 ? (
<DownloadsPopup /> <DownloadsPopup />
) : popupMode === 2 ? ( ) : popupMode === 2 ? (
<ManagingVersionPopup /> <VersionVersionPopup />
) : popupMode === 3 ? (
<VersionInfoPopup />
) : null} ) : null}
</div> </div>
</div> </div>
+1 -1
View File
@@ -54,7 +54,7 @@ export default function Installs () {
<div className='downloads-container'> <div className='downloads-container'>
<div <div
className={`downloads-scroll ${ className={`downloads-scroll ${
platform() === 'windows' platform() == 'windows'
? 'h-[calc(100vh-116px)]' ? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]' : 'h-[calc(100vh-84px)]'
}`} }`}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

-4
View File
@@ -13,10 +13,6 @@
@apply bg-(--col3) border-(--col4); @apply bg-(--col3) border-(--col4);
} }
.logo {
@apply flex items-center pl-2 h-10 w-60 pt-2;
}
.nav-links { .nav-links {
@apply flex flex-col px-4 space-y-1; @apply flex flex-col px-4 space-y-1;
} }
+5 -10
View File
@@ -59,15 +59,10 @@ export default function Sidebar () {
}} }}
></div> ></div>
<div <div
className='logo' className={`flex items-center h-10 w-60 ${
style={{ (platform() == 'windows' ? 'pl-1 pt-1' : 'pl-2 pt-2') +
marginTop: (platform() == 'macos' ? ' mt-7' : '')
platform() == 'windows' }`}
? '32px'
: platform() == 'macos'
? '28px'
: ''
}}
onMouseDown={e => { onMouseDown={e => {
if (platform() != 'macos') return if (platform() != 'macos') return
if (e.buttons === 1) { if (e.buttons === 1) {
@@ -142,7 +137,7 @@ export default function Sidebar () {
if (!info) return false if (!info) return false
if ( if (
platform() === 'linux' && platform() == 'linux' &&
info.wine && info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded !normalConfig?.settings.useWineOnUnixWhenNeeded
) )
+2 -2
View File
@@ -58,7 +58,7 @@ export default function DownloadsPopup () {
v.hash_checking ? 'text-blue-300' : 'text-green-300' v.hash_checking ? 'text-blue-300' : 'text-green-300'
} inline-block w-full text-center`} } inline-block w-full text-center`}
> >
{v.hash_checking ? 'Checking hash' : 'Finishing'} {v.hash_checking ? 'Verifying file integerty' : 'Finishing'}
... ...
</span> </span>
) : ( ) : (
@@ -70,7 +70,7 @@ export default function DownloadsPopup () {
maximumFractionDigits: 1 maximumFractionDigits: 1
})}{' '} })}{' '}
of{' '} of{' '}
{prettyBytes(getVersionInfo(v.version)?.size ?? 0, { {prettyBytes(v.size, {
minimumFractionDigits: 1, minimumFractionDigits: 1,
maximumFractionDigits: 1 maximumFractionDigits: 1
})}{' '} })}{' '}
-106
View File
@@ -1,106 +0,0 @@
import { useGlobal } from '@/app/GlobalProvider'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
export default function ManageVersionPopup () {
const {
getVersionInfo,
managingVersion,
closePopup,
setDownloadedVersionsConfig,
setManagingVersion,
downloadVersions,
setSelectedVersionList,
setPopupMode
} = useGlobal()
if (!managingVersion) return <></>
return (
<>
<p className='text-xl text-center'>
Manage {getVersionInfo(managingVersion)?.displayName}
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<button
className='button btntheme2'
onClick={async () => {
closePopup()
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
}}
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
>
Uninstall
</button>
<button
className='button btntheme2'
onClick={async () => {
//change popup to downloads
setManagingVersion(null)
setPopupMode(1)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([managingVersion])
downloadVersions([managingVersion])
}}
title="Click to reinstall this game. This will NOT remove any progress or any save files. This WILL uninstall any modifications to the game's executable files."
>
Reinstall
</button>
<button
className='button btntheme2'
onClick={async () => openFolder(managingVersion)}
title="Click to browse the game's files."
>
Open Folder
</button>
</div>
</>
)
}
-131
View File
@@ -1,131 +0,0 @@
'use client'
import {
faArrowUpRightFromSquare,
faCheck,
faCode,
faHardDrive,
faShieldHalved,
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useGlobal } from '@/app/GlobalProvider'
import { invoke } from '@tauri-apps/api/core'
import { useEffect, useState } from 'react'
import prettyBytes from 'pretty-bytes'
import { message } from '@tauri-apps/plugin-dialog'
export default function VersionInfoPopup () {
const {
getGameInfo,
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
viewingInfoFromDownloads
} = useGlobal()
const [versionSize, setVersionSize] = useState<number>(0)
useEffect(() => {
invoke<string>('folder_size', {
version: managingVersion
}).then(size => {
setVersionSize(Number(size))
})
}, [managingVersion, setVersionSize])
if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion)
const gameInfo = getGameInfo(versionInfo?.game)
return (
<>
<p className='text-xl text-center'>
Viewing info for {versionInfo?.displayName}
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads}
>
<p>
Installed{' '}
{new Intl.DateTimeFormat(undefined).format(
downloadedVersionsConfig.list[managingVersion] ?? 0
)}
</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={!versionInfo || versionInfo.releaseDate == 0}
>
<p>
Released{' '}
{new Intl.DateTimeFormat(undefined).format(
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
)}
</p>
</div>
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
<FontAwesomeIcon
icon={gameInfo?.verified ? faShieldHalved : faWarning}
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
/>
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={gameInfo?.developer == null}
>
<FontAwesomeIcon icon={faCode} color='lightgray' />
<p>Developer: {gameInfo?.developer}</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads || versionSize === null}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size on disk:{' '}
{versionSize > 0
? prettyBytes(versionSize, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
: 'N/A'}
</p>
</div>
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size when downloaded (zipped):{' '}
{versionInfo
? prettyBytes(versionInfo.size, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
: 'Loading...'}
</p>
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
if (!versionInfo) return
await message(atob(versionInfo.changelog), {
title: 'Changelog for ' + versionInfo.displayName,
kind: 'info'
})
}}
hidden={!versionInfo?.changelog}
>
<p>View Changelog</p>
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
</div>
</>
)
}
+209
View File
@@ -0,0 +1,209 @@
'use client'
import {
faArrowUpRightFromSquare,
faCheck,
faCode,
faHardDrive,
faShieldHalved,
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useGlobal } from '@/app/GlobalProvider'
import { invoke } from '@tauri-apps/api/core'
import { useEffect, useState } from 'react'
import prettyBytes from 'pretty-bytes'
import { message } from '@tauri-apps/plugin-dialog'
import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs'
import { writeVersionsConfig } from '@/lib/BazookaManager'
import { openFolder } from '@/lib/Util'
export default function VersionVersionPopup () {
const {
getGameInfo,
getVersionInfo,
managingVersion,
downloadedVersionsConfig,
viewingInfoFromDownloads,
setManagingVersion,
closePopup,
setDownloadedVersionsConfig,
setPopupMode,
setSelectedVersionList,
downloadVersions
} = useGlobal()
const [versionSize, setVersionSize] = useState<number>(0)
useEffect(() => {
if (viewingInfoFromDownloads) return
invoke<string>('folder_size', {
version: managingVersion
}).then(size => {
setVersionSize(Number(size))
})
}, [managingVersion, setVersionSize, viewingInfoFromDownloads])
if (!managingVersion || !downloadedVersionsConfig) return <></>
const versionInfo = getVersionInfo(managingVersion)
const gameInfo = getGameInfo(versionInfo?.game)
return (
<>
<p className='text-xl text-center'>Viewing {versionInfo?.displayName}</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads}
>
<p>
Installed{' '}
{new Intl.DateTimeFormat(undefined).format(
downloadedVersionsConfig.list[managingVersion] ?? 0
)}
</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={!versionInfo || versionInfo.releaseDate == 0}
>
<p>
Released{' '}
{new Intl.DateTimeFormat(undefined).format(
versionInfo?.releaseDate ? versionInfo.releaseDate * 1000 : 0
)}
</p>
</div>
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
<FontAwesomeIcon
icon={gameInfo?.verified ? faShieldHalved : faWarning}
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
/>
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={gameInfo?.developer == null}
>
<FontAwesomeIcon icon={faCode} color='lightgray' />
<p>Developer: {gameInfo?.developer}</p>
</div>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size on disk:{' '}
{versionSize && versionSize > 0
? prettyBytes(versionSize, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
: 'N/A'}
</p>
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
if (!versionInfo) return
await message(atob(versionInfo.changelog), {
title: 'Changelog for ' + versionInfo.displayName,
kind: 'info'
})
}}
hidden={!versionInfo?.changelog}
>
<p>View Changelog</p>
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => openFolder(managingVersion)}
title="Click to browse the game's files."
hidden={viewingInfoFromDownloads}
>
Open Folder
<FontAwesomeIcon icon={faArrowUpRightFromSquare} color='lightgray' />
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
closePopup()
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
}}
title='Click to uninstall this game. This will NOT remove any progress or any save files.'
hidden={viewingInfoFromDownloads}
>
Uninstall
</div>
<div
className='entry-info-item btntheme2'
onClick={async () => {
//change popup to downloads
setManagingVersion(null)
setPopupMode(1)
//uninstall
setDownloadedVersionsConfig(prev => {
if (!prev) return prev
const updatedList = Object.fromEntries(
Object.entries(prev.list).filter(([k]) => k !== managingVersion)
)
const updatedConfig = {
...prev,
list: updatedList
}
writeVersionsConfig(updatedConfig)
return updatedConfig
})
if (
await exists('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData
})
)
await remove('game/' + managingVersion, {
baseDir: BaseDirectory.AppLocalData,
recursive: true
})
//reinstall
setSelectedVersionList([managingVersion])
downloadVersions([managingVersion])
}}
title="Click to reinstall this game. This will NOT remove any progress or any save files. This WILL uninstall any modifications to the game's executable files."
hidden={viewingInfoFromDownloads}
>
Reinstall
</div>
</div>
</>
)
}
+6 -14
View File
@@ -12,10 +12,8 @@ export default function VersionsDownloadPopup () {
selectedGame, selectedGame,
setViewingInfoFromDownloads, setViewingInfoFromDownloads,
downloadedVersionsConfig, downloadedVersionsConfig,
downloadProgress,
downloadVersions, downloadVersions,
getGameInfo, getGameInfo
downloadQueue
} = useGlobal() } = useGlobal()
if (!selectedGame) return <></> if (!selectedGame) return <></>
@@ -66,7 +64,7 @@ export default function VersionsDownloadPopup () {
onClick={() => { onClick={() => {
setManagingVersion(v.id) setManagingVersion(v.id)
setViewingInfoFromDownloads(true) setViewingInfoFromDownloads(true)
setPopupMode(3) setPopupMode(2)
}} }}
title='Click to view version info' title='Click to view version info'
> >
@@ -81,25 +79,19 @@ export default function VersionsDownloadPopup () {
onClick={() => { onClick={() => {
if (downloadedVersionsConfig) { if (downloadedVersionsConfig) {
downloadVersions(selectedVersionList) downloadVersions(selectedVersionList)
setPopupMode(1)
} }
}} }}
disabled={selectedVersionList.length === 0} disabled={selectedVersionList.length == 0}
title={ title={
selectedVersionList.length === 0 selectedVersionList.length == 0
? 'Select at least one version to download' ? 'Select at least one version to download'
: downloadProgress.length > 0 || downloadQueue.length > 0
? `Add ${selectedVersionList.length} version${
selectedVersionList.length == 1 ? '' : 's'
} to download queue`
: `Download ${selectedVersionList.length} version${ : `Download ${selectedVersionList.length} version${
selectedVersionList.length == 1 ? '' : 's' selectedVersionList.length == 1 ? '' : 's'
} of ${getGameInfo(selectedGame)?.name}` } of ${getGameInfo(selectedGame)?.name}`
} }
> >
{downloadProgress.length > 0 || downloadQueue.length > 0 Download {selectedVersionList.length} version
? `Add ${selectedVersionList.length} to Queue`
: `Download ${selectedVersionList.length}`}{' '}
version
{selectedVersionList.length == 1 ? '' : 's'} {selectedVersionList.length == 1 ? '' : 's'}
</button> </button>
<button <button
+10 -1
View File
@@ -11,11 +11,20 @@ export const openFolder = async (name: string) => {
const folderExists = await exists(relativePath, { const folderExists = await exists(relativePath, {
baseDir: BaseDirectory.AppLocalData baseDir: BaseDirectory.AppLocalData
}) })
if (!folderExists) {
await message(`Game folder "${absolutePath}" not found.`, {
title: 'Folder not found',
kind: 'error'
})
return
}
const folderStat = await stat(relativePath, { const folderStat = await stat(relativePath, {
baseDir: BaseDirectory.AppLocalData baseDir: BaseDirectory.AppLocalData
}) })
if (!folderExists || folderStat.isFile) { if (folderStat.isFile) {
await message(`Game folder "${absolutePath}" not found.`, { await message(`Game folder "${absolutePath}" not found.`, {
title: 'Folder not found', title: 'Folder not found',
kind: 'error' kind: 'error'
+1
View File
@@ -8,6 +8,7 @@ export class DownloadProgress {
public hash_checking: boolean, public hash_checking: boolean,
public finishing: boolean, public finishing: boolean,
public speed: number, public speed: number,
public size: number,
public etaSecs: number public etaSecs: number
) {} ) {}
} }
+2 -4
View File
@@ -3,13 +3,11 @@ export interface GameVersion {
displayName: string displayName: string
releaseDate: number releaseDate: number
game: number game: number
downloadUrl: string
executable: string
sha512sum: string
size: number
place: number place: number
changelog: string changelog: string
wine: number | undefined wine: number | undefined
category: number category: number
lastRevision: number lastRevision: number
download: number
executable: string
} }