From ba8c07f34759c7b49b1e301ae469bb5274d893ba Mon Sep 17 00:00:00 2001 From: Bot Date: Sun, 1 Mar 2026 01:39:52 +0100 Subject: [PATCH] =?UTF-8?q?feat(web):=20complete=20frontend=20redesign=20?= =?UTF-8?q?=E2=80=94=20DECK=20theme=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redesigned the entire frontend with a new "DECK" aesthetic: - 5 distinctive themes: Midnight, Daylight, Neon, Vapor, Matrix - Compact pill-shaped sound buttons with category color bars - Tab navigation: All Sounds / Favorites / Recently Added - Horizontal category filter chips with color coding - Fixed bottom control bar: Stop, Random, Party, Volume - Responsive layout optimized for 800+ sounds - Syne + Outfit typography pairing - Party mode with animated gradient effects - Search with clear button, "Now Playing" indicator - Admin panel as modal overlay - Subtle dot grid background pattern on dark themes Replaces the previous Apple Music-style card layout with a dense, efficient grid that scales properly for large sound libraries. Co-Authored-By: Claude Opus 4.6 --- web/index.html | 11 +- web/package-lock.json | 1734 +++++++++++++++++++++++++++++++++++++++++ web/src/App.tsx | 694 +++++++++-------- web/src/styles.css | 1690 +++++++++++++++++++++++++++------------ 4 files changed, 3291 insertions(+), 838 deletions(-) create mode 100644 web/package-lock.json diff --git a/web/index.html b/web/index.html index e3aa098..34f6580 100644 --- a/web/index.html +++ b/web/index.html @@ -4,10 +4,11 @@ - Soundboard - - + + Jukebox + + + @@ -16,4 +17,4 @@
- \ No newline at end of file + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..dd263c5 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,1734 @@ +{ + "name": "discord-soundboard-web", + "version": "1.1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "discord-soundboard-web", + "version": "1.1.1", + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "typescript": "^5.5.4", + "vite": "^5.3.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001775", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz", + "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/web/src/App.tsx b/web/src/App.tsx index 7479572..59639cb 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,81 +1,101 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { fetchChannels, fetchSounds, playSound, setVolumeLive, getVolume, adminStatus, adminLogin, adminLogout, adminDelete, adminRename, playUrl, fetchCategories, createCategory, assignCategories, clearBadges, updateCategory, deleteCategory, partyStart, partyStop, subscribeEvents, getSelectedChannels, setSelectedChannel } from './api'; +import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react'; +import { + fetchChannels, fetchSounds, playSound, setVolumeLive, getVolume, + adminStatus, adminLogin, adminLogout, adminDelete, adminRename, + playUrl, fetchCategories, createCategory, assignCategories, clearBadges, + updateCategory, deleteCategory, partyStart, partyStop, subscribeEvents, + getSelectedChannels, setSelectedChannel, +} from './api'; import type { VoiceChannelInfo, Sound, Category } from './types'; import { getCookie, setCookie } from './cookies'; +/* ── Category Color Palette ── */ +const CAT_PALETTE = [ + '#3b82f6', '#f59e0b', '#8b5cf6', '#ec4899', '#14b8a6', + '#f97316', '#06b6d4', '#ef4444', '#a855f7', '#84cc16', + '#d946ef', '#0ea5e9', '#f43f5e', '#10b981', +]; + +const THEMES = [ + { id: 'midnight', label: 'Midnight' }, + { id: 'daylight', label: 'Daylight' }, + { id: 'neon', label: 'Neon' }, + { id: 'vapor', label: 'Vapor' }, + { id: 'matrix', label: 'Matrix' }, +]; + +type Tab = 'all' | 'favorites' | 'recent'; + export default function App() { + /* ── State ── */ const [sounds, setSounds] = useState([]); - const [total, setTotal] = useState(0); + const [total, setTotal] = useState(0); const [folders, setFolders] = useState>([]); - const [activeFolder, setActiveFolder] = useState('__all__'); const [categories, setCategories] = useState([]); - const [activeCategoryId, setActiveCategoryId] = useState(''); - const [channels, setChannels] = useState([]); + + const [activeTab, setActiveTab] = useState('all'); + const [activeFolder, setActiveFolder] = useState(''); const [query, setQuery] = useState(''); - const [selected, setSelected] = useState(''); - const selectedRef = useRef(''); - const [loading, setLoading] = useState(false); - const [notification, setNotification] = useState<{ msg: string, type: 'info' | 'error' } | null>(null); + const [channels, setChannels] = useState([]); + const [selected, setSelected] = useState(''); + const selectedRef = useRef(''); - const [volume, setVolume] = useState(1); + const [volume, setVolume] = useState(1); const [favs, setFavs] = useState>({}); - const [theme, setTheme] = useState(() => localStorage.getItem('theme') || 'dark'); - const [isAdmin, setIsAdmin] = useState(false); + const [theme, setTheme] = useState(() => localStorage.getItem('jb-theme') || 'midnight'); + const [isAdmin, setIsAdmin] = useState(false); + const [showAdmin, setShowAdmin] = useState(false); - // Chaos Mode (Partymode) - const [chaosMode, setChaosMode] = useState(false); + const [chaosMode, setChaosMode] = useState(false); const [partyActiveGuilds, setPartyActiveGuilds] = useState([]); - const chaosTimeoutRef = useRef(null); - const chaosModeRef = useRef(false); + const chaosModeRef = useRef(false); - // Scrolled State for Header blur - const [isScrolled, setIsScrolled] = useState(false); + const [lastPlayed, setLastPlayed] = useState(''); + const [notification, setNotification] = useState<{ msg: string; type: 'info' | 'error' } | null>(null); + /* ── Refs ── */ useEffect(() => { chaosModeRef.current = chaosMode; }, [chaosMode]); useEffect(() => { selectedRef.current = selected; }, [selected]); - const showNotification = (msg: string, type: 'info' | 'error' = 'info') => { + /* ── Helpers ── */ + const notify = useCallback((msg: string, type: 'info' | 'error' = 'info') => { setNotification({ msg, type }); setTimeout(() => setNotification(null), 3000); - }; + }, []); - // ---------------- Init Load ---------------- + const guildId = selected ? selected.split(':')[0] : ''; + const channelId = selected ? selected.split(':')[1] : ''; + + /* ── Init ── */ useEffect(() => { (async () => { try { - const [c, selectedMap] = await Promise.all([fetchChannels(), getSelectedChannels()]); - setChannels(c); - let initial = ''; - if (c.length > 0) { - const firstGuild = c[0].guildId; - const serverCid = selectedMap[firstGuild]; - if (serverCid && c.find(x => x.guildId === firstGuild && x.channelId === serverCid)) { - initial = `${firstGuild}:${serverCid}`; - } else { - initial = `${c[0].guildId}:${c[0].channelId}`; - } + const [ch, selMap] = await Promise.all([fetchChannels(), getSelectedChannels()]); + setChannels(ch); + if (ch.length) { + const g = ch[0].guildId; + const serverCid = selMap[g]; + const match = serverCid && ch.find(x => x.guildId === g && x.channelId === serverCid); + setSelected(match ? `${g}:${serverCid}` : `${ch[0].guildId}:${ch[0].channelId}`); } - if (initial) setSelected(initial); - } catch (e: any) { - showNotification(e?.message || 'Fehler beim Laden der Channels', 'error'); - } + } catch (e: any) { notify(e?.message || 'Channel-Fehler', 'error'); } try { setIsAdmin(await adminStatus()); } catch { } - try { const cats = await fetchCategories(); setCategories(cats.categories || []); } catch { } + try { const c = await fetchCategories(); setCategories(c.categories || []); } catch { } })(); }, []); - // ---------------- Theme ---------------- + /* ── Theme ── */ useEffect(() => { document.body.setAttribute('data-theme', theme); - localStorage.setItem('theme', theme); + localStorage.setItem('jb-theme', theme); }, [theme]); - // ---------------- SSE Events ---------------- + /* ── SSE ── */ useEffect(() => { const unsub = subscribeEvents((msg) => { if (msg?.type === 'party') { - setPartyActiveGuilds((prev) => { + setPartyActiveGuilds(prev => { const s = new Set(prev); if (msg.active) s.add(msg.guildId); else s.delete(msg.guildId); return Array.from(s); @@ -84,370 +104,418 @@ export default function App() { setPartyActiveGuilds(Array.isArray(msg.party) ? msg.party : []); try { const sel = msg?.selected || {}; - const currentSelected = selectedRef.current || ''; - const gid = currentSelected ? currentSelected.split(':')[0] : ''; - if (gid && sel[gid]) { - const newVal = `${gid}:${sel[gid]}`; - setSelected(newVal); - } + const g = selectedRef.current?.split(':')[0]; + if (g && sel[g]) setSelected(`${g}:${sel[g]}`); } catch { } try { const vols = msg?.volumes || {}; - const cur = selectedRef.current || ''; - const gid = cur ? cur.split(':')[0] : ''; - if (gid && typeof vols[gid] === 'number') { - setVolume(vols[gid]); - } + const g = selectedRef.current?.split(':')[0]; + if (g && typeof vols[g] === 'number') setVolume(vols[g]); } catch { } } else if (msg?.type === 'channel') { - try { - const gid = msg.guildId; - const cid = msg.channelId; - if (gid && cid) { - const currentSelected = selectedRef.current || ''; - const curGid = currentSelected ? currentSelected.split(':')[0] : ''; - if (curGid === gid) setSelected(`${gid}:${cid}`); - } - } catch { } + const g = selectedRef.current?.split(':')[0]; + if (msg.guildId === g) setSelected(`${msg.guildId}:${msg.channelId}`); } else if (msg?.type === 'volume') { - try { - const gid = msg.guildId; - const v = msg.volume; - const cur = selectedRef.current || ''; - const curGid = cur ? cur.split(':')[0] : ''; - if (gid && curGid === gid && typeof v === 'number') { - setVolume(v); - } - } catch { } + const g = selectedRef.current?.split(':')[0]; + if (msg.guildId === g && typeof msg.volume === 'number') setVolume(msg.volume); } }); return () => { try { unsub(); } catch { } }; }, []); useEffect(() => { - const gid = selected ? selected.split(':')[0] : ''; - setChaosMode(gid ? partyActiveGuilds.includes(gid) : false); + setChaosMode(guildId ? partyActiveGuilds.includes(guildId) : false); }, [selected, partyActiveGuilds]); - // ---------------- Data Fetching ---------------- + /* ── Data Fetch ── */ useEffect(() => { (async () => { try { - const folderParam = activeFolder === '__favs__' ? '__all__' : activeFolder; - const s = await fetchSounds(query, folderParam, activeCategoryId || undefined, false); // Removed fuzzy param for cleaner UI + let folderParam = '__all__'; + if (activeTab === 'recent') folderParam = '__recent__'; + else if (activeFolder) folderParam = activeFolder; + + const s = await fetchSounds(query, folderParam, undefined, false); setSounds(s.items); setTotal(s.total); setFolders(s.folders); - } catch (e: any) { - showNotification(e?.message || 'Fehler beim Laden der Sounds', 'error'); - } + } catch (e: any) { notify(e?.message || 'Sounds-Fehler', 'error'); } })(); - }, [activeFolder, query, activeCategoryId]); + }, [activeTab, activeFolder, query]); + /* ── Favs persistence ── */ useEffect(() => { const c = getCookie('favs'); - if (c) { try { setFavs(JSON.parse(c)); } catch { } } + if (c) try { setFavs(JSON.parse(c)); } catch { } }, []); useEffect(() => { try { setCookie('favs', JSON.stringify(favs)); } catch { } }, [favs]); + /* ── Volume sync ── */ useEffect(() => { - (async () => { - if (selected) { - localStorage.setItem('selectedChannel', selected); - try { - const [guildId] = selected.split(':'); - const v = await getVolume(guildId); - setVolume(v); - } catch { } - } - })(); + if (selected) { + (async () => { + try { const v = await getVolume(guildId); setVolume(v); } catch { } + })(); + } }, [selected]); - // ---------------- Actions ---------------- - async function handlePlay(name: string, rel?: string) { - if (!selected) return showNotification('Bitte einen Voice-Channel auswählen', 'error'); - const [guildId, channelId] = selected.split(':'); + /* ── Actions ── */ + async function handlePlay(s: Sound) { + if (!selected) return notify('Bitte einen Voice-Channel auswählen', 'error'); try { - setLoading(true); - await playSound(name, guildId, channelId, volume, rel); - } catch (e: any) { - showNotification(e?.message || 'Wiedergabe fehlgeschlagen', 'error'); - } finally { - setLoading(false); + await playSound(s.name, guildId, channelId, volume, s.relativePath); + setLastPlayed(s.name); + setTimeout(() => setLastPlayed(''), 4000); + } catch (e: any) { notify(e?.message || 'Play fehlgeschlagen', 'error'); } + } + + async function handleStop() { + if (!selected) return; + try { await fetch(`/api/stop?guildId=${encodeURIComponent(guildId)}`, { method: 'POST' }); } catch { } + } + + async function handleRandom() { + if (!sounds.length || !selected) return; + const rnd = sounds[Math.floor(Math.random() * sounds.length)]; + handlePlay(rnd); + } + + async function toggleParty() { + if (chaosMode) { + await handleStop(); + try { await partyStop(guildId); } catch { } + } else { + if (!selected) return notify('Bitte einen Channel auswählen', 'error'); + try { await partyStart(guildId, channelId); } catch { } } } - // Chaos Mode Logic - const startChaosMode = async () => { - if (!selected || !sounds.length) return; - const playRandomSound = async () => { - const pool = sounds; - if (!pool.length || !selected) return; - const randomSound = pool[Math.floor(Math.random() * pool.length)]; - const [guildId, channelId] = selected.split(':'); - try { - await playSound(randomSound.name, guildId, channelId, volume, randomSound.relativePath); - } catch (e: any) { console.error('Chaos sound play failed:', e); } - }; - - const scheduleNextPlay = async () => { - if (!chaosModeRef.current) return; - await playRandomSound(); - const delay = 30_000 + Math.floor(Math.random() * 60_000); // 30-90 Sekunden - chaosTimeoutRef.current = window.setTimeout(scheduleNextPlay, delay); - }; - - await playRandomSound(); - const firstDelay = 30_000 + Math.floor(Math.random() * 60_000); - chaosTimeoutRef.current = window.setTimeout(scheduleNextPlay, firstDelay); - }; - - const stopChaosMode = async () => { - if (chaosTimeoutRef.current) { - clearTimeout(chaosTimeoutRef.current); - chaosTimeoutRef.current = null; + /* ── Computed ── */ + const displaySounds = useMemo(() => { + if (activeTab === 'favorites') { + return sounds.filter(s => favs[s.relativePath ?? s.fileName]); } - if (selected) { - const [guildId] = selected.split(':'); - try { await fetch(`/api/stop?guildId=${encodeURIComponent(guildId)}`, { method: 'POST' }); } catch { } - } - }; + return sounds; + }, [sounds, activeTab, favs]); - const toggleChaosMode = async () => { - if (chaosMode) { - setChaosMode(false); - await stopChaosMode(); - if (selected) { const [guildId] = selected.split(':'); try { await partyStop(guildId); } catch { } } - } else { - setChaosMode(true); - await startChaosMode(); - if (selected) { const [guildId, channelId] = selected.split(':'); try { await partyStart(guildId, channelId); } catch { } } - } - }; - - // Filter Data - const filtered = sounds; const favCount = useMemo(() => Object.values(favs).filter(Boolean).length, [favs]); - // Scroll Handler for Top Bar Blur - const handleScroll = (e: React.UIEvent) => { - setIsScrolled(e.currentTarget.scrollTop > 20); - }; + const visibleFolders = useMemo(() => + folders.filter(f => !['__all__', '__recent__', '__top3__'].includes(f.key)), + [folders]); + const folderColorMap = useMemo(() => { + const m: Record = {}; + visibleFolders.forEach((f, i) => { m[f.key] = CAT_PALETTE[i % CAT_PALETTE.length]; }); + return m; + }, [visibleFolders]); + + /* ── Admin State ── */ + const [adminPwd, setAdminPwd] = useState(''); + + async function handleAdminLogin() { + try { + const ok = await adminLogin(adminPwd); + if (ok) { setIsAdmin(true); setAdminPwd(''); notify('Admin eingeloggt'); } + else notify('Falsches Passwort', 'error'); + } catch { notify('Login fehlgeschlagen', 'error'); } + } + + async function handleAdminLogout() { + try { await adminLogout(); setIsAdmin(false); notify('Ausgeloggt'); } catch { } + } + + /* ── Render ── */ return ( -
- {/* ---------------- Sidebar ---------------- */} - - - {/* ---------------- Main Content ---------------- */} -
-
-

- {activeFolder === '__all__' ? 'All Sounds' : - activeFolder === '__favs__' ? 'Favorites' : - activeFolder === '__recent__' ? 'Recently Added' : - folders.find(f => f.key === activeFolder)?.name.replace(/\s*\(\d+\)\s*$/, '') || 'Library'} -

- -
-
- search - setQuery(e.target.value)} - /> -
- -
-
+ )} +
-
-
- {(activeFolder === '__favs__' ? filtered.filter((s) => !!favs[s.relativePath ?? s.fileName]) : filtered).map((s) => { - const key = `${s.relativePath ?? s.fileName}`; - const isFav = !!favs[key]; - return ( -
handlePlay(s.name, s.relativePath)}> - -
- music_note -
-
{s.name}
- - {Array.isArray((s as any).badges) && (s as any).badges!.includes('new') && ( -
NEW
- )} -
- ); - })} -
-
- - - {/* ---------------- Bottom Control Bar ---------------- */} -
-
- {/* Target Channel */} -
- headset_mic + {/* ════════ Control Bar ════════ */} +
+
+
+ headset_mic
+ + {lastPlayed && ( +
+ play_arrow + {lastPlayed} +
+ )}
-
- {/* Playback Controls */} - + + - -
-
- {/* Volume */} -
- volume_down +
+
+ { + const newVol = volume > 0 ? 0 : 0.5; + setVolume(newVol); + if (guildId) setVolumeLive(guildId, newVol).catch(() => {}); + }} + > + {volume === 0 ? 'volume_off' : volume < 0.5 ? 'volume_down' : 'volume_up'} + { + onChange={async e => { const v = parseFloat(e.target.value); setVolume(v); - try { (e.target as HTMLInputElement).style.setProperty('--_fill', `${Math.round(v * 100)}%`); } catch { } - if (selected) { const [guildId] = selected.split(':'); try { await setVolumeLive(guildId, v); } catch { } } + if (guildId) try { await setVolumeLive(guildId, v); } catch { } }} - style={{ ['--_fill' as any]: `${Math.round(volume * 100)}%` }} + style={{ '--fill': `${Math.round(volume * 100)}%` } as React.CSSProperties} /> - volume_up + {Math.round(volume * 100)}%
+ {/* ════════ Notification Toast ════════ */} {notification && ( -
- +
+ {notification.type === 'error' ? 'error_outline' : 'check_circle'} {notification.msg}
)} + {/* ════════ Admin Panel Overlay ════════ */} + {showAdmin && ( +
{ if (e.target === e.currentTarget) setShowAdmin(false); }}> +
+

+ Admin + +

+ + {!isAdmin ? ( +
+
+ + setAdminPwd(e.target.value)} + onKeyDown={e => e.key === 'Enter' && handleAdminLogin()} + placeholder="Admin-Passwort..." + /> +
+ +
+ ) : ( +
+

+ Eingeloggt als Admin +

+ +
+ )} +
+
+ )}
); } diff --git a/web/src/styles.css b/web/src/styles.css index dd353d0..f514fe4 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1,650 +1,1300 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'); +/* ═══════════════════════════════════════════════════════════════ + JUKEBOX — Discord Soundboard + Design: "DECK" — Cyberpunk Audio Console + Fonts: Syne (display) + Outfit (body) + ═══════════════════════════════════════════════════════════════ */ +@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=Outfit:wght@300;400;500;600;700&display=swap'); + +/* ──────────────────────────────────────────── + Theme: Midnight (default dark) + ──────────────────────────────────────────── */ :root { - color-scheme: light dark; + --bg-base: #0b0b0f; + --bg-surface-0: #101016; + --bg-surface-1: #16161e; + --bg-surface-2: #1e1e28; + --bg-surface-3: #282834; - /* Apple Light Theme Variables */ - --bg-color: #f5f5f7; - --bg-sidebar: rgba(245, 245, 247, 0.6); - --bg-player: rgba(255, 255, 255, 0.7); - --bg-card: #ffffff; - --bg-card-hover: #f0f0f2; - --bg-input: rgba(0, 0, 0, 0.05); + --text-primary: #e4e4ec; + --text-secondary: #7a7a90; + --text-muted: #4a4a5e; - --text-primary: #1d1d1f; - --text-secondary: #86868b; - --text-inverse: #ffffff; + --accent: #3b82f6; + --accent-hover: #2563eb; + --accent-glow: rgba(59, 130, 246, 0.15); + --accent-subtle: rgba(59, 130, 246, 0.08); - --border-color: rgba(0, 0, 0, 0.1); - --border-card: rgba(0, 0, 0, 0.05); + --danger: #ef4444; + --danger-glow: rgba(239, 68, 68, 0.15); + --success: #22c55e; + --warning: #f59e0b; - --accent-blue: #0071e3; - --accent-red: #ff3b30; - --accent-green: #34c759; + --border: rgba(255, 255, 255, 0.06); + --border-hover: rgba(255, 255, 255, 0.12); + --border-active: rgba(59, 130, 246, 0.4); - --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.04); - --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.08); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 20px var(--accent-glow); - /* Dimensions */ - --sidebar-w: 260px; - --player-h: 80px; + --grid-opacity: 1; + --grid-dot: rgba(255, 255, 255, 0.03); + + --header-h: 64px; + --tabs-h: 44px; + --cats-h: 44px; + --control-h: 72px; + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 16px; + --radius-pill: 999px; + + color-scheme: dark; } -[data-theme="dark"] { - --bg-color: #000000; - --bg-sidebar: rgba(28, 28, 30, 0.5); - --bg-player: rgba(28, 28, 30, 0.65); - --bg-card: rgba(44, 44, 46, 0.7); - --bg-card-hover: rgba(58, 58, 60, 0.9); - --bg-input: rgba(255, 255, 255, 0.1); +/* ──────────────────────────────────────────── + Theme: Daylight + ──────────────────────────────────────────── */ +[data-theme="daylight"] { + --bg-base: #f4f2ee; + --bg-surface-0: #eae8e3; + --bg-surface-1: #ffffff; + --bg-surface-2: #f0eee9; + --bg-surface-3: #e4e2dd; - --text-primary: #f5f5f7; - --text-secondary: #86868b; - --text-inverse: #ffffff; + --text-primary: #1a1a2e; + --text-secondary: #6b6b80; + --text-muted: #a0a0b0; - --border-color: rgba(255, 255, 255, 0.1); - --border-card: rgba(255, 255, 255, 0.05); + --accent: #2563eb; + --accent-hover: #1d4ed8; + --accent-glow: rgba(37, 99, 235, 0.1); + --accent-subtle: rgba(37, 99, 235, 0.05); - --accent-blue: #0a84ff; - --accent-red: #ff453a; - --accent-green: #32d74b; + --border: rgba(0, 0, 0, 0.08); + --border-hover: rgba(0, 0, 0, 0.14); + --border-active: rgba(37, 99, 235, 0.4); - --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.2); - --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.3); - --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.4); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.1); + --shadow-glow: 0 0 20px var(--accent-glow); + + --grid-opacity: 0; + --grid-dot: transparent; + + color-scheme: light; } -* { +/* ──────────────────────────────────────────── + Theme: Neon + ──────────────────────────────────────────── */ +[data-theme="neon"] { + --bg-base: #08080e; + --bg-surface-0: #0e0e18; + --bg-surface-1: #141422; + --bg-surface-2: #1c1c30; + --bg-surface-3: #24243c; + + --text-primary: #f0f0ff; + --text-secondary: #8080b0; + --text-muted: #505078; + + --accent: #e040fb; + --accent-hover: #d020eb; + --accent-glow: rgba(224, 64, 251, 0.2); + --accent-subtle: rgba(224, 64, 251, 0.08); + + --border: rgba(224, 64, 251, 0.08); + --border-hover: rgba(224, 64, 251, 0.18); + --border-active: rgba(224, 64, 251, 0.45); + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.6); + --shadow-glow: 0 0 24px var(--accent-glow); + + --grid-opacity: 1; + --grid-dot: rgba(224, 64, 251, 0.025); + + color-scheme: dark; +} + +/* ──────────────────────────────────────────── + Theme: Vapor (Retrowave) + ──────────────────────────────────────────── */ +[data-theme="vapor"] { + --bg-base: #140a22; + --bg-surface-0: #1a0e2e; + --bg-surface-1: #22143a; + --bg-surface-2: #2c1c48; + --bg-surface-3: #362456; + + --text-primary: #e8daf8; + --text-secondary: #8a6aaa; + --text-muted: #5a4070; + + --accent: #06d6a0; + --accent-hover: #05c090; + --accent-glow: rgba(6, 214, 160, 0.18); + --accent-subtle: rgba(6, 214, 160, 0.07); + + --border: rgba(6, 214, 160, 0.07); + --border-hover: rgba(6, 214, 160, 0.15); + --border-active: rgba(6, 214, 160, 0.4); + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.6); + --shadow-glow: 0 0 24px var(--accent-glow); + + --grid-opacity: 1; + --grid-dot: rgba(6, 214, 160, 0.02); + + color-scheme: dark; +} + +/* ──────────────────────────────────────────── + Theme: Matrix + ──────────────────────────────────────────── */ +[data-theme="matrix"] { + --bg-base: #050a05; + --bg-surface-0: #0a120a; + --bg-surface-1: #0f1a0f; + --bg-surface-2: #162216; + --bg-surface-3: #1e2e1e; + + --text-primary: #c0ecc0; + --text-secondary: #5a8a5a; + --text-muted: #2e5a2e; + + --accent: #22c55e; + --accent-hover: #16a34a; + --accent-glow: rgba(34, 197, 94, 0.18); + --accent-subtle: rgba(34, 197, 94, 0.06); + + --border: rgba(34, 197, 94, 0.07); + --border-hover: rgba(34, 197, 94, 0.15); + --border-active: rgba(34, 197, 94, 0.4); + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.6); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.7); + --shadow-glow: 0 0 24px var(--accent-glow); + + --grid-opacity: 1; + --grid-dot: rgba(34, 197, 94, 0.025); + + color-scheme: dark; +} + +/* ──────────────────────────────────────────── + Reset & Base + ──────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { - font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - background-color: var(--bg-color); + font-family: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif; + background: var(--bg-base); color: var(--text-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow: hidden; - /* Prevent body scroll, layout handles it */ - height: 100vh; + height: 100dvh; width: 100vw; } -/* ---------------- Layout & Structure ---------------- */ - -.app-layout { - display: flex; - height: 100vh; - width: 100vw; - overflow: hidden; - position: relative; -} - -.sidebar { - width: var(--sidebar-w); - flex-shrink: 0; - background: var(--bg-sidebar); - border-right: 1px solid var(--border-color); - backdrop-filter: blur(25px) saturate(200%); - -webkit-backdrop-filter: blur(25px) saturate(200%); - display: flex; - flex-direction: column; - z-index: 10; - padding: 32px 16px; - overflow-y: auto; -} - -.main-content { - flex: 1; - display: flex; - flex-direction: column; - height: 100vh; - padding-bottom: var(--player-h); - /* Space for bottom player */ - overflow: hidden; - position: relative; -} - -/* ---------------- Typography ---------------- */ - -h1, -h2, -h3, -h4 { - font-weight: 700; - letter-spacing: -0.5px; -} - -.title-large { - font-size: 34px; - font-weight: 800; - letter-spacing: -1px; - margin-bottom: 24px; -} - -.sidebar-title { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 600; - color: var(--text-secondary); - margin: 24px 0 8px 12px; -} - -/* ---------------- Sidebar Items ---------------- */ - -.nav-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 12px; - border-radius: 8px; - cursor: pointer; - color: var(--text-primary); - font-size: 14px; - font-weight: 500; - transition: background 0.2s ease; - margin-bottom: 2px; - border: none; - background: transparent; - width: 100%; - text-align: left; -} - -.nav-item:hover { - background: var(--bg-card); -} - -.nav-item.active { - background: var(--bg-input); - color: var(--accent-blue); - font-weight: 600; -} - -/* ---------------- Header Area ---------------- */ - -.top-bar { - padding: 24px 40px; - display: flex; - justify-content: space-between; - align-items: flex-end; - border-bottom: 1px solid transparent; - backdrop-filter: blur(20px); - position: sticky; - top: 0; - z-index: 5; - transition: all 0.3s ease; -} - -.top-bar.scrolled { - background: var(--bg-player); - border-bottom: 1px solid var(--border-color); - padding: 16px 40px; -} - -.top-bar .title-large { - margin: 0; - transition: font-size 0.3s ease; -} - -.top-bar.scrolled .title-large { - font-size: 24px; -} - -.header-actions { - display: flex; - gap: 12px; - align-items: center; -} - -/* ---------------- Search & Inputs ---------------- */ - -.search-box { - position: relative; - width: 280px; -} - -.input-modern { - width: 100%; - background: var(--bg-input); - border: 1px solid transparent; - border-radius: 12px; - padding: 12px 16px 12px 36px; - font-size: 14px; - color: var(--text-primary); - transition: all 0.2s ease; +button { font-family: inherit; + cursor: pointer; + border: none; + background: none; + color: inherit; } -.input-modern:focus { - outline: none; - border-color: var(--accent-blue); - box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.2); - background: var(--bg-card); +input, select { + font-family: inherit; + color: inherit; } -[data-theme="dark"] .input-modern:focus { - box-shadow: 0 0 0 4px rgba(10, 132, 255, 0.3); +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--text-muted); + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); } -.search-icon { - position: absolute; - left: 10px; - top: 50%; - transform: translateY(-50%); - color: var(--text-secondary); - font-size: 18px; +::selection { + background: var(--accent); + color: white; } -/* ---------------- Track Grid ---------------- */ - -.track-container { - padding: 0 40px 40px 40px; - overflow-y: auto; - flex: 1; -} - -.track-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 20px; -} - -.track-card { - background: var(--bg-card); - border: 1px solid var(--border-card); - border-radius: 16px; - padding: 20px; +/* ──────────────────────────────────────────── + App Shell + ──────────────────────────────────────────── */ +.app-shell { display: flex; flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); - box-shadow: var(--shadow-sm); - position: relative; - aspect-ratio: 1 / 1; -} - -.track-card:hover { - transform: scale(1.03) translateY(-4px); - box-shadow: var(--shadow-md); - background: var(--bg-card-hover); -} - -.track-icon { - width: 64px; - height: 64px; - background: linear-gradient(135deg, var(--bg-input), var(--border-color)); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 16px; - font-size: 28px; - color: var(--text-secondary); - transition: all 0.3s ease; -} - -.track-card:hover .track-icon { - background: var(--accent-blue); - color: white; - transform: scale(1.1); - box-shadow: 0 8px 16px rgba(0, 113, 227, 0.4); -} - -.track-name { - font-weight: 600; - font-size: 15px; - line-height: 1.3; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; + height: 100dvh; + width: 100vw; overflow: hidden; - text-overflow: ellipsis; - word-break: break-word; + position: relative; } -.fav-btn { - position: absolute; - top: 12px; - right: 12px; - background: transparent; - border: none; - color: var(--text-secondary); - cursor: pointer; - opacity: 0; - transition: all 0.2s ease; - font-size: 20px; +/* Dot grid pattern overlay */ +.app-shell::after { + content: ''; + position: fixed; + inset: 0; + opacity: var(--grid-opacity); + background-image: radial-gradient( + circle, var(--grid-dot) 1px, transparent 1px + ); + background-size: 28px 28px; + pointer-events: none; + z-index: 0; } -.track-card:hover .fav-btn, -.fav-btn.active { - opacity: 1; -} - -.fav-btn.active { - color: #ff9f0a; - /* Apple Orange */ -} - -/* ---------------- Bottom Control Bar ---------------- */ - -.bottom-player { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: var(--player-h); - background: var(--bg-player); - border-top: 1px solid var(--border-color); - backdrop-filter: blur(30px) saturate(200%); - -webkit-backdrop-filter: blur(30px) saturate(200%); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 32px; - z-index: 20; -} - -.player-section { +/* ──────────────────────────────────────────── + Header + ──────────────────────────────────────────── */ +.header { + height: var(--header-h); + min-height: var(--header-h); display: flex; align-items: center; gap: 16px; + padding: 0 24px; + background: var(--bg-surface-0); + border-bottom: 1px solid var(--border); + position: relative; + z-index: 20; +} + +.logo { + font-family: 'Syne', sans-serif; + font-weight: 800; + font-size: 22px; + letter-spacing: -0.5px; + background: linear-gradient(135deg, var(--accent), var(--text-primary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + flex-shrink: 0; + user-select: none; +} + +.header-search { flex: 1; + max-width: 420px; + position: relative; } -.player-section.center { - justify-content: center; - flex: 2; +.header-search .search-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + font-size: 18px; + color: var(--text-muted); + pointer-events: none; } -.player-section.right { - justify-content: flex-end; -} - -/* ---------------- Buttons & Controls ---------------- */ - -.btn-icon { - width: 40px; - height: 40px; - border-radius: 50%; - border: none; - background: transparent; +.header-search input { + width: 100%; + background: var(--bg-surface-2); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 8px 36px 8px 38px; + font-size: 13.5px; + font-weight: 400; color: var(--text-primary); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.2s; -} - -.btn-icon:hover { - background: var(--bg-input); -} - -.btn-icon.active { - color: var(--accent-blue); -} - -.btn-primary { - background: var(--accent-blue); - color: white; - border: none; - padding: 10px 20px; - border-radius: 999px; - font-weight: 600; - font-size: 14px; - cursor: pointer; - transition: all 0.2s ease; - box-shadow: 0 4px 12px rgba(0, 113, 227, 0.3); -} - -.btn-primary:hover { - transform: scale(1.05); - filter: brightness(1.1); -} - -.btn-danger { - background: rgba(255, 59, 48, 0.1); - color: var(--accent-red); - border: none; - padding: 10px 20px; - border-radius: 999px; - font-weight: 600; - font-size: 14px; - cursor: pointer; transition: all 0.2s ease; } -.btn-danger:hover { - background: var(--accent-red); - color: white; - box-shadow: 0 4px 12px rgba(255, 59, 48, 0.3); +.header-search input::placeholder { + color: var(--text-muted); } -/* Partymode Chaos Animation Style */ -.btn-chaos { - background: linear-gradient(45deg, #ff2a6d, #d1f7ff, #05d9e8, #01012b); - background-size: 300% 300%; - color: white; - border: none; - padding: 10px 20px; - border-radius: 999px; - font-weight: 600; - font-size: 14px; - cursor: pointer; - animation: bgPulse 3s ease infinite; - box-shadow: 0 4px 15px rgba(5, 217, 232, 0.5); - transition: transform 0.2s; -} - -.btn-chaos:hover { - transform: scale(1.05); -} - -@keyframes bgPulse { - 0% { - background-position: 0% 50%; - } - - 50% { - background-position: 100% 50%; - } - - 100% { - background-position: 0% 50%; - } -} - -/* ---------------- Select & Volume ---------------- */ - -.select-modern { - appearance: none; - background: var(--bg-input); - border: 1px solid transparent; - padding: 8px 36px 8px 16px; - border-radius: 999px; - color: var(--text-primary); - font-weight: 500; - font-size: 13px; - cursor: pointer; - transition: all 0.2s ease; - font-family: inherit; -} - -.select-modern:hover { - background: var(--bg-card); - border-color: var(--border-color); -} - -.select-modern:focus { +.header-search input:focus { outline: none; - border-color: var(--accent-blue); - box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.2); + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); + background: var(--bg-surface-1); } -.volume-container { +.header-search .search-clear { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + width: 22px; + height: 22px; + border-radius: 50%; display: flex; align-items: center; + justify-content: center; + color: var(--text-muted); + transition: all 0.15s; +} + +.header-search .search-clear:hover { + background: var(--bg-surface-3); + color: var(--text-primary); +} + +.header-meta { + display: flex; + align-items: center; + gap: 16px; + margin-left: auto; + flex-shrink: 0; +} + +.sound-count { + font-size: 12px; + font-weight: 600; + color: var(--text-secondary); + font-variant-numeric: tabular-nums; + white-space: nowrap; +} + +.sound-count strong { + color: var(--accent); + font-weight: 700; +} + +/* Theme & Channel selects */ +.select-clean { + appearance: none; + background: var(--bg-surface-2); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 6px 28px 6px 10px; + font-size: 12.5px; + font-weight: 500; + color: var(--text-primary); + cursor: pointer; + transition: all 0.15s; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237a7a90' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 8px center; +} + +.select-clean:hover { + border-color: var(--border-hover); + background-color: var(--bg-surface-3); +} + +.select-clean:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); +} + +.select-clean option { + background: var(--bg-surface-1); + color: var(--text-primary); +} + +/* ──────────────────────────────────────────── + Tab Bar + ──────────────────────────────────────────── */ +.tab-bar { + height: var(--tabs-h); + min-height: var(--tabs-h); + display: flex; + align-items: stretch; + gap: 0; + padding: 0 24px; + background: var(--bg-surface-0); + border-bottom: 1px solid var(--border); + position: relative; + z-index: 15; +} + +.tab-btn { + position: relative; + padding: 0 18px; + font-size: 13px; + font-weight: 600; + color: var(--text-secondary); + transition: color 0.2s; + display: flex; + align-items: center; + gap: 6px; + white-space: nowrap; +} + +.tab-btn:hover { + color: var(--text-primary); +} + +.tab-btn.active { + color: var(--accent); +} + +.tab-btn.active::after { + content: ''; + position: absolute; + bottom: 0; + left: 12px; + right: 12px; + height: 2px; + background: var(--accent); + border-radius: 2px 2px 0 0; +} + +.tab-badge { + font-size: 10.5px; + font-weight: 700; + background: var(--accent-subtle); + color: var(--accent); + padding: 1px 6px; + border-radius: var(--radius-pill); + font-variant-numeric: tabular-nums; +} + +/* ──────────────────────────────────────────── + Category Filter Strip + ──────────────────────────────────────────── */ +.category-strip { + min-height: var(--cats-h); + display: flex; + align-items: center; + gap: 6px; + padding: 0 24px; + background: var(--bg-surface-0); + border-bottom: 1px solid var(--border); + overflow-x: auto; + overflow-y: hidden; + position: relative; + z-index: 14; + scrollbar-width: none; +} + +.category-strip::-webkit-scrollbar { + display: none; +} + +.cat-chip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + border-radius: var(--radius-pill); + font-size: 12px; + font-weight: 600; + color: var(--text-secondary); + background: var(--bg-surface-2); + border: 1px solid var(--border); + white-space: nowrap; + transition: all 0.15s ease; + flex-shrink: 0; +} + +.cat-chip:hover { + border-color: var(--border-hover); + color: var(--text-primary); + background: var(--bg-surface-3); +} + +.cat-chip.active { + background: var(--accent-subtle); + border-color: var(--accent); + color: var(--accent); +} + +.cat-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +/* ──────────────────────────────────────────── + Sounds Area & Grid + ──────────────────────────────────────────── */ +.sounds-area { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + position: relative; + z-index: 1; +} + +.sounds-grid { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 16px 24px; + padding-bottom: calc(var(--control-h) + 24px); + align-content: flex-start; +} + +.sounds-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 80px 24px; + color: var(--text-muted); + text-align: center; + width: 100%; +} + +.sounds-empty .material-icons { + font-size: 48px; + margin-bottom: 12px; + opacity: 0.4; +} + +.sounds-empty p { + font-size: 14px; + font-weight: 500; +} + +/* ── Sound Button (Compact Pill) ── */ +.sound-btn { + display: inline-flex; + align-items: center; + gap: 0; + height: 36px; + padding: 0 12px 0 0; + border-radius: var(--radius-sm); + background: var(--bg-surface-1); + border: 1px solid var(--border); + font-size: 12.5px; + font-weight: 500; + color: var(--text-primary); + cursor: pointer; + transition: all 0.12s ease; + position: relative; + overflow: hidden; + max-width: 220px; + flex-shrink: 0; +} + +.sound-btn .cat-bar { + width: 3px; + height: 100%; + flex-shrink: 0; + border-radius: var(--radius-sm) 0 0 var(--radius-sm); +} + +.sound-btn .sound-label { + padding: 0 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 36px; +} + +.sound-btn:hover { + background: var(--bg-surface-2); + border-color: var(--border-hover); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.sound-btn:active { + transform: scale(0.96); + box-shadow: none; + transition-duration: 0.05s; +} + +.sound-btn .fav-star { + position: absolute; + right: 3px; + top: 50%; + transform: translateY(-50%); + font-size: 14px; + opacity: 0; + color: var(--text-muted); + transition: all 0.15s; + padding: 2px; + line-height: 1; +} + +.sound-btn:hover .fav-star { + opacity: 0.6; +} + +.sound-btn .fav-star.is-fav { + opacity: 1; + color: var(--warning); +} + +.sound-btn:hover .fav-star.is-fav { + opacity: 1; +} + +/* Playing animation */ +.sound-btn.is-playing { + border-color: var(--accent); + box-shadow: var(--shadow-glow); +} + +.sound-btn.is-playing::after { + content: ''; + position: absolute; + inset: 0; + background: var(--accent-glow); + animation: pulse-bg 1s ease infinite; + pointer-events: none; +} + +/* Badge indicators */ +.sound-btn .badge-dot { + position: absolute; + top: 3px; + right: 3px; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--accent); +} + +.sound-btn .badge-dot.new { + background: var(--success); +} + +.sound-btn .badge-dot.top { + background: var(--warning); +} + +/* ──────────────────────────────────────────── + Control Bar (Bottom) + ──────────────────────────────────────────── */ +.control-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: var(--control-h); + background: var(--bg-surface-0); + border-top: 1px solid var(--border); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + display: flex; + align-items: center; + padding: 0 24px; gap: 12px; - width: 140px; + z-index: 30; +} + +.ctrl-section { + display: flex; + align-items: center; + gap: 8px; +} + +.ctrl-section.left { + flex: 1; + min-width: 0; +} + +.ctrl-section.center { + flex-shrink: 0; +} + +.ctrl-section.right { + flex: 1; + justify-content: flex-end; + min-width: 0; +} + +/* Channel select in control bar */ +.channel-wrap { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.channel-wrap .material-icons { + font-size: 18px; + color: var(--text-muted); + flex-shrink: 0; +} + +.channel-select { + appearance: none; + background: var(--bg-surface-2); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 6px 28px 6px 10px; + font-size: 12px; + font-weight: 500; + color: var(--text-primary); + cursor: pointer; + min-width: 160px; + max-width: 280px; + transition: all 0.15s; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237a7a90' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 8px center; +} + +.channel-select:hover { + border-color: var(--border-hover); +} + +.channel-select:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); +} + +.channel-select option { + background: var(--bg-surface-1); +} + +/* ── Control Buttons ── */ +.ctrl-btn { + height: 38px; + padding: 0 16px; + border-radius: var(--radius-pill); + font-size: 12.5px; + font-weight: 600; + display: flex; + align-items: center; + gap: 6px; + transition: all 0.15s ease; + white-space: nowrap; +} + +.ctrl-btn .material-icons { + font-size: 18px; +} + +/* Stop */ +.ctrl-btn.stop { + background: var(--danger-glow); + color: var(--danger); +} +.ctrl-btn.stop:hover { + background: var(--danger); + color: white; + box-shadow: 0 4px 16px rgba(239, 68, 68, 0.3); +} + +/* Random / Shuffle */ +.ctrl-btn.shuffle { + background: var(--accent); + color: white; +} +.ctrl-btn.shuffle:hover { + background: var(--accent-hover); + box-shadow: 0 4px 16px var(--accent-glow); + transform: scale(1.03); +} + +/* Party Mode */ +.ctrl-btn.party { + background: var(--bg-surface-2); + color: var(--text-secondary); + border: 1px solid var(--border); +} +.ctrl-btn.party:hover { + border-color: var(--border-hover); + color: var(--text-primary); + background: var(--bg-surface-3); +} + +.ctrl-btn.party.active { + background: linear-gradient(135deg, #ec4899, #8b5cf6, #3b82f6, #06b6d4); + background-size: 300% 300%; + animation: party-gradient 4s ease infinite; + color: white; + border: none; + box-shadow: 0 4px 20px rgba(139, 92, 246, 0.4); +} + +.ctrl-btn.party.active:hover { + transform: scale(1.03); +} + +/* ── Volume ── */ +.volume-wrap { + display: flex; + align-items: center; + gap: 8px; + width: 150px; + flex-shrink: 0; +} + +.volume-wrap .material-icons { + font-size: 16px; + color: var(--text-muted); + cursor: pointer; + flex-shrink: 0; +} + +.volume-wrap .material-icons:hover { + color: var(--text-secondary); } .volume-slider { + -webkit-appearance: none; appearance: none; width: 100%; height: 4px; - background: var(--border-color); + background: var(--bg-surface-3); border-radius: 2px; outline: none; - background-image: linear-gradient(var(--text-secondary), var(--text-secondary)); - background-size: var(--_fill, 0%) 100%; + position: relative; + background-image: linear-gradient(var(--accent), var(--accent)); + background-size: var(--fill, 100%) 100%; background-repeat: no-repeat; } .volume-slider::-webkit-slider-thumb { - appearance: none; - width: 16px; - height: 16px; + -webkit-appearance: none; + width: 14px; + height: 14px; border-radius: 50%; background: var(--text-primary); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); cursor: pointer; + transition: transform 0.1s; } -/* ---------------- Admin Badges & Checkboxes ---------------- */ +.volume-slider::-webkit-slider-thumb:hover { + transform: scale(1.2); +} -.admin-checkbox { - position: absolute; - top: 12px; - left: 12px; - appearance: none; - width: 20px; - height: 20px; - border: 2px solid var(--border-color); - border-radius: 6px; - background: var(--bg-card); +.volume-slider::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--text-primary); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); cursor: pointer; - transition: all 0.2s; - z-index: 2; + border: none; } -.admin-checkbox:checked { - background: var(--accent-blue) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E") no-repeat center center; - background-size: 14px; - border-color: var(--accent-blue); +.volume-pct { + font-size: 11px; + font-weight: 600; + color: var(--text-muted); + min-width: 30px; + text-align: right; + font-variant-numeric: tabular-nums; } -.badge-new { - position: absolute; - bottom: 0; - left: 50%; - transform: translate(-50%, 50%); - background: var(--accent-blue); - color: white; - font-size: 10px; - font-weight: 700; - padding: 2px 8px; - border-radius: 10px; - box-shadow: 0 2px 8px rgba(0, 113, 227, 0.4); +/* ── Now Playing ── */ +.now-playing { + font-size: 11px; + font-weight: 500; + color: var(--text-muted); + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.badge-custom { - position: absolute; - bottom: -4px; - right: -4px; - font-size: 20px; - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); +.now-playing span { + color: var(--accent); } -/* ---------------- Error & Notification ---------------- */ - -.notification { +/* ──────────────────────────────────────────── + Notifications / Toast + ──────────────────────────────────────────── */ +.toast { position: fixed; - bottom: calc(var(--player-h) + 20px); + bottom: calc(var(--control-h) + 16px); left: 50%; transform: translateX(-50%); - padding: 12px 24px; - border-radius: 999px; - font-weight: 500; - font-size: 14px; + padding: 10px 20px; + border-radius: var(--radius-pill); + font-size: 13px; + font-weight: 600; z-index: 100; - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - box-shadow: var(--shadow-lg); display: flex; align-items: center; gap: 8px; - animation: slideUp 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + box-shadow: var(--shadow-lg); + animation: toast-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + pointer-events: none; } -@keyframes slideUp { +.toast .material-icons { + font-size: 16px; +} + +.toast.error { + background: var(--danger); + color: white; +} + +.toast.info { + background: var(--success); + color: white; +} + +/* ──────────────────────────────────────────── + Admin Panel (Overlay) + ──────────────────────────────────────────── */ +.admin-toggle { + width: 32px; + height: 32px; + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + transition: all 0.15s; +} + +.admin-toggle:hover { + background: var(--bg-surface-2); + color: var(--text-primary); +} + +.admin-toggle.is-admin { + color: var(--accent); +} + +.admin-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(8px); + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + animation: fade-in 0.2s ease; +} + +.admin-panel { + background: var(--bg-surface-1); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 28px; + width: 90%; + max-width: 480px; + max-height: 80vh; + overflow-y: auto; + box-shadow: var(--shadow-lg); +} + +.admin-panel h3 { + font-family: 'Syne', sans-serif; + font-size: 18px; + font-weight: 700; + margin-bottom: 20px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.admin-panel .admin-close { + width: 28px; + height: 28px; + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary); + transition: all 0.15s; +} + +.admin-panel .admin-close:hover { + background: var(--bg-surface-3); + color: var(--text-primary); +} + +.admin-field { + margin-bottom: 16px; +} + +.admin-field label { + display: block; + font-size: 12px; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.admin-field input { + width: 100%; + background: var(--bg-surface-2); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 10px 12px; + font-size: 14px; + transition: all 0.15s; +} + +.admin-field input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); +} + +.admin-btn { + padding: 10px 20px; + border-radius: var(--radius-sm); + font-size: 13px; + font-weight: 600; + transition: all 0.15s; +} + +.admin-btn.primary { + background: var(--accent); + color: white; +} +.admin-btn.primary:hover { + background: var(--accent-hover); +} + +.admin-btn.outline { + background: transparent; + border: 1px solid var(--border); + color: var(--text-secondary); +} +.admin-btn.outline:hover { + border-color: var(--border-hover); + color: var(--text-primary); +} + +.admin-btn.danger { + background: var(--danger-glow); + color: var(--danger); +} +.admin-btn.danger:hover { + background: var(--danger); + color: white; +} + +/* ──────────────────────────────────────────── + Party Mode Global Effects + ──────────────────────────────────────────── */ +.app-shell.party-active .header { + border-bottom-color: transparent; + background: linear-gradient(90deg, + rgba(236, 72, 153, 0.08), + rgba(139, 92, 246, 0.08), + rgba(59, 130, 246, 0.08), + rgba(6, 182, 212, 0.08) + ); + background-size: 400% 100%; + animation: party-gradient 6s ease infinite; +} + +.app-shell.party-active .control-bar { + border-top-color: transparent; + box-shadow: 0 -2px 20px rgba(139, 92, 246, 0.15); +} + +/* ──────────────────────────────────────────── + Animations & Keyframes + ──────────────────────────────────────────── */ +@keyframes party-gradient { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +@keyframes pulse-bg { + 0%, 100% { opacity: 0.3; } + 50% { opacity: 0.6; } +} + +@keyframes toast-in { from { - transform: translate(-50%, 20px); + transform: translate(-50%, 16px); opacity: 0; } - to { transform: translate(-50%, 0); opacity: 1; } } -.notification.error { - background: rgba(255, 59, 48, 0.9); - color: white; +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } } -.notification.info { - background: rgba(52, 199, 89, 0.9); - color: white; +@keyframes spin { + to { transform: rotate(360deg); } } -/* Responsive */ +/* Staggered entrance for sound buttons */ +.sounds-grid .sound-btn { + animation: sound-enter 0.2s ease both; +} + +@keyframes sound-enter { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ──────────────────────────────────────────── + Responsive + ──────────────────────────────────────────── */ @media (max-width: 768px) { - .app-layout { - flex-direction: column; + .header { + padding: 0 16px; + gap: 10px; + height: auto; + min-height: 56px; + flex-wrap: wrap; + padding-top: 10px; + padding-bottom: 10px; } - .sidebar { - width: 100%; - height: 60px; - flex-direction: row; - padding: 10px; - border-right: none; - border-bottom: 1px solid var(--border-color); - overflow-x: auto; - overflow-y: hidden; + .logo { + font-size: 18px; } - .sidebar-title { + .header-search { + order: 10; + max-width: 100%; + flex-basis: 100%; + } + + .header-meta { + gap: 8px; + } + + .sound-count { display: none; } - .nav-item { - white-space: nowrap; - margin-right: 8px; - margin-bottom: 0; - width: auto; + .tab-bar { + padding: 0 16px; + overflow-x: auto; + scrollbar-width: none; } - .main-content { - padding-bottom: calc(var(--player-h) + 60px); + .tab-bar::-webkit-scrollbar { + display: none; } - .bottom-player { - flex-direction: column; + .category-strip { + padding: 0 16px; + } + + .sounds-grid { + padding: 12px 16px; + gap: 5px; + } + + .sound-btn { + font-size: 11.5px; + height: 32px; + max-width: 180px; + } + + .control-bar { + padding: 0 12px; height: auto; - padding: 16px; - gap: 16px; + min-height: var(--control-h); + flex-wrap: wrap; + padding-top: 10px; + padding-bottom: 10px; + gap: 8px; } - .search-box { - width: 100%; + .ctrl-section.left { + order: 1; + flex-basis: 100%; } -} \ No newline at end of file + + .ctrl-section.center { + order: 2; + } + + .ctrl-section.right { + order: 3; + } + + .volume-wrap { + width: 120px; + } + + .channel-select { + min-width: 120px; + } + + .sounds-grid { + padding-bottom: calc(140px + 24px); + } + + .toast { + bottom: 150px; + } +} + +@media (max-width: 480px) { + .ctrl-btn span:not(.material-icons) { + display: none; + } + + .ctrl-btn { + padding: 0 10px; + } + + .volume-wrap { + width: 100px; + } + + .now-playing { + display: none; + } +} + +/* ──────────────────────────────────────────── + Utility + ──────────────────────────────────────────── */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* Vapor theme gradient overlay */ +[data-theme="vapor"] .app-shell::before { + content: ''; + position: fixed; + inset: 0; + background: linear-gradient(180deg, + rgba(120, 40, 200, 0.06) 0%, + transparent 40%, + rgba(6, 214, 160, 0.04) 100% + ); + pointer-events: none; + z-index: 0; +}