@@ -18,6 +18,7 @@
|
|||||||
"animejs": "^4.1.3",
|
"animejs": "^4.1.3",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"lucide-svelte": "^1.0.1",
|
"lucide-svelte": "^1.0.1",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
@@ -389,6 +390,8 @@
|
|||||||
|
|
||||||
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
"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=="],
|
"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=="],
|
"crypto-js": ["crypto-js@4.2.0", "", {}, "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="],
|
||||||
@@ -483,12 +486,16 @@
|
|||||||
|
|
||||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
||||||
|
|
||||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||||
@@ -499,6 +506,8 @@
|
|||||||
|
|
||||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
"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=="],
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
||||||
@@ -511,6 +520,8 @@
|
|||||||
|
|
||||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
"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=="],
|
"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=="],
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
@@ -519,6 +530,8 @@
|
|||||||
|
|
||||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||||
|
|
||||||
|
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||||
|
|
||||||
"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": ["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-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
||||||
@@ -605,6 +618,8 @@
|
|||||||
|
|
||||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
"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=="],
|
"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=="],
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
@@ -633,6 +648,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=="],
|
"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=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
@@ -643,6 +660,8 @@
|
|||||||
|
|
||||||
"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=="],
|
"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=="],
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
@@ -659,6 +678,8 @@
|
|||||||
|
|
||||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
"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": ["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=="],
|
"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=="],
|
||||||
@@ -705,6 +726,8 @@
|
|||||||
|
|
||||||
"set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="],
|
"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=="],
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
@@ -715,6 +738,8 @@
|
|||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"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=="],
|
"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=="],
|
"style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="],
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"animejs": "^4.1.3",
|
"animejs": "^4.1.3",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"lucide-svelte": "^1.0.1",
|
"lucide-svelte": "^1.0.1",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
|
|||||||
Generated
+7
@@ -1613,6 +1613,12 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-range"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
@@ -4035,6 +4041,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"http",
|
"http",
|
||||||
|
"http-range",
|
||||||
"jni",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||||||
tauri-build = { version = "2.5.6", features = [] }
|
tauri-build = { version = "2.5.6", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2.10.3", features = [] }
|
tauri = { version = "2.10.3", features = ["protocol-asset"] }
|
||||||
tauri-plugin-shell = "2.3.5"
|
tauri-plugin-shell = "2.3.5"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
|
|||||||
@@ -71,6 +71,14 @@
|
|||||||
{ "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/**/*" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,13 @@
|
|||||||
],
|
],
|
||||||
"withGlobalTauri": true,
|
"withGlobalTauri": true,
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null,
|
||||||
|
"assetProtocol": {
|
||||||
|
"enable": true,
|
||||||
|
"scope": [
|
||||||
|
"$HOME/.ezpplauncher/themes/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
@@ -74,12 +74,18 @@
|
|||||||
|
|
||||||
<div class="wrapper" bind:this={wrapper}>
|
<div class="wrapper" bind:this={wrapper}>
|
||||||
<!-- Scrollable content -->
|
<!-- Scrollable content -->
|
||||||
<div class={`content ${className}`} bind:this={container} onscroll={updateScrollbar}>
|
<div
|
||||||
|
class={`content ${className}`}
|
||||||
|
bind:this={container}
|
||||||
|
onscroll={updateScrollbar}
|
||||||
|
id="scrollable-content"
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Custom scrollbar -->
|
<!-- Custom scrollbar -->
|
||||||
<div class="scrollbar" style="top: {topOffset}px;">
|
<div class="scrollbar" style="top: {topOffset}px;">
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div
|
<div
|
||||||
class="thumb"
|
class="thumb"
|
||||||
bind:this={thumb}
|
bind:this={thumb}
|
||||||
@@ -87,6 +93,7 @@
|
|||||||
onpointermove={drag}
|
onpointermove={drag}
|
||||||
onpointerup={endDrag}
|
onpointerup={endDrag}
|
||||||
onpointercancel={endDrag}
|
onpointercancel={endDrag}
|
||||||
|
aria-controls="scrollable-content"
|
||||||
style="height:{thumbHeight}px; transform:translateY({thumbTop}px);"
|
style="height:{thumbHeight}px; transform:translateY({thumbTop}px);"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,8 +124,8 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 2px;
|
right: 2px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 12px;
|
width: 8px;
|
||||||
background: var(--color-theme-950);
|
background: transparent;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +133,7 @@
|
|||||||
.thumb {
|
.thumb {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--color-theme-800);
|
background: var(--color-theme-700);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
|
|||||||
+4
-4
@@ -3,8 +3,7 @@ import { ezppfarm } from './api/ezpp';
|
|||||||
import type { Component } from 'svelte';
|
import type { Component } from 'svelte';
|
||||||
import Loading from '../screens/Loading.svelte';
|
import Loading from '../screens/Loading.svelte';
|
||||||
import type { Release } from './types';
|
import type { Release } from './types';
|
||||||
import { THEMES } from './themes';
|
import type { Theme } from './themes';
|
||||||
|
|
||||||
export const currentView = writable<Component>(Loading);
|
export const currentView = writable<Component>(Loading);
|
||||||
|
|
||||||
export const platform = writable<string>('');
|
export const platform = writable<string>('');
|
||||||
@@ -25,8 +24,9 @@ export const currentLoadingInfo = writable<string>('Initializing...');
|
|||||||
export const firstStartup = writable<boolean>(false);
|
export const firstStartup = writable<boolean>(false);
|
||||||
|
|
||||||
export const launching = writable<boolean>(false);
|
export const launching = writable<boolean>(false);
|
||||||
export const theme = writable<(typeof THEMES)[number]>(THEMES[0]);
|
export const custom_themes = writable<Theme[]>([]);
|
||||||
export const theme_video = writable<HTMLVideoElement | undefined>(undefined);
|
export const custom_theme_container = writable<HTMLElement | undefined>(undefined);
|
||||||
|
export const active_custom_theme = writable<Theme | undefined>(undefined);
|
||||||
|
|
||||||
export const serverPing = writable<number | undefined>(undefined);
|
export const serverPing = writable<number | undefined>(undefined);
|
||||||
export const serverConnectionFails = writable(0);
|
export const serverConnectionFails = writable(0);
|
||||||
|
|||||||
+257
-40
@@ -1,43 +1,260 @@
|
|||||||
import executionclap_video from '$assets/themes/execution_clap/video.webm';
|
import * as path from '@tauri-apps/api/path';
|
||||||
import lostumbrella_video from '$assets/themes/lost_umbrella/video.webm';
|
import * as fs from '@tauri-apps/plugin-fs';
|
||||||
import nyancat_image from '$assets/themes/nyan_cat/image.gif';
|
import { convertFileSrc } from '@tauri-apps/api/core';
|
||||||
import nyancat_audio from '$assets/themes/nyan_cat/audio.mp3';
|
import { active_custom_theme, custom_themes } from './global';
|
||||||
|
import { setGlobalVolume } from './utils';
|
||||||
|
import { betterFetch } from '@better-fetch/fetch';
|
||||||
|
import zip from 'jszip';
|
||||||
|
|
||||||
export const THEMES = [
|
export type Theme = {
|
||||||
{
|
name: string;
|
||||||
name: 'default',
|
version: string;
|
||||||
display_name: 'Default',
|
author: string;
|
||||||
assets: {
|
scriptUrl: string;
|
||||||
background_video: undefined,
|
assets: string;
|
||||||
background_image: undefined,
|
preview: string;
|
||||||
audio: undefined,
|
status: 'installed' | 'downloading' | 'extracting' | 'not-installed';
|
||||||
|
progress: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
name: 'execution_clap',
|
for (const theme of downloadableThemes.data) {
|
||||||
display_name: 'Execution Clap',
|
themes.push({
|
||||||
assets: {
|
name: theme.name,
|
||||||
background_video: executionclap_video,
|
version: theme.version,
|
||||||
background_image: undefined,
|
author: theme.author,
|
||||||
audio: undefined,
|
scriptUrl: '',
|
||||||
|
assets: '',
|
||||||
|
preview: `https://git.ez-pp.farm/EZPPFarm/EZPPLauncher-Themes/raw/branch/main/previews/${toSafeName(theme.name)}.png`,
|
||||||
|
status: 'not-installed',
|
||||||
|
progress: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} 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({
|
||||||
|
author: 'EZPPFarm',
|
||||||
|
name: 'Default',
|
||||||
|
version: '1.0.0',
|
||||||
|
scriptUrl: '',
|
||||||
|
assets: '',
|
||||||
|
preview: '',
|
||||||
|
status: 'installed',
|
||||||
|
progress: 0,
|
||||||
|
});
|
||||||
|
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 {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
apiVersion: string;
|
||||||
|
author: string;
|
||||||
|
entry: string;
|
||||||
|
style: string;
|
||||||
|
preview: string;
|
||||||
|
};
|
||||||
|
const scriptFile = await path.join(folderPath, themeFolder.name, theme.entry);
|
||||||
|
if (await fs.exists(scriptFile)) {
|
||||||
|
themes.push({
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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): Promise<boolean> => {
|
||||||
|
if (theme.status !== 'not-installed') return false;
|
||||||
|
|
||||||
|
custom_themes.update((themes) => {
|
||||||
|
const matchedTheme = themes.find((t) => t.name === theme.name);
|
||||||
|
if (matchedTheme) {
|
||||||
|
matchedTheme.status = 'downloading';
|
||||||
|
}
|
||||||
|
return themes;
|
||||||
|
});
|
||||||
|
const downloadUrl = `https://git.ez-pp.farm/EZPPFarm/EZPPLauncher-Themes/raw/branch/main/themes/${toSafeName(
|
||||||
|
theme.name
|
||||||
|
)}.ezpplauncher-theme`;
|
||||||
|
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',
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
{
|
|
||||||
name: 'lost_umbrella',
|
if (!downloadReq.ok || downloadReq.body === null) {
|
||||||
display_name: 'Lost Umbrella',
|
console.log(downloadUrl);
|
||||||
assets: {
|
custom_themes.update((themes) => {
|
||||||
background_video: lostumbrella_video,
|
const matchedTheme = themes.find((t) => t.name === theme.name);
|
||||||
background_image: undefined,
|
if (matchedTheme) {
|
||||||
audio: undefined,
|
matchedTheme.status = 'not-installed';
|
||||||
},
|
}
|
||||||
},
|
return themes;
|
||||||
{
|
});
|
||||||
name: 'nyan_cat',
|
return false;
|
||||||
display_name: 'Nyan Cat',
|
}
|
||||||
assets: {
|
const total = Number(downloadReq.headers.get('content-length')) || 0;
|
||||||
background_video: undefined,
|
const reader = downloadReq.body.getReader();
|
||||||
background_image: nyancat_image,
|
let received = 0;
|
||||||
audio: nyancat_audio,
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(combineChunks(chunks, total));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const themeConfig = await fs.readTextFile(await path.join(themeFolder, 'theme.json'));
|
||||||
|
const themeConfigObj = JSON.parse(themeConfig) as {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
apiVersion: string;
|
||||||
|
author: string;
|
||||||
|
entry: string;
|
||||||
|
style: string;
|
||||||
|
preview: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const themeScript = convertFileSrc(
|
||||||
|
await path.normalize(`${themeFolder}/${themeConfigObj.entry}`)
|
||||||
|
);
|
||||||
|
const themeAssets = convertFileSrc(await path.normalize(`${themeFolder}/assets`));
|
||||||
|
const themePreview = convertFileSrc(
|
||||||
|
await path.normalize(`${themeFolder}/assets/${themeConfigObj.preview}`)
|
||||||
|
);
|
||||||
|
custom_themes.update((themes) => {
|
||||||
|
const matchedTheme = themes.find((t) => t.name === theme.name);
|
||||||
|
if (matchedTheme) {
|
||||||
|
matchedTheme.scriptUrl = themeScript;
|
||||||
|
matchedTheme.assets = themeAssets;
|
||||||
|
matchedTheme.preview = themePreview;
|
||||||
|
matchedTheme.name = themeConfigObj.name;
|
||||||
|
matchedTheme.version = themeConfigObj.version;
|
||||||
|
matchedTheme.author = themeConfigObj.author;
|
||||||
|
matchedTheme.status = 'installed';
|
||||||
|
}
|
||||||
|
return themes;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadTheme = async (theme: Theme, themeContainer: HTMLElement) => {
|
||||||
|
active_custom_theme.set(theme);
|
||||||
|
themeContainer.innerHTML = '';
|
||||||
|
if (theme.scriptUrl.length > 0) {
|
||||||
|
const themeScript = await import(theme.scriptUrl);
|
||||||
|
/* @vite-ignore */
|
||||||
|
themeScript.mountTheme(themeContainer, { assets: theme.assets });
|
||||||
|
}
|
||||||
|
setGlobalVolume(0.15);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toSafeName = (name: string) => {
|
||||||
|
return name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]/g, '_')
|
||||||
|
.replace(/-+/g, '_');
|
||||||
|
};
|
||||||
|
|||||||
@@ -99,3 +99,48 @@ export const urlIsValidImage = async (url: string) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function toURL(data: Buffer, type: string) {
|
||||||
|
return URL.createObjectURL(new Blob([new Uint8Array(data)], { type }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setGlobalVolume(volume: number) {
|
||||||
|
const audioElements = document.querySelectorAll('audio');
|
||||||
|
audioElements.forEach((audio) => {
|
||||||
|
audio.volume = volume;
|
||||||
|
});
|
||||||
|
|
||||||
|
const videoElements = document.querySelectorAll('video');
|
||||||
|
videoElements.forEach((video) => {
|
||||||
|
video.volume = volume;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fadeGlobalVolume(volumeFrom: number, volumeTo: number, duration: number) {
|
||||||
|
const audioElements = document.querySelectorAll('audio');
|
||||||
|
const videoElements = document.querySelectorAll('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;
|
||||||
|
|
||||||
|
audioElements.forEach((audio) => {
|
||||||
|
audio.volume = interpolatedVolume;
|
||||||
|
});
|
||||||
|
|
||||||
|
videoElements.forEach((video) => {
|
||||||
|
video.volume = interpolatedVolume;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
|||||||
+42
-33
@@ -5,15 +5,16 @@
|
|||||||
import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
|
import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
|
||||||
import * as AlertDialog from '@/components/ui/alert-dialog';
|
import * as AlertDialog from '@/components/ui/alert-dialog';
|
||||||
import {
|
import {
|
||||||
|
active_custom_theme,
|
||||||
currentLoadingInfo,
|
currentLoadingInfo,
|
||||||
|
custom_theme_container,
|
||||||
|
custom_themes,
|
||||||
discordPresence,
|
discordPresence,
|
||||||
firstStartup,
|
firstStartup,
|
||||||
launcherVersion,
|
launcherVersion,
|
||||||
platform,
|
platform,
|
||||||
presenceLoading,
|
presenceLoading,
|
||||||
setupValues,
|
setupValues,
|
||||||
theme,
|
|
||||||
theme_video,
|
|
||||||
trackingEnabled,
|
trackingEnabled,
|
||||||
} from '@/global';
|
} from '@/global';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
@@ -36,8 +37,9 @@
|
|||||||
|
|
||||||
import '@fontsource/sora';
|
import '@fontsource/sora';
|
||||||
import '@fontsource/space-mono';
|
import '@fontsource/space-mono';
|
||||||
|
import { getDownloadableThemes, getThemes, loadTheme } from '@/themes';
|
||||||
|
import { sileo } from 'sileo';
|
||||||
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
|
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
|
||||||
import NyanCatBg from '@/components/ui/animated-bg/NyanCatBg.svelte';
|
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
|
||||||
@@ -103,6 +105,32 @@
|
|||||||
firstStartup.set(isFirstStartup);
|
firstStartup.set(isFirstStartup);
|
||||||
$userAuth.init();
|
$userAuth.init();
|
||||||
|
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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!);
|
||||||
|
} else {
|
||||||
|
loadTheme(theme, $custom_theme_container!);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
sileo.error({
|
||||||
|
title: 'An error occured!',
|
||||||
|
description: 'Failed to load theme',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
currentLoadingInfo.set('Loading config...');
|
currentLoadingInfo.set('Loading config...');
|
||||||
const config_patching = $userSettings.value('patching');
|
const config_patching = $userSettings.value('patching');
|
||||||
const config_custom_cursor = $userSettings.value('custom_cursor');
|
const config_custom_cursor = $userSettings.value('custom_cursor');
|
||||||
@@ -181,38 +209,19 @@
|
|||||||
</AlertDialog.Content>
|
</AlertDialog.Content>
|
||||||
</AlertDialog.Root>
|
</AlertDialog.Root>
|
||||||
|
|
||||||
<main data-theme={$theme.name ?? 'default'}>
|
<main>
|
||||||
<div class="opacity-30">
|
<div class="opacity-30">
|
||||||
{#if $theme}
|
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
|
||||||
{#if $theme.assets.background_video}
|
<div
|
||||||
{#key $theme.name}
|
class={$active_custom_theme && $active_custom_theme.name === 'Default'
|
||||||
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
|
? 'hidden'
|
||||||
<video
|
: 'absolute z-0 top-0 left-0 w-full h-full object-cover object-center aspect-video'}
|
||||||
class="absolute z-0 top-0 left-0 w-full h-full object-cover object-center aspect-video"
|
bind:this={$custom_theme_container}
|
||||||
autoplay
|
></div>
|
||||||
playsinline
|
{#if ($active_custom_theme && $active_custom_theme.name === 'Default') || !$active_custom_theme}
|
||||||
loop
|
|
||||||
volume={0.15}
|
|
||||||
bind:this={$theme_video}
|
|
||||||
>
|
|
||||||
<source src={$theme.assets.background_video} />
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
{/key}
|
|
||||||
{:else if $theme.name === 'nyan_cat'}
|
|
||||||
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
|
|
||||||
<NyanCatBg />
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
|
|
||||||
<AnimatedBg />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div transition:fade={{ duration: $reduceAnimations ? 0 : 500 }}>
|
|
||||||
<AnimatedBg />
|
<AnimatedBg />
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+96
-73
@@ -1,12 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Logo from '$assets/logo.png';
|
import Logo from '$assets/logo.png';
|
||||||
|
import DefaultThemePreview from '$assets/default_preview.png';
|
||||||
import * as Avatar from '@/components/ui/avatar';
|
import * as Avatar from '@/components/ui/avatar';
|
||||||
import Badge from '@/components/ui/badge/badge.svelte';
|
import Badge from '@/components/ui/badge/badge.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
import * as Select from '@/components/ui/select';
|
import * as Select from '@/components/ui/select';
|
||||||
import {
|
import {
|
||||||
|
active_custom_theme,
|
||||||
beatmapSets,
|
beatmapSets,
|
||||||
currentSkin,
|
currentSkin,
|
||||||
|
custom_theme_container,
|
||||||
|
custom_themes,
|
||||||
discordPresence,
|
discordPresence,
|
||||||
launcherStream,
|
launcherStream,
|
||||||
launcherStreams,
|
launcherStreams,
|
||||||
@@ -21,8 +25,6 @@
|
|||||||
serverPing,
|
serverPing,
|
||||||
skins,
|
skins,
|
||||||
skinsCount,
|
skinsCount,
|
||||||
theme,
|
|
||||||
theme_video,
|
|
||||||
trackingEnabled,
|
trackingEnabled,
|
||||||
} from '@/global';
|
} from '@/global';
|
||||||
import {
|
import {
|
||||||
@@ -40,12 +42,14 @@
|
|||||||
ArrowRight,
|
ArrowRight,
|
||||||
Settings,
|
Settings,
|
||||||
House,
|
House,
|
||||||
|
Paintbrush,
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import NumberFlow from '@number-flow/svelte';
|
import NumberFlow from '@number-flow/svelte';
|
||||||
import * as AlertDialog from '@/components/ui/alert-dialog';
|
import * as AlertDialog from '@/components/ui/alert-dialog';
|
||||||
import Progress from '@/components/ui/progress/progress.svelte';
|
import Progress from '@/components/ui/progress/progress.svelte';
|
||||||
import {
|
import {
|
||||||
compareBuildNumbers,
|
compareBuildNumbers,
|
||||||
|
fadeGlobalVolume,
|
||||||
formatBytes,
|
formatBytes,
|
||||||
numberHumanReadable,
|
numberHumanReadable,
|
||||||
openURL,
|
openURL,
|
||||||
@@ -111,7 +115,8 @@
|
|||||||
import DownloadButton from '@/components/ui/download-button/DownloadButton.svelte';
|
import DownloadButton from '@/components/ui/download-button/DownloadButton.svelte';
|
||||||
import { animate } from 'animejs';
|
import { animate } from 'animejs';
|
||||||
import { sileo } from 'sileo';
|
import { sileo } from 'sileo';
|
||||||
import { THEMES } from '@/themes';
|
import ScrollContainer from '@/components/ui/scroll-container/ScrollContainer.svelte';
|
||||||
|
import { downloadTheme, loadTheme } from '@/themes';
|
||||||
|
|
||||||
let selectedView = $state('home');
|
let selectedView = $state('home');
|
||||||
let progress = $state(-1);
|
let progress = $state(-1);
|
||||||
@@ -437,23 +442,7 @@
|
|||||||
await new Promise((res) => setTimeout(res, 1500));
|
await new Promise((res) => setTimeout(res, 1500));
|
||||||
launchInfo = 'Launching osu!...';
|
launchInfo = 'Launching osu!...';
|
||||||
|
|
||||||
if ($theme_video) {
|
fadeGlobalVolume(0.15, 0, 2000);
|
||||||
const video = $theme_video;
|
|
||||||
const fadeOutDuration = 2000; // 2 seconds
|
|
||||||
const fadeOutSteps = 20;
|
|
||||||
const fadeOutStepTime = fadeOutDuration / fadeOutSteps;
|
|
||||||
const volumeStep = video.volume / fadeOutSteps;
|
|
||||||
|
|
||||||
const fadeOutInterval = setInterval(() => {
|
|
||||||
if (video.volume > 0) {
|
|
||||||
video.volume = Math.max(0, video.volume - volumeStep);
|
|
||||||
} else {
|
|
||||||
clearInterval(fadeOutInterval);
|
|
||||||
video.pause();
|
|
||||||
video.currentTime = 0;
|
|
||||||
}
|
|
||||||
}, fadeOutStepTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
await replaceUIFiles(osuPath, false);
|
await replaceUIFiles(osuPath, false);
|
||||||
await new Promise((res) => setTimeout(res, 1000));
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
@@ -565,26 +554,7 @@
|
|||||||
cleanup = true;
|
cleanup = true;
|
||||||
launchInfo = 'Cleaning up...';
|
launchInfo = 'Cleaning up...';
|
||||||
|
|
||||||
if ($theme_video) {
|
fadeGlobalVolume(0, 0.15, 2000);
|
||||||
const video = $theme_video;
|
|
||||||
video.play();
|
|
||||||
video.currentTime = 0;
|
|
||||||
video.volume = 0;
|
|
||||||
|
|
||||||
const fadeInVolume = 0.15;
|
|
||||||
const fadeInDuration = 2000; // 2 seconds
|
|
||||||
const fadeInSteps = 20;
|
|
||||||
const fadeInStepTime = fadeInDuration / fadeInSteps;
|
|
||||||
const volumeStep = (fadeInVolume - video.volume) / fadeInSteps;
|
|
||||||
|
|
||||||
const fadeInInterval = setInterval(() => {
|
|
||||||
if (video.volume < fadeInVolume) {
|
|
||||||
video.volume = Math.min(fadeInVolume, video.volume + volumeStep);
|
|
||||||
} else {
|
|
||||||
clearInterval(fadeInInterval);
|
|
||||||
}
|
|
||||||
}, fadeInStepTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
await getCurrentWindow().show();
|
await getCurrentWindow().show();
|
||||||
if (presenceUpdater) {
|
if (presenceUpdater) {
|
||||||
@@ -944,6 +914,18 @@
|
|||||||
>
|
>
|
||||||
<House class="text-theme-200 !size-5" />
|
<House class="text-theme-200 !size-5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
class="flex size-12 items-center gap-2 ring-1 ring-inset ring-white/15 {selectedView ===
|
||||||
|
'themes'
|
||||||
|
? 'bg-primary/50'
|
||||||
|
: 'bg-black/20 border-black/20'} hover:bg-primary/50 rounded-[0.85rem] p-3 mt-3"
|
||||||
|
disabled={$launching}
|
||||||
|
onclick={() => {
|
||||||
|
if (!$launching) selectedView = 'themes';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paintbrush class="text-theme-200 !size-5" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="flex size-12 items-center gap-2 ring-1 ring-inset ring-white/15 {selectedView ===
|
class="flex size-12 items-center gap-2 ring-1 ring-inset ring-white/15 {selectedView ===
|
||||||
'settings'
|
'settings'
|
||||||
@@ -1484,39 +1466,6 @@
|
|||||||
</Select.Root>
|
</Select.Root>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex flex-col">
|
|
||||||
<Label class="text-sm" for="setting-custom-cursor">launcher theme</Label>
|
|
||||||
<div class="text-muted-foreground text-xs">select the theme of this interface</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row w-full">
|
|
||||||
<Select.Root
|
|
||||||
type="single"
|
|
||||||
value={$theme.name ?? 'default'}
|
|
||||||
onValueChange={async (them) => {
|
|
||||||
const newTheme = THEMES.find((theme) => theme.name === them);
|
|
||||||
if (newTheme) theme.set(newTheme);
|
|
||||||
$userSettings.value('launcherTheme').set(them);
|
|
||||||
await $userSettings.save();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Select.Trigger
|
|
||||||
class="border-theme-800 bg-theme-950 !text-muted-foreground font-semibold"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row items-center gap-2 font-normal text-foreground">
|
|
||||||
{$theme.display_name}
|
|
||||||
</div>
|
|
||||||
</Select.Trigger>
|
|
||||||
<Select.Content class="bg-theme-950 border border-theme-950 rounded-lg">
|
|
||||||
{#each THEMES as theme (theme.name)}
|
|
||||||
<Select.Item value={theme.name}>
|
|
||||||
<div class="flex flex-row gap-2 items-center">
|
|
||||||
{theme.display_name}
|
|
||||||
</div>
|
|
||||||
</Select.Item>
|
|
||||||
{/each}
|
|
||||||
</Select.Content>
|
|
||||||
</Select.Root>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if selectedView === 'login'}
|
{:else if selectedView === 'login'}
|
||||||
@@ -1570,6 +1519,80 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if selectedView === 'themes'}
|
||||||
|
<div
|
||||||
|
class="h-[100vh] w-full flex flex-col items-center bg-black/20 backdrop-blur-sm"
|
||||||
|
in:fly={{
|
||||||
|
duration: $reduceAnimations ? 0 : 400,
|
||||||
|
delay: $reduceAnimations ? 0 : 400,
|
||||||
|
y: 5,
|
||||||
|
opacity: 0,
|
||||||
|
}}
|
||||||
|
out:fly={{ duration: $reduceAnimations ? 0 : 400, y: -5, opacity: 0 }}
|
||||||
|
>
|
||||||
|
<ScrollContainer class="pt-8" topOffset={45}>
|
||||||
|
<div class="grid w-full gap-1 grid-cols-3 p-3 pr-5">
|
||||||
|
{#each $custom_themes as theme (theme.name)}
|
||||||
|
<div
|
||||||
|
class="group overflow-hidden rounded-3xl border {$active_custom_theme &&
|
||||||
|
$active_custom_theme.name === theme.name
|
||||||
|
? 'border-primary'
|
||||||
|
: 'border-theme-800'} bg-theme-950 transition hover:border-white/20 h-[295px]"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={theme.preview || DefaultThemePreview}
|
||||||
|
alt="Preview of {theme.name}"
|
||||||
|
class="mb-2 h-40 w-full rounded-t-3xl object-cover object-center pointer-events-none select-none"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-col gap-1 p-3">
|
||||||
|
<div class="mb-4 flex items-center justify-between gap-2">
|
||||||
|
<div class="flex flex-col items-start">
|
||||||
|
<span class="text-sm font-semibold">{theme.name}</span>
|
||||||
|
<span class="text-xs text-muted-foreground">by {theme.author}</span>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-muted-foreground">v{theme.version}</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
disabled={($active_custom_theme && $active_custom_theme.name === theme.name) ||
|
||||||
|
theme.status === 'downloading' ||
|
||||||
|
theme.status === 'extracting'}
|
||||||
|
onclick={() => {
|
||||||
|
if (theme.status === 'installed') {
|
||||||
|
if ($custom_theme_container) {
|
||||||
|
loadTheme(theme, $custom_theme_container);
|
||||||
|
$userSettings.value('theme').set(theme.name);
|
||||||
|
$userSettings.save();
|
||||||
|
} else {
|
||||||
|
sileo.error({
|
||||||
|
title: 'Uhhm..',
|
||||||
|
description: 'Failed to apply theme.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
downloadTheme(theme);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if $active_custom_theme && $active_custom_theme.name === theme.name}
|
||||||
|
Theme in use
|
||||||
|
{:else if theme.status !== 'installed'}
|
||||||
|
{#if theme.status === 'downloading'}
|
||||||
|
Downloading Theme... ({Math.round(theme.progress * 100)}%)
|
||||||
|
{:else if theme.status === 'extracting'}
|
||||||
|
Extracting Theme... ({Math.round(theme.progress * 100)}%)
|
||||||
|
{:else}
|
||||||
|
Download Theme
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
Use Theme
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</ScrollContainer>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
osuStream,
|
osuStream,
|
||||||
skins,
|
skins,
|
||||||
skinsCount,
|
skinsCount,
|
||||||
theme,
|
|
||||||
} from '@/global';
|
} from '@/global';
|
||||||
import {
|
import {
|
||||||
cursorSmoothness,
|
cursorSmoothness,
|
||||||
@@ -43,7 +42,6 @@
|
|||||||
import { git } from '@/api/git';
|
import { git } from '@/api/git';
|
||||||
import { sileo } from 'sileo';
|
import { sileo } from 'sileo';
|
||||||
import type { EZPPUser } from '@/types';
|
import type { EZPPUser } from '@/types';
|
||||||
import { THEMES } from '@/themes';
|
|
||||||
|
|
||||||
let ezppLogo: HTMLImageElement;
|
let ezppLogo: HTMLImageElement;
|
||||||
let spinnerCircle: SVGCircleElement;
|
let spinnerCircle: SVGCircleElement;
|
||||||
@@ -133,9 +131,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$firstStartup) {
|
if (!$firstStartup) {
|
||||||
const launcherTheme = $userSettings.value('launcherTheme').get('default');
|
/* const launcherTheme = $userSettings.value('launcherTheme').get('default');
|
||||||
const them = THEMES.find((theme) => theme.name === launcherTheme);
|
const them = THEMES.find((theme) => theme.name === launcherTheme);
|
||||||
if (them) theme.set(them);
|
if (them) theme.set(them); */
|
||||||
currentLoadingInfo.set('Checking osu installation path...');
|
currentLoadingInfo.set('Checking osu installation path...');
|
||||||
const validFolder = await isValidOsuFolder($osuInstallationPath);
|
const validFolder = await isValidOsuFolder($osuInstallationPath);
|
||||||
if (!validFolder) {
|
if (!validFolder) {
|
||||||
@@ -256,8 +254,8 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col items-center justify-center h-[100vh] w-full">
|
<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">
|
<div class="relative w-80 h-80 flex items-center justify-center z-10">
|
||||||
<svg
|
<svg
|
||||||
class="absolute top-0 left-0 w-full h-full animate-spin"
|
class="absolute top-0 left-0 w-full h-full animate-spin"
|
||||||
style="animation-duration: 5s;"
|
style="animation-duration: 5s;"
|
||||||
@@ -283,5 +281,5 @@
|
|||||||
bind:this={ezppLogo}
|
bind:this={ezppLogo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user