Compare commits

...

25 Commits

Author SHA1 Message Date
HorizonCode cbfe5ec868 fix: Setup Wizard not working 2026-02-19 15:41:29 +01:00
HorizonCode c5b18dacbb chore: update lucide-svelte 2026-02-19 15:41:10 +01:00
HorizonCode ef01984875 fix: osu installation detection 2026-02-19 15:40:56 +01:00
HorizonCode 46ee36ce81 fix: disable all buttons and dropdowns on launch 2026-02-17 20:34:57 +01:00
HorizonCode c4658ae4cc fix: sort streaks by depth 2026-02-17 20:13:43 +01:00
HorizonCode ca15ff566f chore: bump version 2026-02-17 19:20:19 +01:00
HorizonCode a6fdda4487 fix: also use color-primary for pp stat 2026-02-17 19:19:09 +01:00
HorizonCode a30946eb60 chore: update animatedbg gradient background colors 2026-02-17 19:18:10 +01:00
HorizonCode 9106ad89b3 chore: replace all bg-pink and border-pink with bg-primary and border-primary 2026-02-17 19:15:38 +01:00
HorizonCode 194709df0c style: adjust primary color 2026-02-17 19:13:23 +01:00
HorizonCode 20f625be60 chore: improve line spreading alogrythm 2026-02-17 19:04:04 +01:00
HorizonCode 66a5fd84ae chore: set AnimatedBg globally 2026-02-17 19:02:37 +01:00
HorizonCode 44d7401947 chore: set AnimatedBg globally 2026-02-17 19:02:28 +01:00
HorizonCode f973e48561 chore: add border to view buttons 2026-02-17 18:58:16 +01:00
HorizonCode de186b7ff9 fix: change beatmapSets count icon 2026-02-17 18:55:45 +01:00
HorizonCode 244d526933 chore: make page switch transitions smoother 2026-02-17 18:52:41 +01:00
HorizonCode c7a990e3fe fix: show login result after redirect to home view 2026-02-17 18:46:12 +01:00
HorizonCode ed1f741a9e chore: remove all console.log 2026-02-17 17:43:05 +01:00
HorizonCode b6616a37f4 fix: isNet8Installed condition 2026-02-17 17:41:04 +01:00
HorizonCode 63433ac626 chore: update sileo 2026-02-17 17:40:46 +01:00
HorizonCode 149b4715d7 fix: updateUrl 2026-02-17 17:40:35 +01:00
HorizonCode ae3443d0b9 fix: osu running check 2026-02-16 21:53:53 +01:00
HorizonCode abc7b9d9d9 chore: replace sonner with sileo 2026-02-16 21:53:07 +01:00
HorizonCode d9dd21d3be chore: cleanup commands, fix osu folder check 2026-02-16 20:46:58 +01:00
HorizonCode 0df31178f4 fix: make count functions async to prevent frontend lock 2026-02-16 20:20:29 +01:00
20 changed files with 364 additions and 198 deletions
+29 -2
View File
@@ -19,7 +19,11 @@
"buffer": "^6.0.3", "buffer": "^6.0.3",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"lucide-svelte": "^0.562.0", "lucide-svelte": "^0.562.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-to-webcomponent": "^2.0.1",
"semver": "^7.7.2", "semver": "^7.7.2",
"sileo": "^0.1.3",
"svelte-confetti": "^2.3.2", "svelte-confetti": "^2.3.2",
"tw-animate-css": "^1.3.8", "tw-animate-css": "^1.3.8",
}, },
@@ -27,13 +31,14 @@
"@eslint/compat": "^2.0.0", "@eslint/compat": "^2.0.0",
"@eslint/js": "^9.35.0", "@eslint/js": "^9.35.0",
"@iarna/toml": "2.2.5", "@iarna/toml": "2.2.5",
"@lucide/svelte": "^0.482.0", "@lucide/svelte": "^0.562.0",
"@sveltejs/adapter-static": "^3.0.9", "@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.37.1", "@sveltejs/kit": "^2.37.1",
"@sveltejs/vite-plugin-svelte": "^6.1.4", "@sveltejs/vite-plugin-svelte": "^6.1.4",
"@tauri-apps/cli": "^2.9.6", "@tauri-apps/cli": "^2.9.6",
"@types/bun": "^1.2.21", "@types/bun": "^1.2.21",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"autoprefixer": "10.4.21", "autoprefixer": "10.4.21",
"bits-ui": "^1.4.7", "bits-ui": "^1.4.7",
@@ -174,7 +179,7 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@lucide/svelte": ["@lucide/svelte@0.482.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-n2ycHU9cNcleRDwwpEHBJ6pYzVhHIaL3a+9dQa8kns9hB2g05bY+v2p2KP8v0pZwtNhYTHk/F2o2uZ1bVtQGhw=="], "@lucide/svelte": ["@lucide/svelte@0.562.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-wDMULwtTFN2Sc/TFBm6gfuVCNb4Y5P9LDrwxNnUbV52+IEU7NXZmvxwXoz+vrrpad6Xupq+Hw5eUlqIHEGhouw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -214,6 +219,8 @@
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@r2wc/core": ["@r2wc/core@1.3.0", "", {}, "sha512-aPBnND92Itl+SWWbWyyxdFFF0+RqKB6dptGHEdiPB8ZvnHWHlVzOfEvbEcyUYGtB6HBdsfkVuBiaGYyBFVTzVQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
@@ -346,6 +353,8 @@
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="], "@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
"@types/umami": ["@types/umami@2.10.1", "", {}, "sha512-CvOEMq+BFOUXj8yciq4hPsdOt0o9KBg5jQXnF1FrdOMKZMxJiwryyZNLa9Ud2NVO6nXSwU22YMKR3E+Xhbo+DA=="], "@types/umami": ["@types/umami@2.10.1", "", {}, "sha512-CvOEMq+BFOUXj8yciq4hPsdOt0o9KBg5jQXnF1FrdOMKZMxJiwryyZNLa9Ud2NVO6nXSwU22YMKR3E+Xhbo+DA=="],
@@ -506,6 +515,8 @@
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
"framer-motion": ["framer-motion@12.34.1", "", { "dependencies": { "motion-dom": "^12.34.1", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-kcZyNaYQfvE2LlH6+AyOaJAQV4rGp5XbzfhsZpiSZcwDMfZUHhuxLWeyRzf5I7jip3qKRpuimPA9pXXfr111kQ=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
@@ -610,6 +621,12 @@
"mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="], "mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="],
"motion": ["motion@12.34.1", "", { "dependencies": { "framer-motion": "^12.34.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-N9RVNGn/NSo85OgHX1wGaUWHvReuQ7dZUwuQRhHyzY2wfVOvY3cEgn0Mw4NXOsXMHL/y7EYuzA+b59PYI6EejA=="],
"motion-dom": ["motion-dom@12.34.1", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-SC7ZC5dRcGwku2g7EsPvI4q/EzHumUbqsDNumBmZTLFg+goBO5LTJvDu9MAxx+0mtX4IA78B2be/A3aRjY0jnw=="],
"motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
@@ -666,6 +683,12 @@
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
"react-to-webcomponent": ["react-to-webcomponent@2.0.1", "", { "dependencies": { "@r2wc/core": "^1.0.0" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-jpVztcA0SbV6TnBRV6EFuU+hmuuSIxgEww3wRcbneT0ytqn57y9ygeRfUmhzz8+bYg53u1h4COKk8iAextHE+Q=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
@@ -722,6 +745,8 @@
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.92.1", "", { "os": "win32", "cpu": "x64" }, "sha512-B6Nz/GbH7Vkpb2TkQHsGcczWM5t+70VWopWF1x5V5yxLpA8ZzVQ7NTKKi+jDoVY2Efu6ZyzgT9n5KgG2kWliXA=="], "sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.92.1", "", { "os": "win32", "cpu": "x64" }, "sha512-B6Nz/GbH7Vkpb2TkQHsGcczWM5t+70VWopWF1x5V5yxLpA8ZzVQ7NTKKi+jDoVY2Efu6ZyzgT9n5KgG2kWliXA=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
@@ -730,6 +755,8 @@
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"sileo": ["sileo@0.1.3", "", { "dependencies": { "motion": "^12.34.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-kmGjM2ts2TpuPzOaD7llxcMpU37Pyj5dO8s4wGXvDlR143zV2sB6sJ76PCVP4kY2+hfkrLJhiSm/wXGdUHDFjQ=="],
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="], "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+8 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "ezpplauncher", "name": "ezpplauncher",
"version": "3.1.0", "version": "3.1.1",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -32,8 +32,12 @@
"animejs": "^4.1.3", "animejs": "^4.1.3",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"lucide-svelte": "^0.562.0", "lucide-svelte": "^0.575.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-to-webcomponent": "^2.0.1",
"semver": "^7.7.2", "semver": "^7.7.2",
"sileo": "^0.1.3",
"svelte-confetti": "^2.3.2", "svelte-confetti": "^2.3.2",
"tw-animate-css": "^1.3.8" "tw-animate-css": "^1.3.8"
}, },
@@ -41,13 +45,14 @@
"@eslint/compat": "^2.0.0", "@eslint/compat": "^2.0.0",
"@eslint/js": "^9.35.0", "@eslint/js": "^9.35.0",
"@iarna/toml": "2.2.5", "@iarna/toml": "2.2.5",
"@lucide/svelte": "^0.562.0", "@lucide/svelte": "^0.575.0",
"@sveltejs/adapter-static": "^3.0.9", "@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.37.1", "@sveltejs/kit": "^2.37.1",
"@sveltejs/vite-plugin-svelte": "^6.1.4", "@sveltejs/vite-plugin-svelte": "^6.1.4",
"@tauri-apps/cli": "^2.9.6", "@tauri-apps/cli": "^2.9.6",
"@types/bun": "^1.2.21", "@types/bun": "^1.2.21",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"autoprefixer": "10.4.21", "autoprefixer": "10.4.21",
"bits-ui": "^1.4.7", "bits-ui": "^1.4.7",
+1 -1
View File
@@ -1007,7 +1007,7 @@ dependencies = [
[[package]] [[package]]
name = "ezpplauncher" name = "ezpplauncher"
version = "3.1.0" version = "3.1.1"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"discord-rich-presence", "discord-rich-presence",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ezpplauncher" name = "ezpplauncher"
version = "3.1.0" version = "3.1.1"
description = "EZPPLauncher redefined." description = "EZPPLauncher redefined."
authors = ["HorizonCode"] authors = ["HorizonCode"]
edition = "2024" edition = "2024"
+39 -62
View File
@@ -56,18 +56,12 @@ pub fn valid_osu_folder(folder: String) -> bool {
"scores.db", "scores.db",
]; ];
let folder_files: Vec<&str> = osu_folder_files.iter().map(|&s| s).collect();
if folder_files.iter().any(|&file| file == "osu!.exe") == false {
return false;
}
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let match_percentage = check_folder_completeness(path, &osu_folder_files) >= 70.0; if !path.join("osu!.exe").exists() {
if match_percentage {
return true;
}
return false; return false;
} }
check_folder_completeness(path, &osu_folder_files) >= 70.0
}
#[cfg(not(windows))] #[cfg(not(windows))]
#[tauri::command] #[tauri::command]
@@ -83,10 +77,10 @@ pub fn find_osu_installation() -> Option<String> {
let hklm_registry_paths = ["SOFTWARE\\Classes\\osu\\DefaultIcon"]; let hklm_registry_paths = ["SOFTWARE\\Classes\\osu\\DefaultIcon"];
let hkcr_registry_paths = [ let hkcu_registry_paths = [
"osustable.File.osk\\DefaultIcon", "Software\\Classes\\osustable.File.osk\\DefaultIcon",
"osustable.File.osr\\DefaultIcon", "Software\\Classes\\osustable.File.osr\\DefaultIcon",
"osustable.File.osz\\DefaultIcon", "Software\\Classes\\osustable.File.osz\\DefaultIcon",
]; ];
let osu_folder_files = [ let osu_folder_files = [
@@ -135,10 +129,10 @@ pub fn find_osu_installation() -> Option<String> {
} }
} }
let hkcr = RegKey::predef(HKEY_CLASSES_ROOT); let hkcu = RegKey::predef(HKEY_CURRENT_USER);
for reg_path in &hkcr_registry_paths { for reg_path in &hkcu_registry_paths {
if let Ok(subkey) = hkcr.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) { if let Ok(subkey) = hkcu.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) {
let value_names = [""]; let value_names = [""];
for value_name in &value_names { for value_name in &value_names {
if let Ok(value) = subkey.get_value::<String, _>(value_name) { if let Ok(value) = subkey.get_value::<String, _>(value_name) {
@@ -160,7 +154,7 @@ pub fn find_osu_installation() -> Option<String> {
} }
#[tauri::command] #[tauri::command]
pub fn get_beatmapsets_count(folder: String) -> Option<u64> { pub async fn get_beatmapsets_count(folder: String) -> Option<u64> {
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let osu_user_config = get_osu_user_config(path.clone()); let osu_user_config = get_osu_user_config(path.clone());
let songs_path = osu_user_config let songs_path = osu_user_config
@@ -173,12 +167,12 @@ pub fn get_beatmapsets_count(folder: String) -> Option<u64> {
} }
let mut count = 0; let mut count = 0;
if let Ok(entries) = std::fs::read_dir(songs_folder) { if let Ok(mut entries) = fs::read_dir(songs_folder).await {
for entry in entries.flatten() { while let Ok(Some(entry)) = entries.next_entry().await {
if entry.file_type().map_or(false, |ft| ft.is_dir()) { if entry.file_type().await.map_or(false, |ft| ft.is_dir()) {
let dir_path = entry.path(); let dir_path = entry.path();
if let Ok(files) = std::fs::read_dir(&dir_path) { if let Ok(mut files) = fs::read_dir(&dir_path).await {
for file in files.flatten() { while let Ok(Some(file)) = files.next_entry().await {
if file.path().extension().map_or(false, |ext| ext == "osu") { if file.path().extension().map_or(false, |ext| ext == "osu") {
count += 1; count += 1;
break; break;
@@ -192,7 +186,7 @@ pub fn get_beatmapsets_count(folder: String) -> Option<u64> {
} }
#[tauri::command] #[tauri::command]
pub fn get_skins_count(folder: String) -> Option<u64> { pub async fn get_skins_count(folder: String) -> Option<u64> {
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let skins_folder = path.join("Skins"); let skins_folder = path.join("Skins");
@@ -201,12 +195,12 @@ pub fn get_skins_count(folder: String) -> Option<u64> {
} }
let mut count = 0; let mut count = 0;
if let Ok(entries) = std::fs::read_dir(skins_folder) { if let Ok(mut entries) = fs::read_dir(skins_folder).await {
for entry in entries.flatten() { while let Ok(Some(entry)) = entries.next_entry().await {
if entry.file_type().map_or(false, |ft| ft.is_dir()) { if entry.file_type().await.map_or(false, |ft| ft.is_dir()) {
let dir_path = entry.path(); let dir_path = entry.path();
if let Ok(files) = std::fs::read_dir(&dir_path) { if let Ok(mut files) = fs::read_dir(&dir_path).await {
for file in files.flatten() { while let Ok(Some(file)) = files.next_entry().await {
if file.path().extension().map_or(false, |ext| ext == "ini") { if file.path().extension().map_or(false, |ext| ext == "ini") {
count += 1; count += 1;
break; break;
@@ -222,28 +216,28 @@ pub fn get_skins_count(folder: String) -> Option<u64> {
#[tauri::command] #[tauri::command]
pub fn get_osu_skin(folder: String) -> String { pub fn get_osu_skin(folder: String) -> String {
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let osu_user_config = get_osu_user_config(path.clone()); let osu_user_config = get_osu_user_config(path);
return osu_user_config osu_user_config
.and_then(|config| config.get("Skin").cloned()) .and_then(|config| config.get("Skin").cloned())
.unwrap_or_else(|| "Default".to_string()); .unwrap_or_else(|| "Default".to_string())
} }
#[tauri::command] #[tauri::command]
pub fn get_osu_version(folder: String) -> String { pub fn get_osu_version(folder: String) -> String {
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let osu_user_config = get_osu_user_config(path.clone()); let osu_user_config = get_osu_user_config(path);
return osu_user_config osu_user_config
.and_then(|config| config.get("LastVersion").cloned()) .and_then(|config| config.get("LastVersion").cloned())
.unwrap_or_else(|| "failed".to_string()); .unwrap_or_else(|| "failed".to_string())
} }
#[tauri::command] #[tauri::command]
pub fn get_osu_release_stream(folder: String) -> String { pub fn get_osu_release_stream(folder: String) -> String {
let path = PathBuf::from(folder); let path = PathBuf::from(folder);
let osu_config = get_osu_config(path.clone()); let osu_config = get_osu_config(path);
return osu_config osu_config
.and_then(|config| config.get("_ReleaseStream").cloned()) .and_then(|config| config.get("_ReleaseStream").cloned())
.unwrap_or_else(|| "Stable40".to_string()); .unwrap_or_else(|| "Stable40".to_string())
} }
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
@@ -261,10 +255,7 @@ pub fn set_osu_user_config_values(
.iter() .iter()
.map(|entry| (entry.key.as_str(), Some(entry.value.as_str()))) .map(|entry| (entry.key.as_str(), Some(entry.value.as_str())))
.collect(); .collect();
match set_osu_user_config_vals(&osu_folder_path, &converted) { Ok(set_osu_user_config_vals(&osu_folder_path, &converted).is_ok())
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
} }
#[tauri::command] #[tauri::command]
@@ -276,22 +267,16 @@ pub fn set_osu_config_values(
.iter() .iter()
.map(|entry| (entry.key.as_str(), Some(entry.value.as_str()))) .map(|entry| (entry.key.as_str(), Some(entry.value.as_str())))
.collect(); .collect();
match set_osu_config_vals(&osu_folder_path, &converted) { Ok(set_osu_config_vals(&osu_folder_path, &converted).is_ok())
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
} }
#[tauri::command] #[tauri::command]
pub async fn run_osu_updater(folder: String) -> Result<(), String> { pub async fn run_osu_updater(folder: String) -> Result<(), String> {
#[cfg(windows)]
const DETACHED_PROCESS: u32 = 0x00000008;
#[cfg(windows)]
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
let mut updater_process = { let mut updater_process = {
#[cfg(windows)] #[cfg(windows)]
{ {
const DETACHED_PROCESS: u32 = 0x00000008;
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
let osu_exe_path = PathBuf::from(&folder).join("osu!.exe"); let osu_exe_path = PathBuf::from(&folder).join("osu!.exe");
Command::new(&osu_exe_path) Command::new(&osu_exe_path)
.arg("-repair") .arg("-repair")
@@ -368,9 +353,6 @@ pub async fn run_osu_updater(folder: String) -> Result<(), String> {
#[tauri::command] #[tauri::command]
pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> { pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
/* #[cfg(windows)]
use std::os::windows::process::CommandExt; */
#[cfg(windows)] #[cfg(windows)]
const DETACHED_PROCESS: u32 = 0x00000008; const DETACHED_PROCESS: u32 = 0x00000008;
#[cfg(windows)] #[cfg(windows)]
@@ -385,7 +367,7 @@ pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
.arg("ez-pp.farm") .arg("ez-pp.farm")
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP) .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.spawn() .spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))? .map_err(|e| format!("Failed to spawn osu: {}", e))?
} }
#[cfg(not(windows))] #[cfg(not(windows))]
@@ -394,7 +376,7 @@ pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
.arg("--devserver") .arg("--devserver")
.arg("ez-pp.farm") .arg("ez-pp.farm")
.spawn() .spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))? .map_err(|e| format!("Failed to spawn osu: {}", e))?
} }
}; };
@@ -732,12 +714,7 @@ pub async fn has_net8() -> bool {
#[tauri::command] #[tauri::command]
pub fn encrypt_string(string: String, entropy: String) -> String { pub fn encrypt_string(string: String, entropy: String) -> String {
let encrypted = encrypt_password(&string, &entropy); encrypt_password(&string, &entropy).unwrap_or(string)
match encrypted {
Ok(encrypted_vec) => encrypted_vec,
Err(_) => string,
}
} }
#[tauri::command] #[tauri::command]
@@ -773,7 +750,7 @@ pub async fn download_ezpp_launcher_update(app: AppHandle, url: String) -> Resul
UpdateStatus { UpdateStatus {
file_name: "Update".to_string(), file_name: "Update".to_string(),
downloaded, downloaded,
size: size, size,
progress: ((downloaded as f64 / size as f64 * 100.0) * 100.0).trunc() / 100.0, progress: ((downloaded as f64 / size as f64 * 100.0) * 100.0).trunc() / 100.0,
}, },
) )
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "EZPPLauncher", "productName": "EZPPLauncher",
"version": "3.1.0", "version": "3.1.1",
"identifier": "farm.EZPPFarm.Launcher", "identifier": "farm.EZPPFarm.Launcher",
"mainBinaryName": "ezpplauncher", "mainBinaryName": "ezpplauncher",
"build": { "build": {
+16 -18
View File
@@ -55,7 +55,7 @@
--card-foreground: oklch(89.24% 0.079 263.24); --card-foreground: oklch(89.24% 0.079 263.24);
--popover: oklch(20.3% 0.05 282.86); --popover: oklch(20.3% 0.05 282.86);
--popover-foreground: oklch(89.24% 0.079 263.24); --popover-foreground: oklch(89.24% 0.079 263.24);
--primary: oklch(81.45% 0.159 300.53); --primary: oklch(72.08% 0.196 356.34);
--primary-foreground: oklch(20.22% 0.048 282.86); --primary-foreground: oklch(20.22% 0.048 282.86);
--secondary: oklch(42.99% 0.031 270.49); --secondary: oklch(42.99% 0.031 270.49);
--secondary-foreground: oklch(89.24% 0.079 263.24); --secondary-foreground: oklch(89.24% 0.079 263.24);
@@ -67,8 +67,8 @@
--destructive-foreground: oklch(20.22% 0.048 282.86); --destructive-foreground: oklch(20.22% 0.048 282.86);
--border: oklch(27.7% 0.041 276.87); --border: oklch(27.7% 0.041 276.87);
--input: oklch(27.7% 0.041 276.87); --input: oklch(27.7% 0.041 276.87);
--ring: oklch(81.45% 0.159 300.53); --ring: oklch(72.08% 0.196 356.34);
--chart-1: oklch(81.45% 0.159 300.53); --chart-1: oklch(72.08% 0.196 356.34);
--chart-2: oklch(77.19% 0.121 223.01); --chart-2: oklch(77.19% 0.121 223.01);
--chart-3: oklch(79.1% 0.119 150.21); --chart-3: oklch(79.1% 0.119 150.21);
--chart-4: oklch(79.74% 0.167 57.97); --chart-4: oklch(79.74% 0.167 57.97);
@@ -76,12 +76,12 @@
--sidebar: oklch(13.29% 0.036 282.86); --sidebar: oklch(13.29% 0.036 282.86);
--sidebar-background: oklch(13.29% 0.036 282.86); --sidebar-background: oklch(13.29% 0.036 282.86);
--sidebar-foreground: oklch(89.24% 0.079 263.24); --sidebar-foreground: oklch(89.24% 0.079 263.24);
--sidebar-primary: oklch(81.45% 0.159 300.53); --sidebar-primary: oklch(72.08% 0.196 356.34);
--sidebar-primary-foreground: oklch(20.22% 0.048 282.86); --sidebar-primary-foreground: oklch(20.22% 0.048 282.86);
--sidebar-accent: oklch(75.21% 0.121 293.01); --sidebar-accent: oklch(75.21% 0.121 293.01);
--sidebar-accent-foreground: oklch(20.22% 0.048 282.86); --sidebar-accent-foreground: oklch(20.22% 0.048 282.86);
--sidebar-border: oklch(35.13% 0.03 273.31); --sidebar-border: oklch(35.13% 0.03 273.31);
--sidebar-ring: oklch(81.45% 0.159 300.53); --sidebar-ring: oklch(72.08% 0.196 356.34);
} }
@theme inline { @theme inline {
@@ -108,20 +108,18 @@
--color-popover: var(--popover); --color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground); --color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary); --color-primary: var(--primary);
--color-primary-50: #fefdff; --color-primary-50: #ffe5f1;
--color-primary-100: #faf6fe; --color-primary-100: #ffcce2;
--color-primary-200: #f2e9fc; --color-primary-200: #ff99c5;
--color-primary-300: #e9dbfa; --color-primary-300: #ff66a8;
--color-primary-400: #e1cef9; --color-primary-400: #ff338b;
--color-primary-500: #d9c0f7; --color-primary-500: #ff006e;
--color-primary-600: #c6a0f3; --color-primary-600: #cc0058;
--color-primary-700: #b381ef; --color-primary-700: #990042;
--color-primary-800: #a061eb; --color-primary-800: #66002c;
--color-primary-900: #8d41e7; --color-primary-900: #330016;
--color-primary-950: #8331e5; --color-primary-950: #24000f;
--color-primary-foreground: var(--primary-foreground); --color-primary-foreground: var(--primary-foreground);
--color-primary-50: #fefdff;
--color-primary-100: #faf6fe;
--color-secondary: var(--secondary); --color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground); --color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted); --color-muted: var(--muted);
@@ -1,13 +1,16 @@
<script lang="ts"> <script lang="ts">
import Logo from '$assets/logo.png';
//TODO: maybe dynamic background images fetched from ezpp? //TODO: maybe dynamic background images fetched from ezpp?
const COUNT = 120; const COUNT = 60;
function rand(min: number, max: number) { function rand(min: number, max: number) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
const streaks = Array.from({ length: COUNT }).map(() => { const streaks = Array.from({ length: COUNT })
.map((_, i) => {
const depth = rand(0, 2); const depth = rand(0, 2);
const scale = 0.6 + depth * 0.8; const scale = 0.6 + depth * 0.8;
@@ -17,16 +20,17 @@
const duration = distance / speed; const duration = distance / speed;
return { return {
width: rand(180, 320) * scale, width: rand(300, 320) * scale,
height: rand(16, 32) * scale, height: 32 * scale,
x: rand(-80, 100), x: (i / COUNT) * 200 - 50,
y: rand(-80, 100), y: ((i * 137.5) % 100) - 20,
opacity: 0.25 + depth * 0.65, opacity: 0.25 + depth * 0.65,
duration, duration,
delay: rand(0, duration), delay: rand(0, duration),
scale, scale,
}; };
}); })
.sort((a, b) => a.scale - b.scale);
</script> </script>
<div class="absolute bg blur-[2px]"> <div class="absolute bg blur-[2px]">
@@ -54,7 +58,12 @@
position: fixed; position: fixed;
inset: 0; inset: 0;
overflow: hidden; overflow: hidden;
background: linear-gradient(180deg, #8f2f64 0%, #963f75 40%, #9a6c7f 100%); background: linear-gradient(
180deg,
var(--color-primary-300) 0%,
var(--color-primary-600) 40%,
var(--color-primary-900) 100%
);
} }
.streak { .streak {
@@ -17,7 +17,7 @@
</script> </script>
<button <button
class="group flex items-center h-12 rounded-full bg-pink-400 disabled:bg-pink-400/70 shadow-lg shadow-primary/25 transition-all not-disabled:hover:shadow-primary/40 not-disabled:hover:brightness-110 not-disabled:active:scale-[0.98] cursor-pointer w-fit" class="group flex items-center h-12 rounded-full bg-primary-300 disabled:bg-primary-300/70 shadow-lg shadow-primary/25 transition-all not-disabled:hover:shadow-primary/40 not-disabled:hover:brightness-110 not-disabled:active:scale-[0.98] cursor-pointer w-fit"
disabled={prop.disabled} disabled={prop.disabled}
onclick={prop.onClick} onclick={prop.onClick}
> >
@@ -15,7 +15,7 @@
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
bind:ref bind:ref
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 'data-[highlighted]:bg-primary data-[highlighted]:text-primary-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8', inset && 'pl-8',
className className
)} )}
@@ -17,7 +17,7 @@
bind:ref bind:ref
{value} {value}
class={cn( class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'data-[highlighted]:bg-primary-300 data-[highlighted]:text-primary-foreground relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className className
)} )}
{...restProps} {...restProps}
@@ -0,0 +1,21 @@
<script lang="ts">
import { createElement } from 'react'
import { createRoot } from 'react-dom/client'
import type { SileoOptions } from 'sileo'
let props: SileoOptions = $props()
let rootEl: HTMLElement
$effect(() => {
const root = createRoot(rootEl)
import('sileo').then(({ Toaster }) => {
const sileo = createElement(Toaster, { ...props })
root.render(sileo)
})
return () => root.unmount()
})
</script>
<div bind:this={rootEl}></div>
+1 -4
View File
@@ -44,8 +44,7 @@ export class Config {
this.encrypt && this.crypto ? this.crypto.decrypt(fileStream) : fileStream this.encrypt && this.crypto ? this.crypto.decrypt(fileStream) : fileStream
) as Record<string, unknown>; ) as Record<string, unknown>;
this.config = decryptedJSON; this.config = decryptedJSON;
} catch (err) { } catch {
console.log(err);
this.config = {}; this.config = {};
await this.save(); await this.save();
} }
@@ -65,13 +64,11 @@ export class Config {
} }
value(key: string) { value(key: string) {
console.log('Accessing config key:', key, this.config[key]);
return { return {
set: <T>(val: T) => { set: <T>(val: T) => {
this.config[key] = val; this.config[key] = val;
}, },
exists: () => { exists: () => {
console.log('Checking existence of key:', key, this.config[key] !== undefined);
return this.config[key] !== undefined; return this.config[key] !== undefined;
}, },
get: <T>(fallback: T): T => { get: <T>(fallback: T): T => {
-1
View File
@@ -8,7 +8,6 @@ export function estimateRefreshRate(): Promise<number> {
frames++; frames++;
if (now - last >= 1000) { if (now - last >= 1000) {
console.log(`Estimated Refresh Rate: ${frames - 2} FPS`);
resolve(frames - 2); resolve(frames - 2);
} else { } else {
requestAnimationFrame(loop); requestAnimationFrame(loop);
+1 -2
View File
@@ -3,7 +3,7 @@ import type { UpdateFile, UpdateStatus } from './types';
import { listen } from '@tauri-apps/api/event'; import { listen } from '@tauri-apps/api/event';
import { betterFetch } from '@better-fetch/fetch'; import { betterFetch } from '@better-fetch/fetch';
const updateUrl = 'https://next.ez-pp.farm/api/ezpplauncher'; const updateUrl = 'https://ez-pp.farm/api/ezpplauncher';
export const getHWID = async () => { export const getHWID = async () => {
const hwid = await invoke('get_hwid'); const hwid = await invoke('get_hwid');
@@ -82,7 +82,6 @@ export const getEZPPLauncherStreams = async () => {
if (!resp.error) { if (!resp.error) {
return resp.data.streams; return resp.data.streams;
} }
return undefined; return undefined;
}; };
-1
View File
@@ -44,7 +44,6 @@ export const formatTimeReadable = (initialSeconds: number) => {
}; };
export const releaseStreamToReadable = (releaseStream: string) => { export const releaseStreamToReadable = (releaseStream: string) => {
console.log(releaseStream.replaceAll(' ', '').toLowerCase());
if (releaseStream.replaceAll(' ', '').toLowerCase() === 'cuttingedge') return 'Cutting Edge'; if (releaseStream.replaceAll(' ', '').toLowerCase() === 'cuttingedge') return 'Cutting Edge';
return 'Stable'; return 'Stable';
}; };
+6 -2
View File
@@ -25,7 +25,7 @@
userSettings, userSettings,
} from '@/userSettings'; } from '@/userSettings';
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { Toaster } from '@/components/ui/sonner'; import SileoToast from '@/components/ui/sileo/sileo-toast.svelte';
import { userAuth } from '@/userAuthentication'; import { userAuth } from '@/userAuthentication';
import { encryptString, exit, getLauncherVersion, getPlatform } from '@/osuUtil'; import { encryptString, exit, getLauncherVersion, getPlatform } from '@/osuUtil';
import Button from '@/components/ui/button/button.svelte'; import Button from '@/components/ui/button/button.svelte';
@@ -33,6 +33,7 @@
import '@fontsource/sora'; import '@fontsource/sora';
import '@fontsource/space-mono'; import '@fontsource/space-mono';
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
let { children } = $props(); let { children } = $props();
@@ -150,7 +151,7 @@
<OsuCursor smoothCursor={$cursorSmoothening} /> <OsuCursor smoothCursor={$cursorSmoothening} />
{/if} {/if}
<Toaster richColors closeButton theme="dark" position="top-right" /> <SileoToast position="top-center" fill="black" />
<Titlebar /> <Titlebar />
@@ -178,5 +179,8 @@
</AlertDialog.Root> </AlertDialog.Root>
<main> <main>
<div class="opacity-30">
<AnimatedBg />
</div>
{@render children()} {@render children()}
</main> </main>
+142 -49
View File
@@ -24,7 +24,7 @@
} from '@/global'; } from '@/global';
import { import {
LoaderCircle, LoaderCircle,
Music2, Music,
Wifi, Wifi,
WifiOff, WifiOff,
Drum, Drum,
@@ -64,7 +64,6 @@
} from '@/userSettings'; } from '@/userSettings';
import Input from '@/components/ui/input/input.svelte'; import Input from '@/components/ui/input/input.svelte';
import { open } from '@tauri-apps/plugin-dialog'; import { open } from '@tauri-apps/plugin-dialog';
import { toast } from 'svelte-sonner';
import { currentUser, userAuth } from '@/userAuthentication'; import { currentUser, userAuth } from '@/userAuthentication';
import { import {
getGamemodeInt, getGamemodeInt,
@@ -109,6 +108,7 @@
import DownloadButton from '@/components/ui/download-button/DownloadButton.svelte'; import DownloadButton from '@/components/ui/download-button/DownloadButton.svelte';
import { animate } from 'animejs'; import { animate } from 'animejs';
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte'; import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
import { sileo } from 'sileo';
let selectedView = $state('home'); let selectedView = $state('home');
let progress = $state(-1); let progress = $state(-1);
@@ -146,16 +146,28 @@
} }
const validFolder = await isValidOsuFolder(selectedPath); const validFolder = await isValidOsuFolder(selectedPath);
if (!validFolder) { if (!validFolder) {
toast.error('Oops...', { sileo.error({
title: 'Hmm...',
description: description:
'The selected folder is not a valid osu! installation folder. Please select the correct folder.', 'The selected folder is not a valid osu! installation folder. Please select the correct folder.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
return; return;
} }
osuInstallationPath.set(selectedPath); osuInstallationPath.set(selectedPath);
$userSettings.value('osu_installation_path').set(selectedPath); $userSettings.value('osu_installation_path').set(selectedPath);
$userSettings.save(); $userSettings.save();
toast.success('osu! installation path set successfully.'); sileo.success({
title: 'Yay!',
description: 'osu! installation path set successfully.',
fill: '#181825',
styles: {
description: 'text-center!',
},
});
const beatmapSetCount: number | null = await getBeatmapSetsCount(selectedPath); const beatmapSetCount: number | null = await getBeatmapSetsCount(selectedPath);
if (beatmapSetCount) beatmapSets.set(beatmapSetCount); if (beatmapSetCount) beatmapSets.set(beatmapSetCount);
@@ -178,15 +190,25 @@
if ($trackingEnabled) umami.track('app_launch_osu'); if ($trackingEnabled) umami.track('app_launch_osu');
const osuRunning = await isOsuRunning(); const osuRunning = await isOsuRunning();
if (osuRunning) { if (osuRunning) {
toast.error('Hold on a second!', { sileo.error({
title: 'Hold on a second!',
description: description:
'osu! is currently running, please exit osu! before launching via EZPPLauncher!', 'osu! is currently running, please exit osu! before launching via EZPPLauncher!',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
return; return;
} }
if (!$osuBuild) { if (!$osuBuild) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'There was an issue detecting your installed osu! version', description: 'There was an issue detecting your installed osu! version',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
return; return;
} }
@@ -197,8 +219,13 @@
const validFolder = await isValidOsuFolder(osuPath); const validFolder = await isValidOsuFolder(osuPath);
if (!validFolder) { if (!validFolder) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'Your selected osu! installation folder is not valid.', description: 'Your selected osu! installation folder is not valid.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
@@ -206,15 +233,25 @@
if ($platform === 'linux') { if ($platform === 'linux') {
if (!(await hasWMCTRL())) { if (!(await hasWMCTRL())) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'wmctrl seems to be missing, please install via AUR.', description: 'wmctrl seems to be missing, please install via AUR.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
} }
if (!(await hasOsuWinello())) { if (!(await hasOsuWinello())) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'osu-winello seems to be missing, please install it.', description: 'osu-winello seems to be missing, please install it.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
@@ -249,7 +286,6 @@
} }
} }
} catch (err) { } catch (err) {
console.log(err);
launchError = err as Error; launchError = err as Error;
launching.set(false); launching.set(false);
return; return;
@@ -258,8 +294,13 @@
try { try {
const streamInfo = await osuapi.latestBuildVersion('stable40'); const streamInfo = await osuapi.latestBuildVersion('stable40');
if (!streamInfo) { if (!streamInfo) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'Failed to check for updates, maybe osu! is down?', description: 'Failed to check for updates, maybe osu! is down?',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
@@ -268,8 +309,13 @@
const releaseStream = await getReleaseStream(osuPath); const releaseStream = await getReleaseStream(osuPath);
if (releaseStream === undefined) { if (releaseStream === undefined) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'Failed to get osu! release stream.', description: 'Failed to get osu! release stream.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
@@ -277,8 +323,13 @@
// only stable osu! release streams are supported for now // only stable osu! release streams are supported for now
if (!releaseStream.toLowerCase().includes('stable')) { if (!releaseStream.toLowerCase().includes('stable')) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'You are not on the stable release stream, please switch to it.', description: 'You are not on the stable release stream, please switch to it.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
return; return;
@@ -487,7 +538,6 @@
await getCurrentWindow().show(); await getCurrentWindow().show();
if (presenceUpdater) { if (presenceUpdater) {
window.clearInterval(presenceUpdater); window.clearInterval(presenceUpdater);
console.log('clearing discord presence...');
try { try {
await Promise.all([ await Promise.all([
presence.updateUser({ presence.updateUser({
@@ -501,7 +551,6 @@
}), }),
]); ]);
} catch {} } catch {}
console.log('discord presence cleared...');
} }
await new Promise((res) => setTimeout(res, 1000)); await new Promise((res) => setTimeout(res, 1000));
await replaceUIFiles(osuPath, true); await replaceUIFiles(osuPath, true);
@@ -531,8 +580,13 @@
cleanup = false; cleanup = false;
const error = err as Error; const error = err as Error;
if (error.name === 'AbortError') { if (error.name === 'AbortError') {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'Failed to launch.', description: 'Failed to launch.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
launchError = { launchError = {
@@ -541,8 +595,13 @@
}; };
} else { } else {
launchError = error; launchError = error;
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'Failed to launch.', description: 'Failed to launch.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
launching.set(false); launching.set(false);
if ($trackingEnabled) umami.track('app_launch_fail', { error: err }); if ($trackingEnabled) umami.track('app_launch_fail', { error: err });
@@ -560,8 +619,13 @@
try { try {
const loginResult = await ezppfarm.login(username, password); const loginResult = await ezppfarm.login(username, password);
if (loginResult && loginResult.user) { if (loginResult && loginResult.user) {
toast.success('Login successful!', { sileo.success({
title: 'Login successful!',
description: `Welcome back, ${loginResult.user.name}!`, description: `Welcome back, ${loginResult.user.name}!`,
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
$userAuth.value('username').set(username); $userAuth.value('username').set(username);
@@ -571,14 +635,24 @@
currentUser.set(loginResult.user); currentUser.set(loginResult.user);
selectedView = 'home'; selectedView = 'home';
} else { } else {
toast.error('Login failed!', { sileo.error({
title: 'Login failed!',
description: 'Please check your username and password.', description: 'Please check your username and password.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
loginIsLoading = false; loginIsLoading = false;
} }
} catch { } catch {
toast.error('Server error occurred during login.', { sileo.error({
title: 'Login failed!',
description: 'There was an issue connecting to the server. Please try again later.', description: 'There was an issue connecting to the server. Please try again later.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
loginIsLoading = false; loginIsLoading = false;
} }
@@ -706,8 +780,13 @@
onclick={async () => { onclick={async () => {
const updateFile = $newVersion?.assets.find((asset) => asset.name.endsWith('.exe')); const updateFile = $newVersion?.assets.find((asset) => asset.name.endsWith('.exe'));
if (!updateFile) { if (!updateFile) {
toast.error('Hmmm...', { sileo.error({
title: 'Hmmm...',
description: 'No update file found.', description: 'No update file found.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
$newVersion = undefined; $newVersion = undefined;
return; return;
@@ -792,35 +871,38 @@
</AlertDialog.Root> </AlertDialog.Root>
<div class="grid grid-cols-[0.085fr_1fr] h-[100vh] relative"> <div class="grid grid-cols-[0.085fr_1fr] h-[100vh] relative">
<div class="absolute top-0 left-0 w-full h-[100vh] opacity-30 z-0">
<AnimatedBg />
</div>
<div <div
class="p-3 border-r border-r-theme-900 flex flex-col items-center gap-2 z-10 bg-black/40 backdrop-blur-sm" class="p-3 border-r border-r-theme-900 flex flex-col items-center gap-2 z-10 bg-black/40 backdrop-blur-sm"
> >
<div class="bg-pink-950/80 border border-pink-900/50 rounded-[1.1rem] p-1"> <div class="bg-primary-800/80 border border-primary-700/50 rounded-[1.1rem] p-1">
<img class="pointer-events-none" src={Logo} alt="logo" bind:this={ezppLogo} /> <img class="pointer-events-none" src={Logo} alt="logo" bind:this={ezppLogo} />
</div> </div>
<Badge class="text-[0.5rem] py-0 px-2">{$launcherVersion || 'dev'}</Badge> <Badge class="text-[0.5rem] py-0 px-2">{$launcherVersion || 'dev'}</Badge>
<Button <Button
class="flex size-12 items-center gap-2 {selectedView === 'home' class="flex size-12 items-center gap-2 border {selectedView === 'home'
? 'bg-pink-300/50' ? 'bg-primary-300/50 border-primary-300/50'
: 'bg-black/20'} hover:bg-pink-300/50 rounded-[0.85rem] p-3 mt-3" : 'bg-black/20 border-black/20'} hover:bg-primary-300/50 hover:border-primary-300/50 rounded-[0.85rem] p-3 mt-3"
onclick={() => (selectedView = 'home')} disabled={$launching}
onclick={() => {
if (!$launching) selectedView = 'home';
}}
> >
<House class="text-theme-200 !size-5" /> <House class="text-theme-200 !size-5" />
</Button> </Button>
<Button <Button
class="flex size-12 items-center gap-2 {selectedView === 'settings' class="flex size-12 items-center gap-2 border {selectedView === 'settings'
? 'bg-pink-300/50' ? 'bg-primary-300/50 border-primary-300/50'
: 'bg-black/20'} hover:bg-pink-300/50 rounded-[0.85rem] p-3 mt-3" : 'bg-black/20 border-black/20'} hover:bg-primary-300/50 hover:border-primary-300/50 rounded-[0.85rem] p-3 mt-3"
onclick={() => (selectedView = 'settings')} disabled={$launching}
onclick={() => {
if (!$launching) selectedView = 'settings';
}}
> >
<Settings class="text-theme-200 !size-5" /> <Settings class="text-theme-200 !size-5" />
</Button> </Button>
<div class="mt-auto"> <div class="mt-auto">
<DropdownMenu.Root> <DropdownMenu.Root>
<DropdownMenu.Trigger> <DropdownMenu.Trigger disabled={$launching}>
<Avatar.Root class="size-10"> <Avatar.Root class="size-10">
<Avatar.Image src="https://a.ez-pp.farm/{$currentUser?.id ?? 0}" /> <Avatar.Image src="https://a.ez-pp.farm/{$currentUser?.id ?? 0}" />
<Avatar.Fallback class="bg-theme-900"> <Avatar.Fallback class="bg-theme-900">
@@ -836,8 +918,13 @@
$userAuth.value('username').del(); $userAuth.value('username').del();
$userAuth.value('password').del(); $userAuth.value('password').del();
await $userAuth.save(); await $userAuth.save();
toast.success('Logout successful!', { sileo.success({
title: 'Logout successful!',
description: 'See you soon!', description: 'See you soon!',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
currentUser.set(undefined); currentUser.set(undefined);
currentUserInfo.set(undefined); currentUserInfo.set(undefined);
@@ -861,14 +948,14 @@
{#if selectedView === 'home'} {#if selectedView === 'home'}
<div <div
class="flex flex-col-reverse h-full" class="flex flex-col-reverse h-full"
in:fly={{ duration: 400, delay: 400, y: 10, opacity: 0 }} in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }}
out:fly={{ duration: 400, y: -10, opacity: 0 }} out:fly={{ duration: 400, y: -5, opacity: 0 }}
> >
<div class="relative z-10 px-8 py-4 flex items-center justify-between"> <div class="relative z-10 px-8 py-4 flex items-center justify-between">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
{#if $osuInstallationPath !== ''} {#if $osuInstallationPath !== ''}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Music2 class="size-3.5 text-blue-400" /> <Music class="size-3.5 text-blue-400" />
<span class="text-[11px] text-white/70 font-medium"> <span class="text-[11px] text-white/70 font-medium">
{#if !$beatmapSets && $beatmapSets !== 0} {#if !$beatmapSets && $beatmapSets !== 0}
<LoaderCircle class="animate-spin" size={12} /> <LoaderCircle class="animate-spin" size={12} />
@@ -983,8 +1070,8 @@
{#if $currentUserInfo} {#if $currentUserInfo}
<div <div
class="m-1 w-72 h-48 bg-black/40 backdrop-blur-sm rounded-md border border-black/40 flex flex-col items-center p-3" class="m-1 w-72 h-48 bg-black/40 backdrop-blur-sm rounded-md border border-black/40 flex flex-col items-center p-3"
in:fly={{ duration: 400, y: 10, opacity: 0 }} in:fly={{ duration: 400, y: 5, opacity: 0 }}
out:fly={{ duration: 400, y: -10, opacity: 0 }} out:fly={{ duration: 400, y: -5, opacity: 0 }}
> >
<Select.Root <Select.Root
type="single" type="single"
@@ -1054,7 +1141,7 @@
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-xs text-muted-foreground font-semibold">PP</span> <span class="text-xs text-muted-foreground font-semibold">PP</span>
<div class="flex items-center h-full font-semibold text-pink-300"> <div class="flex items-center h-full font-semibold text-primary-200">
{#if $currentUserInfo} {#if $currentUserInfo}
<div in:fade> <div in:fade>
{#if $reduceAnimations} {#if $reduceAnimations}
@@ -1136,8 +1223,8 @@
{:else if selectedView === 'settings'} {:else if selectedView === 'settings'}
<div <div
class="h-[100vh] w-full flex flex-col items-center justify-center" class="h-[100vh] w-full flex flex-col items-center justify-center"
in:fly={{ duration: 400, delay: 400, y: 10, opacity: 0 }} in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }}
out:fly={{ duration: 400, y: -10, opacity: 0 }} out:fly={{ duration: 400, y: -5, opacity: 0 }}
> >
<div class="p-8 w-full"> <div class="p-8 w-full">
<div class="bg-black/40 backdrop-blur-sm py-8 px-6 rounded-lg"> <div class="bg-black/40 backdrop-blur-sm py-8 px-6 rounded-lg">
@@ -1289,11 +1376,17 @@
bind:value={$launcherStream} bind:value={$launcherStream}
onValueChange={async (newStream) => { onValueChange={async (newStream) => {
const isNet8Installed = await hasNet8(); const isNet8Installed = await hasNet8();
if (newStream === 'experimental' && !isNet8Installed) { if (!isNet8Installed) {
launcherStream.set('stable'); launcherStream.set('stable');
toast.error('.NET 8.0 Desktop Runtime not found!', { sileo.error({
action: { title: 'Hmm...',
label: 'Download .NET 8.0', description: '.NET 8.0 Desktop Runtime not found!',
fill: '#181825',
styles: {
description: 'text-center!',
},
button: {
title: 'Download .NET 8.0',
onClick: async () => onClick: async () =>
await openURL( await openURL(
'https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.22-windows-x64-installer' 'https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.22-windows-x64-installer'
@@ -1332,8 +1425,8 @@
{:else if selectedView === 'login'} {:else if selectedView === 'login'}
<div <div
class="h-[100vh] w-full flex flex-col items-center justify-center" class="h-[100vh] w-full flex flex-col items-center justify-center"
in:fly={{ duration: 400, delay: 400, y: 10, opacity: 0 }} in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }}
out:fly={{ duration: 400, y: -10, opacity: 0 }} out:fly={{ duration: 400, y: -5, opacity: 0 }}
> >
<div class="p-8 px-52 w-full"> <div class="p-8 px-52 w-full">
<form onsubmit={performLogin} class="bg-black/40 backdrop-blur-sm rounded-lg p-8"> <form onsubmit={performLogin} class="bg-black/40 backdrop-blur-sm rounded-lg p-8">
+50 -13
View File
@@ -28,7 +28,6 @@
import Launch from './Launch.svelte'; import Launch from './Launch.svelte';
import { currentUser, userAuth } from '@/userAuthentication'; import { currentUser, userAuth } from '@/userAuthentication';
import { ezppfarm } from '@/api/ezpp'; import { ezppfarm } from '@/api/ezpp';
import { toast } from 'svelte-sonner';
import { currentUserInfo } from '@/data'; import { currentUserInfo } from '@/data';
import { import {
getBeatmapSetsCount, getBeatmapSetsCount,
@@ -40,6 +39,8 @@
isValidOsuFolder, isValidOsuFolder,
} from '@/osuUtil'; } from '@/osuUtil';
import { git } from '@/api/git'; import { git } from '@/api/git';
import { sileo } from 'sileo';
import type { EZPPUser } from '@/types';
let ezppLogo: HTMLImageElement; let ezppLogo: HTMLImageElement;
let spinnerCircle: SVGCircleElement; let spinnerCircle: SVGCircleElement;
@@ -84,25 +85,32 @@
const username = $userAuth.value('username').get(''); const username = $userAuth.value('username').get('');
const password = $userAuth.value('password').get(''); const password = $userAuth.value('password').get('');
if (username.length > 0 && password.length > 0) {
const shouldLogin = username.length > 0 && password.length > 0;
let userLoginResult: { message: string; user?: EZPPUser } = {
message: 'An unknown error occured!',
};
if (shouldLogin) {
currentLoadingInfo.set('Logging in...'); currentLoadingInfo.set('Logging in...');
try { try {
const loginResult = await ezppfarm.login(username, password); const loginResult = await ezppfarm.login(username, password);
if (loginResult && loginResult.user) { if (loginResult && loginResult.user) {
toast.success('Login successful!', { userLoginResult = {
description: `Welcome back, ${loginResult.user.name}!`, message: `Welcome back, ${loginResult.user.name}!`,
}); user: loginResult.user,
};
currentUser.set(loginResult.user); currentUser.set(loginResult.user);
} else { } else {
toast.error('Login failed!', { userLoginResult = {
description: 'Please check your username and password.', message: 'Please check your username and password.',
}); };
} }
} catch { } catch {
toast.error('Server error occurred during login.', { userLoginResult = {
description: 'There was an issue connecting to the server. Please try again later.', message: 'There was an issue connecting to the server. Please try again later.',
}); };
} }
} }
if ($currentUser) { if ($currentUser) {
@@ -123,8 +131,13 @@
osuInstallationPath.set(''); osuInstallationPath.set('');
$userSettings.value('osu_installation_path').del(); $userSettings.value('osu_installation_path').del();
await $userSettings.save(); await $userSettings.save();
toast.error('Oops...', { sileo.error({
title: 'Hmm...',
description: 'Your previously set osu! installation path seems to be invalid.', description: 'Your previously set osu! installation path seems to be invalid.',
fill: '#181825',
styles: {
description: 'text-center!',
},
}); });
} else { } else {
currentLoadingInfo.set('Getting osu version...'); currentLoadingInfo.set('Getting osu version...');
@@ -174,7 +187,31 @@
}); });
setTimeout(() => { setTimeout(() => {
if ($firstStartup) currentView.set(SetupWizard); if ($firstStartup) currentView.set(SetupWizard);
else currentView.set(Launch); else {
currentView.set(Launch);
if (shouldLogin) {
if (userLoginResult.user) {
sileo.success({
title: 'Login successful!',
description: userLoginResult.message,
fill: '#181825',
styles: {
description: 'text-center!',
},
duration: 2500,
});
} else {
sileo.error({
title: 'Login failed!',
description: userLoginResult.message,
fill: '#181825',
styles: {
description: 'text-center!',
},
});
}
}
}
}, 250); }, 250);
}; };
+15 -14
View File
@@ -110,6 +110,7 @@
onMount(async () => { onMount(async () => {
const osuPath = await autoDetectOsuInstallFolder(); const osuPath = await autoDetectOsuInstallFolder();
console.log(osuPath);
if (osuPath) { if (osuPath) {
osuInstallPath = osuPath; osuInstallPath = osuPath;
autoDetectedOsuPath = true; autoDetectedOsuPath = true;
@@ -119,7 +120,7 @@
</script> </script>
{#if wizardFinished} {#if wizardFinished}
<div class="relative flex flex-col items-center justify-center h-[100vh]"> <div class="relative flex flex-col items-center justify-center h-[100vh] z-50">
<div class="absolute h-fit w-fit top-1/2 left-1/2"> <div class="absolute h-fit w-fit top-1/2 left-1/2">
<Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} /> <Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} />
</div> </div>
@@ -128,14 +129,14 @@
<Button class="mt-4" onclick={saveConfig}>Finish</Button> <Button class="mt-4" onclick={saveConfig}>Finish</Button>
</div> </div>
{:else} {:else}
<div class="grid grid-cols-[0.41fr_1fr] h-[100vh]"> <div class="grid grid-cols-[0.41fr_1fr] h-[100vh] z-50">
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3"> <div class="w-full h-full border-r border-theme-800 bg-black/20 backdrop-blur-md flex flex-col gap-6 p-3 z-50">
{#each steps as step, i (step)} {#each steps as step, i (step)}
<div <div
class="flex flex-row items-center gap-2 border {selectedStep === i + 1 class="flex flex-row items-center gap-2 border {selectedStep === i + 1
? 'border-primary-800/30 bg-primary-900/30' ? 'border-primary-800 bg-primary-900'
: selectedStep > i : selectedStep > i
? 'border-green-800/30 bg-green-900/30' ? 'border-green-800 bg-green-900'
: 'border-theme-800 bg-theme-900'} rounded-lg p-2 transition-all" : 'border-theme-800 bg-theme-900'} rounded-lg p-2 transition-all"
> >
<div <div
@@ -160,10 +161,10 @@
</div> </div>
{/each} {/each}
</div> </div>
<div class="flex flex-col gap-6 w-full h-full bg-theme-900/40 p-6"> <div class="flex flex-col gap-6 w-full h-full backdrop-blur-md p-6 z-50">
{#if selectedStep === 1} {#if selectedStep === 1}
<div <div
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 mt-5 flex flex-col items-center justify-center" class="my-auto h-full w-full bg-black/20 rounded-lg border border-theme-900/20 p-6 mt-5 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }} in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
> >
<img <img
@@ -178,13 +179,13 @@
<p class="text-muted-foreground mt-2"> <p class="text-muted-foreground mt-2">
This setup wizard will guide you through the initial setup of EZPPLauncher. This setup wizard will guide you through the initial setup of EZPPLauncher.
</p> </p>
<div class="bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4"> <div class="bg-red-800 border border-red-900 text-red-500 p-4 rounded-lg mt-4">
Please make sure you have osu! installed on your system before proceeding. Please make sure you have osu! installed on your system before proceeding.
</div> </div>
</div> </div>
{:else if selectedStep === 2} {:else if selectedStep === 2}
<div <div
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 mt-5 flex flex-col items-center justify-center" class="my-auto h-full w-full bg-theme-800 rounded-lg border border-theme-900 p-6 mt-5 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }} in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
> >
<h1 class="text-3xl font-semibold">Locate your osu! Installation</h1> <h1 class="text-3xl font-semibold">Locate your osu! Installation</h1>
@@ -207,14 +208,14 @@
{#if !manualSelect} {#if !manualSelect}
{#if autoDetectedOsuPath} {#if autoDetectedOsuPath}
<div <div
class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-green-800 border border-green-900 text-green-500 p-4 rounded-lg mt-4"
> >
<CheckCircle /> <CheckCircle />
<span>Auto-detected osu! installation path! Please check if its correct!</span> <span>Auto-detected osu! installation path! Please check if its correct!</span>
</div> </div>
{:else} {:else}
<div <div
class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-red-800 border border-red-900 text-red-500 p-4 rounded-lg mt-4"
> >
<CircleOff /> <CircleOff />
<span>Could not auto-detect osu! installation path. Please select it manually.</span <span>Could not auto-detect osu! installation path. Please select it manually.</span
@@ -223,14 +224,14 @@
{/if} {/if}
{:else if manualSelectValid} {:else if manualSelectValid}
<div <div
class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-green-800 border border-green-900 text-green-500 p-4 rounded-lg mt-4"
> >
<CheckCircle /> <CheckCircle />
<span>Selected osu! installation path is valid!</span> <span>Selected osu! installation path is valid!</span>
</div> </div>
{:else} {:else}
<div <div
class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-red-800 border border-red-900 text-red-500 p-4 rounded-lg mt-4"
> >
<CircleOff /> <CircleOff />
<span <span
@@ -242,7 +243,7 @@
</div> </div>
{:else if selectedStep === 3} {:else if selectedStep === 3}
<div <div
class="bg-theme-900/90 flex flex-col justify-center gap-3 mt-5 border border-theme-800/90 rounded-lg" class="bg-theme-900 flex flex-col justify-center gap-3 mt-5 border border-theme-800 rounded-lg"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }} in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
> >
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3"> <div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">