Compare commits

..

97 Commits

Author SHA1 Message Date
HorizonCode 44ad025d3d fix: font importing 2026-05-21 23:12:41 +02:00
HorizonCode ec80b13de3 chore: remove unneeded tailwind config file 2026-05-21 23:12:03 +02:00
HorizonCode 6a279de728 chore: update deps, switch from lucide-svelte to @lucide/svelte 2026-05-21 23:02:45 +02:00
HorizonCode 4e33ec3451 fix: resolve vite dynamic import warning 2026-05-21 23:02:14 +02:00
HorizonCode 6fdb2376a1 chore: switch to async rpc framework, fix buttons 2026-05-21 21:24:17 +02:00
HorizonCode afdd74175a fix: cursor smooth movement on higher refreshrate monitors 2026-05-21 20:30:00 +02:00
HorizonCode 6f8043d2d0 fix: cursor rotating on higher refreshrate monitors 2026-05-21 20:23:04 +02:00
HorizonCode f7fff7748a chore: format 2026-05-21 15:31:13 +02:00
HorizonCode d5286263b9 chore: add nsis hook on uninstall to delete user .ezpplauncher dir 2026-05-21 15:30:54 +02:00
HorizonCode 1cc1a4a0c3 chore: remove theme debug script 2026-05-21 14:54:33 +02:00
HorizonCode d2ae9e939f chore: add dynamic button for beatmap display 2026-05-21 14:48:10 +02:00
HorizonCode bde922ab9f fix: file theme import by association file 2026-05-21 14:26:16 +02:00
HorizonCode a1f96a7de3 fix: build 2026-05-21 14:25:13 +02:00
HorizonCode 3b2c902264 fix: build 2026-05-21 14:25:08 +02:00
HorizonCode 40a32d7a60 chore: add theme import for file association 2026-05-21 13:27:31 +02:00
HorizonCode 52c60acf37 chore: add drag and drop theme import 2026-05-21 13:23:03 +02:00
HorizonCode 2788cd6e8b chore: format 2026-05-21 11:44:56 +02:00
HorizonCode 2d1941db33 chore: add drag-and-drop box to Launch page 2026-05-21 11:43:59 +02:00
HorizonCode 00958d5238 chore: only handle .ezpplauncher-theme files 2026-05-21 10:51:51 +02:00
HorizonCode b8a0b448c7 fix: type def for beatmapSetCount 2026-05-21 10:42:13 +02:00
HorizonCode 09487e945e fix: cleanup type defs 2026-05-21 10:41:22 +02:00
HorizonCode 2532ab0059 fix: add ezpplauncher-theme file association to tauri.config.json 2026-05-21 10:27:44 +02:00
HorizonCode 16fe91b3a5 chore: rework presence, add dynamic button functions to presence 2026-05-21 09:21:15 +02:00
HorizonCode eca08f16c5 fix: wait spawning patcher until osu is spawned 2026-05-21 08:18:50 +02:00
HorizonCode e76d67f3a8 fix: add missing imports 2026-05-20 16:32:26 +02:00
HorizonCode 9811b36e58 wip: theme file import from fs 2026-05-20 16:09:05 +02:00
HorizonCode 0f10beb3b4 chore: bump version 2026-05-08 18:06:58 +02:00
HorizonCode 4326199a56 chore: add theme hash checking and alerts 2026-05-08 18:03:26 +02:00
HorizonCode 7f65384b1c chore: add code signing to vscode config 2026-05-08 18:02:32 +02:00
HorizonCode aacd0d1d91 chore: update readme 2026-05-08 18:02:16 +02:00
HorizonCode 079a076f99 chore: update launcher preview 2026-05-08 18:01:55 +02:00
HorizonCode 6d88c39ce2 chore: pause media when launcher is hidden 2026-05-03 17:50:06 +02:00
HorizonCode 56608a64fc chore: use folder name as identification 2026-05-03 15:24:31 +02:00
HorizonCode eaf6f5976d fix: always use underscore 2026-05-03 00:17:50 +02:00
HorizonCode 93f1f380ec fix: regex es6 issue 2026-05-03 00:16:26 +02:00
HorizonCode c47ea1f976 fix: toSafeName windows forbidden symbols 2026-05-03 00:09:01 +02:00
HorizonCode 09af78f1aa fix: use actual setted volume for fade 2026-05-02 22:21:28 +02:00
HorizonCode 3900b5c6cf chore: add volume slider
Co-authored-by: Copilot <copilot@github.com>
2026-05-02 22:15:39 +02:00
HorizonCode adf607e24c chore: clean up comments 2026-05-02 19:56:37 +02:00
HorizonCode 1128b51778 fix: multiple downloads 2026-05-02 19:55:49 +02:00
HorizonCode d435f0c09a chore: add theme uninstall and update 2026-05-02 02:19:00 +02:00
HorizonCode b188e2dee8 chore: remove bundled themes 2026-05-02 00:39:47 +02:00
HorizonCode 8fd56ae749 chore: add custom themes
Co-authored-by: Copilot <copilot@github.com>
2026-05-02 00:38:56 +02:00
HorizonCode b2e7d22d28 chore: add lost umbrella video 2026-04-29 23:33:51 +02:00
HorizonCode d871e06172 chore: add execution clap video 2026-04-29 23:33:15 +02:00
HorizonCode 6a1c5441c3 chore: remove konami code, add themes 2026-04-29 23:32:47 +02:00
HorizonCode ef36f49a6f fix: improve presence reconnection logic and error handling
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 14:04:06 +02:00
HorizonCode 7e40536ec8 chore: add installerIcon to NSIS configuration 2026-04-26 11:29:31 +02:00
HorizonCode 694d403aeb chore: bump version 2026-04-26 11:19:53 +02:00
HorizonCode 9515892b2f fix: remove console log 2026-04-26 11:19:42 +02:00
HorizonCode 0a58f0f63b chore: update profile settings 2026-04-26 11:19:27 +02:00
HorizonCode 60e3ef4131 chore: add umami typing 2026-04-21 09:25:59 +02:00
HorizonCode 7375cccc05 chore: bump version 2026-04-20 12:16:26 +02:00
HorizonCode 251bf8177c chore: add all avalilable fields to beatmap struct 2026-04-20 08:22:40 +02:00
HorizonCode bf4ce18671 chore: replace osynic_osudb with own osudb parser 2026-04-19 18:43:22 +02:00
HorizonCode 4dc83c882d todo: check if this is still needed for linux 2026-04-18 02:34:16 +02:00
HorizonCode e20f11be64 chore: bump version 2026-04-18 02:34:00 +02:00
HorizonCode 407996269e fix: do not save config in setupwizard on value change 2026-04-18 02:33:43 +02:00
HorizonCode 95c6db6a1d fix: remove unused import, set firstStartup on top 2026-04-18 02:33:24 +02:00
HorizonCode a87a4bcc54 chore: bump version and update deps 2026-04-18 02:32:54 +02:00
HorizonCode 97f072f4ce refactor: improve error handling and code readability in replace_ui_files and check_for_corruption functions 2026-04-14 22:52:16 +02:00
HorizonCode 1aa3070ea1 refactor: remove PermissionDenied variant from ReplaceUIError enum 2026-04-14 22:37:33 +02:00
HorizonCode d3c3e4e771 refactor: replace symlink approach with a marker file approach 2026-04-14 22:36:26 +02:00
HorizonCode 921dbd716c chore: update dependencies and version bump to 3.1.3 2026-04-12 22:46:15 +02:00
HorizonCode 34e82f89e8 chore: remove todo note 2026-04-11 21:50:30 +02:00
HorizonCode 901fbefd1b chore: remove unused imports 2026-04-10 12:00:16 +02:00
HorizonCode a795978827 Merge branch 'master' of https://git.ez-pp.farm/EZPPFarm/EZPPLauncher 2026-04-10 11:59:20 +02:00
HorizonCode a9152cc376 chore: remove unused imports 2026-04-10 11:59:18 +02:00
HorizonCode 8d790c7da3 chore: improve UI file replacement logic and fix corruption check 2026-04-10 11:58:11 +02:00
HorizonCode f5a2c4365b fix: check connection issues on load 2026-03-20 13:33:07 +01:00
HorizonCode a925548ae9 refactor: conditionally render patcher release stream for Windows platform 2026-03-05 21:24:37 +01:00
HorizonCode a6495ea353 fix: add schema.tauri.app as trusted schema url 2026-03-03 23:22:10 +01:00
HorizonCode 24802b5132 fix: read beatmapset count from osu!.db instead of iterating songs folder 2026-03-01 23:50:36 +01:00
HorizonCode f09405ed58 refactor: enhance audio fade-in logic for improved user experience 2026-03-01 23:50:06 +01:00
HorizonCode eeda85f48f style: enhance button animations in titlebar for improved visual feedback 2026-03-01 23:49:42 +01:00
HorizonCode 9770e81d6f refactor: improve presence handling and update disconnect logic for clarity 2026-02-28 16:56:46 +01:00
HorizonCode 31b7d17b09 feat: implement sorting functionality for skins using default windows sorting algotythm 2026-02-23 18:23:46 +01:00
HorizonCode 1fc0391113 style: format code for consistency and readability in sileo-toast component 2026-02-22 20:18:37 +01:00
HorizonCode 62f03cf7c0 fix: update ESLint configuration to disable no-empty rule and adjust unused vars handling 2026-02-22 20:18:21 +01:00
HorizonCode 94f6e850cf feat: update icons and launcher files 2026-02-22 20:14:38 +01:00
HorizonCode d6fdb2a152 chore: update Tauri dependencies to latest versions 2026-02-22 20:14:11 +01:00
HorizonCode 9cd990cf96 chore: update dependencies in Cargo.toml 2026-02-22 20:10:15 +01:00
HorizonCode 2e0ef81bcb feat: implement skin management 2026-02-22 17:05:52 +01:00
HorizonCode 96680ff8b5 fix: update button styles to use ring for better accessibility 2026-02-22 14:12:32 +01:00
HorizonCode 0b9d6373bf fix: add fade to bg and audio 2026-02-22 14:03:44 +01:00
HorizonCode 295c1424c9 chore: add audio 2026-02-22 12:35:08 +01:00
HorizonCode 0e12a801d2 chore: add secret :^) 2026-02-22 00:21:42 +01:00
HorizonCode cf86dba5c4 chore: rework titlebar buttons 2026-02-21 23:31:43 +01:00
HorizonCode 540c653399 fix: reconnect presence after pipe was closed 2026-02-21 23:18:01 +01:00
HorizonCode c9b8d9f599 fix: respect reducedAnimations 2026-02-21 22:59:19 +01:00
HorizonCode 49665d94bc fix: Setup WIzard styling 2026-02-19 19:38:21 +01:00
HorizonCode 1a19c19399 chore: bump version 2026-02-19 15:48:26 +01:00
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
95 changed files with 4458 additions and 1627 deletions
+9
View File
@@ -1,7 +1,16 @@
{
"svelte.enable-ts-plugin": true,
"git.enableCommitSigning": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
},
"json.schemaDownload.trustedDomains": {
"https://schemastore.azurewebsites.net/": true,
"https://raw.githubusercontent.com/": true,
"https://www.schemastore.org/": true,
"https://json.schemastore.org/": true,
"https://json-schema.org/": true,
"https://schema.tauri.app/": true
}
}
+7
View File
@@ -22,6 +22,7 @@ It enhances the osu! experience with quality-of-life features and integration sp
- [x] Displays ping to the EZPPFarm server
- [x] Discord Rich Presence
- [x] Performance Display Overlay (currently in experimental patcher release stream)
- [x] Launcher Theming
---
@@ -73,3 +74,9 @@ src-tauri/target/release/ezpplauncher.exe
├── package.json # Bun project config
└── README.md
```
---
## 🎨 Launcher Themes
By default, the launcher includes several downloadable themes. If you want to create your own custom theme, please use the [create-ezpplauncher-theme](https://git.ez-pp.farm/EZPPFarm/create-ezpplauncher-theme) repository for a template and instructions.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 157 KiB

+168 -191
View File
@@ -1,24 +1,25 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "ezpplauncher",
"dependencies": {
"@better-fetch/fetch": "^1.1.18",
"@fontsource/sora": "^5.2.6",
"@fontsource/space-mono": "^5.2.8",
"@number-flow/svelte": "^0.3.9",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.11",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-shell": "^2.3.3",
"@types/umami": "^2.10.1",
"animejs": "^4.1.3",
"@better-fetch/fetch": "^1.1.21",
"@fontsource-variable/sora": "^5.2.8",
"@fontsource/space-mono": "^5.2.9",
"@number-flow/svelte": "^0.4.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.3.0",
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-fs": "^2.5.1",
"@tauri-apps/plugin-shell": "^2.3.5",
"@types/node": "^25.9.1",
"animejs": "^4.4.1",
"buffer": "^6.0.3",
"crypto-js": "^4.2.0",
"lucide-svelte": "^0.562.0",
"jszip": "^3.10.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-to-webcomponent": "^2.0.1",
@@ -31,11 +32,11 @@
"@eslint/compat": "^2.0.0",
"@eslint/js": "^9.35.0",
"@iarna/toml": "2.2.5",
"@lucide/svelte": "^0.562.0",
"@lucide/svelte": "^1.16.0",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.37.1",
"@sveltejs/vite-plugin-svelte": "^6.1.4",
"@tauri-apps/cli": "^2.9.6",
"@sveltejs/kit": "^2.57.1",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tauri-apps/cli": "^2.10.0",
"@types/bun": "^1.2.21",
"@types/crypto-js": "^4.2.2",
"@types/react-dom": "^19.2.3",
@@ -51,7 +52,7 @@
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"sass-embedded": "^1.92.1",
"svelte": "^5.38.7",
"svelte": "^5.55.3",
"svelte-check": "^4.3.1",
"svelte-sonner": "^1.0.5",
"tailwind-merge": "^3.3.1",
@@ -59,69 +60,23 @@
"tailwindcss": "^4.1.7",
"tailwindcss-animate": "^1.0.7",
"tslib": "^2.8.1",
"typescript": "^5.9.2",
"typescript": "^6.0.2",
"typescript-eslint": "^8.42.0",
"vite": "^7.1.5",
"vite": "^8.0.8",
"vite-plugin-devtools-json": "^1.0.0",
},
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="],
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.5.2", "", {}, "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="],
"@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
@@ -149,9 +104,9 @@
"@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
"@fontsource/sora": ["@fontsource/sora@5.2.6", "", {}, "sha512-OPknOmn82HIJg32UGg9iEjEVFdC+xOZBqrmQNUgsem6hFwBBrqXnTwIpro7leq/uFRdupkN42hc0JJKH20j22w=="],
"@fontsource-variable/sora": ["@fontsource-variable/sora@5.2.8", "", {}, "sha512-sP+ILTfi5r3cqmMBntI4ooQNxFI+LyHyD5hcPbWwwL4uh4kKFAkOz0w2veCXjz18fKRdva6OG0dY+5yQ02f30w=="],
"@fontsource/space-mono": ["@fontsource/space-mono@5.2.8", "", {}, "sha512-25X0fg1+kxxLes9/c6AozhrrAuBW1uLaOL584IRg4DZxVxNOMzcS3mHS1UtmsyTwtU2HpiIt9Kv23Q+7xQO+hg=="],
"@fontsource/space-mono": ["@fontsource/space-mono@5.2.9", "", {}, "sha512-b61faFOHEISQ/pD25G+cfGY9o/WW6lRv6hBQQfpWvEJ4y1V+S4gmth95EVyBE2VL3qDYHeVQ8nBzrplzdXTDDg=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -165,8 +120,6 @@
"@internationalized/date": ["@internationalized/date@3.8.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA=="],
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
@@ -179,7 +132,9 @@
"@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.562.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-wDMULwtTFN2Sc/TFBm6gfuVCNb4Y5P9LDrwxNnUbV52+IEU7NXZmvxwXoz+vrrpad6Xupq+Hw5eUlqIHEGhouw=="],
"@lucide/svelte": ["@lucide/svelte@1.16.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-AvvPJnaWxeiNkAljI5MsSEc84yHPLMaWQIAJOcbX7k9au/f9ITS7cxTTQiautDiOFKVOXiYdZ+d6mtl88J+Kbg=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
"@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=="],
@@ -187,7 +142,9 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@number-flow/svelte": ["@number-flow/svelte@0.3.9", "", { "dependencies": { "esm-env": "^1.1.4", "number-flow": "0.5.8" }, "peerDependencies": { "svelte": "^4 || ^5" } }, "sha512-CTw1+e0074GzbPX2IHcNCaK8nqxGNCOIUnQUjEjhcmBwBxOAhN3GYLQ6cJHvhQnWwplVe4eQ3z+c25Vttr2stQ=="],
"@number-flow/svelte": ["@number-flow/svelte@0.4.0", "", { "dependencies": { "esm-env": "^1.1.4", "number-flow": "0.6.0" }, "peerDependencies": { "svelte": "^4 || ^5" } }, "sha512-9tnowrlZlBV3IVe3Gm1V7yXSf4Ugag2k7iW45xqb04HXSa1ApEImopvGWAjJpHDvS849o+UCb0YH461Mtde9lA=="],
"@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
@@ -221,45 +178,37 @@
"@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=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
@@ -267,77 +216,77 @@
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.9", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw=="],
"@sveltejs/kit": ["@sveltejs/kit@2.37.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-4T9rF2Roe7RGvHfcn6+n92Yc2NF88k7ljFz9+wE0jWxyencqRpadr2/CvlcQbbTXf1ozmFxgMO6af+qm+1mPFw=="],
"@sveltejs/kit": ["@sveltejs/kit@2.57.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.4", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.1.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-4jfkfvsGI+U2OhHX8OPCKtMCf7g7ledXhs3E6UcA4EY0jQWsiVbe83pTAHp9XTifzYNOiD4AJieJUsI0qqxsbw=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.0", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.0.0", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g=="],
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
"@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="],
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="],
"@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="],
"@tauri-apps/api": ["@tauri-apps/api@2.9.1", "", {}, "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw=="],
"@tauri-apps/api": ["@tauri-apps/api@2.11.0", "", {}, "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA=="],
"@tauri-apps/cli": ["@tauri-apps/cli@2.9.6", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.9.6", "@tauri-apps/cli-darwin-x64": "2.9.6", "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.6", "@tauri-apps/cli-linux-arm64-gnu": "2.9.6", "@tauri-apps/cli-linux-arm64-musl": "2.9.6", "@tauri-apps/cli-linux-riscv64-gnu": "2.9.6", "@tauri-apps/cli-linux-x64-gnu": "2.9.6", "@tauri-apps/cli-linux-x64-musl": "2.9.6", "@tauri-apps/cli-win32-arm64-msvc": "2.9.6", "@tauri-apps/cli-win32-ia32-msvc": "2.9.6", "@tauri-apps/cli-win32-x64-msvc": "2.9.6" }, "bin": { "tauri": "tauri.js" } }, "sha512-3xDdXL5omQ3sPfBfdC8fCtDKcnyV7OqyzQgfyT5P3+zY6lcPqIYKQBvUasNvppi21RSdfhy44ttvJmftb0PCDw=="],
"@tauri-apps/cli": ["@tauri-apps/cli@2.10.0", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.10.0", "@tauri-apps/cli-darwin-x64": "2.10.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0", "@tauri-apps/cli-linux-arm64-gnu": "2.10.0", "@tauri-apps/cli-linux-arm64-musl": "2.10.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.0", "@tauri-apps/cli-linux-x64-gnu": "2.10.0", "@tauri-apps/cli-linux-x64-musl": "2.10.0", "@tauri-apps/cli-win32-arm64-msvc": "2.10.0", "@tauri-apps/cli-win32-ia32-msvc": "2.10.0", "@tauri-apps/cli-win32-x64-msvc": "2.10.0" }, "bin": { "tauri": "tauri.js" } }, "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q=="],
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.9.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gf5no6N9FCk1qMrti4lfwP77JHP5haASZgVbBgpZG7BUepB3fhiLCXGUK8LvuOjP36HivXewjg72LTnPDScnQQ=="],
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.10.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA=="],
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.9.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-oWh74WmqbERwwrwcueJyY6HYhgCksUc6NT7WKeXyrlY/FPmNgdyQAgcLuTSkhRFuQ6zh4Np1HZpOqCTpeZBDcw=="],
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q=="],
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.9.6", "", { "os": "linux", "cpu": "arm" }, "sha512-/zde3bFroFsNXOHN204DC2qUxAcAanUjVXXSdEGmhwMUZeAQalNj5cz2Qli2elsRjKN/hVbZOJj0gQ5zaYUjSg=="],
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.10.0", "", { "os": "linux", "cpu": "arm" }, "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g=="],
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-pvbljdhp9VOo4RnID5ywSxgBs7qiylTPlK56cTk7InR3kYSTJKYMqv/4Q/4rGo/mG8cVppesKIeBMH42fw6wjg=="],
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.10.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA=="],
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-02TKUndpodXBCR0oP//6dZWGYcc22Upf2eP27NvC6z0DIqvkBBFziQUcvi2n6SrwTRL0yGgQjkm9K5NIn8s6jw=="],
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.10.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA=="],
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.9.6", "", { "os": "linux", "cpu": "none" }, "sha512-fmp1hnulbqzl1GkXl4aTX9fV+ubHw2LqlLH1PE3BxZ11EQk+l/TmiEongjnxF0ie4kV8DQfDNJ1KGiIdWe1GvQ=="],
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.10.0", "", { "os": "linux", "cpu": "none" }, "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw=="],
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-vY0le8ad2KaV1PJr+jCd8fUF9VOjwwQP/uBuTJvhvKTloEwxYA/kAjKK9OpIslGA9m/zcnSo74czI6bBrm2sYA=="],
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.10.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng=="],
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-TOEuB8YCFZTWVDzsO2yW0+zGcoMiPPwcUgdnW1ODnmgfwccpnihDRoks+ABT1e3fHb1ol8QQWsHSCovb3o2ENQ=="],
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.10.0", "", { "os": "linux", "cpu": "x64" }, "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q=="],
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.9.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ujmDGMRc4qRLAnj8nNG26Rlz9klJ0I0jmZs2BPpmNNf0gM/rcVHhqbEkAaHPTBVIrtUdf7bGvQAD2pyIiUrBHQ=="],
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.10.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g=="],
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.9.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-S4pT0yAJgFX8QRCyKA1iKjZ9Q/oPjCZf66A/VlG5Yw54Nnr88J1uBpmenINbXxzyhduWrIXBaUbEY1K80ZbpMg=="],
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.10.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A=="],
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-ldWuWSSkWbKOPjQMJoYVj9wLHcOniv7diyI5UAJ4XsBdtaFB0pKHQsqw/ItUma0VXGC7vB4E9fZjivmxur60aw=="],
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.10.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ=="],
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ=="],
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.7.1", "", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ=="],
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.4.4", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-MTorXxIRmOnOPT1jZ3w96vjSuScER38ryXY88vl5F0uiKdnvTKKTtaEjTEo8uPbl4e3gnUtfsDVwC7h77GQLvQ=="],
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.5.1", "", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-9Lz+Jopp6QyeEWhlpkMx4R/+P9HgR+AVAI4vOZhlT8Xaymtz8iVI/Ov984/XTqgJz/5gz5NretqPB/XEMS3NhQ=="],
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Xod+pRcFxmOWFWEnqH5yZcA7qwAMuaaDkMR1Sply+F8VfBj++CGnj2xf5UoialmjZ2Cvd8qrvSCbU+7GgNVsKQ=="],
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.5", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
@@ -349,7 +298,7 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
@@ -357,7 +306,7 @@
"@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/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.42.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/type-utils": "8.42.0", "@typescript-eslint/utils": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ=="],
@@ -385,13 +334,13 @@
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"animejs": ["animejs@4.1.3", "", {}, "sha512-4XzlIsQsku1ycSPzchxxT0N+ohEMZObG71nOSBBkZoV4sgQvtXa/qAANkFpTE6pegdV8JnIBZiB0LfdxNoRNMw=="],
"animejs": ["animejs@4.4.1", "", {}, "sha512-XimhJw4vPb6tAd6Zaoa6BaspJ8UteQMKvL7Pvrt6MW/6u2UvJs0TB/LLqR1sigaiYvpVPC3Tokr9emcCMhWFhw=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="],
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
@@ -423,8 +372,6 @@
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
@@ -437,6 +384,8 @@
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"crypto-js": ["crypto-js@4.2.0", "", {}, "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="],
@@ -455,13 +404,11 @@
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
"devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="],
"devalue": ["devalue@5.7.1", "", {}, "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.176", "", {}, "sha512-2nDK9orkm7M9ZZkjO3PjbEd3VUulQLyg5T9O3enJdFvUg46Hzd4DUvTvAuEgbdHYXyFsiG4A5sO9IzToMH1cDg=="],
"enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
"esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
"enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@@ -483,7 +430,7 @@
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
"esrap": ["esrap@2.2.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
@@ -533,12 +480,16 @@
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
"immutable": ["immutable@5.1.3", "", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
@@ -549,6 +500,8 @@
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
@@ -561,6 +514,8 @@
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
@@ -569,27 +524,31 @@
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
@@ -597,15 +556,9 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="],
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lucide-svelte": ["lucide-svelte@0.562.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-kSJDH/55lf0mun/o4nqWBXOcq0fWYzPeIjbTD97ywoeumAB9kWxtM06gC7oynqjtK3XhAljWSz5RafIzPEYIQA=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
@@ -613,12 +566,6 @@
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
"minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
"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=="],
@@ -643,7 +590,9 @@
"normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
"number-flow": ["number-flow@0.5.8", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA=="],
"number-flow": ["number-flow@0.6.0", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-K8flNq2Wqus53vjp/btVo3qXFkagF8dIdYavreBfE7hlvFFG/b1HMGEH6nZL+mlrJ+4lbLP9OmPv3t2rmRkpSQ=="],
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
@@ -651,6 +600,8 @@
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
@@ -659,7 +610,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
@@ -679,6 +630,8 @@
"prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="],
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
@@ -689,13 +642,15 @@
"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=="],
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rollup": ["rollup@4.44.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.1", "@rollup/rollup-android-arm64": "4.44.1", "@rollup/rollup-darwin-arm64": "4.44.1", "@rollup/rollup-darwin-x64": "4.44.1", "@rollup/rollup-freebsd-arm64": "4.44.1", "@rollup/rollup-freebsd-x64": "4.44.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.1", "@rollup/rollup-linux-arm64-gnu": "4.44.1", "@rollup/rollup-linux-arm64-musl": "4.44.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-musl": "4.44.1", "@rollup/rollup-linux-s390x-gnu": "4.44.1", "@rollup/rollup-linux-x64-gnu": "4.44.1", "@rollup/rollup-linux-x64-musl": "4.44.1", "@rollup/rollup-win32-arm64-msvc": "4.44.1", "@rollup/rollup-win32-ia32-msvc": "4.44.1", "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg=="],
"rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
@@ -705,6 +660,8 @@
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"sass": ["sass@1.92.1", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ=="],
"sass-embedded": ["sass-embedded@1.92.1", "", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-all-unknown": "1.92.1", "sass-embedded-android-arm": "1.92.1", "sass-embedded-android-arm64": "1.92.1", "sass-embedded-android-riscv64": "1.92.1", "sass-embedded-android-x64": "1.92.1", "sass-embedded-darwin-arm64": "1.92.1", "sass-embedded-darwin-x64": "1.92.1", "sass-embedded-linux-arm": "1.92.1", "sass-embedded-linux-arm64": "1.92.1", "sass-embedded-linux-musl-arm": "1.92.1", "sass-embedded-linux-musl-arm64": "1.92.1", "sass-embedded-linux-musl-riscv64": "1.92.1", "sass-embedded-linux-musl-x64": "1.92.1", "sass-embedded-linux-riscv64": "1.92.1", "sass-embedded-linux-x64": "1.92.1", "sass-embedded-unknown-all": "1.92.1", "sass-embedded-win32-arm64": "1.92.1", "sass-embedded-win32-x64": "1.92.1" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-28YwLnF5atAhogt3E4hXzz/NB9dwKffyw08a7DEasLh94P7+aELkG3ENSHYCWB9QFN14hYNLfwr9ozUsPDhcDQ=="],
@@ -749,7 +706,9 @@
"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@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="],
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
@@ -761,13 +720,15 @@
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="],
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"svelte": ["svelte@5.38.7", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-1ld9TPZSdUS3EtYGQzisU2nhwXoIzNQcZ71IOU9fEmltaUofQnVfW5CQuhgM/zFsZ43arZXS1BRKi0MYgUV91w=="],
"svelte": ["svelte@5.55.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-dS1N+i3bA1v+c4UDb750MlN5vCO82G6vxh8HeTsPsTdJ1BLsN1zxSyDlIdBBqUjqZ/BxEwM8UrFf98aaoVnZFQ=="],
"svelte-check": ["svelte-check@4.3.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg=="],
@@ -793,9 +754,7 @@
"tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
"tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
"tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
@@ -811,11 +770,11 @@
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
"typescript-eslint": ["typescript-eslint@8.42.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/parser": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/utils": "8.42.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
@@ -827,18 +786,16 @@
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
"vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="],
"vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="],
"vite-plugin-devtools-json": ["vite-plugin-devtools-json@1.0.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw=="],
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
"vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
@@ -855,22 +812,32 @@
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
"@tailwindcss/node/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
"@tailwindcss/node/tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tailwindcss/vite/tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="],
"@tauri-apps/plugin-shell/@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"bun-types/@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"eslint/@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
@@ -879,22 +846,32 @@
"eslint-plugin-svelte/globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="],
"esrap/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="],
"svelte/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"svelte-eslint-parser/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
"tinyglobby/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"vite/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"vite/postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"eslint-plugin-svelte/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
}
}
+9
View File
@@ -36,5 +36,14 @@ export default ts.config(
svelteConfig,
},
},
},
{
rules: {
'no-empty': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^\\$\\$(Props|Events|Slots|Generic)$' },
],
},
}
);
+23 -23
View File
@@ -1,13 +1,13 @@
{
"name": "ezpplauncher",
"version": "3.1.1",
"version": "3.2.0",
"description": "",
"type": "module",
"scripts": {
"debug": "bun run tauri dev",
"build": "bun ./scripts/sync-version.ts && bun run vite:build && bun run tauri:build",
"build": "bun ./scripts/build.ts",
"vite:dev": "vite dev",
"vite:build": "vite build",
"vite:build": "vite build && exit 0",
"tauri:build": "tauri build",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
@@ -18,21 +18,21 @@
},
"license": "GPL-3.0-only",
"dependencies": {
"@better-fetch/fetch": "^1.1.18",
"@fontsource/sora": "^5.2.6",
"@fontsource/space-mono": "^5.2.8",
"@number-flow/svelte": "^0.3.9",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.11",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-shell": "^2.3.3",
"@types/umami": "^2.10.1",
"animejs": "^4.1.3",
"@better-fetch/fetch": "^1.1.21",
"@fontsource-variable/sora": "^5.2.8",
"@fontsource/space-mono": "^5.2.9",
"@number-flow/svelte": "^0.4.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.3.0",
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-fs": "^2.5.1",
"@tauri-apps/plugin-shell": "^2.3.5",
"@types/node": "^25.9.1",
"animejs": "^4.4.1",
"buffer": "^6.0.3",
"crypto-js": "^4.2.0",
"lucide-svelte": "^0.562.0",
"jszip": "^3.10.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-to-webcomponent": "^2.0.1",
@@ -45,11 +45,11 @@
"@eslint/compat": "^2.0.0",
"@eslint/js": "^9.35.0",
"@iarna/toml": "2.2.5",
"@lucide/svelte": "^0.562.0",
"@lucide/svelte": "^1.16.0",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.37.1",
"@sveltejs/vite-plugin-svelte": "^6.1.4",
"@tauri-apps/cli": "^2.9.6",
"@sveltejs/kit": "^2.57.1",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tauri-apps/cli": "^2.10.0",
"@types/bun": "^1.2.21",
"@types/crypto-js": "^4.2.2",
"@types/react-dom": "^19.2.3",
@@ -65,7 +65,7 @@
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"sass-embedded": "^1.92.1",
"svelte": "^5.38.7",
"svelte": "^5.55.3",
"svelte-check": "^4.3.1",
"svelte-sonner": "^1.0.5",
"tailwind-merge": "^3.3.1",
@@ -73,9 +73,9 @@
"tailwindcss": "^4.1.7",
"tailwindcss-animate": "^1.0.7",
"tslib": "^2.8.1",
"typescript": "^5.9.2",
"typescript": "^6.0.2",
"typescript-eslint": "^8.42.0",
"vite": "^7.1.5",
"vite": "^8.0.8",
"vite-plugin-devtools-json": "^1.0.0"
}
}
+37
View File
@@ -0,0 +1,37 @@
import { spawn } from 'child_process';
function runVite(): Promise<void> {
return new Promise((resolve, reject) => {
const proc = spawn('bunx', ['--bun', 'vite', 'build'], { stdio: ['inherit', 'pipe', 'pipe'] });
proc.stdout?.on('data', (data: Buffer) => {
process.stdout.write(data);
if (data.toString().includes('✔ done')) {
setTimeout(() => {
proc.kill();
resolve();
}, 500);
}
});
proc.stderr?.on('data', (data: Buffer) => process.stderr.write(data));
proc.on('close', (code) => {
if (code === 0 || code === null) resolve();
else reject(new Error(`vite build exited with code ${code}`));
});
});
}
function run(cmd: string, args: string[]): Promise<void> {
return new Promise((resolve, reject) => {
const proc = spawn(cmd, args, { stdio: 'inherit' });
proc.on('close', (code) => {
if (code === 0) resolve();
else reject(new Error(`${cmd} exited with code ${code}`));
});
});
}
await run('bun', ['./scripts/sync-version.ts']);
await runVite();
await run('bunx', ['tauri', 'build']);
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"types": ["@types/bun", "@types/node"]
},
"include": ["*.ts"]
}
+741 -491
View File
File diff suppressed because it is too large Load Diff
+31 -17
View File
@@ -1,12 +1,22 @@
[package]
name = "ezpplauncher"
version = "3.1.1"
version = "3.2.0"
description = "EZPPLauncher redefined."
authors = ["HorizonCode"]
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev]
incremental = true # Compile your binary in smaller steps.
[profile.release]
codegen-units = 1 # Allows LLVM to perform better optimization.
lto = true # Enables link-time-optimizations.
opt-level = "s" # Prioritizes small binary size. Use `3` if you prefer speed.
panic = "abort" # Higher performance by disabling panic handlers.
strip = true # Ensures debug symbols are removed.
[lib]
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
@@ -15,33 +25,37 @@ name = "ezpplauncher_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.5.3", features = [] }
tauri-build = { version = "2.6.2", features = [] }
[dependencies]
tauri = { version = "2.9.5", features = [] }
tauri-plugin-shell = "2.3.3"
tauri = { version = "2.11.2", features = ["protocol-asset"] }
tauri-plugin-shell = "2.3.5"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.148"
serde_json = "1.0.150"
serde_repr = "0.1.20"
tauri-plugin-dialog = "2.4.2"
tauri-plugin-fs = "2.4.4"
tauri-plugin-dialog = "2.7.1"
tauri-plugin-fs = "2.5.1"
hardware-id = "0.3.0"
tauri-plugin-cors-fetch = "4.1.0"
sysinfo = "0.37.2"
reqwest = { version = "0.13.1", features = ["json", "stream", "query"] }
tauri-plugin-cors-fetch = "5.0.0"
sysinfo = "0.39.2"
reqwest = { version = "0.13.3", features = ["json", "stream", "query"] }
md5 = "0.8.0"
tokio = { version = "1.48.0", features = ["full"] }
open = "5.3.3"
windows-sys = "0.61.2"
discord-rich-presence = "1.0.0"
once_cell = "1.21.3"
tokio = { version = "1.52.2", features = ["full"] }
open = "5.3.5"
presenceforge = { version = "0.2.1", features = ["tokio-runtime"] }
once_cell = "1.21.4"
futures-util = "0.3.32"
byteorder = "1.5.0"
anyhow = "1.0.102"
url = "2.5.8"
[target.'cfg(windows)'.dependencies]
winreg = "0.55.0"
winreg = "0.56.0"
winapi = { version = "0.3", features = ["winuser", "wincrypt", "memoryapi", "winbase", "dpapi"] }
base64 = "0.22.1"
widestring = "1.2.1"
windows-sys = "0.61.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = "2.3.6"
tauri-plugin-single-instance = "2.4.2"
+95 -23
View File
@@ -2,7 +2,9 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"windows": [
"main"
],
"permissions": [
"core:default",
"shell:allow-open",
@@ -19,58 +21,128 @@
{
"identifier": "fs:allow-read",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-write",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-exists",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-read-file",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-write-file",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-read-text-file",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-mkdir",
"allow": [
{ "path": "$HOME/.ezpplauncher" },
{ "path": "$HOME/.ezpplauncher/*" },
{ "path": "$HOME/.ezpplauncher/**/*" }
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-read-dir",
"allow": [
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
},
{
"identifier": "fs:allow-remove",
"allow": [
{
"path": "$HOME/.ezpplauncher"
},
{
"path": "$HOME/.ezpplauncher/*"
},
{
"path": "$HOME/.ezpplauncher/**/*"
}
]
}
]
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<background android:drawable="@color/ic_launcher_background"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#fff</color>
</resources>
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+15
View File
@@ -3,4 +3,19 @@
${OrIf} ${Silent}
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
${EndIf}
!macroend
!macro NSIS_HOOK_PREUNINSTALL
${If} $DeleteAppDataCheckboxState = 1
MessageBox MB_YESNO "Do you want to remove all EZPPLauncher data and themes?" IDYES do_delete IDNO skip_delete
do_delete:
StrCpy $R9 1
skip_delete:
${EndIf}
!macroend
!macro NSIS_HOOK_POSTUNINSTALL
${If} $R9 = 1
RMDir /r "$PROFILE\.ezpplauncher"
${EndIf}
!macroend
+287 -97
View File
@@ -12,6 +12,8 @@ use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use tokio::time::{Duration, sleep};
use crate::OpenedUrls;
use crate::osudb::parse_osudb;
use crate::presence;
use crate::utils::{
check_folder_completeness, encrypt_password, get_osu_config, get_osu_user_config,
@@ -19,6 +21,11 @@ use crate::utils::{
set_osu_config_vals, set_osu_user_config_vals,
};
#[tauri::command]
pub fn opened_urls(app: tauri::AppHandle) -> Vec<PathBuf> {
app.state::<OpenedUrls>().0.lock().unwrap().clone()
}
#[tauri::command]
pub fn get_launcher_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
@@ -77,10 +84,10 @@ pub fn find_osu_installation() -> Option<String> {
let hklm_registry_paths = ["SOFTWARE\\Classes\\osu\\DefaultIcon"];
let hkcr_registry_paths = [
"osustable.File.osk\\DefaultIcon",
"osustable.File.osr\\DefaultIcon",
"osustable.File.osz\\DefaultIcon",
let hkcu_registry_paths = [
"Software\\Classes\\osustable.File.osk\\DefaultIcon",
"Software\\Classes\\osustable.File.osr\\DefaultIcon",
"Software\\Classes\\osustable.File.osz\\DefaultIcon",
];
let osu_folder_files = [
@@ -129,10 +136,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 {
if let Ok(subkey) = hkcr.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) {
for reg_path in &hkcu_registry_paths {
if let Ok(subkey) = hkcu.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) {
let value_names = [""];
for value_name in &value_names {
if let Ok(value) = subkey.get_value::<String, _>(value_name) {
@@ -156,33 +163,88 @@ pub fn find_osu_installation() -> Option<String> {
#[tauri::command]
pub async fn get_beatmapsets_count(folder: String) -> Option<u64> {
let path = PathBuf::from(folder);
let osu_user_config = get_osu_user_config(path.clone());
let songs_path = osu_user_config
.and_then(|config| config.get("Songs").cloned())
.unwrap_or_else(|| path.join("Songs").to_string_lossy().into_owned());
let songs_folder = PathBuf::from(songs_path);
let osu_db_path = path.join("osu!.db");
if !osu_db_path.exists() {
return Some(0);
}
let osu_db_bytes = fs::read(osu_db_path).await.ok()?;
let osu_db = parse_osudb(osu_db_bytes).ok()?;
let beatmap_sets = osu_db
.beatmaps
.iter()
.map(|b| b.beatmapset_id)
.collect::<HashSet<_>>()
.len();
Some(beatmap_sets as u64)
}
if !songs_folder.exists() {
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SkinInfo {
pub name: String,
pub author: Option<String>,
pub modified: u64,
}
#[tauri::command]
pub async fn get_skins(folder: String) -> Option<Vec<SkinInfo>> {
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
use tokio::fs;
let skins_folder = PathBuf::from(folder).join("Skins");
if !skins_folder.exists() {
return None;
}
let mut count = 0;
if let Ok(mut entries) = fs::read_dir(songs_folder).await {
while let Ok(Some(entry)) = entries.next_entry().await {
if entry.file_type().await.map_or(false, |ft| ft.is_dir()) {
let dir_path = entry.path();
if let Ok(mut files) = fs::read_dir(&dir_path).await {
while let Ok(Some(file)) = files.next_entry().await {
if file.path().extension().map_or(false, |ext| ext == "osu") {
count += 1;
break;
}
}
}
}
let mut entries = fs::read_dir(skins_folder).await.ok()?;
let mut skins = Vec::new();
while let Some(entry) = entries.next_entry().await.ok()? {
if !entry.file_type().await.ok()?.is_dir() {
continue;
}
let dir_path = entry.path();
let skin_ini = dir_path.join("skin.ini");
if !skin_ini.exists() {
continue;
}
let name = entry.file_name().to_string_lossy().to_string();
let author = fs::read_to_string(&skin_ini)
.await
.ok()
.and_then(|content| {
content.lines().find_map(|line| {
let (key, value) = line.split_once(':')?;
key.trim()
.eq_ignore_ascii_case("Author")
.then(|| value.trim().to_string())
})
});
let modified = entry
.metadata()
.await
.ok()
.and_then(|m| m.modified().ok())
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
.map(|d| d.as_secs())
.unwrap_or(0);
skins.push(SkinInfo {
name,
author,
modified,
});
}
return Some(count);
Some(skins)
}
#[tauri::command]
@@ -191,7 +253,7 @@ pub async fn get_skins_count(folder: String) -> Option<u64> {
let skins_folder = path.join("Skins");
if !skins_folder.exists() {
return None;
return Some(0);
}
let mut count = 0;
@@ -380,21 +442,48 @@ pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
}
};
if patch {
sleep(Duration::from_secs(3)).await;
#[cfg(windows)]
{
if patch {
let patcher_exe_path = PathBuf::from(&folder)
.join("EZPPLauncher")
.join("patcher")
.join("osu!.patcher.exe");
let patcher_exe_path = PathBuf::from(&folder)
.join("EZPPLauncher")
.join("patcher")
.join("osu!.patcher.exe");
if patcher_exe_path.exists() {
#[cfg(windows)]
{
let mut sys = System::new_all();
if patcher_exe_path.exists() {
#[cfg(windows)]
{
let _ = Command::new(&patcher_exe_path)
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.spawn()
.map_err(|e| format!("Failed to run patcher: {e}"))?;
loop {
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
let mut found = false;
for (_pid, process) in sys.processes() {
if process.name() == "osu!.exe" {
let pid = process.pid();
let title = get_window_title_by_pid(pid);
if !title.is_empty() && !title.contains("updater") {
found = true;
break;
}
}
}
if found {
break;
}
sleep(Duration::from_millis(500)).await;
}
let _ = Command::new(&patcher_exe_path)
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.spawn()
.map_err(|e| format!("Failed to run patcher: {e}"))?;
}
}
}
}
@@ -539,7 +628,6 @@ pub async fn download_ezpp_launcher_update_files(
#[serde(tag = "type", content = "details")]
pub enum ReplaceUIError {
FileNotFound(String),
PermissionDenied(String),
IoError(String),
}
@@ -557,44 +645,160 @@ pub fn replace_ui_files(folder: String, revert: bool) -> Result<(), ReplaceUIErr
let osu_seasonal_bak = osu_path.join("osu!seasonal.dll.bak");
let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak");
let try_rename = |from: &PathBuf, to: &PathBuf| -> Result<(), ReplaceUIError> {
if !from.exists() {
return Err(ReplaceUIError::FileNotFound(from.display().to_string()));
let temp_file = osu_path.join(".ezpplauncher_files");
let copy_file = |source: &PathBuf, dest: &PathBuf, name: &str| -> Result<(), ReplaceUIError> {
if !source.exists() {
return Err(ReplaceUIError::FileNotFound(source.display().to_string()));
}
std::fs::rename(from, to).map_err(|e| match e.kind() {
std::io::ErrorKind::NotFound => {
ReplaceUIError::FileNotFound(from.display().to_string())
}
std::io::ErrorKind::PermissionDenied => {
ReplaceUIError::PermissionDenied(from.display().to_string())
}
_ => ReplaceUIError::IoError(e.to_string()),
})
if dest.exists() {
std::fs::remove_file(dest).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to remove {}: {}", name, e))
})?;
}
std::fs::copy(source, dest)
.map_err(|e| ReplaceUIError::IoError(format!("Failed to copy {}: {}", name, e)))?;
Ok(())
};
let restore_backup =
|bak: &PathBuf, dest: &PathBuf, name: &str| -> Result<(), ReplaceUIError> {
if bak.exists() {
if dest.exists() {
std::fs::remove_file(dest).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to remove {}: {}", name, e))
})?;
}
std::fs::rename(bak, dest).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to rename {} from backup: {}", name, e))
})?;
}
Ok(())
};
if !revert {
try_rename(&osu_ui, &osu_ui_bak)?;
try_rename(&ezpp_ui, &osu_ui)?;
if osu_ui.exists() && !osu_ui_bak.exists() {
std::fs::rename(&osu_ui, &osu_ui_bak).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to backup osu!ui.dll: {}", e))
})?;
}
if osu_seasonal.exists() && !osu_seasonal_bak.exists() {
std::fs::rename(&osu_seasonal, &osu_seasonal_bak).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to backup osu!seasonal.dll: {}", e))
})?;
}
if osu_gameplay.exists() && !osu_gameplay_bak.exists() {
std::fs::rename(&osu_gameplay, &osu_gameplay_bak).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to backup osu!gameplay.dll: {}", e))
})?;
}
try_rename(&osu_seasonal, &osu_seasonal_bak)?;
try_rename(&ezpp_seasonal, &osu_seasonal)?;
copy_file(&ezpp_ui, &osu_ui, "osu!ui.dll")?;
copy_file(&ezpp_seasonal, &osu_seasonal, "osu!seasonal.dll")?;
copy_file(&ezpp_gameplay, &osu_gameplay, "osu!gameplay.dll")?;
try_rename(&osu_gameplay, &osu_gameplay_bak)?;
try_rename(&ezpp_gameplay, &osu_gameplay)?;
std::fs::write(&temp_file, b"1")
.map_err(|e| ReplaceUIError::IoError(format!("Failed to write marker file: {}", e)))?;
} else {
try_rename(&osu_ui, &ezpp_ui)?;
try_rename(&osu_ui_bak, &osu_ui)?;
restore_backup(&osu_ui_bak, &osu_ui, "osu!ui.dll")?;
restore_backup(&osu_seasonal_bak, &osu_seasonal, "osu!seasonal.dll")?;
restore_backup(&osu_gameplay_bak, &osu_gameplay, "osu!gameplay.dll")?;
try_rename(&osu_seasonal, &ezpp_seasonal)?;
try_rename(&osu_seasonal_bak, &osu_seasonal)?;
try_rename(&osu_gameplay, &ezpp_gameplay)?;
try_rename(&osu_gameplay_bak, &osu_gameplay)?;
if osu_ui.exists() && osu_seasonal.exists() && osu_gameplay.exists() {
std::fs::remove_file(&temp_file).map_err(|e| {
ReplaceUIError::IoError(format!("Failed to remove marker file: {}", e))
})?;
}
}
Ok(())
}
#[derive(Serialize)]
#[serde(tag = "type", content = "details")]
pub enum CorruptionCheckError {
IoError(String),
FileNotFound(String),
}
#[tauri::command]
pub async fn check_for_corruption(folder: String) -> Result<bool, CorruptionCheckError> {
let osu_path = PathBuf::from(folder);
let osu_ui = osu_path.join("osu!ui.dll");
let osu_gameplay = osu_path.join("osu!gameplay.dll");
let osu_seasonal = osu_path.join("osu!seasonal.dll");
let osu_ui_bak = osu_path.join("osu!ui.dll.bak");
let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak");
let osu_seasonal_bak = osu_path.join("osu!seasonal.dll.bak");
let temp_file = osu_path.join(".ezpplauncher_files");
if temp_file.exists() {
if osu_ui_bak.exists() {
if osu_ui.exists() {
fs::remove_file(&osu_ui).await.map_err(|e| {
CorruptionCheckError::IoError(format!("Failed to remove osu!ui.dll: {}", e))
})?;
}
fs::rename(&osu_ui_bak, &osu_ui).await.map_err(|e| {
CorruptionCheckError::IoError(format!(
"Failed to restore osu!ui.dll from backup: {}",
e
))
})?;
}
if osu_gameplay_bak.exists() {
if osu_gameplay.exists() {
fs::remove_file(&osu_gameplay).await.map_err(|e| {
CorruptionCheckError::IoError(format!(
"Failed to remove osu!gameplay.dll: {}",
e
))
})?;
}
fs::rename(&osu_gameplay_bak, &osu_gameplay)
.await
.map_err(|e| {
CorruptionCheckError::IoError(format!(
"Failed to restore osu!gameplay.dll from backup: {}",
e
))
})?;
}
if osu_seasonal_bak.exists() {
if osu_seasonal.exists() {
fs::remove_file(&osu_seasonal).await.map_err(|e| {
CorruptionCheckError::IoError(format!(
"Failed to remove osu!seasonal.dll: {}",
e
))
})?;
}
fs::rename(&osu_seasonal_bak, &osu_seasonal)
.await
.map_err(|e| {
CorruptionCheckError::IoError(format!(
"Failed to restore osu!seasonal.dll from backup: {}",
e
))
})?;
}
return Ok(false);
}
if !osu_ui.exists() || !osu_gameplay.exists() || !osu_seasonal.exists() {
return Ok(true);
}
Ok(false)
}
#[tauri::command]
pub fn is_osu_running() -> bool {
let mut sys = System::new_all();
@@ -624,35 +828,6 @@ pub fn get_platform() -> String {
std::env::consts::OS.to_string()
}
#[tauri::command]
pub async fn check_for_corruption(folder: String) -> Result<bool, String> {
let osu_path = PathBuf::from(folder);
let osu_ui = osu_path.join("osu!ui.dll");
let osu_gameplay = osu_path.join("osu!gameplay.dll");
let osu_ui_bak = osu_path.join("osu!ui.dll.bak");
let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak");
let required_files = [&osu_ui, &osu_gameplay];
for file in &required_files {
if !file.exists() {
return Ok(true);
}
}
let bak_files = [&osu_ui_bak, &osu_gameplay_bak];
for bak in &bak_files {
if bak.exists() {
if let Err(e) = fs::remove_file(bak).await {
return Err(format!("Failed to delete {}: {}", bak.display(), e));
}
}
}
Ok(false)
}
#[tauri::command]
pub async fn presence_connect() -> bool {
presence::connect().await
@@ -692,6 +867,21 @@ pub async fn presence_update_user(user: PresenceUser) {
presence::update_user(user.username.as_deref(), user.id.as_deref());
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PresenceButton {
text: Option<String>,
url: Option<String>,
}
#[tauri::command]
pub async fn presence_update_button(button: PresenceButton) {
match (button.text.as_deref(), button.url.as_deref()) {
(Some(text), Some(url)) => presence::set_button(text, url),
_ =>presence::clear_button()
}
}
#[tauri::command]
pub async fn presence_is_connected() -> bool {
presence::has_presence().await
+66 -14
View File
@@ -1,42 +1,98 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
use std::path::PathBuf;
use std::sync::Mutex;
use tauri::Manager;
use tauri_plugin_fs::FsExt;
pub mod commands;
pub mod osudb;
pub mod presence;
pub mod utils;
use crate::commands::{
check_for_corruption, download_ezpp_launcher_update, download_ezpp_launcher_update_files,
encrypt_string, exit, find_osu_installation, get_beatmapsets_count,
get_ezpp_launcher_update_files, get_hwid, get_launcher_version, get_osu_release_stream,
get_osu_skin, get_osu_version, get_platform, get_skins_count, has_net8, has_osuwinello,
has_wmctrl, install_ezpp_launcher_update, is_osu_running, open_url_in_browser,
presence_connect, presence_disconnect, presence_is_connected, presence_update_status,
presence_update_user, replace_ui_files, run_osu, run_osu_updater, set_osu_config_values,
set_osu_user_config_values, valid_osu_folder,
get_osu_skin, get_osu_version, get_platform, get_skins, get_skins_count, has_net8,
has_osuwinello, has_wmctrl, install_ezpp_launcher_update, is_osu_running, open_url_in_browser,
opened_urls, presence_connect, presence_disconnect, presence_is_connected,
presence_update_status, presence_update_user, presence_update_button, replace_ui_files, run_osu, run_osu_updater,
set_osu_config_values, set_osu_user_config_values, valid_osu_folder,
};
struct OpenedUrls(Mutex<Vec<PathBuf>>);
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
#[cfg(target_os = "linux")]
unsafe {
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
// std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
}
let mut builder = tauri::Builder::default();
#[cfg(desktop)]
{
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
builder = builder.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
let app_window = app.get_webview_window("main").expect("no main window");
app_window.set_focus().expect("failed to focus");
if args.len() > 1 {
use tauri::Emitter;
let mut files = Vec::new();
for maybe_file in args.iter().skip(1) {
if maybe_file.starts_with("-") {
continue;
}
let path = PathBuf::from(maybe_file);
if path.extension().and_then(|e| e.to_str()) == Some("ezpplauncher-theme") {
app.fs_scope().allow_file(&path).ok();
files.push(path);
}
}
app.state::<OpenedUrls>()
.0
.lock()
.unwrap()
.extend(files.clone());
app.emit("opened", files.clone()).ok();
}
}));
}
let app = builder
.manage(OpenedUrls(Mutex::new(vec![])))
.setup(|app| {
let args = std::env::args().collect::<Vec<_>>();
if args.len() > 1 {
let mut files = Vec::new();
for maybe_file in args.iter().skip(1) {
if maybe_file.starts_with("-") {
continue;
}
let path = PathBuf::from(maybe_file);
if path.extension().and_then(|e| e.to_str()) == Some("ezpplauncher-theme") {
app.fs_scope().allow_file(&path).ok();
files.push(path);
}
}
app.state::<OpenedUrls>().0.lock().unwrap().extend(files);
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
opened_urls,
get_hwid,
find_osu_installation,
valid_osu_folder,
get_beatmapsets_count,
get_skins,
get_skins_count,
get_osu_version,
get_osu_release_stream,
@@ -58,6 +114,7 @@ pub fn run() {
presence_disconnect,
presence_update_status,
presence_update_user,
presence_update_button,
presence_is_connected,
has_osuwinello,
has_wmctrl,
@@ -73,12 +130,7 @@ pub fn run() {
.build(tauri::generate_context!())
.expect("error while building tauri application");
app.run(|_app_handle, event| {
if let tauri::RunEvent::ExitRequested { api, .. } = event {
api.prevent_exit();
tauri::async_runtime::block_on(presence::disconnect());
std::process::exit(0);
}
app.run(|app, event| {
presence::handle_run_event(app, &event);
});
}
+370
View File
@@ -0,0 +1,370 @@
use std::io::{Cursor, Read};
use anyhow::{Result, anyhow};
use byteorder::{LittleEndian, ReadBytesExt};
use std::collections::HashMap;
pub struct OsuReader {
cursor: Cursor<Vec<u8>>,
}
impl OsuReader {
pub fn new(data: Vec<u8>) -> Self {
Self {
cursor: Cursor::new(data),
}
}
fn read_i16(&mut self) -> Result<i16> {
Ok(self.cursor.read_i16::<LittleEndian>()?)
}
fn read_i32(&mut self) -> Result<i32> {
Ok(self.cursor.read_i32::<LittleEndian>()?)
}
fn read_i64(&mut self) -> Result<i64> {
Ok(self.cursor.read_i64::<LittleEndian>()?)
}
fn read_u8(&mut self) -> Result<u8> {
Ok(self.cursor.read_u8()?)
}
fn read_f32(&mut self) -> Result<f32> {
Ok(self.cursor.read_f32::<LittleEndian>()?)
}
fn read_f64(&mut self) -> Result<f64> {
Ok(self.cursor.read_f64::<LittleEndian>()?)
}
fn read_bool(&mut self) -> Result<bool> {
Ok(self.read_u8()? != 0)
}
fn read_string(&mut self) -> Result<Option<String>> {
let indicator = self.read_u8()?;
if indicator == 0 {
return Ok(None);
}
if indicator != 0x0B {
return Err(anyhow!("Invalid osu string indicator"));
}
let len = self.read_uleb128()? as usize;
let mut buf = vec![0; len];
self.cursor.read_exact(&mut buf)?;
Ok(Some(String::from_utf8(buf)?))
}
fn read_uleb128(&mut self) -> Result<u64> {
let mut result = 0;
let mut shift = 0;
loop {
let byte = self.read_u8()?;
result |= ((byte & 0x7F) as u64) << shift;
if (byte & 0x80) == 0 {
break;
}
shift += 7;
}
Ok(result)
}
}
#[derive(Debug)]
pub struct TimingPoint {
pub bpm: f64,
pub offset: f64,
pub inherited: bool,
}
#[derive(Debug)]
pub struct Beatmap {
pub artist_name: Option<String>,
pub artist_unicode: Option<String>,
pub song_title: Option<String>,
pub song_title_unicode: Option<String>,
pub creator: Option<String>,
pub difficulty: Option<String>,
pub audio: Option<String>,
pub md5: Option<String>,
pub osu_file_name: Option<String>,
pub ranked_status: u8,
pub hitcircles: i16,
pub slider_count: i16,
pub spinner_count: i16,
pub last_modification: i64,
pub approach_rate: f32,
pub circle_size: f32,
pub hp_drain: f32,
pub overall_difficulty: f32,
pub slider_velocity: f64,
pub difficulties: HashMap<i32, f64>,
pub drain_time: i32,
pub total_time: i32,
pub preview_offset: i32,
pub beatmap_id: i32,
pub beatmapset_id: i32,
pub thread_id: i32,
pub grade_standard: u8,
pub grade_taiko: u8,
pub grade_catch: u8,
pub grade_mania: u8,
pub local_beatmap_offset: i16,
pub stack_leniency: f32,
pub mode: u8,
pub song_source: Option<String>,
pub song_tags: Option<String>,
pub online_beatmap_offset: i16,
pub title_font: Option<String>,
pub unplayed: bool,
pub last_played: i64,
pub osz2: bool,
pub folder_name: Option<String>,
pub last_checked_against_repository: i64,
pub ignore_sound: bool,
pub ignore_skin: bool,
pub disable_storyboard: bool,
pub disable_video: bool,
pub visual_override: bool,
pub last_modification_time: i32,
pub mania_scroll_speed: u8,
pub timing_points: Vec<TimingPoint>,
}
#[derive(Debug)]
pub struct OsuDbData {
pub osuver: i32,
pub folder_count: i32,
pub is_unlocked: bool,
pub date_unlock_ticks: i64,
pub username: Option<String>,
pub beatmaps: Vec<Beatmap>,
pub userperms: i32,
}
//
// =========================
// PARSER
// =========================
//
pub fn parse_osudb(bytes: Vec<u8>) -> Result<OsuDbData> {
let mut reader = OsuReader::new(bytes);
let osuver = reader.read_i32()?;
let folder_count = reader.read_i32()?;
let is_unlocked = reader.read_bool()?;
let date_unlock_ticks = reader.read_i64()?;
let username = reader.read_string()?;
let beatmaps_count = reader.read_i32()?;
let mut beatmaps = Vec::new();
for _ in 0..beatmaps_count {
if osuver < 20191107 {
reader.read_i32()?; // entry size
}
let artist_name = reader.read_string()?;
let artist_unicode = reader.read_string()?;
let song_title = reader.read_string()?;
let song_title_unicode = reader.read_string()?;
let creator = reader.read_string()?;
let difficulty = reader.read_string()?;
let audio = reader.read_string()?;
let md5 = reader.read_string()?;
let osu_file_name = reader.read_string()?;
let ranked_status = reader.read_u8()?;
let hitcircles = reader.read_i16()?;
let slider_count = reader.read_i16()?;
let spinner_count = reader.read_i16()?;
let last_modification = reader.read_i64()?;
let approach_rate = if osuver < 20140609 {
reader.read_u8()? as f32
} else {
reader.read_f32()?
};
let circle_size = if osuver < 20140609 {
reader.read_u8()? as f32
} else {
reader.read_f32()?
};
let hp_drain = if osuver < 20140609 {
reader.read_u8()? as f32
} else {
reader.read_f32()?
};
let overall_difficulty = if osuver < 20140609 {
reader.read_u8()? as f32
} else {
reader.read_f32()?
};
let slider_velocity = reader.read_f64()?;
let mut difficulties: HashMap<i32, f64> = HashMap::new();
if osuver >= 20140609 {
for _ in 0..4 {
let len = reader.read_i32()?;
for _ in 0..len {
reader.read_u8()?;
let mode = reader.read_i32()?;
reader.read_u8()?;
let diff = if osuver > 20250107 {
reader.read_f32()? as f64
} else {
reader.read_f64()?
};
difficulties.insert(mode, diff);
}
}
}
let drain_time = reader.read_i32()?;
let total_time = reader.read_i32()?;
let preview_offset = reader.read_i32()?;
let timing_len = reader.read_i32()?;
let mut timing_points = Vec::new();
for _ in 0..timing_len {
timing_points.push(TimingPoint {
bpm: reader.read_f64()?,
offset: reader.read_f64()?,
inherited: reader.read_bool()?,
});
}
let beatmap_id = reader.read_i32()?;
let beatmapset_id = reader.read_i32()?;
let thread_id = reader.read_i32()?;
let grade_standard = reader.read_u8()?;
let grade_taiko = reader.read_u8()?;
let grade_catch = reader.read_u8()?;
let grade_mania = reader.read_u8()?;
let local_beatmap_offset = reader.read_i16()?;
let stack_leniency = reader.read_f32()?;
let mode = reader.read_u8()?;
let song_source = reader.read_string()?;
let song_tags = reader.read_string()?;
let online_beatmap_offset = reader.read_i16()?;
let title_font = reader.read_string()?;
let unplayed = reader.read_bool()?;
let last_played = reader.read_i64()?;
let osz2 = reader.read_bool()?;
let folder_name = reader.read_string()?;
let last_checked_against_repository = reader.read_i64()?;
let ignore_sound = reader.read_bool()?;
let ignore_skin = reader.read_bool()?;
let disable_storyboard = reader.read_bool()?;
let disable_video = reader.read_bool()?;
let visual_override = reader.read_bool()?;
if osuver < 20140609 {
reader.read_i16()?;
}
let last_modification_time = reader.read_i32()?;
let mania_scroll_speed = reader.read_u8()?;
beatmaps.push(Beatmap {
artist_name,
artist_unicode,
song_title,
song_title_unicode,
creator,
difficulty,
audio,
md5,
osu_file_name,
ranked_status,
hitcircles,
slider_count,
spinner_count,
last_modification,
approach_rate,
circle_size,
hp_drain,
overall_difficulty,
slider_velocity,
difficulties,
drain_time,
total_time,
preview_offset,
beatmap_id,
beatmapset_id,
thread_id,
grade_standard,
grade_taiko,
grade_catch,
grade_mania,
local_beatmap_offset,
stack_leniency,
mode,
song_source,
song_tags,
online_beatmap_offset,
title_font,
unplayed,
last_played,
osz2,
folder_name,
last_checked_against_repository,
ignore_sound,
ignore_skin,
disable_storyboard,
disable_video,
visual_override,
last_modification_time,
mania_scroll_speed,
timing_points,
});
}
let userperms = reader.read_i32()?;
Ok(OsuDbData {
osuver,
folder_count,
is_unlocked,
date_unlock_ticks,
username,
beatmaps,
userperms,
})
}
+232 -111
View File
@@ -1,13 +1,17 @@
use discord_rich_presence::{
DiscordIpc, DiscordIpcClient,
activity::{Activity, Assets, Button, Timestamps},
};
use once_cell::sync::Lazy;
use presenceforge::{ActivityBuilder, AsyncDiscordIpcClient};
use std::sync::Mutex as StdMutex;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::{AppHandle, RunEvent};
use tokio::sync::{mpsc, oneshot};
use tokio::time::{Duration, interval};
#[derive(Clone, Debug)]
pub struct PresenceButton {
pub label: String,
pub url: String,
}
#[derive(Clone, Debug)]
pub struct PresenceData {
pub state: String,
@@ -16,6 +20,21 @@ pub struct PresenceData {
pub large_image_text: String,
pub small_image_key: Option<String>,
pub small_image_text: Option<String>,
pub dynamic_button: Option<PresenceButton>,
}
impl Default for PresenceData {
fn default() -> Self {
Self {
state: "Idle in Launcher...".to_string(),
details: " ".to_string(),
large_image_key: "ezppfarm".to_string(),
large_image_text: "EZPPFarm".to_string(),
small_image_key: None,
small_image_text: None,
dynamic_button: None,
}
}
}
#[derive(Debug)]
@@ -26,11 +45,19 @@ enum PresenceCommand {
IsConnected(oneshot::Sender<bool>),
}
enum ReconnectResult {
Connected(AsyncDiscordIpcClient),
Failed,
}
struct PresenceActor {
receiver: mpsc::Receiver<PresenceCommand>,
client: Option<DiscordIpcClient>,
reconnect_rx: mpsc::Receiver<ReconnectResult>,
reconnect_tx: mpsc::Sender<ReconnectResult>,
client: Option<AsyncDiscordIpcClient>,
data: PresenceData,
start_timestamp: i64,
reconnecting: bool,
}
impl PresenceActor {
@@ -40,137 +67,184 @@ impl PresenceActor {
.unwrap()
.as_secs() as i64;
let data = PresenceData {
state: "Idle in Launcher...".to_string(),
details: " ".to_string(),
large_image_key: "ezppfarm".to_string(),
large_image_text: "EZPPFarm".to_string(),
small_image_key: None,
small_image_text: None,
};
let (reconnect_tx, reconnect_rx) = mpsc::channel(1);
PresenceActor {
Self {
receiver,
reconnect_rx,
reconnect_tx,
client: None,
data,
data: PresenceData::default(),
start_timestamp: start,
reconnecting: false,
}
}
async fn run(&mut self) {
let mut update_interval = interval(Duration::from_millis(2500));
let mut heartbeat = interval(Duration::from_secs(5));
heartbeat.tick().await;
loop {
tokio::select! {
Some(cmd) = self.receiver.recv() => {
match cmd {
PresenceCommand::Connect(responder) => self.handle_connect(responder).await,
PresenceCommand::Disconnect(responder) => {
self.handle_disconnect(responder).await;
},
PresenceCommand::UpdateData(new_data) => {
self.data = new_data;
},
PresenceCommand::IsConnected(responder) => {
let _ = responder.send(self.client.is_some());
PresenceCommand::Connect(resp) => self.cmd_connect(resp).await,
PresenceCommand::Disconnect(resp) => self.cmd_disconnect(resp).await,
PresenceCommand::UpdateData(data) => {
self.data = data;
self.push_presence().await;
}
PresenceCommand::IsConnected(resp) => {
let _ = resp.send(self.client.is_some());
}
}
}
_ = update_interval.tick() => {
if self.client.is_some() {
self.handle_update().await;
Some(result) = self.reconnect_rx.recv() => {
self.reconnecting = false;
match result {
ReconnectResult::Connected(client) => {
println!("Presence: reconnect succeeded.");
self.client = Some(client);
self.push_presence().await;
}
ReconnectResult::Failed => {
eprintln!("Presence: all reconnect attempts exhausted.");
}
}
}
_ = heartbeat.tick() => {
self.push_presence().await;
}
}
}
}
async fn handle_connect(&mut self, responder: oneshot::Sender<bool>) {
async fn cmd_connect(&mut self, resp: oneshot::Sender<bool>) {
if self.client.is_some() {
let _ = responder.send(true);
let _ = resp.send(true);
return;
}
println!("Actor: Connecting to Discord...");
let mut dc_client = DiscordIpcClient::new("1032772293220384808");
match dc_client.connect() {
Ok(()) => {
self.client = Some(dc_client);
println!("Actor: Connected successfully.");
self.handle_update().await;
let _ = responder.send(true);
match try_connect_once().await {
Some(client) => {
self.client = Some(client);
self.reconnecting = false;
self.push_presence().await;
let _ = resp.send(true);
}
Err(e) => {
eprintln!("Failed to create Discord client: {:?}", e);
let _ = responder.send(false);
None => {
eprintln!("Presence: initial connect failed; spawning reconnect task.");
let _ = resp.send(false);
self.spawn_reconnect_task();
}
}
}
async fn handle_disconnect(&mut self, responder: oneshot::Sender<()>) {
async fn cmd_disconnect(&mut self, resp: oneshot::Sender<()>) {
if let Some(mut client) = self.client.take() {
println!("Actor: Disconnecting...");
let _ = client.clear_activity().map_err(|e| e.to_string());
let _ = client.close().map_err(|e| e.to_string());
println!("Actor: Disconnected successfully.");
let _ = client.clear_activity().await;
println!("Presence: disconnected.");
}
let _ = responder.send(());
let _ = resp.send(());
}
async fn handle_update(&mut self) {
if let Some(client) = self.client.as_mut() {
let mut assets = Assets::new()
.large_image(&self.data.large_image_key)
.large_text(&self.data.large_image_text);
async fn push_presence(&mut self) {
let client = match self.client.as_mut() {
Some(c) => c,
None => return,
};
if let Some(key) = &self.data.small_image_key {
assets = assets.small_image(key);
}
if let Some(text) = &self.data.small_image_text {
assets = assets.small_text(text);
}
let mut builder = ActivityBuilder::new()
.state(&self.data.state)
.details(&self.data.details)
.start_timestamp(self.start_timestamp as u64)
.large_image(&self.data.large_image_key)
.large_text(&self.data.large_image_text);
let activity = Activity::new()
.state(&self.data.state)
.details(&self.data.details)
.timestamps(Timestamps::new().start(self.start_timestamp))
.assets(assets)
.buttons(vec![
Button::new(
"Download the Launcher",
"https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest",
),
Button::new("Join EZPPFarm", "https://ez-pp.farm/discord"),
]);
if let Some(key) = &self.data.small_image_key {
builder = builder.small_image(key);
}
if let Some(text) = &self.data.small_image_text {
builder = builder.small_text(text);
}
if let Err(e) = client.set_activity(activity).map_err(|e| e.to_string()) {
eprintln!("Failed to set activity, disconnecting: {:?}", e);
if let Some(mut client) = self.client.take() {
let _ = client.clear_activity();
let _ = client.close();
if let Some(btn) = &self.data.dynamic_button {
builder = builder.button(&btn.label, &btn.url);
} else {
builder = builder.button(
"Download the Launcher",
"https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest",
);
}
builder = builder.button("Join EZPPFarm", "https://ez-pp.farm/discord");
let activity = builder.build();
/* println!("Presence: updating activity: {:?}", activity); */
if let Err(e) = client.set_activity(&activity).await {
eprintln!("Presence: set_activity failed ({:?}); will reconnect.", e);
self.client = None;
self.spawn_reconnect_task();
}
}
fn spawn_reconnect_task(&mut self) {
if self.reconnecting {
return;
}
self.reconnecting = true;
let tx = self.reconnect_tx.clone();
tokio::spawn(async move {
const MAX_ATTEMPTS: u32 = 5;
for attempt in 1..=MAX_ATTEMPTS {
let delay = Duration::from_secs(5 * attempt as u64);
println!(
"Presence: reconnect attempt {}/{} in {:?}…",
attempt, MAX_ATTEMPTS, delay
);
tokio::time::sleep(delay).await;
if let Some(client) = try_connect_once().await {
let _ = tx.send(ReconnectResult::Connected(client)).await;
return;
}
}
let _ = tx.send(ReconnectResult::Failed).await;
});
}
}
async fn try_connect_once() -> Option<AsyncDiscordIpcClient> {
match AsyncDiscordIpcClient::new("1032772293220384808").await {
Ok(mut client) => match client.connect().await {
Ok(_) => {
println!("Presence: IPC connected.");
Some(client)
}
Err(e) => {
eprintln!("Presence: IPC connect error: {:?}", e);
None
}
},
Err(e) => {
eprintln!("Presence: failed to create client: {:?}", e);
None
}
}
}
static PRESENCE_TX: Lazy<mpsc::Sender<PresenceCommand>> = Lazy::new(|| {
let (tx, rx) = mpsc::channel(10);
let (tx, rx) = mpsc::channel(32);
let mut actor = PresenceActor::new(rx);
tokio::spawn(async move { actor.run().await });
tx
});
pub static PRESENCE_DATA: Lazy<StdMutex<PresenceData>> = Lazy::new(|| {
StdMutex::new(PresenceData {
state: "Idle in Launcher...".to_string(),
details: " ".to_string(),
large_image_key: "ezppfarm".to_string(),
large_image_text: "EZPPFarm".to_string(),
small_image_key: None,
small_image_text: None,
})
});
pub static PRESENCE_DATA: Lazy<StdMutex<PresenceData>> =
Lazy::new(|| StdMutex::new(PresenceData::default()));
pub async fn connect() -> bool {
let (tx, rx) = oneshot::channel();
@@ -182,14 +256,8 @@ pub async fn connect() -> bool {
pub async fn disconnect() {
let (tx, rx) = oneshot::channel();
if PRESENCE_TX
.send(PresenceCommand::Disconnect(tx))
.await
.is_ok()
{
let _ = rx.await;
} else {
println!("Could not send disconnect command; actor may not be running.");
if PRESENCE_TX.send(PresenceCommand::Disconnect(tx)).await.is_ok() {
let _ = tokio::time::timeout(Duration::from_secs(2), rx).await;
}
}
@@ -206,30 +274,83 @@ pub async fn has_presence() -> bool {
}
pub fn update_status(state: Option<&str>, details: Option<&str>, large_image_key: Option<&str>) {
let mut data = PRESENCE_DATA.lock().unwrap();
if let Some(s) = state {
data.state = s.to_string();
}
if let Some(d) = details {
data.details = d.to_string();
}
if let Some(img) = large_image_key {
data.large_image_key = img.to_string();
}
let data_clone = data.clone();
let data = {
let mut guard = PRESENCE_DATA.lock().unwrap();
if let Some(s) = state {
guard.state = s.to_string();
}
if let Some(d) = details {
guard.details = d.to_string();
}
if let Some(img) = large_image_key {
guard.large_image_key = img.to_string();
}
guard.clone()
};
let tx = PRESENCE_TX.clone();
tokio::spawn(async move {
let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await;
let _ = tx.send(PresenceCommand::UpdateData(data)).await;
});
}
pub fn update_user(username: Option<&str>, id: Option<&str>) {
let mut data = PRESENCE_DATA.lock().unwrap();
data.small_image_key = id.map(|id_str| format!("https://a.ez-pp.farm/{}", id_str));
data.small_image_text = username.map(|s| s.to_string());
let data_clone = data.clone();
let data = {
let mut guard = PRESENCE_DATA.lock().unwrap();
guard.small_image_key = id.map(|s| format!("https://a.ez-pp.farm/{}", s));
guard.small_image_text = username.map(str::to_string);
guard.clone()
};
let tx = PRESENCE_TX.clone();
tokio::spawn(async move {
let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await;
let _ = tx.send(PresenceCommand::UpdateData(data)).await;
});
}
pub fn set_button(label: &str, url: &str) {
let data = {
let mut guard = PRESENCE_DATA.lock().unwrap();
guard.dynamic_button = Some(PresenceButton {
label: label.to_string(),
url: url.to_string(),
});
guard.clone()
};
let tx = PRESENCE_TX.clone();
tokio::spawn(async move {
let _ = tx.send(PresenceCommand::UpdateData(data)).await;
});
}
pub fn clear_button() {
let data = {
let mut guard = PRESENCE_DATA.lock().unwrap();
guard.dynamic_button = None;
guard.clone()
};
let tx = PRESENCE_TX.clone();
tokio::spawn(async move {
let _ = tx.send(PresenceCommand::UpdateData(data)).await;
});
}
/// Register a Tauri event handler that cleanly disconnects presence when the
/// application exits. Call this once in your main.rs `tauri::Builder` run callback:
///
/// ```rust
/// .build(tauri::generate_context!())
/// .expect("error building app")
/// .run(|app, event| {
/// presence::handle_run_event(app, &event);
/// });
/// ```
pub fn handle_run_event(_app: &AppHandle, event: &RunEvent) {
if let RunEvent::ExitRequested { .. } | RunEvent::Exit = event {
tauri::async_runtime::block_on(async {
disconnect().await;
});
}
}
+18 -2
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "EZPPLauncher",
"version": "3.1.1",
"version": "3.2.0",
"identifier": "farm.EZPPFarm.Launcher",
"mainBinaryName": "ezpplauncher",
"build": {
@@ -23,7 +23,13 @@
],
"withGlobalTauri": true,
"security": {
"csp": null
"csp": null,
"assetProtocol": {
"enable": true,
"scope": [
"$HOME/.ezpplauncher/themes/**"
]
}
}
},
"bundle": {
@@ -36,6 +42,15 @@
"nsis",
"appimage"
],
"fileAssociations": [
{
"ext": ["ezpplauncher-theme"],
"name": "ezppLauncher Theme",
"description": "ezppLauncher Theme File",
"role": "Viewer",
"mimeType": "application/x-ezpplauncher-theme"
}
],
"windows": {
"webviewInstallMode": {
"silent": true,
@@ -44,6 +59,7 @@
"nsis": {
"installMode": "currentUser",
"compression": "lzma",
"installerIcon": "icons/icon.ico",
"installerHooks": "./nsis/nsis-hooks.nsh"
}
},
+60 -1
View File
@@ -4,6 +4,9 @@
@custom-variant dark (&:where(.dark, .dark *));
@import "@fontsource-variable/sora";
@import "@fontsource/space-mono";
:root {
--radius: 0.625rem;
--background: oklch(95.36% 0.017 256.57);
@@ -41,7 +44,7 @@
--sidebar-ring: oklch(60.7% 0.226 299.68);
--font-sans:
"Sora", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Sora Variable", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", "Noto Color Emoji";
--font-mono:
"Space Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
@@ -145,6 +148,62 @@
--color-sidebar-ring: var(--sidebar-ring);
}
main[data-theme="execution_clap"] {
--primary: #fc112b;
--primary-foreground: #fff;
--ring: #fc112b;
--color-primary-50: #fef2f2;
--color-primary-100: #fee2e2;
--color-primary-200: #fecaca;
--color-primary-300: #fca5a5;
--color-primary-400: #f87171;
--color-primary-500: #ef4444;
--color-primary-600: #dc2626;
--color-primary-700: #b91c1c;
--color-primary-800: #991b1b;
--color-primary-900: #7f1d1d;
--color-primary-950: #450a0a;
--bg-primary-50: #fef2f2;
--bg-primary-100: #fee2e2;
--bg-primary-200: #fecaca;
--bg-primary-300: #fca5a5;
--bg-primary-400: #f87171;
--bg-primary-500: #ef4444;
--bg-primary-600: #dc2626;
--bg-primary-700: #b91c1c;
--bg-primary-800: #991b1b;
--bg-primary-900: #7f1d1d;
--bg-primary-950: #450a0a;
}
main[data-theme="lost_umbrella"] {
--primary: #e5e5e5;
--primary-foreground: #000;
--ring: #e5e5e5;
--color-primary-50: #0d0d0d;
--color-primary-100: #1a1a1a;
--color-primary-200: #333333;
--color-primary-300: #4d4d4d;
--color-primary-400: #666666;
--color-primary-500: #808080;
--color-primary-600: #999999;
--color-primary-700: #b3b3b3;
--color-primary-800: #cccccc;
--color-primary-900: #e6e6e6;
--color-primary-950: #f2f2f2;
--bg-primary-50: #0d0d0d;
--bg-primary-100: #1a1a1a;
--bg-primary-200: #333333;
--bg-primary-300: #4d4d4d;
--bg-primary-400: #666666;
--bg-primary-500: #808080;
--bg-primary-600: #999999;
--bg-primary-700: #b3b3b3;
--bg-primary-800: #cccccc;
--bg-primary-900: #e6e6e6;
--bg-primary-950: #f2f2f2;
}
@layer base {
* {
@apply border-border outline-ring/50;
Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

@@ -1,4 +1,6 @@
<script lang="ts">
import { reduceAnimations } from '@/userSettings';
//TODO: maybe dynamic background images fetched from ezpp?
const COUNT = 60;
@@ -7,41 +9,43 @@
return Math.random() * (max - min) + min;
}
const streaks = Array.from({ length: COUNT }).map((_, i) => {
const depth = rand(0, 2);
const streaks = Array.from({ length: COUNT })
.map((_, i) => {
const depth = rand(0, 2);
const scale = 0.6 + depth * 0.8;
const speed = 0.4 + depth * 1.35;
const scale = 0.6 + depth * 0.8;
const speed = 0.4 + depth * 1.35;
const distance = 150;
const duration = distance / speed;
const distance = 150;
const duration = distance / speed;
return {
width: rand(180, 320) * scale,
height: rand(16, 32) * scale,
x: (i / COUNT) * 200 - 50,
y: ((i * 137.5) % 100) - 20,
opacity: 0.25 + depth * 0.65,
duration,
delay: rand(0, duration),
scale,
};
});
return {
width: rand(300, 320) * scale,
height: 32 * scale,
x: (i / COUNT) * 200 - 50,
y: ((i * 137.5) % 100) - 20,
opacity: 0.25 + depth * 0.65,
duration,
delay: rand(0, duration),
scale,
};
})
.sort((a, b) => a.scale - b.scale);
</script>
<div class="absolute bg blur-[2px]">
<div
class="relative h-full w-full from-0% to-90% bg-gradient-to-b from-transparent to-black z-50"
></div>
{#each streaks as s}
{#each streaks as s, index (index)}
<div
class="streak drop-shadow-lg"
class="streak drop-shadow-lg transition-opacity duration-1000"
style="
width:{s.width}px;
height:{s.height}px;
left:{s.x}%;
top:{s.y}%;
opacity:{s.opacity};
opacity:{$reduceAnimations ? 0 : s.opacity};
animation-duration:{s.duration}s;
animation-delay:-{s.delay}s;
"
@@ -54,7 +58,12 @@
position: fixed;
inset: 0;
overflow: hidden;
background: linear-gradient(180deg, var(--color-primary-300) 0%, var(--color-primary-600) 40%, var(--color-primary-900) 100%);
background: linear-gradient(
180deg,
var(--color-primary-300) 0%,
var(--color-primary-600) 40%,
var(--color-primary-900) 100%
);
}
.streak {
@@ -0,0 +1,91 @@
<script lang="ts">
import NyanCat from '$assets/themes/nyan_cat/image.gif';
import { reduceAnimations } from '@/userSettings';
//TODO: maybe dynamic background images fetched from ezpp?
const COUNT = 18;
function rand(min: number, max: number) {
return Math.random() * (max - min) + min;
}
const streaks = Array.from({ length: COUNT })
.map((_, i) => {
const depth = rand(0, 2);
const scale = 0.6 + depth * 0.8;
const speed = 0.4 + depth * 1.35;
const distance = 150;
const duration = distance / speed;
return {
width: 290 * scale,
height: 140 * scale,
x: (i / COUNT) * 200 - 50,
y: ((i * 137.5) % 100) - 20,
opacity: 0.25 + depth * 0.65,
duration,
delay: rand(0, duration),
scale,
};
})
.sort((a, b) => a.scale - b.scale);
</script>
<div class="absolute bg blur-[2px]">
<div
class="relative h-full w-full from-0% to-90% bg-gradient-to-b from-transparent to-black z-50"
></div>
{#each streaks as s, index (index)}
<div
class="streak drop-shadow-lg transition-opacity duration-1000"
style="
width:{s.width}px;
height:{s.height}px;
left:{s.x}%;
top:{s.y}%;
opacity:{$reduceAnimations ? 0 : s.opacity};
animation-duration:{s.duration}s;
animation-delay:-{s.delay}s;
"
>
<img
src={NyanCat}
alt="nyan cat"
class="w-full h-full"
style="opacity:{$reduceAnimations ? 0 : s.opacity};"
/>
</div>
{/each}
</div>
<style>
.bg {
position: fixed;
inset: 0;
overflow: hidden;
background: linear-gradient(
180deg,
var(--color-blue-800) 0%,
var(--color-blue-600) 40%,
var(--color-blue-900) 100%
);
}
.streak {
position: absolute;
animation: move linear infinite;
will-change: transform;
}
@keyframes move {
from {
transform: translate(-250vw, 0);
}
to {
transform: translate(250vw, 0);
}
}
</style>
@@ -1,6 +1,6 @@
<script lang="ts">
import Ellipsis from 'lucide-svelte/icons/ellipsis';
import Play from 'lucide-svelte/icons/play';
import Ellipsis from '@lucide/svelte/icons/ellipsis';
import Play from '@lucide/svelte/icons/play';
const prop: {
downloading: boolean;
@@ -17,7 +17,7 @@
</script>
<button
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"
class="group flex items-center h-12 rounded-full bg-primary disabled:bg-primary/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}
onclick={prop.onClick}
>
+1 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { cn } from '$lib/utils';
import { Heart } from 'lucide-svelte';
import { Heart } from '@lucide/svelte';
interface Sparkle {
id: string;
@@ -7,17 +7,26 @@
let { smoothCursor = true }: { smoothCursor?: boolean } = $props();
const VELOCITY_WINDOW_MS = 16;
const SPEED_THRESHOLD = 9;
const REQUIRED_CONSECUTIVE = 3;
let mouseX = $state(0);
let mouseY = $state(0);
let lastMouseX = $state(0);
let lastMouseY = $state(0);
let lastMouseX = 0;
let lastMouseY = 0;
let isMouseDown = $state(false);
let isHoveringInteractive = $state(false);
let dragStartX = $state(0);
let dragStartY = $state(0);
let dragStartX = 0;
let dragStartY = 0;
let degrees = $state(0);
let applyRotation = $state(false);
let lastVelocityTime = 0;
let consecutiveHighSpeed = 0;
let velocityAccumX = 0;
let velocityAccumY = 0;
function isInteractive(el: Element | null): boolean {
while (el) {
const tag = el.tagName.toLowerCase();
@@ -30,7 +39,6 @@
) {
return true;
}
el = el.parentElement;
}
return false;
@@ -43,17 +51,32 @@
const deltaX = e.pageX - window.pageXOffset - dragStartX;
const deltaY = e.pageY - window.pageYOffset - dragStartY;
const velocityX = mouseX - lastMouseX;
const velocityY = mouseY - lastMouseY;
velocityAccumX += mouseX - lastMouseX;
velocityAccumY += mouseY - lastMouseY;
const dist = Math.sqrt(velocityX * velocityX + velocityY * velocityY);
const now = performance.now();
const dt = now - lastVelocityTime;
if (!applyRotation && isMouseDown && dist > 80) applyRotation = true;
if (dt >= VELOCITY_WINDOW_MS) {
const speed = Math.sqrt(velocityAccumX ** 2 + velocityAccumY ** 2) / dt;
if (speed > SPEED_THRESHOLD) {
consecutiveHighSpeed++;
if (!applyRotation && isMouseDown && consecutiveHighSpeed >= REQUIRED_CONSECUTIVE) {
applyRotation = true;
}
} else {
consecutiveHighSpeed = 0;
}
velocityAccumX = 0;
velocityAccumY = 0;
lastVelocityTime = now;
}
let newDegrees = (Math.atan2(-deltaX, deltaY) * 180) / Math.PI + 24.3;
let diff = (newDegrees - degrees) % 360;
if (diff < -180) diff += 360;
if (diff > 180) diff -= 360;
@@ -67,26 +90,18 @@
isHoveringInteractive = isInteractive(el);
if (!isMouseDown) {
if (isHoveringInteractive) {
animate(cursorAdditive, {
opacity: 1,
duration: 800,
ease: (t: number) => (t - 1) ** 5 + 1,
});
} else {
animate(cursorAdditive, {
opacity: 0,
duration: 800,
ease: (t: number) => (t - 1) ** 5 + 1,
});
}
animate(cursorAdditive, {
opacity: isHoveringInteractive ? 1 : 0,
duration: 800,
ease: (t: number) => (t - 1) ** 5 + 1,
});
}
animate(cursor, {
duration: smoothCursor ? $cursorSmoothness : 0,
translateX: mouseX,
translateY: mouseY,
ease: (t: number) => (t - 1) ** 5 + 1,
ease: (t: number) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
});
animate(cursor, {
@@ -107,6 +122,11 @@
dragStartX = event.clientX;
dragStartY = event.clientY;
velocityAccumX = 0;
velocityAccumY = 0;
consecutiveHighSpeed = 0;
lastVelocityTime = performance.now();
isMouseDown = true;
animate(cursorInner, {
scale: 0.9,
@@ -124,6 +144,10 @@
const handleMouseUp = () => {
isMouseDown = false;
applyRotation = false;
consecutiveHighSpeed = 0;
velocityAccumX = 0;
velocityAccumY = 0;
animate(cursorInner, {
scale: 1,
duration: 500,
@@ -179,4 +203,4 @@
:global(html.hiddenCursor textarea) {
cursor: none !important;
}
</style>
</style>
@@ -0,0 +1,150 @@
<script lang="ts">
// Props
let { children, class: className = '', topOffset = 0 } = $props();
let wrapper: HTMLDivElement;
let container: HTMLDivElement;
let thumb: HTMLDivElement;
// Thumb state
let thumbHeight = $state(20);
let thumbTop = $state(0);
// Drag state
let dragging = false;
let startY = 0;
let startScroll = 0;
/** Update thumb height & position */
function updateScrollbar() {
if (!container) return;
const { scrollTop, scrollHeight, clientHeight } = container;
const scrollable = scrollHeight - clientHeight;
if (scrollable <= 0) {
thumbHeight = 0;
thumbTop = 0;
return;
}
thumbHeight = Math.max((clientHeight / scrollHeight) * clientHeight, 24);
const trackHeight = clientHeight - thumbHeight - topOffset;
thumbTop = (scrollTop / scrollable) * trackHeight;
}
/** Start dragging the thumb */
function startDrag(e: PointerEvent) {
dragging = true;
startY = e.clientY;
startScroll = container.scrollTop;
thumb.setPointerCapture(e.pointerId);
}
/** Drag the thumb */
function drag(e: PointerEvent) {
if (!dragging) return;
const delta = e.clientY - startY;
const { scrollHeight, clientHeight } = container;
const scrollable = scrollHeight - clientHeight;
const trackHeight = clientHeight - thumbHeight - topOffset;
container.scrollTop = startScroll + (delta / trackHeight) * scrollable;
}
/** End dragging */
function endDrag(e: PointerEvent) {
dragging = false;
thumb.releasePointerCapture(e.pointerId);
}
// Reactive effect to update scrollbar
$effect(() => updateScrollbar());
// ResizeObserver for dynamic content
$effect(() => {
if (!container) return;
const observer = new ResizeObserver(updateScrollbar);
observer.observe(container);
return () => observer.disconnect();
});
</script>
<div class="wrapper" bind:this={wrapper}>
<!-- Scrollable content -->
<div
class={`content ${className}`}
bind:this={container}
onscroll={updateScrollbar}
id="scrollable-content"
>
{@render children?.()}
</div>
<!-- Custom scrollbar -->
<div class="scrollbar" style="top: {topOffset}px;">
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="thumb"
bind:this={thumb}
onpointerdown={startDrag}
onpointermove={drag}
onpointerup={endDrag}
onpointercancel={endDrag}
aria-controls="scrollable-content"
style="height:{thumbHeight}px; transform:translateY({thumbTop}px);"
></div>
</div>
</div>
<style>
.wrapper {
position: relative;
height: 100%;
width: 100%;
}
/* Scrollable content */
.content {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: none; /* Firefox */
}
.content::-webkit-scrollbar {
display: none; /* Chrome, Safari, Edge */
}
/* Scrollbar track */
.scrollbar {
position: absolute;
right: 2px;
bottom: 0;
width: 8px;
background: transparent;
border-radius: 999px;
}
/* Thumb */
.thumb {
position: absolute;
width: 100%;
background: var(--color-theme-700);
border-radius: 999px;
cursor: grab;
transition: background 0.15s;
}
.thumb:active {
cursor: grabbing;
background: var(--color-theme-600);
}
.thumb:hover {
background: var(--color-theme-700);
}
</style>
@@ -17,7 +17,7 @@
bind:ref
{value}
class={cn(
'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',
'data-[highlighted]:bg-primary 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
)}
{...restProps}
+14 -14
View File
@@ -1,21 +1,21 @@
<script lang="ts">
import { createElement } from 'react'
import { createRoot } from 'react-dom/client'
import type { SileoOptions } from 'sileo'
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import type { SileoOptions } from 'sileo';
let props: SileoOptions = $props()
let rootEl: HTMLElement
let props: SileoOptions = $props();
let rootEl: HTMLElement;
$effect(() => {
const root = createRoot(rootEl)
$effect(() => {
const root = createRoot(rootEl);
import('sileo').then(({ Toaster }) => {
const sileo = createElement(Toaster, { ...props })
root.render(sileo)
})
import('sileo').then(({ Toaster }) => {
const sileo = createElement(Toaster, { ...props });
root.render(sileo);
});
return () => root.unmount()
})
return () => root.unmount();
});
</script>
<div bind:this={rootEl}></div>
<div bind:this={rootEl}></div>
+128 -25
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import Minimize from 'lucide-svelte/icons/minus';
import Close from 'lucide-svelte/icons/x';
import Logo from '$assets/logo.png';
import Minimize from '@lucide/svelte/icons/minus';
import Close from '@lucide/svelte/icons/x';
import Volume2 from '@lucide/svelte/icons/volume-2';
import { getCurrentWindow } from '@tauri-apps/api/window';
import { onMount } from 'svelte';
import { launcherVersion } from '@/global';
import Badge from '../badge/badge.svelte';
import { custom_theme_audio_playing, custom_theme_volume } from '@/global';
import { reduceAnimations, userSettings } from '@/userSettings';
import { Volume, Volume1, VolumeOff } from '@lucide/svelte';
onMount(() => {
const appWindow = getCurrentWindow();
@@ -20,22 +20,92 @@
</script>
<div data-tauri-drag-region class="titlebar z-[100]">
<div class="titlebar-button rounded transition-colors duration-75" id="titlebar-minimize">
<Minimize size={14} />
{#if $custom_theme_audio_playing}
{#if $custom_theme_audio_playing}
<div class="relative flex items-center justify-center group">
<div class="relative flex items-center justify-center p-1 cursor-pointer">
<div
class="absolute inset-0 bg-background rounded
scale-80 opacity-0
group-hover:opacity-100
group-hover:scale-100
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}"
></div>
{#if $custom_theme_volume > 0.7}
<Volume2 class="z-10 pointer-events-none" size={14} />
{:else if $custom_theme_volume >= 0.15}
<Volume1 class="z-10 pointer-events-none" size={14} />
{:else if $custom_theme_volume > 0}
<Volume class="z-10 pointer-events-none" size={14} />
{:else}
<VolumeOff class="z-10 pointer-events-none" size={14} />
{/if}
</div>
<div
class="absolute top-full right-0 translate-x-[7px]
opacity-0 invisible pointer-events-none -translate-y-2
group-hover:opacity-100 group-hover:visible group-hover:pointer-events-auto group-hover:translate-y-0
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
z-50"
>
<div class="h-2 w-full"></div>
<div
class="flex items-center justify-center h-[120px] w-[35px]
bg-black/20 backdrop-blur-sm border border-white/10 rounded-xl"
>
<input
type="range"
min="0"
max="1"
step="0.01"
bind:value={$custom_theme_volume}
on:input={() => {
$userSettings.value('volume').set($custom_theme_volume);
$userSettings.save();
}}
class="volume-slider"
/>
</div>
</div>
</div>
{/if}
{/if}
<div class="relative flex items-center justify-center p-1 group" id="titlebar-minimize">
<div
class="absolute inset-0 bg-background rounded
scale-80 opacity-0
group-hover:opacity-100
group-hover:scale-100
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
origin-center"
></div>
<Minimize class="z-10 pointer-events-none" size={14} />
</div>
<div class="titlebar-button close rounded transition-colors duration-75" id="titlebar-close">
<Close size={14} />
<div class="relative flex items-center justify-center p-1 group" id="titlebar-close">
<div
class="absolute inset-0 bg-red-500 rounded
scale-80 opacity-0
group-hover:opacity-100
group-hover:scale-100
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
origin-center"
></div>
<Close class="z-10 pointer-events-none" size={14} />
</div>
</div>
<style lang="scss">
.titlebar {
height: 35px;
/* background: #040612; */
user-select: none;
display: flex;
justify-content: flex-end;
gap: 10px;
gap: 5px;
align-items: center;
position: fixed;
top: 0;
@@ -45,19 +115,52 @@
padding-right: 5px;
pointer-events: auto;
}
.titlebar-button {
display: inline-flex;
justify-content: center;
align-items: center;
width: 25px;
height: 25px;
user-select: none;
-webkit-user-select: none;
.volume-slider {
appearance: none;
-webkit-appearance: none;
width: 100px; /* becomes vertical */
height: 6px;
background: transparent;
cursor: pointer;
transform: rotate(-90deg);
}
.titlebar-button:hover {
&.close {
background: #c22e2e;
}
background: #2d3049;
/* track */
.volume-slider::-webkit-slider-runnable-track {
height: 6px;
background: rgba(255, 255, 255, 0.25);
border-radius: 999px;
}
/* thumb */
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
border: none;
margin-top: -4px; /* center thumb */
}
/* Firefox */
.volume-slider::-moz-range-track {
height: 6px;
background: rgba(255, 255, 255, 0.25);
border-radius: 999px;
}
.volume-slider::-moz-range-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
border: none;
}
</style>
+12 -10
View File
@@ -1,19 +1,21 @@
export function estimateRefreshRate(): Promise<number> {
export async function estimateFrameRate(): Promise<number> {
return new Promise((resolve) => {
const last = performance.now();
let frames = 0;
const samples: number[] = [];
let last = performance.now();
function loop() {
const tick = () => {
const now = performance.now();
frames++;
samples.push(now - last);
last = now;
if (now - last >= 1000) {
resolve(frames - 2);
if (samples.length < 60) {
requestAnimationFrame(tick);
} else {
requestAnimationFrame(loop);
const avgDelta = samples.reduce((a, b) => a + b) / samples.length;
resolve(Math.round(1000 / avgDelta));
}
}
};
requestAnimationFrame(loop);
requestAnimationFrame(tick);
});
}
+60
View File
@@ -0,0 +1,60 @@
import { listen } from '@tauri-apps/api/event';
import { onDestroy } from 'svelte';
type TauriDragDropEvent = {
paths: string[];
position: { x: number; y: number };
};
export const useDropZone = ({ onDrop }: { onDrop: (paths: string[]) => void }) => {
let element = $state<HTMLDivElement | null>(null);
let dragIn = $state(false);
let isDraggingOverApp = $state(false);
const isOver = (x: number, y: number) => {
if (!element) return false;
const el = document.elementFromPoint(x, y);
return element === el || element.contains(el); // ← fix
};
const dropUnlisten = listen<TauriDragDropEvent>('tauri://drag-drop', (e) => {
const { x, y } = e.payload.position;
if (isOver(x, y)) {
onDrop(e.payload.paths);
}
dragIn = false;
isDraggingOverApp = false;
});
const overUnlisten = listen<TauriDragDropEvent>('tauri://drag-over', (e) => {
const { x, y } = e.payload.position;
isDraggingOverApp = true;
dragIn = isOver(x, y);
});
const leaveUnlisten = listen('tauri://drag-leave', () => {
dragIn = false;
isDraggingOverApp = false;
});
onDestroy(async () => {
(await dropUnlisten)();
(await overUnlisten)();
(await leaveUnlisten)();
});
return {
get ref() {
return element;
},
set ref(el: HTMLDivElement | null) {
element = el;
},
get dragIn() {
return dragIn;
},
get isDraggingOverApp() {
return isDraggingOverApp;
},
};
};
+15 -2
View File
@@ -3,7 +3,9 @@ import { ezppfarm } from './api/ezpp';
import type { Component } from 'svelte';
import Loading from '../screens/Loading.svelte';
import type { Release } from './types';
import type { Theme } from './themes';
import { browser } from '$app/environment';
import { setGlobalVolume } from './utils';
export const currentView = writable<Component>(Loading);
export const platform = writable<string>('');
@@ -24,6 +26,16 @@ export const currentLoadingInfo = writable<string>('Initializing...');
export const firstStartup = writable<boolean>(false);
export const launching = writable<boolean>(false);
export const custom_themes = writable<Theme[]>([]);
export const custom_theme_container = writable<HTMLElement | undefined>(undefined);
export const active_custom_theme = writable<Theme | undefined>(undefined);
export const custom_theme_audio_playing = writable<boolean>(false);
export const custom_theme_volume = writable<number>(0.15);
custom_theme_volume.subscribe((val) => {
if (browser) {
setGlobalVolume(val);
}
});
export const serverPing = writable<number | undefined>(undefined);
export const serverConnectionFails = writable(0);
@@ -31,7 +43,8 @@ export const serverConnectionFails = writable(0);
export const onlineFriends = writable<number | undefined>(undefined);
export const beatmapSets = writable<number | undefined>(undefined);
export const skins = writable<number | undefined>(undefined);
export const skinsCount = writable<number | undefined>(undefined);
export const skins = writable<{ name: string; author: string | undefined; modified: number }[]>([]);
export const osuStream = writable<string | undefined>(undefined);
export const osuBuild = writable<string | undefined>(undefined);
+40 -45
View File
@@ -5,20 +5,26 @@ import { betterFetch } from '@better-fetch/fetch';
const updateUrl = 'https://ez-pp.farm/api/ezpplauncher';
export const getHWID = async () => {
const hwid = await invoke('get_hwid');
return typeof hwid === 'string' ? hwid : undefined;
};
export function normalizeOsuName(name: string): string {
return (
name
.trim()
// Remove leading symbols/spaces
.replace(/^[^a-zA-Z0-9]+/, '')
// Normalize spacing
.replace(/\s+/g, ' ')
.toLowerCase()
);
}
export const isValidOsuFolder = async (folder: string): Promise<boolean> => {
const result = await invoke('valid_osu_folder', { folder });
return typeof result === 'boolean' ? result : false;
};
export const getHWID = async (): Promise<string | undefined> =>
await invoke<string | undefined>('get_hwid');
export const autoDetectOsuInstallFolder = async () => {
const result = await invoke('find_osu_installation');
return typeof result === 'string' ? result : undefined;
};
export const isValidOsuFolder = async (folder: string): Promise<boolean | undefined> =>
await invoke<boolean | undefined>('valid_osu_folder', { folder });
export const autoDetectOsuInstallFolder = async () =>
await invoke<string | undefined>('find_osu_installation');
export const setUserConfigValues = async (
osuFolderPath: string,
@@ -38,36 +44,23 @@ export const setConfigValues = async (
entries,
});
export const getReleaseStream = async (folder: string) => {
const result = await invoke('get_osu_release_stream', { folder });
return typeof result === 'string' ? result : undefined;
};
export const getReleaseStream = async (folder: string): Promise<string | undefined> =>
await invoke<string | undefined>('get_osu_release_stream', { folder });
export const getVersion = async (folder: string) => {
const result = await invoke('get_osu_version', { folder });
return typeof result === 'string' ? result : undefined;
};
export const getVersion = async (folder: string): Promise<string | undefined> =>
await invoke<string | undefined>('get_osu_version', { folder });
export const getBeatmapSetsCount = async (folder: string) => {
const result = await invoke('get_beatmapsets_count', {
export const getBeatmapSetsCount = async (folder: string): Promise<number | undefined> =>
await invoke<number | undefined>('get_beatmapsets_count', {
folder,
});
return typeof result === 'number' ? result : 0;
};
export const getSkinsCount = async (folder: string) => {
const result = await invoke('get_skins_count', {
folder,
});
return typeof result === 'number' ? result : 0;
};
export const getSkin = async (folder: string) => {
const result = await invoke('get_osu_skin', {
const result = await invoke<string | undefined>('get_osu_skin', {
folder,
});
return typeof result === 'string' ? result : 'Default';
return result || 'Default';
};
export const runUpdater = async (folder: string) => await invoke('run_osu_updater', { folder });
@@ -108,8 +101,8 @@ export const downloadEZPPLauncherUpdateFiles = async (
allFiles: UpdateFile[],
progressCallback: (file: UpdateStatus) => void
) => {
const downloadStatusListen = await listen('download-progress', (event) =>
progressCallback(event.payload as UpdateStatus)
const downloadStatusListen = await listen<UpdateStatus>('download-progress', (event) =>
progressCallback(event.payload)
);
try {
await invoke('download_ezpp_launcher_update_files', { folder, updateFiles, allFiles });
@@ -122,20 +115,17 @@ export const replaceUIFiles = async (folder: string, revert: boolean) => {
await invoke('replace_ui_files', { folder, revert });
};
export const isOsuRunning = async () => {
const result = await invoke('is_osu_running');
return typeof result === 'boolean' ? result : false;
};
export const isOsuRunning = async (): Promise<boolean> => await invoke<boolean>('is_osu_running');
export const getLauncherVersion = async () => await invoke<string>('get_launcher_version');
export const exit = async () => await invoke('exit');
export const getPlatform = async () => await invoke<string>('get_platform');
export const isOsuCorrupted = async (folder: string) =>
export const getPlatform = async (): Promise<string> => await invoke<string>('get_platform');
export const isOsuCorrupted = async (folder: string): Promise<boolean> =>
await invoke<boolean>('check_for_corruption', { folder });
export const hasWMCTRL = async () => await invoke<boolean>('has_wmctrl');
export const hasOsuWinello = async () => await invoke<boolean>('has_osuwinello');
export const hasNet8 = async () => await invoke<boolean>('has_net8');
export const encryptString = async (str: string, entropy: string) =>
export const hasWMCTRL = async (): Promise<boolean> => await invoke<boolean>('has_wmctrl');
export const hasOsuWinello = async (): Promise<boolean> => await invoke<boolean>('has_osuwinello');
export const hasNet8 = async (): Promise<boolean> => await invoke<boolean>('has_net8');
export const encryptString = async (str: string, entropy: string): Promise<string> =>
await invoke<string>('encrypt_string', { string: str, entropy });
export const downloadUpdate = async (
url: string,
@@ -151,3 +141,8 @@ export const downloadUpdate = async (
}
};
export const installUpdate = async () => await invoke('install_ezpp_launcher_update');
export const getSkins = async (folder: string) =>
await invoke<{ name: string; author: string | undefined; modified: number }[] | undefined>(
'get_skins',
{ folder }
);
+2
View File
@@ -16,4 +16,6 @@ export const updateStatus = async (status: {
});
export const updateUser = async (user: { username: string; id?: string | null }) =>
await invoke('presence_update_user', { user: { username: user.username, id: user.id } });
export const updateButton = async (button: { text?: string; url?: string }) =>
await invoke('presence_update_button', { button: button });
export const isConnected = async () => await invoke<boolean>('presence_is_connected');
+24
View File
@@ -0,0 +1,24 @@
const specialOrder = "'-!#$%&(),;@[]^_`{}~+=";
const priority = Object.fromEntries([...specialOrder].map((c, i) => [c, i]));
const osuSkinsCollator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
});
export const sortSkins = (
skins: { name: string; author: string | undefined; modified: number }[]
) =>
skins.sort((a, b) => {
const pa = priority[a.name[0]];
const pb = priority[b.name[0]];
if (pa !== undefined || pb !== undefined) {
if (pa === undefined) return 1;
if (pb === undefined) return -1;
return pa - pb;
}
return osuSkinsCollator.compare(a.name, b.name);
});
+545
View File
@@ -0,0 +1,545 @@
import * as path from '@tauri-apps/api/path';
import * as fs from '@tauri-apps/plugin-fs';
import { convertFileSrc } from '@tauri-apps/api/core';
import { active_custom_theme, custom_themes } from './global';
import { calculateGitBlobSha, setGlobalVolume } from './utils';
import { betterFetch } from '@better-fetch/fetch';
import zip from 'jszip';
import { SemVer } from 'semver';
export type Theme = {
folder_name: string;
name: string;
version: string;
author: string;
scriptUrl: string;
assets: string;
preview: string;
status: 'installed' | 'downloading' | 'extracting' | 'deleting' | 'not-installed';
progress: number;
updateAvailable: boolean;
};
export type ThemeInfo = {
name: string;
version: string;
apiVersion: string;
author: string;
entry: string;
style: string;
preview: string;
};
export const getDownloadableThemes = async () => {
const themes: Theme[] = [];
const downloadableThemes = await betterFetch<
{
name: string;
version: string;
author: string;
}[]
>('https://git.ez-pp.farm/EZPPFarm/EZPPLauncher-Themes/raw/branch/main/themes.json', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
},
});
if (downloadableThemes.data) {
for (const theme of downloadableThemes.data) {
themes.push({
folder_name: `${toSafeName(theme.name)}-${generateRandomString(16)}`,
name: theme.name,
version: theme.version,
author: theme.author,
scriptUrl: '',
assets: '',
preview: `https://git.ez-pp.farm/EZPPFarm/EZPPLauncher-Themes/raw/branch/main/previews/${toSafeName(theme.name)}.png`,
status: 'not-installed',
progress: 0,
updateAvailable: false,
});
}
} else {
console.error('Failed to fetch downloadable themes');
console.error(downloadableThemes.error);
}
return themes;
};
export const getThemes = async (): Promise<Theme[]> => {
const themes: Theme[] = [];
themes.push({
folder_name: 'default',
author: 'EZPPFarm',
name: 'Default',
version: '1.0.0',
scriptUrl: '',
assets: '',
preview: '',
status: 'installed',
progress: 0,
updateAvailable: false,
});
const folderPath = await path.join(await path.homeDir(), '.ezpplauncher', 'themes');
if (await fs.exists(folderPath)) {
const themeFolders = await fs.readDir(folderPath);
for (const themeFolder of themeFolders) {
const themeFolderPath = await path.join(folderPath, themeFolder.name);
const themeConfigFilePath = await path.join(folderPath, themeFolder.name, 'theme.json');
if (await fs.exists(themeConfigFilePath)) {
const themeConfig = await fs.readTextFile(themeConfigFilePath);
const theme = JSON.parse(themeConfig) as ThemeInfo;
const scriptFile = await path.join(folderPath, themeFolder.name, theme.entry);
if (await fs.exists(scriptFile)) {
themes.push({
folder_name: themeFolder.name,
name: theme.name,
version: theme.version,
author: theme.author,
scriptUrl: convertFileSrc(await path.normalize(`${themeFolderPath}/${theme.entry}`)),
assets: convertFileSrc(await path.normalize(`${themeFolderPath}/assets`)),
preview: convertFileSrc(
await path.normalize(`${themeFolderPath}/assets/${theme.preview}`)
),
status: 'installed',
progress: 0,
updateAvailable: false,
});
}
}
}
}
return themes;
};
function combineChunks(chunks: Uint8Array[], totalLength: number): Uint8Array {
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
export const downloadTheme = async (
theme: Theme,
force = false
): Promise<{ success: boolean; error?: string }> => {
if (theme.status !== 'not-installed' && !force) return { success: false };
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'downloading';
}
return themes;
});
const themeFileName = `${toSafeName(theme.name)}.ezpplauncher-theme`;
const themeContents = await betterFetch<{
file_contents: {
sha: string;
download_url: string;
};
}>(
`https://git.ez-pp.farm/api/v1/repos/EZPPFarm/EZPPLauncher-Themes/contents-ext/themes/${themeFileName}?ref=main`
);
if (themeContents.error) {
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'not-installed';
}
return themes;
});
return {
success: false,
error: themeContents.error.message ?? 'An unknown error occurred',
};
}
const downloadUrl = `https://git.ez-pp.farm/EZPPFarm/EZPPLauncher-Themes/raw/branch/main/themes/${themeFileName}`;
const downloadReq = await fetch(downloadUrl, {
method: 'GET',
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
},
});
if (!downloadReq.ok || downloadReq.body === null) {
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'not-installed';
}
return themes;
});
return {
success: false,
error: 'Failed to download theme file.',
};
}
const total = Number(downloadReq.headers.get('content-length')) || 0;
const reader = downloadReq.body.getReader();
let received = 0;
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
received += value.length;
const progress = received / total;
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.progress = progress;
}
return themes;
});
}
const fileBuffer = combineChunks(chunks, total);
const sha = calculateGitBlobSha(Buffer.from(fileBuffer));
if (sha !== themeContents.data.file_contents.sha) {
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'not-installed';
}
return themes;
});
return {
success: false,
error: 'Failed to verify downloaded theme file hash.',
};
}
//TODO: check file hash
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'extracting';
}
return themes;
});
const baseThemeFolder = await path.join(await path.homeDir(), '.ezpplauncher', 'themes');
const themeFolder = await path.join(baseThemeFolder, toSafeName(theme.name));
if (!(await fs.exists(baseThemeFolder))) await fs.mkdir(baseThemeFolder, { recursive: true });
if (!(await fs.exists(themeFolder))) await fs.mkdir(themeFolder, { recursive: true });
const zipFile = await zip.loadAsync(fileBuffer);
const totalFiles = Object.keys(zipFile.files).length;
let extractedFiles = 0;
for (const zipEntry of Object.keys(zipFile.files)) {
const file = zipFile.file(zipEntry);
if (file) {
try {
const fileData = await file.async('uint8array');
const filePath = await path.join(themeFolder, zipEntry);
const dirPath = await path.dirname(filePath);
if (!(await fs.exists(dirPath))) await fs.mkdir(dirPath, { recursive: true });
await new Promise((res) => setTimeout(res, 250));
await fs.writeFile(filePath, fileData);
extractedFiles++;
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.progress = extractedFiles / totalFiles;
}
return themes;
});
} catch (err) {
console.log(err);
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === theme.name);
if (matchedTheme) {
matchedTheme.status = 'not-installed';
}
return themes;
});
return {
success: false,
error: 'Failed to extract theme file.',
};
}
}
}
const themeInfo = await fs.readTextFile(await path.join(themeFolder, 'theme.json'));
const themeInfoObj = JSON.parse(themeInfo) as ThemeInfo;
const themeScriptUrl = convertFileSrc(
await path.normalize(`${themeFolder}/${themeInfoObj.entry}`)
);
const themeAssets = convertFileSrc(await path.normalize(`${themeFolder}/assets`));
const themePreview = convertFileSrc(
await path.normalize(`${themeFolder}/assets/${themeInfoObj.preview}`)
);
const downloadableThemes = await getDownloadableThemes();
custom_themes.update((themes) => {
const reloadedThemes = [...themes];
for (const theme of downloadableThemes) {
if (theme.name === 'Default') continue; // never overwrite Default from remote
if (!reloadedThemes.find((t) => t.name === theme.name)) {
reloadedThemes.push(theme);
}
const installedTheme = reloadedThemes.find((t) => t.name === theme.name);
if (installedTheme) {
const installedThemeVersion = new SemVer(installedTheme.version);
const downloadableThemeVersion = new SemVer(theme.version);
if (downloadableThemeVersion.compare(installedThemeVersion) > 0) {
installedTheme.updateAvailable = true;
const index = reloadedThemes.findIndex((t) => t.name === theme.name);
reloadedThemes[index] = installedTheme;
}
}
}
for (const installedTheme of reloadedThemes) {
if (installedTheme.name === 'Default') continue; // never touch Default
if (installedTheme.name === theme.name) {
installedTheme.name = themeInfoObj.name;
installedTheme.author = themeInfoObj.author;
installedTheme.version = themeInfoObj.version;
installedTheme.scriptUrl = themeScriptUrl;
installedTheme.assets = themeAssets;
installedTheme.preview = themePreview;
installedTheme.status = 'installed';
}
}
return reloadedThemes.sort((a, b) => {
if (a.name === 'Default') return -1;
if (b.name === 'Default') return 1;
if (a.status === 'installed' && b.status !== 'installed') return -1;
if (a.status !== 'installed' && b.status === 'installed') return 1;
return a.name.localeCompare(b.name);
});
});
return {
success: true,
};
};
export const loadTheme = async (theme: Theme, themeContainer: HTMLElement, volume = 0.15) => {
active_custom_theme.set(theme);
themeContainer.innerHTML = '';
if (theme.scriptUrl.length > 0) {
const themeScript = await import(/* @vite-ignore */ theme.scriptUrl);
themeScript.mountTheme(themeContainer, { assets: theme.assets });
}
setGlobalVolume(volume);
};
export const deleteTheme = async (themeToUninstall: Theme) => {
if (themeToUninstall.status !== 'installed') return false;
const baseThemeFolder = await path.join(await path.homeDir(), '.ezpplauncher', 'themes');
if (!(await fs.exists(baseThemeFolder))) return false;
const themeFolder = await path.join(baseThemeFolder, themeToUninstall.folder_name);
if (!(await fs.exists(themeFolder))) return false;
custom_themes.update((themes) => {
const matchedTheme = themes.find((t) => t.name === themeToUninstall.name);
if (matchedTheme) {
matchedTheme.status = 'deleting';
}
return themes;
});
await fs.remove(themeFolder, { recursive: true });
const downloadableThemes = await getDownloadableThemes();
custom_themes.update((themes) => {
const reloadedThemes = [...themes];
for (const theme of downloadableThemes) {
if (theme.name === themeToUninstall.name) {
const themeIndex = reloadedThemes.findIndex((t) => t.name === theme.name);
if (themeIndex) {
reloadedThemes.splice(themeIndex, 1);
reloadedThemes.push(theme);
}
}
const installedTheme = reloadedThemes.find((t) => t.name === theme.name);
if (installedTheme) {
const installedThemeVersion = new SemVer(installedTheme.version);
const downloadableThemeVersion = new SemVer(theme.version);
if (downloadableThemeVersion.compare(installedThemeVersion) > 0) {
installedTheme.updateAvailable = true;
const index = reloadedThemes.findIndex((t) => t.name === theme.name);
reloadedThemes[index] = installedTheme;
}
}
}
return reloadedThemes.sort((a, b) => {
if (a.name === 'Default') return -1;
if (b.name === 'Default') return 1;
if (a.status === 'installed' && b.status !== 'installed') return -1;
if (a.status !== 'installed' && b.status === 'installed') return 1;
return a.name.localeCompare(b.name);
});
});
return true;
};
export const checkThemeFromFile = async (filePath: string) => {
const normalizedFilePath = await path.normalize(filePath);
if (!(await fs.exists(normalizedFilePath))) {
return {
success: false,
error: 'File not found',
};
}
const fileBuffer = await fs.readFile(normalizedFilePath);
let themeInfo: ThemeInfo | undefined = undefined;
try {
const zipFile = await zip.loadAsync(fileBuffer);
const themeConfig = zipFile.file('theme.json');
if (!themeConfig) {
return {
success: false,
error: 'Theme config file not found',
};
}
const themeConfigData = await themeConfig.async('string');
themeInfo = JSON.parse(themeConfigData) as {
name: string;
version: string;
apiVersion: string;
author: string;
entry: string;
style: string;
preview: string;
};
} catch {
return {
success: false,
error: 'Could not read theme file',
};
}
return {
success: !!themeInfo,
themeInfo,
};
};
export const importThemeFromFile = async (themeName: string, filePath: string) => {
const baseThemeFolder = await path.join(await path.homeDir(), '.ezpplauncher', 'themes');
const themeFolder = await path.join(baseThemeFolder, toSafeName(themeName));
if (!(await fs.exists(baseThemeFolder))) await fs.mkdir(baseThemeFolder, { recursive: true });
if (!(await fs.exists(themeFolder))) await fs.mkdir(themeFolder, { recursive: true });
const fileBuffer = await fs.readFile(filePath);
const zipFile = await zip.loadAsync(fileBuffer);
for (const zipEntry of Object.keys(zipFile.files)) {
const file = zipFile.file(zipEntry);
if (file) {
try {
const fileData = await file.async('uint8array');
const filePath = await path.join(themeFolder, zipEntry);
const dirPath = await path.dirname(filePath);
if (!(await fs.exists(dirPath))) await fs.mkdir(dirPath, { recursive: true });
await new Promise((res) => setTimeout(res, 250));
await fs.writeFile(filePath, fileData);
} catch (err) {
console.log(err);
return {
success: false,
error: 'Failed to extract theme file.',
};
}
}
}
const themeInfo = await fs.readTextFile(await path.join(themeFolder, 'theme.json'));
const themeInfoObj = JSON.parse(themeInfo) as ThemeInfo;
const themeScriptUrl = convertFileSrc(
await path.normalize(`${themeFolder}/${themeInfoObj.entry}`)
);
const themeAssets = convertFileSrc(await path.normalize(`${themeFolder}/assets`));
const themePreview = convertFileSrc(
await path.normalize(`${themeFolder}/assets/${themeInfoObj.preview}`)
);
const downloadableThemes = await getDownloadableThemes();
custom_themes.update((themes) => {
const reloadedThemes = [...themes];
for (const theme of downloadableThemes) {
if (theme.name === 'Default') continue; // never overwrite Default from remote
if (!reloadedThemes.find((t) => t.name === theme.name)) {
reloadedThemes.push(theme);
}
const installedTheme = reloadedThemes.find((t) => t.name === theme.name);
if (installedTheme) {
const installedThemeVersion = new SemVer(installedTheme.version);
const downloadableThemeVersion = new SemVer(theme.version);
if (downloadableThemeVersion.compare(installedThemeVersion) > 0) {
installedTheme.updateAvailable = true;
const index = reloadedThemes.findIndex((t) => t.name === theme.name);
reloadedThemes[index] = installedTheme;
}
}
}
for (const installedTheme of reloadedThemes) {
if (installedTheme.name === 'Default') continue; // never touch Default
if (installedTheme.name === themeName) {
installedTheme.name = themeInfoObj.name;
installedTheme.author = themeInfoObj.author;
installedTheme.version = themeInfoObj.version;
installedTheme.scriptUrl = themeScriptUrl;
installedTheme.assets = themeAssets;
installedTheme.preview = themePreview;
installedTheme.status = 'installed';
}
}
return reloadedThemes.sort((a, b) => {
if (a.name === 'Default') return -1;
if (b.name === 'Default') return 1;
if (a.status === 'installed' && b.status !== 'installed') return -1;
if (a.status !== 'installed' && b.status === 'installed') return 1;
return a.name.localeCompare(b.name);
});
});
return {
success: true,
};
};
export const toSafeName = (name: string) => {
return (
name
.toLowerCase()
.replace(/[<>:"/\\|?*]/g, '_')
// eslint-disable-next-line no-control-regex
.replace(/[\x00-\x1F\x7F]/g, '_')
.replace(/[\s-]+/g, '_')
.replace(/_+/g, '_')
.replace(/[. ]+$/g, '_')
);
};
const generateRandomString = (length: number) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length }, () =>
characters.charAt(Math.floor(Math.random() * characters.length))
).join('');
};
+34
View File
@@ -0,0 +1,34 @@
declare global {
const umami: Umami;
}
/**
* @see {@link https://umami.is/docs/tracker-functions|Umami Docs}
*/
interface Umami {
track(): Promise<string> | undefined;
track(event_name: string, event_data?: Record<string, unknown>): Promise<string> | undefined;
track(custom_payload: { website: string; [key: string]: unknown }): Promise<string> | undefined;
track(
callback: (props: {
hostname: string;
language: string;
referrer: string;
screen: string;
title: string;
url: string;
website: string;
}) => { website: string; [key: string]: unknown }
): Promise<string> | undefined;
/** Pass in your own ID to identify a user. */
identify(identity_id: string): Promise<void>;
/** Save data about the current session. */
identify(identity_id: string, data: Record<string, unknown>): Promise<void>;
/** To save data without a unique ID, pass in only a JSON object. */
identify(data: Record<string, unknown>): Promise<void>;
}
export {};
+110
View File
@@ -1,6 +1,8 @@
import { invoke } from '@tauri-apps/api/core';
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { custom_theme_audio_playing } from './global';
import * as crypto from 'crypto-js';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -99,3 +101,111 @@ export const urlIsValidImage = async (url: string) => {
return false;
}
};
export function toURL(data: Buffer, type: string) {
return URL.createObjectURL(new Blob([new Uint8Array(data)], { type }));
}
export function setGlobalVolume(volume: number) {
const mediaElements: NodeListOf<HTMLMediaElement> = document.querySelectorAll('audio, video');
mediaElements.forEach((element) => {
element.volume = volume;
});
}
export function fadeGlobalVolume(
volumeFrom: number,
volumeTo: number,
duration: number,
onDone?: () => void
) {
const mediaElements: NodeListOf<HTMLMediaElement> = document.querySelectorAll('audio, video');
const startTime = performance.now();
function animate() {
const currentTime = performance.now();
const elapsedTime = currentTime - startTime;
const progress = Math.min(elapsedTime / duration, 1);
const interpolatedVolume = volumeFrom + (volumeTo - volumeFrom) * progress;
mediaElements.forEach((element) => {
element.volume = interpolatedVolume;
});
if (progress < 1) {
requestAnimationFrame(animate);
} else {
if (onDone) onDone();
}
}
animate();
}
export const pauseGlobalMedia = () => {
const mediaElements: NodeListOf<HTMLMediaElement> = document.querySelectorAll('audio, video');
mediaElements.forEach((element) => element.pause());
};
export const resumeGlobalMedia = () => {
const mediaElements: NodeListOf<HTMLMediaElement> = document.querySelectorAll('audio, video');
mediaElements.forEach((element) => element.play());
};
export const setCurrentTimeGlobalMedia = (time: number) => {
const mediaElements: NodeListOf<HTMLMediaElement> = document.querySelectorAll('audio, video');
mediaElements.forEach((element) => (element.currentTime = time));
};
export function trackMediaContainer(node: HTMLElement) {
const media = new Set<HTMLMediaElement>();
function update() {
custom_theme_audio_playing.set(Array.from(media).some((el) => !el.paused));
}
function register(el: HTMLMediaElement) {
media.add(el);
const handler = () => update();
el.addEventListener('play', handler);
el.addEventListener('pause', handler);
return () => {
media.delete(el);
el.removeEventListener('play', handler);
el.removeEventListener('pause', handler);
};
}
// find existing media inside the div
node.querySelectorAll<HTMLMediaElement>('video, audio').forEach(register);
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
m.addedNodes.forEach((n) => {
if (n instanceof HTMLMediaElement) register(n);
if (n instanceof HTMLElement) {
n.querySelectorAll<HTMLMediaElement>('video, audio').forEach(register);
}
});
}
});
observer.observe(node, { childList: true, subtree: true });
return {
destroy() {
observer.disconnect();
},
};
}
export function calculateGitBlobSha(fileBuffer: Buffer): string {
const header = Buffer.from(`blob ${fileBuffer.length}\0`);
const store = Buffer.concat([header, fileBuffer]);
return crypto.SHA1(crypto.lib.WordArray.create(store)).toString(crypto.enc.Hex);
}
+65 -7
View File
@@ -5,7 +5,11 @@
import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
import * as AlertDialog from '@/components/ui/alert-dialog';
import {
active_custom_theme,
currentLoadingInfo,
custom_theme_container,
custom_theme_volume,
custom_themes,
discordPresence,
firstStartup,
launcherVersion,
@@ -27,13 +31,15 @@
import { Buffer } from 'buffer';
import SileoToast from '@/components/ui/sileo/sileo-toast.svelte';
import { userAuth } from '@/userAuthentication';
import { encryptString, exit, getLauncherVersion, getPlatform } from '@/osuUtil';
import { exit, getLauncherVersion, getPlatform } from '@/osuUtil';
import Button from '@/components/ui/button/button.svelte';
import * as presence from '@/presence';
import '@fontsource/sora';
import '@fontsource/space-mono';
import { fade } from 'svelte/transition';
import { getDownloadableThemes, getThemes, loadTheme } from '@/themes';
import { sileo } from 'sileo';
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
import { SemVer } from 'semver';
import { trackMediaContainer } from '@/utils';
let { children } = $props();
@@ -96,8 +102,51 @@
platform.set(await getPlatform());
if ($platform !== 'windows' && $platform !== 'linux') unsupported_platform = true;
const isFirstStartup = await $userSettings.init();
firstStartup.set(isFirstStartup);
$userAuth.init();
try {
const themeVolume = $userSettings.value('volume').get(0.15);
custom_theme_volume.set(themeVolume);
currentLoadingInfo.set('Loading themes...');
const downloadableThemes = await getDownloadableThemes();
const themes = await getThemes();
const combinedThemes = [...themes];
for (const theme of downloadableThemes) {
if (!combinedThemes.find((t) => t.name === theme.name)) combinedThemes.push(theme);
const installedTheme = themes.find((t) => t.name === theme.name);
if (installedTheme) {
const installedThemeVersion = new SemVer(installedTheme.version);
const downloadableThemeVersion = new SemVer(theme.version);
if (downloadableThemeVersion.compare(installedThemeVersion) > 0) {
installedTheme.updateAvailable = true;
const index = combinedThemes.findIndex((t) => t.name === theme.name);
combinedThemes[index] = installedTheme;
}
}
}
custom_themes.set(combinedThemes);
const activeTheme = $userSettings.value('theme').get('Default');
const theme = themes.find((theme) => theme.name === activeTheme);
if (!theme) {
$userSettings.value('theme').set('Default');
await $userSettings.save();
loadTheme(themes[0], $custom_theme_container!, themeVolume);
} else {
loadTheme(theme, $custom_theme_container!, themeVolume);
}
} catch {
sileo.error({
title: 'An error occured!',
description: 'Failed to load theme',
fill: '#181825',
styles: {
description: 'text-center!',
},
});
}
currentLoadingInfo.set('Loading config...');
const config_patching = $userSettings.value('patching');
const config_custom_cursor = $userSettings.value('custom_cursor');
@@ -142,8 +191,6 @@
presenceLoading.set(false);
}
} catch {}
firstStartup.set(isFirstStartup);
});
</script>
@@ -180,7 +227,18 @@
<main>
<div class="opacity-30">
<AnimatedBg />
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
<div
class={$active_custom_theme && $active_custom_theme.name === 'Default'
? 'hidden'
: 'absolute z-0 top-0 left-0 w-full h-full object-cover object-center aspect-video'}
bind:this={$custom_theme_container}
use:trackMediaContainer
></div>
{#if ($active_custom_theme && $active_custom_theme.name === 'Default') || !$active_custom_theme}
<AnimatedBg />
{/if}
</div>
</div>
{@render children()}
</main>
+725 -266
View File
File diff suppressed because it is too large Load Diff
+38 -23
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import Logo from '$assets/logo.png';
import { estimateRefreshRate } from '@/displayUtils';
import { estimateFrameRate } from '@/displayUtils';
import {
beatmapSets,
currentLoadingInfo,
@@ -14,6 +14,7 @@
osuBuild,
osuStream,
skins,
skinsCount,
} from '@/global';
import {
cursorSmoothness,
@@ -34,7 +35,7 @@
getEZPPLauncherStreams,
getReleaseStream,
getSkin,
getSkinsCount,
getSkins,
getVersion,
isValidOsuFolder,
} from '@/osuUtil';
@@ -46,8 +47,6 @@
let spinnerCircle: SVGCircleElement;
let animateInterval: number | undefined;
//TODO: use this to check for updates upon launch
const doBPMAnimation = () => {
if (animateInterval) return;
animateInterval = window.setInterval(async () => {
@@ -68,15 +67,15 @@
};
const calculateCursorSmoothness = async () => {
const refreshRate = await estimateRefreshRate();
const refreshRate = await estimateFrameRate();
const hzMin = 60;
const hzMax = 144;
const durationMin = 70;
const durationMax = 180;
const hzMax = 500;
const durationMin = 350;
const durationMax = 600;
const clamped = Math.min(Math.max(refreshRate, hzMin), hzMax);
const duration =
durationMin + ((refreshRate - hzMin) / (hzMax - hzMin)) * (durationMax - durationMin);
durationMin + ((clamped - hzMin) / (hzMax - hzMin)) * (durationMax - durationMin);
console.log(Math.round(duration));
cursorSmoothness.set(Math.round(duration));
};
@@ -114,17 +113,27 @@
}
}
if ($currentUser) {
currentLoadingInfo.set('Loading user info...');
const userInfo = await ezppfarm.getUserInfo($currentUser.id);
if (userInfo) {
currentUserInfo.set(userInfo.player);
try {
currentLoadingInfo.set('Loading user info...');
const userInfo = await ezppfarm.getUserInfo($currentUser.id);
if (userInfo) {
currentUserInfo.set(userInfo.player);
preferredMode.set(userInfo.player.info.preferred_mode);
preferredType.set(userInfo.player.info.preferred_type);
preferredMode.set(userInfo.player.info.preferred_mode);
preferredType.set(userInfo.player.info.preferred_type);
}
} catch {
userLoginResult = {
message: 'There was an issue connecting to the server. Please try again later.',
};
currentUser.set(undefined);
}
}
if (!$firstStartup) {
/* const launcherTheme = $userSettings.value('launcherTheme').get('default');
const them = THEMES.find((theme) => theme.name === launcherTheme);
if (them) theme.set(them); */
currentLoadingInfo.set('Checking osu installation path...');
const validFolder = await isValidOsuFolder($osuInstallationPath);
if (!validFolder) {
@@ -151,8 +160,14 @@
if (beatmapSetCount !== null) beatmapSets.set(beatmapSetCount);
currentLoadingInfo.set('Counting skins...');
const skinCount = await getSkinsCount($osuInstallationPath);
if (skinCount !== null) skins.set(skinCount);
const skins_list = await getSkins($osuInstallationPath);
if (skins_list) {
skins.set(skins_list);
skinsCount.set(skins_list.length);
} else {
skinsCount.set(0);
}
const skin: string = await getSkin($osuInstallationPath);
currentSkin.set(skin);
}
@@ -239,19 +254,19 @@
});
</script>
<div class="flex flex-col items-center justify-center h-[100vh] w-full">
<div class="relative w-80 h-80 flex items-center justify-center">
<div class="flex flex-col items-center justify-center h-[100vh] w-full z-10">
<div class="relative w-80 h-80 flex items-center justify-center z-10">
<svg
class="absolute top-0 left-0 w-full h-full animate-spin"
style="animation-duration: 5s;"
viewBox="0 0 208 208"
>
<circle
class="stroke-primary"
cx="104"
cy="104"
r="90"
fill="none"
stroke="#ff0098"
stroke-width="8"
stroke-linecap="round"
stroke-dasharray="180 385"
@@ -266,5 +281,5 @@
bind:this={ezppLogo}
/>
</div>
<span class="text-theme-200 text-sm mt-5">{$currentLoadingInfo}</span>
<span class="text-theme-200 text-sm mt-5 z-10">{$currentLoadingInfo}</span>
</div>
+131 -91
View File
@@ -5,7 +5,7 @@
import { animate } from 'animejs';
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import { Check, CheckCircle, CircleOff, Settings2 } from 'lucide-svelte';
import { Check, CircleCheckBig, CircleOff, LoaderCircle } from '@lucide/svelte';
import { open } from '@tauri-apps/plugin-dialog';
import Checkbox from '@/components/ui/checkbox/checkbox.svelte';
import {
@@ -17,7 +17,18 @@
userSettings,
} from '@/userSettings';
import Label from '@/components/ui/label/label.svelte';
import { beatmapSets, currentSkin, currentView, osuBuild, osuStream, skins } from '@/global';
import {
beatmapSets,
currentSkin,
currentView,
discordPresence,
osuBuild,
osuStream,
platform,
presenceLoading,
skins,
skinsCount,
} from '@/global';
import Launch from './Launch.svelte';
import Confetti from 'svelte-confetti';
import {
@@ -25,10 +36,11 @@
getBeatmapSetsCount,
getReleaseStream,
getSkin,
getSkinsCount,
getSkins,
getVersion,
isValidOsuFolder,
} from '@/osuUtil';
import { dev } from '$app/environment';
let selectedStep = $state(1);
const steps = ['Welcome', 'Locate your osu! Installation', 'Appearance Settings'];
@@ -92,9 +104,12 @@
beatmapSets.set(beatmapSetCount);
}
const skinsCount: number | null = await getSkinsCount(osuInstallPath);
if (skinsCount) {
skins.set(skinsCount);
const skins_list = await getSkins(osuInstallPath);
if (skins_list) {
skins.set(skins_list);
skinsCount.set(skins_list.length);
} else {
skinsCount.set(0);
}
const skin: string = await getSkin(osuInstallPath);
@@ -119,7 +134,7 @@
</script>
{#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">
<Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} />
</div>
@@ -128,15 +143,17 @@
<Button class="mt-4" onclick={saveConfig}>Finish</Button>
</div>
{:else}
<div class="grid grid-cols-[0.41fr_1fr] h-[100vh]">
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3">
<div class="grid grid-cols-[0.41fr_1fr] h-[100vh] z-50">
<div
class="w-full h-full border-r border-black/40 bg-black/40 backdrop-blur-md flex flex-col gap-6 p-3 z-50"
>
{#each steps as step, i (step)}
<div
class="flex flex-row items-center gap-2 border {selectedStep === i + 1
? 'border-primary-800/30 bg-primary-900/30'
? 'border-primary-600/30 bg-primary-700/30'
: selectedStep > i
? 'border-green-800/30 bg-green-900/30'
: 'border-theme-800 bg-theme-900'} rounded-lg p-2 transition-all"
: 'border-black/30 bg-black/30'} rounded-lg p-2 transition-all"
>
<div
class="flex flex-col items-center justify-center h-8 w-8 border-[2px] {selectedStep >
@@ -160,10 +177,10 @@
</div>
{/each}
</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}
<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/40 rounded-lg border border-black/40 p-6 mt-5 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<img
@@ -178,13 +195,13 @@
<p class="text-muted-foreground mt-2">
This setup wizard will guide you through the initial setup of EZPPLauncher.
</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-200 p-4 rounded-lg mt-4">
Please make sure you have osu! installed on your system before proceeding.
</div>
</div>
{:else if selectedStep === 2}
<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/40 rounded-lg border border-black/40 p-6 mt-5 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<h1 class="text-3xl font-semibold">Locate your osu! Installation</h1>
@@ -207,14 +224,14 @@
{#if !manualSelect}
{#if autoDetectedOsuPath}
<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 />
<CircleCheckBig />
<span>Auto-detected osu! installation path! Please check if its correct!</span>
</div>
{:else}
<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 />
<span>Could not auto-detect osu! installation path. Please select it manually.</span
@@ -223,14 +240,14 @@
{/if}
{:else if manualSelectValid}
<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 />
<CircleCheckBig />
<span>Selected osu! installation path is valid!</span>
</div>
{:else}
<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 />
<span
@@ -241,80 +258,103 @@
{/if}
</div>
{:else if selectedStep === 3}
<div
class="bg-theme-900/90 flex flex-col justify-center gap-3 mt-5 border border-theme-800/90 rounded-lg"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">
<Settings2 /> EZPPLauncher Settings
</div>
<div class="h-full w-full flex items-center justify-center">
<div
class="grid grid-cols-[1fr_auto] gap-y-5 items-center border-t border-theme-800 py-3 px-6"
class="bg-black/40 backdrop-blur-sm py-8 px-8 rounded-lg"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<div class="flex flex-col">
<Label class="text-sm" for="setting-custom-cursor">Patching</Label>
<div class="text-muted-foreground text-xs">Shows misses in Relax and Autopilot</div>
</div>
<Checkbox
id="setting-custom-cursor"
checked={$patch}
onCheckedChange={async (e) => {
patch.set(e);
$userSettings.save();
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="grid grid-cols-[1fr_auto] gap-y-5 items-center px-6">
<div class="flex flex-col">
<Label class="text-sm" for="setting-patch">Patching</Label>
<div class="text-muted-foreground text-xs">
Shows misses in Relax and Autopilot {#if $platform !== 'windows'}<span
class="text-red-500 bg-red-800/20 border border-red-600/20 p-0.5 mx-1 px-2 rounded-lg !text-[0.55rem]"
>currently only on windows!</span
>
{/if}
</div>
</div>
<Checkbox
id="setting-patch"
checked={$platform === 'windows' ? $patch : false}
disabled={$platform !== 'windows'}
onCheckedChange={async (e) => {
patch.set(e);
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-custom-cursor">Lazer-Style Cursor</Label>
<div class="text-muted-foreground text-xs">
Enable a custom cursor in the Launcher like in the lazer build of osu!
<div class="flex flex-col">
<Label class="text-sm" for="setting-custom-cursor">Lazer-Style Cursor</Label>
<div class="text-muted-foreground text-xs">
Enable a custom cursor in the Launcher like in the lazer build of osu!
</div>
</div>
<Checkbox
id="setting-custom-cursor"
checked={$customCursor}
onCheckedChange={async (e) => {
if (!e) {
cursorSmoothening.set(false);
}
customCursor.set(e);
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Cursor Smoothening</Label>
<div class="text-muted-foreground text-xs">
Makes the custom cursor movement smoother.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$cursorSmoothening}
onCheckedChange={async (e) => {
if (!$customCursor) return;
cursorSmoothening.set(e);
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-reduce-animations">Reduce Animations</Label>
<div class="text-muted-foreground text-xs">
Disables some animations in the Launcher to improve performance on low-end
devices.
</div>
</div>
<Checkbox
id="setting-reduce-animations"
checked={$reduceAnimations}
onCheckedChange={async (e) => {
reduceAnimations.set(e);
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-rich-presence">Discord Rich Presence</Label>
<div class="text-muted-foreground text-xs">
Let other discord users show what you are doing right now 👀
</div>
</div>
<div class="relative">
{#if $presenceLoading}
<div class="-left-8 absolute" transition:fade>
<LoaderCircle class="animate-spin" />
</div>
{/if}
<Checkbox
id="setting-rich-presence"
bind:checked={$discordPresence}
disabled={$presenceLoading}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
</div>
</div>
<Checkbox
id="setting-custom-cursor"
checked={$customCursor}
onCheckedChange={async (e) => {
if (!e) {
cursorSmoothening.set(false);
}
customCursor.set(e);
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Cursor Smoothening</Label>
<div class="text-muted-foreground text-xs">
Makes the custom cursor movement smoother.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$cursorSmoothening}
onCheckedChange={async (e) => {
if (!$customCursor) return;
cursorSmoothening.set(e);
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label>
<div class="text-muted-foreground text-xs">
Disables some animations in the Launcher to improve performance on low-end devices.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$reduceAnimations}
onCheckedChange={async (e) => {
reduceAnimations.set(e);
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
</div>
</div>
{/if}
@@ -332,7 +372,7 @@
else selectedStep = Math.min(selectedStep + 1, steps.length);
}}
disabled={selectedStep > steps.length ||
(selectedStep === 2 && osuInstallPath.length <= 0)}
(selectedStep === 2 && !dev && osuInstallPath.length <= 0)}
>{selectedStep >= steps.length ? 'Finish' : 'Next'}</Button
>
</div>
-122
View File
@@ -1,122 +0,0 @@
import { fontFamily } from 'tailwindcss/defaultTheme';
import type { Config } from 'tailwindcss';
import tailwindcssAnimate from 'tailwindcss-animate';
const config: Config = {
darkMode: 'class',
content: ['./src/**/*.{html,js,svelte,ts}'],
safelist: ['dark'],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
theme: {
DEFAULT: '#181825',
50: '#E9E9F1',
100: '#DBDBE8',
200: '#BFBFD6',
300: '#A4A4C4',
400: '#8888B2',
500: '#6C6CA0',
600: '#575786',
700: '#45456B',
800: '#33334F',
900: '#212133',
950: '#181825',
},
border: 'hsl(var(--border) / <alpha-value>)',
input: 'hsl(var(--input) / <alpha-value>)',
ring: 'hsl(var(--ring) / <alpha-value>)',
background: 'hsl(var(--background) / <alpha-value>)',
foreground: 'hsl(var(--foreground) / <alpha-value>)',
primary: {
DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
50: '#FEFDFF',
100: '#FAF6FE',
200: '#F2E9FC',
300: '#E9DBFA',
400: '#E1CEF9',
500: '#D9C0F7',
600: '#C6A0F3',
700: '#B381EF',
800: '#A061EB',
900: '#8D41E7',
950: '#8331E5',
foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
},
secondary: {
DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)',
},
destructive: {
DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)',
},
muted: {
DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
},
accent: {
DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
foreground: 'hsl(var(--accent-foreground) / <alpha-value>)',
},
popover: {
DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
foreground: 'hsl(var(--popover-foreground) / <alpha-value>)',
},
card: {
DEFAULT: 'hsl(var(--card) / <alpha-value>)',
foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
},
},
borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
sans: ['"Sora"', ...fontFamily.sans],
mono: ['"Space Mono"', ...fontFamily.mono],
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--bits-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--bits-accordion-content-height)' },
to: { height: '0' },
},
'caret-blink': {
'0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'caret-blink': 'caret-blink 1.25s ease-out infinite',
},
},
},
plugins: [tailwindcssAnimate],
};
export default config;
+2 -1
View File
@@ -9,7 +9,8 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
"moduleResolution": "bundler",
"types": ["@types/node"]
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files