diff --git a/.gitignore b/.gitignore index 7f4c8b4..88188c6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ __pycache__/ *.pyc data/pyload_settings.json data/pyload_secret.key + +frontend/ diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index 7df8f21..0000000 --- a/frontend/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Klangkiste Control Panel - - -
- - - diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 7795a15..0000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,2860 +0,0 @@ -{ - "name": "klangkiste-frontend", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "klangkiste-frontend", - "version": "0.1.0", - "dependencies": { - "@phosphor-icons/react": "2.1.7", - "primeicons": "^7.0.0", - "primereact": "^10.7.0", - "react": "18.3.1", - "react-dom": "18.3.1" - }, - "devDependencies": { - "@vitejs/plugin-react": "4.3.1", - "vite": "5.4.3" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", - "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.28.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", - "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", - "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6", - "@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.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", - "@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.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.6" - }, - "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/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "license": "MIT", - "engines": { - "node": ">=6.9.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.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", - "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/@phosphor-icons/react": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.7.tgz", - "integrity": "sha512-g2e2eVAn1XG2a+LI09QU3IORLhnFNAFkNbo2iwbX6NOKSLOwvEMmTa7CgOzEbgNWR47z8i8kwjdvYZ5fkGx1mQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">= 16.8", - "react-dom": ">= 16.8" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", - "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/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", - "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", - "license": "MIT", - "peer": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", - "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.5", - "@babel/plugin-transform-react-jsx-self": "^7.24.5", - "@babel/plugin-transform-react-jsx-source": "^7.24.1", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", - "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha512-KbiZEa9/vofNcVJXGwdWWn25reQ3V3dHBWbS07FTF3/TOehLnm9GEhJV4T6ZvGPkShRpmUqYwnaCrkj0mRnP6Q==", - "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "hoek": "2.x.x" - }, - "engines": { - "node": ">=0.10.40" - } - }, - "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/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", - "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/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0", - "optional": true - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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/cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha512-FFN5KwpvvQTTS5hWPxrU8/QE4kQUc6uwZcrnlMBN82t1MgAtq8mnoDwINBly9Tdr02seeIIhtdF+UH1feBYGog==", - "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boom": "2.x.x" - }, - "engines": { - "node": ">=0.10.40" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "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/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": "*" - } - }, - "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/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha512-X8xbmTc1cbPXcQV4WkLcRMALuyoxhfpFATmyuCxJPOAvrDS4DNnsTAOmKUxMTOWU6TzrTOkxPKwIx5ZOpJVSrg==", - "deprecated": "This module moved to @hapi/hawk. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - }, - "engines": { - "node": ">=0.10.32" - } - }, - "node_modules/hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha512-V6Yw1rIcYV/4JsnggjBU0l4Kr+EXhpwqXRusENU1Xx6ro00IHPHYNynCuBTOZAPlr3AAmLvchH9I7N/VUdvOwQ==", - "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.40" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "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/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "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/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)", - "optional": true - }, - "node_modules/json-stable-stringify": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", - "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC", - "optional": true - }, - "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/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true, - "license": "Public Domain", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/less": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=0.12" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.2.11", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "2.81.0", - "source-map": "^0.5.3" - } - }, - "node_modules/less/node_modules/ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "node_modules/less/node_modules/assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha512-u1L0ZLywRziOVjUhRxI0Qg9G+4RnFB9H/Rq40YWn0dieDgO7vAYeJz6jKAO6t/aruzlDFLAPkQTT87e+f8Imaw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/less/node_modules/aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha512-JnJpAS0p9RmixkOvW2XwDxxzs1bd4/VAGIl6Q0EC5YOo+p+hqIhtDhn/nmFnB/xUNXbLkpE2mOjgVIBRKD4xYw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/less/node_modules/form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha512-8HWGSLAPr+AG0hBpsqi5Ob8HrLStN/LWeqhpFl14d7FJgHK48TmgLoALPz69XSUR65YJzDfLUX/BM8+MLJLghQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/less/node_modules/har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha512-f8xf2GOR6Rgwc9FPTLNzgwB+JQ2/zMauYXSWmX5YV5acex6VomT0ocSuwR7BfXo5MpHi+jL+saaux2fwsGJDKQ==", - "dev": true, - "license": "ISC", - "optional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/less/node_modules/har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha512-5Gbp6RAftMYYV3UEI4c4Vv3+a4dQ7taVyvHt+/L6kRt+f4HX1GweAk5UDWN0SvdVnRBzGQ6OG89pGaD9uSFnVw==", - "deprecated": "this library is no longer supported", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/less/node_modules/http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha512-iUn0NcRULlDGtqNLN1Jxmzayk8ogm7NToldASyZBpM2qggbphjXzNOiw3piN8tgz+e/DRs6X5gAzFwTI6BCRcg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/less/node_modules/oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha512-VlF07iu3VV3+BTXj43Nmp6Irt/G7j/NgEctUS6IweH1RGhURjjCc2NWtzXFPXXWWfc7hgbXQdtiQu2LGp6MxUg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/less/node_modules/performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha512-YHk5ez1hmMR5LOkb9iJkLKqoBlL7WD5M8ljC75ZfzXriuBIVNuecaXuU7e+hOwyqf24Wxhh7Vxgt7Hnw9288Tg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/less/node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/less/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/less/node_modules/qs": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.1.tgz", - "integrity": "sha512-LQy1Q1fcva/UsnP/6Iaa4lVeM49WiOitu2T4hZCyA/elLKu37L99qcBJk4VCCk+rdLvnMzfKyiN3SZTqdAZGSQ==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/less/node_modules/request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha512-IZnsR7voF0miGSu29EXPRgPTuEsI/+aibNSBbN1pplrfartF5wDYGADz3iD9vmBVf2r00rckWZf8BtS5kk7Niw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/less/node_modules/tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "punycode": "^1.4.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "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/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "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/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "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/primeicons": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", - "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", - "license": "MIT" - }, - "node_modules/primereact": { - "version": "10.9.7", - "resolved": "https://registry.npmjs.org/primereact/-/primereact-10.9.7.tgz", - "integrity": "sha512-Ap/lg9GGaS8Pq7IIlzguuG3qlaU6PYF6E0cCRo0rnWauRw/SQGvfreSVIIxqEhtR6xqlf7OV759lyvVOvBzmsQ==", - "license": "MIT", - "dependencies": { - "@types/react-transition-group": "^4.4.1", - "react-transition-group": "^4.4.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "license": "MIT", - "optional": true - }, - "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", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", - "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.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "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/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha512-7bgVOAnPj3XjrKY577S+puCKGCRlUrcrEdsMeRXlg9Ghf5df/xNi6sONUa43WrHUd3TjJBF7O04jYoiY0FVa0A==", - "deprecated": "This module moved to @hapi/sntp. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", - "dev": true, - "optional": true, - "dependencies": { - "hoek": "2.x.x" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense", - "optional": true - }, - "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/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/vite": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", - "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", - "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/frontend/package.json b/frontend/package.json deleted file mode 100644 index 41f66e5..0000000 --- a/frontend/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "klangkiste-frontend", - "version": "0.1.0", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview --host 0.0.0.0" - }, - "dependencies": { - "@phosphor-icons/react": "2.1.7", - "primeicons": "^7.0.0", - "primereact": "^10.7.0", - "react": "18.3.1", - "react-dom": "18.3.1" - }, - "devDependencies": { - "@vitejs/plugin-react": "4.3.1", - "vite": "5.4.3" - } -} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx deleted file mode 100644 index 0df5da6..0000000 --- a/frontend/src/App.jsx +++ /dev/null @@ -1,3515 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; -import { - ArrowRight, - ArrowUp, - CaretDown, - CaretRight, - Cube, - ContactlessPayment, - FileAudio, - FloppyDisk, - Folder, - FolderOpen, - GearSix, - House, - PencilSimple, - Plus, - Trash, - UploadSimple, - Wrench, -} from '@phosphor-icons/react'; -import { Avatar } from 'primereact/avatar'; -import { Badge } from 'primereact/badge'; -import { Button } from 'primereact/button'; -import { Card } from 'primereact/card'; -import { Divider } from 'primereact/divider'; -import { Dropdown } from 'primereact/dropdown'; -import { FileUpload } from 'primereact/fileupload'; -import { InputText } from 'primereact/inputtext'; -import { Message } from 'primereact/message'; -import { MeterGroup } from 'primereact/metergroup'; -import { Messages } from 'primereact/messages'; -import { ProgressBar } from 'primereact/progressbar'; -import { Slider } from 'primereact/slider'; -import { Stepper } from 'primereact/stepper'; -import { StepperPanel } from 'primereact/stepperpanel'; -import { Tag } from 'primereact/tag'; -import { Toast } from 'primereact/toast'; -import { - assignTag, - claimTag, - createMediaFolder, - deleteMedia, - deleteTag, - getBoxes, - getBoxLocalTags, - getBoxTags, - getBoxDetails, - getBoxStorage, - getMediaTree, - getStatus, - getTagBlocks, - getTags, - markTagWritten, - moveMedia, - pairBox, - pullTagFromBox, - renameMedia, - sendCommand, - setBoxAlias, - setTagAlias, - setTagBlock, - setTagMedia, - unassignTag, - unpairBox, - uploadMedia, -} from './api.js'; - -const BOX_POLL_MS = 1500; -const STATUS_POLL_MS = 1000; -const STORAGE_POLL_MS = 5000; -const ONLINE_THRESHOLD_SEC = 60; - -const NAV_ITEMS = [ - { id: 'dashboard', label: 'Dashboard', icon: House }, - { id: 'boxes', label: 'Boxen', icon: Cube }, - { id: 'media', label: 'Medien', icon: FolderOpen }, - { id: 'tags', label: 'Tags', icon: ContactlessPayment }, - { id: 'settings', label: 'Einstellungen', icon: GearSix }, -]; - -function formatTime(ts) { - if (!ts) return '-'; - return new Date(ts * 1000).toLocaleString(); -} - -function getAvailability(lastSeen) { - if (!lastSeen) { - return { label: 'Offline', severity: 'danger' }; - } - const nowSec = Date.now() / 1000; - const isOnline = nowSec - lastSeen <= ONLINE_THRESHOLD_SEC; - return { label: isOnline ? 'Online' : 'Offline', severity: isOnline ? 'success' : 'danger' }; -} - -function parseCapabilities(raw) { - if (!raw) return '-'; - try { - const data = JSON.parse(raw); - return Object.entries(data) - .map(([key, value]) => `${key}:${value ? '1' : '0'}`) - .join(' '); - } catch (error) { - return '-'; - } -} - -function formatSize(bytes) { - if (bytes === 0) return '0 B'; - if (bytes === null || bytes === undefined || Number.isNaN(bytes)) return '-'; - const units = ['B', 'KB', 'MB', 'GB']; - let size = bytes; - let idx = 0; - while (size >= 1024 && idx < units.length - 1) { - size /= 1024; - idx += 1; - } - return `${size.toFixed(idx === 0 ? 0 : 1)} ${units[idx]}`; -} - -function formatDuration(seconds) { - if (seconds === null || seconds === undefined || Number.isNaN(seconds)) return '-'; - const total = Math.max(0, Math.floor(seconds)); - const mins = Math.floor(total / 60); - const secs = total % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; -} - -function formatClock(seconds) { - if (seconds === null || seconds === undefined || Number.isNaN(seconds)) return '-'; - const total = Math.max(0, Math.floor(seconds)); - const hrs = Math.floor(total / 3600); - const mins = Math.floor((total % 3600) / 60); - const secs = total % 60; - if (hrs > 0) { - return `${hrs}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; - } - return `${mins}:${secs.toString().padStart(2, '0')}`; -} - -function capacityScale(percent) { - const p = Math.max(0, Math.min(100, percent || 0)); - if (p === 0) return 1; - return 100 / p; -} - -function getNfcKey(nfc) { - if (!nfc) return ''; - const at = nfc.at || ''; - const uid = nfc.uid || ''; - const hardware = nfc.hardwareUid || nfc.hardware_uid || ''; - return `${at}:${uid}:${hardware}`; -} - -function generateTagId() { - const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'; - let generated = ''; - for (let i = 0; i < 10; i += 1) { - generated += alphabet[Math.floor(Math.random() * alphabet.length)]; - } - return generated; -} - -function generateHardwareUid() { - const bytes = []; - for (let i = 0; i < 7; i += 1) { - bytes.push(Math.floor(Math.random() * 256)); - } - return bytes - .map((value) => value.toString(16).padStart(2, '0').toUpperCase()) - .join(':'); -} - -function isValidTagUid(uid) { - return /^(?:[a-z0-9]{10}|TAG_[a-z0-9]{8})$/.test(uid); -} - -function SectionHeader({ title, subtitle, actions }) { - return ( -
-
-

{title}

-

{subtitle}

-
-
{actions}
-
- ); -} - -function StatGrid({ items }) { - return ( -
- {items.map((card) => ( - -

{card.label}

-
{card.value}
- {card.helper} -
- ))} -
- ); -} - -export default function App() { - const [activeSection, setActiveSection] = useState('dashboard'); - const [searchValue, setSearchValue] = useState(''); - const [boxes, setBoxes] = useState([]); - const [boxStorage, setBoxStorage] = useState({}); - const [selectedId, setSelectedId] = useState(''); - const [boxDetailsOpen, setBoxDetailsOpen] = useState(false); - const [boxDetailsLoading, setBoxDetailsLoading] = useState(false); - const [boxDetailsError, setBoxDetailsError] = useState(''); - const [boxDetailsData, setBoxDetailsData] = useState(null); - const [status, setStatus] = useState(null); - const [error, setError] = useState(''); - const [nfcUid, setNfcUid] = useState('UID_1'); - const [mediaTree, setMediaTree] = useState(null); - const [mediaError, setMediaError] = useState(''); - const [currentPath, setCurrentPath] = useState(''); - const [selectedPaths, setSelectedPaths] = useState([]); - const [newFolderName, setNewFolderName] = useState(''); - const [renameName, setRenameName] = useState(''); - const [moveTarget, setMoveTarget] = useState(''); - const [activeModal, setActiveModal] = useState(''); - const [tagDeleteTarget, setTagDeleteTarget] = useState(''); - const [expandedFolders, setExpandedFolders] = useState(() => new Set(['__root__'])); - const [sidebarQuery, setSidebarQuery] = useState(''); - const [uploadAfterCreate, setUploadAfterCreate] = useState(false); - const [pendingUploadFiles, setPendingUploadFiles] = useState([]); - const [uploadProgress, setUploadProgress] = useState(0); - const [uploadInProgress, setUploadInProgress] = useState(false); - const [activeUploadLabel, setActiveUploadLabel] = useState(''); - const [uploadSize, setUploadSize] = useState(0); - const [uploadNameError, setUploadNameError] = useState(''); - const transferTimerRef = useRef(null); - const explorerRef = useRef(null); - const modalRef = useRef(null); - const toastRef = useRef(null); - const fileUploadRef = useRef(null); - const dragSelectRef = useRef(false); - const lastDblClickRef = useRef(0); - const lastAnchorRef = useRef(''); - const explorerInitRef = useRef(false); - const expandedInitRef = useRef(false); - const volumeRef = useRef(null); - const [tags, setTags] = useState([]); - const [boxTags, setBoxTags] = useState([]); - const [blockedByBox, setBlockedByBox] = useState({}); - const [dbTagMedia, setDbTagMedia] = useState({}); - const [localBoxTags, setLocalBoxTags] = useState([]); - const [localBoxError, setLocalBoxError] = useState(''); - const [importTargetFolder, setImportTargetFolder] = useState(''); - const [importTargetUid, setImportTargetUid] = useState(''); - const [scanTagUid, setScanTagUid] = useState(''); - const [tagUidMode, setTagUidMode] = useState('keep'); - const [tagUidError, setTagUidError] = useState(''); - const [tagStep, setTagStep] = useState(0); - const [tagStepOneDone, setTagStepOneDone] = useState(false); - const [tagStepTwoDone, setTagStepTwoDone] = useState(false); - const [tagStepMax, setTagStepMax] = useState(0); - const tagStepperRef = useRef(null); - const tagWizardRestoredRef = useRef(''); - const tagUidMsgRef = useRef(null); - const mediaMsgRef = useRef(null); - const autoReplaceMsg = 'Ungueltige ID erkannt – wird automatisch ersetzt.'; - const [dismissedNfcKey, setDismissedNfcKey] = useState(''); - const [scanTagLabel, setScanTagLabel] = useState(''); - const [scanMediaPath, setScanMediaPath] = useState(''); - const [reuseTagUid, setReuseTagUid] = useState(''); - const lastNfcKeyRef = useRef(''); - const [tagAliasDrafts, setTagAliasDrafts] = useState({}); - const [boxAliasDrafts, setBoxAliasDrafts] = useState({}); - const [showSessionSheet, setShowSessionSheet] = useState(false); - const [drawerTab, setDrawerTab] = useState('boxes'); - const [boxDeleteTarget, setBoxDeleteTarget] = useState(''); - const [lastHardwareUid, setLastHardwareUid] = useState({ uid: '', hardwareUid: '' }); - const [simulatedNfc, setSimulatedNfc] = useState(null); - const [showVolume, setShowVolume] = useState(false); - const [volumeDraft, setVolumeDraft] = useState(50); - const [seekPercent, setSeekPercent] = useState(0); - const [isSeeking, setIsSeeking] = useState(false); - - function addToast(type, message) { - toastRef.current?.show({ - severity: type, - summary: type === 'error' ? 'Fehler' : 'Info', - detail: message, - life: 4000, - }); - } - - async function handleShowBoxDetails(boxId) { - setBoxDetailsOpen(true); - setBoxDetailsLoading(true); - setBoxDetailsError(''); - setBoxDetailsData(null); - const response = await getBoxDetails(boxId); - if (!response.ok) { - const message = response.data.detail || 'Box-Details konnten nicht geladen werden.'; - setBoxDetailsError(message); - addToast('error', message); - setBoxDetailsLoading(false); - return; - } - setBoxDetailsData(response.data || null); - setBoxDetailsLoading(false); - } - - useEffect(() => { - let active = true; - - async function refreshBoxes() { - const response = await getBoxes(); - if (!active) return; - if (!response.ok) { - setError(response.data.detail || 'Fehler beim Laden der Boxen.'); - return; - } - setBoxes(response.data.boxes || []); - setError(''); - } - - refreshBoxes(); - const handle = setInterval(refreshBoxes, BOX_POLL_MS); - return () => { - active = false; - clearInterval(handle); - }; - }, []); - - useEffect(() => { - let active = true; - - async function refreshStorage() { - if (!boxes.length) { - if (active) setBoxStorage({}); - return; - } - const results = await Promise.all( - boxes.map(async (box) => { - const response = await getBoxStorage(box.box_id); - if (!response.ok) { - return { boxId: box.box_id, storage: null }; - } - return { boxId: box.box_id, storage: response.data.storage || null }; - }) - ); - if (!active) return; - const next = {}; - results.forEach(({ boxId, storage }) => { - if (!storage) { - next[boxId] = null; - return; - } - const total = storage.total_bytes ?? null; - const free = storage.free_bytes ?? null; - const used = - storage.used_bytes ?? (total !== null && free !== null ? total - free : null); - next[boxId] = { total, free, used }; - }); - setBoxStorage(next); - } - - refreshStorage(); - const handle = setInterval(refreshStorage, STORAGE_POLL_MS); - return () => { - active = false; - clearInterval(handle); - }; - }, [boxes]); - - useEffect(() => { - function handleOutside(event) { - if (activeModal) return; - if (!explorerRef.current) return; - if (modalRef.current && modalRef.current.contains(event.target)) return; - if (!explorerRef.current.contains(event.target)) { - setSelectedPaths([]); - setRenameName(''); - } - } - function handleMouseUp() { - dragSelectRef.current = false; - } - window.addEventListener('mousedown', handleOutside); - window.addEventListener('mouseup', handleMouseUp); - return () => { - window.removeEventListener('mousedown', handleOutside); - window.removeEventListener('mouseup', handleMouseUp); - }; - }, [activeModal]); - - useEffect(() => { - if (!selectedId) { - setStatus(null); - setMediaTree(null); - setMediaError(''); - setSelectedPaths([]); - setNewFolderName(''); - setRenameName(''); - setActiveModal(''); - setBoxTags([]); - setDbTagMedia({}); - setScanTagUid(''); - setScanTagLabel(''); - setScanMediaPath(''); - setReuseTagUid(''); - setLocalBoxTags([]); - setLocalBoxError(''); - return; - } - - let active = true; - - async function refreshStatus() { - const response = await getStatus(selectedId); - if (!active) return; - if (!response.ok) { - setStatus({ error: response.data.detail || 'Status nicht verfuegbar.' }); - return; - } - setStatus(response.data); - } - - refreshStatus(); - const handle = setInterval(refreshStatus, STATUS_POLL_MS); - return () => { - active = false; - clearInterval(handle); - }; - }, [selectedId]); - - useEffect(() => { - let active = true; - - async function refreshMedia() { - const response = await getMediaTree(); - if (!active) return; - if (!response.ok) { - setMediaError(response.data.detail || 'Medien nicht verfuegbar.'); - setMediaTree(null); - return; - } - setMediaTree(response.data); - setMediaError(''); - } - - refreshMedia(); - return () => { - active = false; - }; - }, []); - - function findPathChain(node, target, chain = []) { - if (!node) return null; - const currentPath = node.path || ''; - const nextChain = [...chain, currentPath]; - if (currentPath === target) { - return nextChain; - } - if (!Array.isArray(node.children)) return null; - for (const child of node.children) { - if (child.type !== 'folder') continue; - const result = findPathChain(child, target, nextChain); - if (result) return result; - } - return null; - } - - useEffect(() => { - if (!mediaTree) return; - const saved = localStorage.getItem('klangkiste_explorer_path'); - if (saved === null) return; - const chain = findPathChain(mediaTree, saved); - if (!chain) return; - setCurrentPath(saved); - if (!expandedInitRef.current) { - const savedExpandedRaw = localStorage.getItem('klangkiste_explorer_expanded'); - let savedExpanded = []; - if (savedExpandedRaw) { - try { - const parsed = JSON.parse(savedExpandedRaw); - if (Array.isArray(parsed)) { - savedExpanded = parsed; - } - } catch (error) { - savedExpanded = []; - } - } - const merged = new Set([ - ...savedExpanded, - ...chain.map((p) => (p ? p : '__root__')), - ]); - setExpandedFolders(merged); - expandedInitRef.current = true; - } - explorerInitRef.current = true; - }, [mediaTree]); - - useEffect(() => { - if (!explorerInitRef.current) return; - localStorage.setItem('klangkiste_explorer_path', currentPath || ''); - }, [currentPath]); - - useEffect(() => { - if (!expandedInitRef.current) return; - localStorage.setItem( - 'klangkiste_explorer_expanded', - JSON.stringify(Array.from(expandedFolders)) - ); - }, [expandedFolders]); - - useEffect(() => { - const lastNfc = status?.last_nfc; - if (!lastNfc || !lastNfc.uid) { - return; - } - if (!lastNfc.known) { - const key = `${status?.last_nfc_at ?? ''}:${lastNfc.uid ?? ''}`; - if (lastNfcKeyRef.current === key) { - return; - } - lastNfcKeyRef.current = key; - setScanTagLabel(''); - setScanMediaPath(''); - if (isValidTagUid(lastNfc.uid)) { - setScanTagUid(lastNfc.uid); - setTagUidMode('keep'); - setTagUidError(''); - } else { - setScanTagUid(generateTagId()); - setTagUidMode('new'); - setTagUidError(autoReplaceMsg); - } - setDismissedNfcKey(''); - setTagStep(0); - setTagStepOneDone(false); - setTagStepTwoDone(false); - setTagStepMax(0); - } - }, [status, tags]); - - useEffect(() => { - tagStepperRef.current?.setActiveStep(tagStep); - }, [tagStep]); - - useEffect(() => { - const ref = tagUidMsgRef.current; - if (!ref) return; - ref.clear(); - if (tagStep === 0 && tagUidError) { - const showMessage = () => { - if (!tagUidMsgRef.current) return false; - tagUidMsgRef.current.clear(); - tagUidMsgRef.current.show({ - severity: 'warn', - summary: 'Ungueltige ID', - detail: tagUidError, - sticky: true, - closable: false, - }); - return true; - }; - if (!showMessage()) { - const timer = setTimeout(() => { - showMessage(); - }, 0); - return () => clearTimeout(timer); - } - } - }, [tagUidError, tagStep]); - - useEffect(() => { - const ref = mediaMsgRef.current; - if (!ref) return; - ref.clear(); - if (tagStep === 1 && !scanMediaPath) { - const showMessage = () => { - if (!mediaMsgRef.current) return false; - mediaMsgRef.current.clear(); - mediaMsgRef.current.show({ - severity: 'info', - summary: 'Keine Medienzuordnung', - detail: 'Ohne Medienauswahl wird der Tag ohne Zuordnung gespeichert.', - sticky: true, - closable: false, - }); - return true; - }; - if (!showMessage()) { - const timer = setTimeout(() => { - showMessage(); - }, 0); - return () => clearTimeout(timer); - } - } - }, [scanMediaPath, tagStep]); - - useEffect(() => { - let active = true; - - async function refreshTags() { - const response = await getTags(); - if (!active) return; - if (!response.ok) { - setError(response.data.detail || 'Tags nicht verfuegbar.'); - return; - } - setTags(response.data.tags || []); - } - - refreshTags(); - return () => { - active = false; - }; - }, []); - - useEffect(() => { - if (!selectedId) return; - let active = true; - - async function refreshBoxTags() { - const response = await getBoxTags(selectedId); - if (!active) return; - if (!response.ok) { - setError(response.data.detail || 'Box-Tags nicht verfuegbar.'); - return; - } - setBoxTags(response.data.tags || []); - } - - refreshBoxTags(); - return () => { - active = false; - }; - }, [selectedId]); - - useEffect(() => { - if (!selectedId) return; - let active = true; - - async function refreshLocalTags() { - const response = await getBoxLocalTags(selectedId); - if (!active) return; - if (!response.ok) { - setLocalBoxError(response.data.detail || 'Lokale Box-Tags nicht verfuegbar.'); - return; - } - setLocalBoxTags(response.data.tags || []); - setLocalBoxError(''); - } - - refreshLocalTags(); - return () => { - active = false; - }; - }, [selectedId]); - - useEffect(() => { - let active = true; - - async function refreshBlocked() { - if (!boxes.length) { - setBlockedByBox({}); - return; - } - const results = await Promise.all( - boxes.map(async (box) => { - const response = await getTagBlocks(box.box_id); - return { boxId: box.box_id, response }; - }) - ); - if (!active) return; - const next = {}; - results.forEach(({ boxId, response }) => { - if (response.ok) { - next[boxId] = response.data.blocked || []; - } - }); - setBlockedByBox(next); - } - - refreshBlocked(); - return () => { - active = false; - }; - }, [boxes]); - - const unpaired = useMemo( - () => boxes.filter((box) => box.state === 'UNPAIRED'), - [boxes] - ); - const paired = useMemo( - () => boxes.filter((box) => box.state === 'PAIRED'), - [boxes] - ); - - const mediaTagCounts = useMemo(() => { - const counts = {}; - tags.forEach((tag) => { - const mediaPath = tag.media_path || ''; - if (!mediaPath) return; - counts[mediaPath] = (counts[mediaPath] || 0) + 1; - }); - return counts; - }, [tags]); - - const topLevelFolders = useMemo(() => { - if (!mediaTree || !Array.isArray(mediaTree.children)) return []; - const folders = mediaTree.children.filter((child) => child.type === 'folder'); - const query = sidebarQuery.trim().toLowerCase(); - if (!query) return folders; - return folders.filter((folder) => (folder.name || '').toLowerCase().includes(query)); - }, [mediaTree, sidebarQuery]); - - const filteredTree = useMemo(() => { - if (!mediaTree) return null; - return { - ...mediaTree, - children: topLevelFolders, - }; - }, [mediaTree, topLevelFolders]); - - const dashboardStats = useMemo(() => { - const sizeLabel = mediaTree ? formatSize(mediaTree.size) : '-'; - const freeLabel = mediaTree ? formatSize(mediaTree.free_bytes) : '-'; - return [ - { - label: 'Gepairte Boxen', - value: paired.length, - helper: `${unpaired.length} neu`, - }, - { - label: 'Tags gesamt', - value: tags.length, - helper: `${boxTags.length} auf Box`, - }, - { - label: 'Medien gesamt', - value: sizeLabel, - helper: `frei ${freeLabel}`, - }, - { - label: 'Letzter NFC', - value: status?.last_nfc?.uid || '-', - helper: status?.last_nfc_at ? formatTime(status.last_nfc_at) : '-', - }, - ]; - }, [paired.length, unpaired.length, tags.length, boxTags.length, mediaTree, status]); - - async function handlePair(boxId) { - const response = await pairBox(boxId); - if (!response.ok) { - setError(response.data.detail || 'Box pairing fehlgeschlagen.'); - addToast('error', response.data.detail || 'Box pairing fehlgeschlagen.'); - return; - } - addToast('success', 'Box gepairt.'); - const updated = await getBoxes(); - if (updated.ok) { - setBoxes(updated.data.boxes || []); - } - } - - async function handleCommand(command, payload = {}) { - if (!selectedId) { - setError('Bitte zuerst eine Box auswaehlen.'); - addToast('error', 'Bitte zuerst eine Box auswaehlen.'); - return; - } - let nextPayload = payload; - if (command === 'nfc_on' || command === 'nfc_off') { - const rawUid = typeof payload?.uid === 'string' ? payload.uid.trim() : ''; - let uid = rawUid; - if (!uid) { - if (command === 'nfc_on') { - setSimulatedNfc({ - uid: '', - known: false, - hardwareUid: generateHardwareUid(), - at: Date.now(), - }); - setScanTagLabel(''); - setScanMediaPath(''); - if (!scanTagUid) { - setScanTagUid(generateTagId()); - } - setTagUidMode('new'); - setTagStep(0); - setTagStepOneDone(false); - setTagStepTwoDone(false); - return; - } - setError('Bitte eine UID angeben.'); - addToast('error', 'Bitte eine UID angeben.'); - return; - } - nextPayload = { ...payload, uid }; - if (command === 'nfc_on') { - setLastHardwareUid({ uid, hardwareUid: generateHardwareUid() }); - setSimulatedNfc(null); - } - } - const response = await sendCommand(selectedId, command, nextPayload); - if (!response.ok) { - setError(response.data.detail || 'Command fehlgeschlagen.'); - addToast('error', response.data.detail || 'Command fehlgeschlagen.'); - return; - } - setError(''); - addToast('success', 'Command gesendet.'); - } - - async function handlePlayerCommand(command, payload = {}) { - if (!selectedId) { - return; - } - let nextPayload = payload; - if (command === 'nfc_on' || command === 'nfc_off') { - const rawUid = typeof payload?.uid === 'string' ? payload.uid.trim() : ''; - const uid = rawUid; - if (!uid) { - if (command === 'nfc_on') { - setSimulatedNfc({ - uid: '', - known: false, - hardwareUid: generateHardwareUid(), - at: Date.now(), - }); - setScanTagLabel(''); - setScanMediaPath(''); - if (!scanTagUid) { - setScanTagUid(generateTagId()); - } - setTagUidMode('new'); - setTagStep(0); - setTagStepOneDone(false); - setTagStepTwoDone(false); - setTagStepMax(0); - } else if (command === 'nfc_off') { - setSimulatedNfc(null); - setScanTagUid(''); - setScanTagLabel(''); - setScanMediaPath(''); - setLastHardwareUid({ uid: '', hardwareUid: '' }); - setTagUidMode('keep'); - setTagStep(0); - setTagStepOneDone(false); - setTagStepTwoDone(false); - setTagStepMax(0); - } - return; - } - if (command === 'nfc_on') { - setLastHardwareUid({ uid, hardwareUid: generateHardwareUid() }); - setSimulatedNfc(null); - } - nextPayload = { ...payload, uid }; - } - await sendCommand(selectedId, command, nextPayload); - } - - async function handleUnpair(boxId) { - const response = await unpairBox(boxId); - if (!response.ok) { - setError(response.data.detail || 'Unpair fehlgeschlagen.'); - addToast('error', response.data.detail || 'Unpair fehlgeschlagen.'); - return; - } - addToast('success', 'Box unpaired.'); - const updated = await getBoxes(); - if (updated.ok) { - setBoxes(updated.data.boxes || []); - } - } - - async function handleMediaRefresh() { - const response = await getMediaTree(); - if (!response.ok) { - setMediaError(response.data.detail || 'Medien nicht verfuegbar.'); - setMediaTree(null); - return; - } - setMediaTree(response.data); - setMediaError(''); - } - - function getNodeByPath(node, targetPath) { - if (!node) return null; - if ((node.path || '') === targetPath) return node; - if (!Array.isArray(node.children)) return null; - for (const child of node.children) { - const found = getNodeByPath(child, targetPath); - if (found) return found; - } - return null; - } - - function listChildren(node) { - if (!node) return []; - if (!Array.isArray(node.children)) return []; - return node.children; - } - - function buildBreadcrumb(pathValue) { - if (!pathValue) return []; - const parts = pathValue.split('/').filter(Boolean); - return parts.map((part, index) => ({ - name: part, - path: parts.slice(0, index + 1).join('/'), - })); - } - - function collectTopLevelFolders(node) { - if (!node || !Array.isArray(node.children)) return []; - return node.children - .filter((child) => child.type === 'folder') - .map((child) => child.path); - } - - function collectFolderPaths(node) { - if (!node || node.type !== 'folder') return []; - const entries = [node.path || '']; - if (!Array.isArray(node.children)) return entries; - node.children.forEach((child) => { - if (child.type !== 'folder') return; - entries.push(...collectFolderPaths(child)); - }); - return entries; - } - - function isSelected(pathValue) { - return selectedPaths.includes(pathValue || ''); - } - - async function handleCreateFolder() { - const trimmedName = newFolderName.trim(); - if (uploadAfterCreate && pendingUploadFiles.length > 0) { - if (!trimmedName && !currentPath) { - addToast('error', 'Im Medien-Hauptordner ist ein Ordnername erforderlich.'); - setUploadNameError('Im Medien-Hauptordner ist ein Ordnername erforderlich.'); - return; - } - const targetPath = trimmedName ? `${currentPath}/${trimmedName}` : currentPath; - setUploadInProgress(true); - setActiveUploadLabel( - pendingUploadFiles.length ? `Upload: ${pendingUploadFiles.length} Datei(en)` : '' - ); - const uploadResponse = await uploadMedia( - targetPath, - pendingUploadFiles, - (percent) => setUploadProgress(percent) - ); - setUploadInProgress(false); - setUploadProgress(0); - if (!uploadResponse.ok) { - setMediaError(uploadResponse.data.detail || 'Upload fehlgeschlagen.'); - addToast('error', uploadResponse.data.detail || 'Upload fehlgeschlagen.'); - return; - } - addToast('success', 'Upload abgeschlossen.'); - await handleMediaRefresh(); - setActiveModal(''); - setUploadAfterCreate(false); - setPendingUploadFiles([]); - setNewFolderName(''); - return; - } - - if (!trimmedName && !uploadAfterCreate) { - setMediaError('Bitte einen Ordnernamen angeben.'); - return; - } - - const response = await createMediaFolder(currentPath, trimmedName); - if (!response.ok) { - setMediaError(response.data.detail || 'Ordner anlegen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Ordner anlegen fehlgeschlagen.'); - return; - } - addToast('success', 'Ordner angelegt.'); - setNewFolderName(''); - setActiveModal(''); - await handleMediaRefresh(); - } - - async function handleRename() { - if (!selectedPaths.length) return; - const response = await renameMedia(selectedPaths[0], renameName.trim()); - if (!response.ok) { - setMediaError(response.data.detail || 'Umbenennen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Umbenennen fehlgeschlagen.'); - return; - } - addToast('success', 'Eintrag umbenannt.'); - setRenameName(''); - setActiveModal(''); - await handleMediaRefresh(); - } - - async function handleDeleteSelected() { - if (!selectedPaths.length) return; - for (const pathValue of selectedPaths) { - const response = await deleteMedia(pathValue); - if (!response.ok) { - setMediaError(response.data.detail || 'Loeschen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Loeschen fehlgeschlagen.'); - return; - } - } - addToast('success', 'Eintraege geloescht.'); - setSelectedPaths([]); - setActiveModal(''); - await handleMediaRefresh(); - } - - async function handleMoveSelected() { - if (!selectedPaths.length) return; - const pathValue = selectedPaths[0]; - const isRootTarget = moveTarget === '__root__' || moveTarget === ''; - const response = await moveMedia(pathValue, isRootTarget ? '' : moveTarget); - if (!response.ok) { - setMediaError(response.data.detail || 'Verschieben fehlgeschlagen.'); - addToast('error', response.data.detail || 'Verschieben fehlgeschlagen.'); - return; - } - addToast('success', 'Eintrag verschoben.'); - setMoveTarget(''); - setSelectedPaths([]); - setActiveModal(''); - await handleMediaRefresh(); - } - - async function handleUpload(event) { - const files = Array.from(event.target.files || []); - const audioFiles = files.filter((file) => file.type.startsWith('audio/')); - if (!audioFiles.length) return; - if (!currentPath) { - addToast('error', 'Im Medien-Hauptordner ist ein Ordnername erforderlich.'); - setUploadNameError('Im Medien-Hauptordner ist ein Ordnername erforderlich.'); - return; - } - setUploadInProgress(true); - setActiveUploadLabel( - audioFiles.length ? `Upload: ${audioFiles.length} Datei(en)` : '' - ); - const response = await uploadMedia(currentPath, audioFiles, (percent) => - setUploadProgress(percent) - ); - setUploadInProgress(false); - setUploadProgress(0); - if (!response.ok) { - setMediaError(response.data.detail || 'Upload fehlgeschlagen.'); - addToast('error', response.data.detail || 'Upload fehlgeschlagen.'); - return; - } - addToast('success', 'Upload abgeschlossen.'); - await handleMediaRefresh(); - } - - const fileHeaderTemplate = (options) => { - const { className, chooseButton, cancelButton } = options; - return ( -
- {chooseButton} - {cancelButton} -
- ); - }; - - const fileItemTemplate = (file, props) => ( -
-
- ); - - const fileEmptyTemplate = () => ( -
- Gesamt: {formatSize(uploadSize)} -
- ); - - function handleSelect(item, event) { - const itemPath = item.path || ''; - if (event.shiftKey && lastAnchorRef.current) { - const items = listChildren(getNodeByPath(mediaTree, currentPath)); - const anchorIndex = items.findIndex((entry) => entry.path === lastAnchorRef.current); - const currentIndex = items.findIndex((entry) => entry.path === itemPath); - if (anchorIndex !== -1 && currentIndex !== -1) { - const [start, end] = anchorIndex < currentIndex - ? [anchorIndex, currentIndex] - : [currentIndex, anchorIndex]; - const range = items.slice(start, end + 1).map((entry) => entry.path || ''); - setSelectedPaths(Array.from(new Set([...selectedPaths, ...range]))); - return; - } - } - if (event.metaKey || event.ctrlKey) { - setSelectedPaths((prev) => - prev.includes(itemPath) - ? prev.filter((pathValue) => pathValue !== itemPath) - : [...prev, itemPath] - ); - lastAnchorRef.current = itemPath; - return; - } - const now = Date.now(); - const isDoubleClick = now - lastDblClickRef.current < 250; - if (!isDoubleClick) { - setSelectedPaths([itemPath]); - lastAnchorRef.current = itemPath; - } - } - - function handleDragSelect(item) { - if (!dragSelectRef.current) return; - const itemPath = item.path || ''; - setSelectedPaths((prev) => - prev.includes(itemPath) ? prev : [...prev, itemPath] - ); - } - - function handleOpen(item) { - if (item.type !== 'folder') return; - setCurrentPath(item.path || ''); - setSelectedPaths([]); - setRenameName(''); - const key = item.path || '__root__'; - setExpandedFolders((prev) => new Set(prev).add(key)); - } - - function handleGoUp() { - if (!currentPath) return; - const parts = currentPath.split('/').filter(Boolean); - parts.pop(); - const next = parts.join('/'); - setCurrentPath(next); - setSelectedPaths([]); - setRenameName(''); - } - - function toggleFolder(pathValue) { - const key = pathValue || '__root__'; - setExpandedFolders((prev) => { - const next = new Set(prev); - if (next.has(key)) { - next.delete(key); - } else { - next.add(key); - } - return next; - }); - } - - function renderFolderTree(node, depth = 0) { - if (!node || node.type !== 'folder') return null; - const isActive = (node.path || '') === currentPath; - const key = node.path || '__root__'; - const isExpanded = expandedFolders.has(key); - const childFolders = - Array.isArray(node.children) && node.children.filter((child) => child.type === 'folder'); - const hasChildren = childFolders && childFolders.length > 0; - const tagCount = mediaTagCounts[node.path || ''] || 0; - return ( -
-
handleOpen(node)} - role="button" - tabIndex={0} - onKeyDown={(event) => { - if (event.key === 'Enter') handleOpen(node); - }} - > - {hasChildren ? ( -
{ - event.stopPropagation(); - toggleFolder(node.path || ''); - }} - onKeyDown={(event) => { - if (event.key === 'Enter') { - event.stopPropagation(); - toggleFolder(node.path || ''); - } - }} - aria-label="Toggle" - > - {isExpanded ? : } -
- ) : ( -
- {isExpanded && - hasChildren && - childFolders.map((child) => renderFolderTree(child, depth + 1))} -
- ); - } - - async function handleClaimTagForScan() { - setTagUidError(''); - const currentUid = scanTagUid.trim(); - const existing = tags.find((tag) => tag.uid === currentUid); - if (existing) { - if (existing.status === 'NEW') { - const written = await markTagWritten(currentUid); - if (!written.ok) { - if (written.data.detail === 'invalid uid') { - setTagUidError( - 'UID ungueltig. Bitte 10 Zeichen (a-z, 0-9) oder TAG_ + 8 Zeichen verwenden.' - ); - return; - } - setError(written.data.detail || 'Tag schreiben fehlgeschlagen.'); - addToast('error', written.data.detail || 'Tag schreiben fehlgeschlagen.'); - return; - } - } - } else { - const response = await claimTag(currentUid, scanTagLabel.trim()); - if (!response.ok) { - if (response.data.detail === 'invalid uid') { - setTagUidError( - 'UID ungueltig. Bitte 10 Zeichen (a-z, 0-9) oder TAG_ + 8 Zeichen verwenden.' - ); - return; - } - setError(response.data.detail || 'Tag schreiben fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag schreiben fehlgeschlagen.'); - return; - } - setScanTagUid(response.data.uid || ''); - } - setError(''); - addToast('success', 'Tag geschrieben.'); - if (activeNfc) { - setDismissedNfcKey(getNfcKey(activeNfc)); - } - const shouldAssign = - selectedId && status?.last_nfc?.known === false && scanMediaPath; - if (shouldAssign) { - const mediaSet = await setTagMedia(currentUid, scanMediaPath); - if (!mediaSet.ok) { - setError(mediaSet.data.detail || 'Medium setzen fehlgeschlagen.'); - addToast('error', mediaSet.data.detail || 'Medium setzen fehlgeschlagen.'); - return; - } - const assigned = await assignTag(currentUid, selectedId); - if (!assigned.ok) { - setError(assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - addToast('error', assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - return; - } - addToast('success', 'Tag zugeordnet.'); - if (activeNfc?.known === false) { - await handlePlayerCommand('nfc_on', { uid: currentUid }); - } - setScanTagUid(''); - setScanTagLabel(''); - setScanMediaPath(''); - setSimulatedNfc(null); - setLastHardwareUid({ uid: '', hardwareUid: '' }); - } else if (selectedId && status?.last_nfc?.known === false) { - addToast('success', 'Tag gespeichert. Medium fehlt noch.'); - setSimulatedNfc(null); - setLastHardwareUid({ uid: '', hardwareUid: '' }); - } - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - if (selectedId) { - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - } - } - - async function handleReuseImportedTag() { - if (!reuseTagUid) { - setError('Bitte eine gespeicherte Tag-ID waehlen.'); - addToast('error', 'Bitte eine gespeicherte Tag-ID waehlen.'); - return; - } - if (!selectedId) { - setError('Bitte zuerst eine Box auswaehlen.'); - addToast('error', 'Bitte zuerst eine Box auswaehlen.'); - return; - } - const tag = tags.find((entry) => entry.uid === reuseTagUid); - if (!tag || !tag.media_path) { - setError('Tag hat keine Medienzuordnung.'); - addToast('error', 'Tag hat keine Medienzuordnung.'); - return; - } - const response = await markTagWritten(reuseTagUid); - if (!response.ok) { - setError(response.data.detail || 'Tag schreiben fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag schreiben fehlgeschlagen.'); - return; - } - const assigned = await assignTag(reuseTagUid, selectedId); - if (!assigned.ok) { - setError(assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - addToast('error', assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - return; - } - addToast('success', 'Tag geschrieben und zugeordnet.'); - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - setScanTagUid(reuseTagUid); - setReuseTagUid(''); - } - - async function handleWriteTag(uid) { - const response = await markTagWritten(uid); - if (!response.ok) { - setError(response.data.detail || 'Tag schreiben fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag schreiben fehlgeschlagen.'); - return; - } - addToast('success', 'Tag geschrieben.'); - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - } - - async function handleStoreTagOnly() { - if (!activeNfc?.uid) { - setError('Keine UID erkannt.'); - addToast('error', 'Keine UID erkannt.'); - return; - } - const existing = tags.find((tag) => tag.uid === activeNfc.uid); - if (existing) { - addToast('success', 'Tag ist bereits in der Datenbank.'); - if (activeNfc) { - setDismissedNfcKey(getNfcKey(activeNfc)); - } - return; - } - const response = await claimTag(activeNfc.uid, ''); - if (!response.ok) { - setError(response.data.detail || 'Tag speichern fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag speichern fehlgeschlagen.'); - return; - } - addToast('success', 'Tag in der Datenbank gespeichert.'); - if (activeNfc) { - setDismissedNfcKey(getNfcKey(activeNfc)); - } - setSimulatedNfc(null); - setLastHardwareUid({ uid: '', hardwareUid: '' }); - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - } - - async function handleAssignFromScan() { - if (!scanMediaPath) { - setError('Bitte zuerst einen Medienordner waehlen.'); - addToast('error', 'Bitte zuerst einen Medienordner waehlen.'); - return; - } - const uid = scanTagUid.trim(); - if (!uid) { - setError('Bitte zuerst eine Tag-ID schreiben.'); - addToast('error', 'Bitte zuerst eine Tag-ID schreiben.'); - return; - } - const matching = tags.find((tag) => tag.uid === uid); - if (!matching) { - setError('Tag-ID existiert nicht. Bitte zuerst schreiben.'); - addToast('error', 'Tag-ID existiert nicht. Bitte zuerst schreiben.'); - return; - } - const mediaSet = await setTagMedia(uid, scanMediaPath); - if (!mediaSet.ok) { - setError(mediaSet.data.detail || 'Medium setzen fehlgeschlagen.'); - addToast('error', mediaSet.data.detail || 'Medium setzen fehlgeschlagen.'); - return; - } - const assigned = await assignTag(uid, selectedId); - if (!assigned.ok) { - setError(assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - addToast('error', assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - return; - } - addToast('success', 'Tag zugeordnet.'); - if (activeNfc?.known === false) { - await handlePlayerCommand('nfc_on', { uid }); - } - if (activeNfc) { - setDismissedNfcKey(getNfcKey(activeNfc)); - } - setScanTagUid(''); - setScanTagLabel(''); - setScanMediaPath(''); - setSimulatedNfc(null); - setLastHardwareUid({ uid: '', hardwareUid: '' }); - const updatedTags = await getTags(); - if (updatedTags.ok) { - setTags(updatedTags.data.tags || []); - } - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - } - - async function handlePullTagFromBox() { - if (!selectedId) { - setError('Bitte zuerst eine Box auswaehlen.'); - addToast('error', 'Bitte zuerst eine Box auswaehlen.'); - return; - } - if (!importTargetUid) { - setError('Kein Tag ausgewaehlt.'); - addToast('error', 'Kein Tag ausgewaehlt.'); - return; - } - const response = await pullTagFromBox( - selectedId, - importTargetUid, - importTargetFolder.trim() - ); - if (!response.ok) { - setError(response.data.detail || 'Import fehlgeschlagen.'); - addToast('error', response.data.detail || 'Import fehlgeschlagen.'); - return; - } - addToast('success', 'Medien vom Box-Tag uebertragen.'); - setImportTargetUid(''); - setImportTargetFolder(''); - setActiveModal(''); - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - const updatedLocal = await getBoxLocalTags(selectedId); - if (updatedLocal.ok) { - setLocalBoxTags(updatedLocal.data.tags || []); - } - await handleMediaRefresh(); - } - - async function handleUnassignTag(uid) { - const response = await unassignTag(uid, selectedId); - if (!response.ok) { - setError(response.data.detail || 'Tag loesen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag loesen fehlgeschlagen.'); - return; - } - addToast('success', 'Tag getrennt.'); - const updated = await getBoxTags(selectedId); - if (updated.ok) { - setBoxTags(updated.data.tags || []); - } - } - - async function handleDeleteTag(uid) { - const response = await deleteTag(uid); - if (!response.ok) { - setError(response.data.detail || 'Tag loeschen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Tag loeschen fehlgeschlagen.'); - return; - } - addToast('success', 'Tag geloescht.'); - const updatedTags = await getTags(); - if (updatedTags.ok) { - setTags(updatedTags.data.tags || []); - } - if (selectedId) { - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - } - if (boxes.length) { - const results = await Promise.all( - boxes.map(async (box) => ({ - boxId: box.box_id, - response: await getTagBlocks(box.box_id), - })) - ); - const next = {}; - results.forEach(({ boxId, response }) => { - if (response.ok) { - next[boxId] = response.data.blocked || []; - } - }); - setBlockedByBox(next); - } - } - - async function handleSetTagMedia(uid) { - const mediaPath = dbTagMedia[uid] || ''; - const response = await setTagMedia(uid, mediaPath); - if (!response.ok) { - setError(response.data.detail || 'Medium setzen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Medium setzen fehlgeschlagen.'); - return; - } - const lastNfcUid = status?.last_nfc?.uid || ''; - const shouldAssign = - selectedId && - status?.last_nfc?.known === false && - (lastNfcUid === uid || (!lastNfcUid && scanTagUid && scanTagUid === uid)); - if (shouldAssign) { - const assigned = await assignTag(uid, selectedId); - if (!assigned.ok) { - setError(assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - addToast('error', assigned.data.detail || 'Zuordnung fehlgeschlagen.'); - return; - } - addToast('success', 'Medium gespeichert und Tag zugeordnet.'); - } else { - addToast('success', 'Medium gespeichert.'); - } - const updatedTags = await getTags(); - if (updatedTags.ok) { - setTags(updatedTags.data.tags || []); - } - if (selectedId) { - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - } - if (boxes.length) { - const results = await Promise.all( - boxes.map(async (box) => ({ - boxId: box.box_id, - response: await getTagBlocks(box.box_id), - })) - ); - const next = {}; - results.forEach(({ boxId, response }) => { - if (response.ok) { - next[boxId] = response.data.blocked || []; - } - }); - setBlockedByBox(next); - } - } - - async function handleClearTagMedia(uid) { - const response = await setTagMedia(uid, ''); - if (!response.ok) { - setError(response.data.detail || 'Medium entfernen fehlgeschlagen.'); - addToast('error', response.data.detail || 'Medium entfernen fehlgeschlagen.'); - return; - } - setError(''); - addToast('success', 'Medienzuweisung entfernt.'); - setDbTagMedia((prev) => ({ ...prev, [uid]: '' })); - const updatedTags = await getTags(); - if (updatedTags.ok) { - setTags(updatedTags.data.tags || []); - } - if (selectedId) { - const updatedBoxTags = await getBoxTags(selectedId); - if (updatedBoxTags.ok) { - setBoxTags(updatedBoxTags.data.tags || []); - } - } - if (boxes.length) { - const results = await Promise.all( - boxes.map(async (box) => ({ - boxId: box.box_id, - response: await getTagBlocks(box.box_id), - })) - ); - const next = {}; - results.forEach(({ boxId, response }) => { - if (response.ok) { - next[boxId] = response.data.blocked || []; - } - }); - setBlockedByBox(next); - } - } - - async function handleToggleTagBlock(boxId, uid, nextBlocked) { - const response = await setTagBlock(boxId, uid, nextBlocked); - if (!response.ok) { - addToast('error', response.data.detail || 'Tag-Sperre fehlgeschlagen.'); - return; - } - setBlockedByBox((prev) => { - const current = new Set(prev[boxId] || []); - if (nextBlocked) { - current.add(uid); - } else { - current.delete(uid); - } - return { ...prev, [boxId]: Array.from(current) }; - }); - addToast( - 'success', - nextBlocked ? 'Tag gesperrt.' : 'Tag freigegeben.' - ); - } - - async function handleSaveTagAlias(uid) { - const value = (tagAliasDrafts[uid] ?? '').trim(); - const response = await setTagAlias(uid, value || null); - if (!response.ok) { - addToast('error', response.data.detail || 'Tag-Alias speichern fehlgeschlagen.'); - return; - } - const updated = await getTags(); - if (updated.ok) { - setTags(updated.data.tags || []); - } - addToast('success', 'Tag-Alias gespeichert.'); - } - - async function handleSaveBoxAlias(boxId) { - const value = (boxAliasDrafts[boxId] ?? '').trim(); - const response = await setBoxAlias(boxId, value || null); - if (!response.ok) { - addToast('error', response.data.detail || 'Box-Alias speichern fehlgeschlagen.'); - return; - } - const updated = await getBoxes(); - if (updated.ok) { - setBoxes(updated.data.boxes || []); - } - addToast('success', 'Box-Alias gespeichert.'); - } - - const currentItems = mediaTree - ? listChildren(getNodeByPath(mediaTree, currentPath)).filter( - (item) => !(item.name || '').startsWith('.') - ) - : []; - const showMeta = currentItems.some((item) => item.type === 'file'); - const mediaBytes = mediaTree?.size ?? null; - const freeBytes = mediaTree?.free_bytes ?? null; - const systemStorage = useMemo(() => { - if (!mediaTree || mediaTree.size === undefined || mediaTree.free_bytes === undefined) { - return null; - } - const used = Math.max(0, mediaTree.size || 0); - const free = Math.max(0, mediaTree.free_bytes || 0); - const total = used + free; - const percent = total > 0 ? Math.min(100, Math.round((used / total) * 100)) : 0; - return { used, free, total, percent, scale: capacityScale(percent) }; - }, [mediaTree]); - - const activeMeta = useMemo( - () => NAV_ITEMS.find((item) => item.id === activeSection), - [activeSection] - ); - const activeBoxLabel = useMemo(() => { - const activeBox = boxes.find((box) => box.box_id === selectedId); - return activeBox?.alias || activeBox?.box_id || ''; - }, [boxes, selectedId]); - const activeNfc = useMemo(() => { - if (status?.last_nfc && status.last_nfc.known === false) { - return { ...status.last_nfc, at: status.last_nfc_at }; - } - return simulatedNfc; - }, [status, simulatedNfc]); - - const activeNfcKey = useMemo( - () => (activeNfc ? getNfcKey(activeNfc) : ''), - [activeNfc] - ); - - useEffect(() => { - if (!activeNfcKey) return; - if (tagWizardRestoredRef.current === activeNfcKey) return; - try { - const raw = sessionStorage.getItem('klangkiste_tag_wizard'); - if (!raw) return; - const parsed = JSON.parse(raw); - if (!parsed || parsed.key !== activeNfcKey) return; - const data = parsed.data || {}; - if (typeof data.scanTagUid === 'string') setScanTagUid(data.scanTagUid); - if (typeof data.scanTagLabel === 'string') setScanTagLabel(data.scanTagLabel); - if (typeof data.scanMediaPath === 'string') setScanMediaPath(data.scanMediaPath); - if (typeof data.tagUidMode === 'string') setTagUidMode(data.tagUidMode); - if (typeof data.tagStep === 'number') setTagStep(data.tagStep); - setTagStepOneDone(Boolean(data.tagStepOneDone)); - setTagStepTwoDone(Boolean(data.tagStepTwoDone)); - if (typeof data.tagStepMax === 'number') setTagStepMax(data.tagStepMax); - if (typeof data.tagUidError === 'string') setTagUidError(data.tagUidError); - if (data.lastHardwareUid?.uid || data.lastHardwareUid?.hardwareUid) { - setLastHardwareUid(data.lastHardwareUid); - } - if (!status?.last_nfc && data.simulatedNfc) { - setSimulatedNfc(data.simulatedNfc); - } - if (typeof data.dismissedNfcKey === 'string') { - setDismissedNfcKey(data.dismissedNfcKey); - } - tagWizardRestoredRef.current = activeNfcKey; - } catch (error) { - // ignore storage restore errors - } - }, [activeNfcKey, status?.last_nfc]); - - useEffect(() => { - if (!activeNfcKey) return; - try { - const payload = { - key: activeNfcKey, - data: { - scanTagUid, - scanTagLabel, - scanMediaPath, - tagUidMode, - tagStep, - tagStepOneDone, - tagStepTwoDone, - tagStepMax, - tagUidError, - lastHardwareUid, - simulatedNfc, - dismissedNfcKey, - }, - }; - sessionStorage.setItem('klangkiste_tag_wizard', JSON.stringify(payload)); - } catch (error) { - // ignore storage write errors - } - }, [ - activeNfcKey, - scanTagUid, - scanTagLabel, - scanMediaPath, - tagUidMode, - tagStep, - tagStepOneDone, - tagStepTwoDone, - tagStepMax, - tagUidError, - lastHardwareUid, - simulatedNfc, - dismissedNfcKey, - ]); - const statusWithHardware = useMemo(() => { - if (!status) return null; - const hardwareUid = - lastHardwareUid.uid === status?.last_nfc?.uid && lastHardwareUid.hardwareUid - ? lastHardwareUid.hardwareUid - : activeNfc?.hardwareUid || null; - if (!status.last_nfc) return status; - return { - ...status, - last_nfc: { - ...status.last_nfc, - hardware_uid: hardwareUid, - }, - }; - }, [status, lastHardwareUid, activeNfc]); - const activeTag = useMemo(() => { - const uid = status?.last_nfc?.uid || activeNfc?.uid || ''; - if (!uid) return null; - return tags.find((tag) => tag.uid === uid) || null; - }, [status, activeNfc, tags]); - const playlistRoot = activeTag?.media_path || ''; - const playlistTitle = playlistRoot ? playlistRoot.split('/').filter(Boolean).pop() : ''; - const playlistItems = useMemo(() => { - if (!mediaTree || !playlistRoot) return []; - const node = getNodeByPath(mediaTree, playlistRoot); - if (!node) return []; - const stack = [node]; - const collected = []; - while (stack.length) { - const current = stack.pop(); - if (!current) continue; - if (current.type === 'file') { - collected.push(current); - continue; - } - if (Array.isArray(current.children)) { - for (let i = current.children.length - 1; i >= 0; i -= 1) { - stack.push(current.children[i]); - } - } - } - return collected; - }, [mediaTree, playlistRoot]); - const playlistDuration = useMemo(() => { - if (!playlistItems.length) return 0; - return playlistItems.reduce((sum, item) => sum + (item.duration || 0), 0); - }, [playlistItems]); - const playbackState = status?.playback_state || {}; - const currentIndex = - typeof playbackState.file_index === 'number' ? playbackState.file_index : null; - const currentTrack = - currentIndex !== null && playlistItems[currentIndex] - ? playlistItems[currentIndex] - : null; - const currentTitle = - currentTrack?.title || - currentTrack?.name || - (playbackState.current_file - ? playbackState.current_file.split('/').pop() - : ''); - const currentArtist = currentTrack?.artist || ''; - const playbackPosition = - typeof playbackState.position === 'number' ? playbackState.position : 0; - const playbackDuration = - typeof playbackState.duration === 'number' ? playbackState.duration : 0; - const playbackProgress = - playbackDuration > 0 - ? Math.min(100, Math.round((playbackPosition / playbackDuration) * 100)) - : 0; - const isPlaying = playbackState.state === 'PLAYING'; - const volumeValue = - typeof playbackState.volume === 'number' ? playbackState.volume : null; - - function handleSeekCommit(percent) { - if (!playbackDuration) return; - const targetSeconds = Math.round((percent / 100) * playbackDuration); - handlePlayerCommand('seek', { position: targetSeconds }); - } - - function handleVolumeCommit(nextValue) { - handlePlayerCommand('set_volume', { volume: nextValue }); - } - - useEffect(() => { - if (isSeeking) return; - setSeekPercent(playbackProgress); - }, [playbackProgress, isSeeking]); - - useEffect(() => { - setIsSeeking(false); - setSeekPercent(playbackProgress); - }, [currentIndex, playbackProgress]); - - useEffect(() => { - setVolumeDraft(volumeValue ?? 50); - }, [volumeValue]); - - useEffect(() => { - if (!showVolume) return; - function handleOutside(event) { - if (!volumeRef.current) return; - if (volumeRef.current.contains(event.target)) return; - setShowVolume(false); - } - document.addEventListener('mousedown', handleOutside); - document.addEventListener('touchstart', handleOutside); - return () => { - document.removeEventListener('mousedown', handleOutside); - document.removeEventListener('touchstart', handleOutside); - }; - }, [showVolume]); - - function renderSection() { - if (activeSection === 'dashboard') { - return ( -
- -
- -

Live-Status

- {!selectedId &&

Waehle eine gepairte Box aus.

} - {selectedId && !status &&

Status wird geladen...

} - {status && status.error &&

{status.error}

} - {status && !status.error && ( -
{JSON.stringify(statusWithHardware, null, 2)}
- )} -
- -
-
-
-
-

{currentTitle || playlistTitle || 'Playlist'}

-

{currentArtist || playlistRoot || 'Kein Medium zugeordnet'}

-
- {playlistItems.length} Tracks - {formatClock(playlistDuration)} -
-
-
-
-
-
-
- {formatClock(playbackPosition)} - { - setSeekPercent(event.value ?? 0); - setIsSeeking(true); - }} - onSlideEnd={(event) => { - setIsSeeking(false); - handleSeekCommit(event.value ?? 0); - }} - /> - {formatClock(playbackDuration)} -
-
- setNfcUid(event.target.value)} - placeholder="UID_1" - className="p-inputtext-sm" - /> -
-
-
- # - Titel - Interpret - Dauer -
- {playlistItems.length === 0 && ( -
- - Keine Playlist gefunden. - - -
- )} - {playlistItems.map((track, index) => ( -
{ - event.preventDefault(); - handlePlayerCommand('jump_to_index', { index }); - }} - role="button" - tabIndex={0} - onKeyDown={(event) => { - if (event.key === 'Enter') { - handlePlayerCommand('jump_to_index', { index }); - } - }} - > - {index + 1} - {track.title || track.name} - {track.artist || '-'} - {formatClock(track.duration)} -
- ))} -
-

- Steuerung ist nur moeglich, wenn die Box gepairt ist. -

-
-
-
-
- ); - } - - if (activeSection === 'boxes') { - return ( -
- {unpaired.length > 0 && ( - -

Neue Boxen

- {unpaired.map((box) => ( -
-
- {box.alias || box.box_id} -
Zuletzt gesehen: {formatTime(box.last_seen)}
-
Firmware: {box.firmware_version}
-
ID: {box.box_id}
-
-
- ))} -
- )} - -

Gepairte Boxen

- {paired.length === 0 &&

Noch keine gepairten Boxen.

} - {paired.length > 0 && ( -
- {paired.map((box) => ( -
setSelectedId(box.box_id)} - role="button" - tabIndex={0} - onKeyDown={(event) => { - if (event.key === 'Enter') setSelectedId(box.box_id); - }} - > -
-
- {box.alias || box.box_id} - {getAvailability(box.last_seen).label} -
-
Zuletzt gesehen: {formatTime(box.last_seen)}
-
Capabilities: {parseCapabilities(box.capabilities_json)}
-
ID: {box.box_id}
-
-
-
- event.stopPropagation()} - onChange={(event) => { - event.stopPropagation(); - setBoxAliasDrafts((prev) => ({ - ...prev, - [box.box_id]: event.target.value, - })); - }} - /> -
-
-
- ))} -
- )} -
-
- ); - } - - if (activeSection === 'media') { - return ( -
- -
-

Medien Explorer

-
- {uploadInProgress && ( -
- {activeUploadLabel || 'Upload laeuft...'} -
-
-
-
- )} -

Ordnerverwaltung, Upload und Datei-Listen im Server-Medienordner.

- {mediaError &&

{mediaError}

} - {!mediaError && !mediaTree &&

Medienliste wird geladen...

} - {!mediaError && mediaTree && ( -
-
-
- setSidebarQuery(event.target.value)} - aria-label="Ordner suchen" - /> -
-
- {topLevelFolders.length === 0 && ( -
Keine Ordner gefunden.
- )} - {filteredTree && renderFolderTree(filteredTree, 0)} -
-
-
-
-
-
-
-
- Name - {showMeta ? ( - <> - Interpret - Titel - Laenge - Groesse - - ) : ( - <> - Typ - Groesse - - )} -
- {currentItems.map((item) => ( -
{ - if (event.button !== 0) return; - if (event.metaKey || event.ctrlKey || event.shiftKey) { - return; - } - dragSelectRef.current = true; - handleSelect(item, event); - }} - onMouseEnter={() => handleDragSelect(item)} - onClick={(event) => handleSelect(item, event)} - onDoubleClick={(event) => { - event.preventDefault(); - event.stopPropagation(); - lastDblClickRef.current = Date.now(); - handleOpen(item); - }} - role="button" - tabIndex={0} - onKeyDown={(event) => { - if (event.key === 'Enter') handleOpen(item); - }} - > - - - {item.type === 'folder' ? ( - - ) : ( - - )} - - {item.name} - - {showMeta ? ( - <> - {item.type === 'folder' ? '-' : item.artist || '-'} - {item.type === 'folder' ? '-' : item.title || '-'} - - {item.type === 'folder' ? '-' : formatDuration(item.duration)} - - {formatSize(item.size)} - - ) : ( - <> - {item.type === 'folder' ? 'Ordner' : 'Datei'} - {formatSize(item.size)} - - )} -
- ))} - {selectedPaths.length > 0 && ( -
- - {showMeta ? ( - <> - - - - - {selectedPaths.length} ausgewaehlt - - - ) : ( - <> - - - {selectedPaths.length} ausgewaehlt - - - )} -
- )} -
-
-
- - Medienordner: {formatSize(mediaBytes)} · Verfuegbar:{' '} - {formatSize(freeBytes)} - -
-
- )} -
-
- ); - } - - if (activeSection === 'tags') { - return ( -
- {(localBoxError || localBoxTags.length > 0) && ( - -
-

Tags nur auf dieser Box

-
- {localBoxError &&

{localBoxError}

} - {localBoxTags.map((tag) => ( -
-
- {tag.uid} -
- Dateien: {tag.file_count} · {formatSize(tag.total_size)} -
- {tag.media_exists && tag.files?.length > 0 && ( -
    - {tag.files.map((file) => ( -
  • {file}
  • - ))} -
- )} - {!tag.media_exists && ( -

Keine Dateien im Box-Ordner gefunden.

- )} -
-
-
-
- ))} -
- )} - - -

Tags (Datenbank)

- {tags.length === 0 &&

Keine Tags vorhanden.

} - {tags.length > 0 && ( -
- {tags.map((tag) => ( -
-
-
- - {tag.alias ? `${tag.alias} (${tag.uid})` : tag.uid} - -
Status: {tag.status}
-
Medium: {tag.media_path || '-'}
- {tag.label ?
Label: {tag.label}
: null} -
-
-
- - setTagAliasDrafts((prev) => ({ - ...prev, - [tag.uid]: event.target.value, - })) - } - /> -
-
- - setDbTagMedia((prev) => ({ - ...prev, - [tag.uid]: event.value, - })) - } - options={collectTopLevelFolders(mediaTree).map((folderPath) => ({ - label: folderPath, - value: folderPath, - }))} - placeholder="Medienordner waehlen" - filter - className="p-inputtext-sm" - /> -
-
-
-
- ))} -
- )} -
- - -

Tag-Matrix (Sperren)

- {paired.length === 0 && ( -

Keine gepairten Boxen vorhanden.

- )} - {tags.length === 0 &&

Keine Tags vorhanden.

} - {paired.length > 0 && tags.length > 0 && ( -
-
- Tag - {paired.map((box) => ( - - {box.alias || box.box_id} - - ))} -
- {tags.map((tag) => ( -
- {tag.alias || tag.uid} - {paired.map((box) => { - const blocked = (blockedByBox[box.box_id] || []).includes(tag.uid); - return ( - - handleToggleTagBlock(box.box_id, tag.uid, !blocked) - } - onKeyDown={(event) => { - if (event.key === 'Enter' || event.key === ' ') { - event.preventDefault(); - handleToggleTagBlock(box.box_id, tag.uid, !blocked); - } - }} - /> - ); - })} -
- ))} -
- )} -
-
- ); - } - - return ( -
- -

System

-

Backend: {import.meta.env.VITE_BACKEND_URL || 'http://127.0.0.1:5001'}

-
- Gepairte Boxen - {paired.length} -
-
- Tags - {tags.length} -
-
-
- ); - } - - const systemPanels = ( - <> - -
-

Live Status

- -
-

System Health und wichtige Events.

-
- API - -
-
- Speicher - -
-
- Letzter Sync - {status?.last_sync_at ? formatTime(status.last_sync_at) : '-'} -
-
- Hardware-UID - - {lastHardwareUid.uid === status?.last_nfc?.uid && lastHardwareUid.hardwareUid - ? lastHardwareUid.hardwareUid - : activeNfc?.hardwareUid || '-'} - -
-
- -

Speicherkapazitaet

-
-
- System - - {systemStorage - ? `${formatSize(systemStorage.used)} / ${formatSize(systemStorage.total)}` - : '-'} - -
- -
- Frei: {systemStorage ? formatSize(systemStorage.free) : '-'} -
-
- - {boxes.length === 0 &&

Keine Boxen verbunden.

} - {boxes.map((box) => { - const storage = boxStorage[box.box_id] || null; - const used = storage?.used ?? null; - const free = storage?.free ?? null; - const total = storage?.total ?? null; - const totalValue = - total !== null ? total : used !== null && free !== null ? used + free : null; - const usedValue = - used !== null ? used : totalValue !== null && free !== null ? totalValue - free : null; - const percent = - totalValue && usedValue !== null - ? Math.min(100, Math.round((usedValue / totalValue) * 100)) - : 0; - const scale = capacityScale(percent); - return ( -
-
- {box.alias || box.box_id} - - {totalValue !== null && usedValue !== null - ? `${formatSize(usedValue)} / ${formatSize(totalValue)}` - : '-'} - -
- -
- {free !== null ? `Frei: ${formatSize(free)}` : 'Keine Kapazitaet gemeldet'} -
-
- ); - })} -
- - ); - - return ( -
-
-
-
K
-
- Klangkiste - PrimeReact GUI -
-
- {selectedId && ( -
- - {activeBoxLabel || selectedId} -
- )} -
-
-
- - - -
- - -
- - {error &&
{error}
} - {activeNfc && dismissedNfcKey !== getNfcKey(activeNfc) && ( -
- -

- {activeNfc.uid && - tags.find((tag) => tag.uid === activeNfc.uid && !tag.media_path) - ? 'Leerer Tag erkannt' - : 'Neuer Tag erkannt'} -

-

- {activeNfc.uid ? ( - <> - UID erkannt: {activeNfc.uid} - - ) : ( - <>keine UID erkannt - )} -

- {activeNfc.uid && - lastHardwareUid.uid === activeNfc.uid && - lastHardwareUid.hardwareUid && ( -

- Hardware-UID erkannt: {lastHardwareUid.hardwareUid} -

- )} - {!activeNfc.uid && activeNfc.hardwareUid && ( -

- Hardware-UID erkannt: {activeNfc.hardwareUid} -

- )} - {activeNfc.uid && tags.find((tag) => tag.uid === activeNfc.uid) && ( -

- Dieser Tag ist bekannt, aber noch nicht zugewiesen. -

- )} -
- - -
- { - if (event.index <= tagStepMax) { - setTagStep(event.index); - return; - } - tagStepperRef.current?.setActiveStep(tagStep); - }} - > - -
-
- setScanTagUid(event.target.value)} - readOnly={tagUidMode === 'keep'} - placeholder="Tag-ID (10 Zeichen)" - className="p-inputtext-sm" - /> -
- setScanTagLabel(event.target.value)} - placeholder="Label (optional)" - className="p-inputtext-sm is-hidden" - /> -
-
- -
-
- setScanMediaPath(event.value)} - options={collectTopLevelFolders(mediaTree).map((folderPath) => ({ - label: folderPath, - value: folderPath, - }))} - placeholder="Medienordner waehlen" - filter - className="p-inputtext-sm" - disabled={!tagStepOneDone} - /> - -
-
-
- -
-
-

Zusammenfassung

-
- System-UID - {scanTagUid || '-'} - UID-Modus - {tagUidMode === 'keep' ? 'Beibehalten' : 'Neu vergeben'} - Label - {scanTagLabel?.trim() || '-'} - Medienordner - {scanMediaPath || 'Spaeter zuordnen'} - Hardware-UID - - {lastHardwareUid.uid === activeNfc?.uid && lastHardwareUid.hardwareUid - ? lastHardwareUid.hardwareUid - : activeNfc?.hardwareUid || '-'} - -
-
-
-
-
-
-
- {tags.some((tag) => tag.status === 'IMPORTED') && ( -
- -
- )} -
-
- )} - {renderSection()} -
- - -
- - - - {activeModal === 'new-folder' && ( -
{ - setActiveModal(''); - setUploadAfterCreate(false); - setPendingUploadFiles([]); - setUploadSize(0); - setUploadNameError(''); - setTagDeleteTarget(''); - }} - > -
event.stopPropagation()} - ref={modalRef} - > -

{uploadAfterCreate ? 'Upload vorbereiten' : 'Neuen Ordner anlegen'}

- { - setNewFolderName(event.target.value); - if (uploadNameError) setUploadNameError(''); - }} - placeholder={ - uploadAfterCreate && !currentPath - ? 'Ordnername' - : uploadAfterCreate - ? 'Optionaler Ordnername (leer = aktueller Ordner)' - : 'Ordnername' - } - className="p-inputtext-sm" - /> - {uploadAfterCreate && uploadNameError && ( -

{uploadNameError}

- )} - {uploadAfterCreate && ( - <> - { - const audioFiles = (event.files || []).filter((file) => - file.type.startsWith('audio/') - ); - setPendingUploadFiles(audioFiles); - const nextSize = audioFiles.reduce((sum, file) => sum + file.size, 0); - setUploadSize(nextSize); - setActiveUploadLabel( - audioFiles.length - ? `Upload: ${audioFiles.length} Datei(en)` - : '' - ); - }} - uploadHandler={(event) => { - const audioFiles = (event.files || []).filter((file) => - file.type.startsWith('audio/') - ); - setPendingUploadFiles(audioFiles); - const nextSize = audioFiles.reduce((sum, file) => sum + file.size, 0); - setUploadSize(nextSize); - setActiveUploadLabel( - audioFiles.length - ? `Upload: ${audioFiles.length} Datei(en)` - : '' - ); - handleCreateFolder(audioFiles); - }} - onClear={() => { - setPendingUploadFiles([]); - setUploadSize(0); - }} - emptyTemplate={ -
- - - Drag and Drop Audio Here - -
- Gesamt: {formatSize(uploadSize)} -
-
- } - /> - {pendingUploadFiles.length > 0 && ( -
- Gesamt: {formatSize(uploadSize)} - - {pendingUploadFiles.length} Datei(en) - -
- )} - - )} -
-
-
-
- )} - - {activeModal === 'rename' && ( -
{ - setActiveModal(''); - setRenameName(''); - }} - > -
event.stopPropagation()} - ref={modalRef} - > -

Umbenennen

- setRenameName(event.target.value)} - placeholder="Neuer Name" - className="p-inputtext-sm" - /> -
-
-
-
- )} - - {activeModal === 'delete' && ( -
{ - setActiveModal(''); - }} - > -
event.stopPropagation()} - ref={modalRef} - > -

Eintraege loeschen

-

{selectedPaths.length} Eintraege werden geloescht.

-
-
-
-
- )} - - {activeModal === 'move' && ( -
{ - setActiveModal(''); - setMoveTarget(''); - }} - > -
event.stopPropagation()} - ref={modalRef} - > -

Verschieben

- setMoveTarget(event.value)} - options={[ - { label: 'media/', value: '__root__' }, - ...collectFolderPaths(mediaTree).map((folder) => ({ - label: folder, - value: folder, - })), - ]} - placeholder="Zielordner waehlen" - filter - className="p-inputtext-sm" - /> -
-
-
-
- )} - - {activeModal === 'tag-delete' && ( -
{ - setActiveModal(''); - setTagDeleteTarget(''); - }} - > -
event.stopPropagation()} - ref={modalRef} - > -

Tag entfernen

-

- Soll der Tag komplett geloescht werden oder nur die Medienzuweisung? -

-
-
-
-
- )} - - {boxDeleteTarget && ( -
setBoxDeleteTarget('')} - > -
event.stopPropagation()} - ref={modalRef} - > -

Box entfernen

-

Soll die Box wirklich entkoppelt werden?

-
-
-
-
- )} - - {boxDetailsOpen && ( -
{ - setBoxDetailsOpen(false); - setBoxDetailsError(''); - setBoxDetailsData(null); - }} - role="presentation" - > -
event.stopPropagation()}> -
-

Box Details

-
-
- {boxDetailsLoading &&

Details werden geladen...

} - {boxDetailsError &&

{boxDetailsError}

} - {!boxDetailsLoading && !boxDetailsError && boxDetailsData && ( -
-
- Box -
ID: {boxDetailsData.box_id}
-
- Firmware: {boxDetailsData.firmware_version || '0.0.0'} -
-
- -
- Settings -
- Max Volume: {boxDetailsData.settings?.max_volume ?? '-'} -
-
- Default Volume: {boxDetailsData.settings?.default_volume ?? '-'} -
-
- -
- Gespeicherte WLANs - {Array.isArray(boxDetailsData.wifi_profiles) && - boxDetailsData.wifi_profiles.length > 0 ? ( - boxDetailsData.wifi_profiles.map((profile) => ( -
- {profile.ssid} (prio {profile.priority ?? 0}) -
- )) - ) : ( -
Keine Profile.
- )} -
- -
- Tags - {boxDetailsData.tags && Object.keys(boxDetailsData.tags).length > 0 ? ( - Object.entries(boxDetailsData.tags).map(([uid, tag]) => ( -
- {uid}: {tag?.path || '-'} -
- )) - ) : ( -
Keine Tags.
- )} -
- -
- Server -
- Active URL: {boxDetailsData.active_server_url || '-'} -
-
- Last SSID: {boxDetailsData.last_ssid || '-'} -
- {boxDetailsData.server_bindings && - Object.keys(boxDetailsData.server_bindings).length > 0 ? ( - Object.entries(boxDetailsData.server_bindings).map( - ([ssid, binding]) => ( -
- {ssid}: {binding?.server_url || '-'} -
- ) - ) - ) : ( -
Keine Bindings.
- )} -
-
- )} -
-
-
- )} - - {activeModal === 'import-tag' && ( -
-
-
-

Tag vom Box-Speicher uebertragen

-
-
- - setImportTargetFolder(event.target.value)} - placeholder="z. B. grimm_volume_3" - className="p-inputtext-sm" - /> - {uploadInProgress && ( -
-
-
- )} -
-
-
-
-
- )} - - {showSessionSheet && ( -
setShowSessionSheet(false)} - > -
event.stopPropagation()} - > -
- ToolHub -
- {drawerTab === 'boxes' && ( - <> - {paired.length === 0 && ( -

Keine gepairten Boxen.

- )} -
- {paired.map((box) => { - const isActive = selectedId === box.box_id; - const label = (box.alias || box.box_id || '').slice(0, 2).toUpperCase(); - const availability = getAvailability(box.last_seen); - return ( - - ); - })} -
- - )} - {drawerTab === 'system' && ( -
{systemPanels}
- )} -
- - -
-
-
- )} -
- ); -} diff --git a/frontend/src/api.js b/frontend/src/api.js deleted file mode 100644 index 38bf249..0000000 --- a/frontend/src/api.js +++ /dev/null @@ -1,249 +0,0 @@ -const defaultHost = - typeof window !== 'undefined' && window.location ? window.location.hostname : '127.0.0.1'; -const API_BASE = '/api'; - -async function readJson(response) { - const text = await response.text(); - try { - return { ok: response.ok, status: response.status, data: JSON.parse(text) }; - } catch (error) { - return { ok: response.ok, status: response.status, data: { detail: text } }; - } -} - -export async function getBoxes() { - const response = await fetch(`${API_BASE}/boxes`); - return readJson(response); -} - -export async function pairBox(boxId) { - const response = await fetch(`${API_BASE}/boxes/pair`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ box_id: boxId }), - }); - return readJson(response); -} - -export async function getStatus(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/status`); - return readJson(response); -} - -export async function getBoxDetails(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/details`); - return readJson(response); -} - -export async function getMediaTree() { - const response = await fetch(`${API_BASE}/media-tree`); - return readJson(response); -} - -export async function createMediaFolder(parentPath, name) { - const response = await fetch(`${API_BASE}/media/folder`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ parent_path: parentPath, name }), - }); - return readJson(response); -} - -export async function renameMedia(path, name) { - const response = await fetch(`${API_BASE}/media/rename`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path, name }), - }); - return readJson(response); -} - -export async function moveMedia(path, targetParent) { - const response = await fetch(`${API_BASE}/media/move`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path, target_parent: targetParent }), - }); - return readJson(response); -} - -export async function deleteMedia(path) { - const response = await fetch(`${API_BASE}/media/delete`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ path }), - }); - return readJson(response); -} - -export function uploadMedia(targetPath, files, onProgress) { - const formData = new FormData(); - formData.append('target_path', targetPath); - files.forEach((file) => { - formData.append('files', file); - }); - - return new Promise((resolve) => { - const request = new XMLHttpRequest(); - request.open('POST', `${API_BASE}/media/upload`); - request.upload.onprogress = (event) => { - if (!event.lengthComputable || !onProgress) return; - const percent = Math.round((event.loaded / event.total) * 100); - onProgress(percent); - }; - request.onload = () => { - try { - const data = JSON.parse(request.responseText || '{}'); - resolve({ ok: request.status >= 200 && request.status < 300, status: request.status, data }); - } catch (error) { - resolve({ - ok: request.status >= 200 && request.status < 300, - status: request.status, - data: { detail: request.responseText || '' }, - }); - } - }; - request.onerror = () => { - resolve({ ok: false, status: 0, data: { detail: 'network error' } }); - }; - request.send(formData); - }); -} - -export async function getTags() { - const response = await fetch(`${API_BASE}/tags`); - return readJson(response); -} - -export async function generateTag(label) { - const response = await fetch(`${API_BASE}/tags/generate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ label }), - }); - return readJson(response); -} - -export async function claimTag(uid, label) { - const response = await fetch(`${API_BASE}/tags/claim`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ uid, label }), - }); - return readJson(response); -} - -export async function markTagWritten(uid) { - const response = await fetch(`${API_BASE}/tags/${uid}/write`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }); - return readJson(response); -} - -export async function getBoxTags(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/tags`); - return readJson(response); -} - -export async function getBoxLocalTags(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/local-tags`); - return readJson(response); -} - -export async function getBoxStorage(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/storage`); - return readJson(response); -} - -export async function getTagBlocks(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/tag-blocks`); - return readJson(response); -} - -export async function setTagBlock(boxId, uid, blocked) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/tag-blocks`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ uid, blocked }), - }); - return readJson(response); -} - -export async function setBoxAlias(boxId, alias) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/alias`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ alias }), - }); - return readJson(response); -} - -export async function setTagAlias(uid, alias) { - const response = await fetch(`${API_BASE}/tags/${uid}/alias`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ alias }), - }); - return readJson(response); -} - -export async function assignTag(uid, boxId) { - const response = await fetch(`${API_BASE}/tags/${uid}/assign`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ box_id: boxId }), - }); - return readJson(response); -} - -export async function unassignTag(uid, boxId) { - const response = await fetch(`${API_BASE}/tags/${uid}/unassign`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ box_id: boxId }), - }); - return readJson(response); -} - -export async function deleteTag(uid) { - const response = await fetch(`${API_BASE}/tags/${uid}`, { - method: 'DELETE', - }); - return readJson(response); -} - -export async function setTagMedia(uid, mediaPath) { - const response = await fetch(`${API_BASE}/tags/${uid}/media`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ media_path: mediaPath }), - }); - return readJson(response); -} - -export async function pullTagFromBox(boxId, uid, targetFolder) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/pull-tag`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ uid, target_folder: targetFolder }), - }); - return readJson(response); -} - -export async function sendCommand(boxId, command, payload = {}) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/command`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ command, payload }), - }); - return readJson(response); -} - -export async function unpairBox(boxId) { - const response = await fetch(`${API_BASE}/boxes/${boxId}/unpair`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }); - return readJson(response); -} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx deleted file mode 100644 index 7057e78..0000000 --- a/frontend/src/main.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { PrimeReactProvider } from 'primereact/api'; -import 'primereact/resources/themes/lara-dark-teal/theme.css'; -import 'primereact/resources/primereact.min.css'; -import 'primeicons/primeicons.css'; -import App from './App.jsx'; -import './styles.css'; - -ReactDOM.createRoot(document.getElementById('root')).render( - - - - - -); diff --git a/frontend/src/styles.css b/frontend/src/styles.css deleted file mode 100644 index c70b8d8..0000000 --- a/frontend/src/styles.css +++ /dev/null @@ -1,1933 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap'); - -:root { - color-scheme: dark; - font-family: 'Space Grotesk', 'IBM Plex Sans', sans-serif; - background-color: #0b0f14; - color: #e6edf6; -} - -* { - box-sizing: border-box; -} - -body { - margin: 0; - min-height: 100vh; - background: - radial-gradient(circle at 20% 10%, rgba(50, 112, 124, 0.35), transparent 45%), - radial-gradient(circle at 80% 0%, rgba(24, 45, 67, 0.6), transparent 40%), - linear-gradient(130deg, #0b0f14 0%, #111826 45%, #0d1220 100%); -} - -#root { - min-height: 100vh; -} - -.app-shell { - min-height: 100vh; - padding-bottom: 80px; -} - -.topbar { - position: sticky; - top: 0; - z-index: 10; - display: flex; - gap: 16px; - align-items: center; - padding: 18px 24px; - background: rgba(11, 15, 20, 0.92); - backdrop-filter: blur(14px); - border-bottom: 1px solid rgba(255, 255, 255, 0.08); -} - -.brand { - display: flex; - align-items: center; - gap: 12px; - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 12px; - color: rgba(230, 237, 246, 0.7); -} - -.brand strong { - display: block; - font-size: 14px; - color: #e6edf6; -} - -.brand span { - display: block; - margin-top: 2px; -} - -.brand-mark { - width: 36px; - height: 36px; - border-radius: 12px; - display: grid; - place-items: center; - background: linear-gradient(130deg, #19a4a1, #0b6f84); - color: #061018; - font-weight: 700; -} - - -.topbar-actions { - display: flex; - align-items: center; - gap: 12px; - margin-left: auto; -} - -.header-box-indicator { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 10px; - border-radius: 999px; - background: rgba(25, 164, 161, 0.18); - border: 1px solid rgba(25, 164, 161, 0.4); - color: rgba(230, 237, 246, 0.9); - font-size: 13px; - margin-left: auto; -} - -.user-avatar { - background: #1c2735; - color: #e6edf6; -} - -.layout { - display: grid; - grid-template-columns: 240px minmax(0, 1fr) 280px; - gap: 24px; - padding: 24px; -} - -.sidebar, -.right-panel { - position: sticky; - top: 96px; - align-self: start; - height: fit-content; -} - -.sidebar { - padding: 16px; - border-radius: 20px; - background: rgba(10, 17, 26, 0.85); - border: 1px solid rgba(255, 255, 255, 0.06); -} - -.sidebar-title { - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 11px; - color: rgba(230, 237, 246, 0.55); - margin: 0 0 12px; -} - -.sidebar-section { - margin-bottom: 16px; -} - -.nav-list { - display: flex; - flex-direction: column; - gap: 6px; -} - -.nav-button { - justify-content: flex-start; - color: rgba(230, 237, 246, 0.7); -} - -.nav-button.active { - background: rgba(25, 164, 161, 0.15); - color: #e6edf6; - border-radius: 12px; -} - -.button-stack { - display: flex; - flex-direction: column; - gap: 8px; -} - -.session-card { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 12px; - align-items: center; - padding: 12px; - border-radius: 14px; - background: rgba(10, 17, 26, 0.7); - border: 1px solid rgba(255, 255, 255, 0.08); - text-align: left; -} - -.sheet-card .session-card { - width: 100%; - height: 72px; -} - -.session-card strong { - color: #2dd4bf; -} - -.session-card .meta { - display: block; - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -.session-list { - display: grid; - gap: 8px; -} - -.sheet-card .session-list { - grid-template-columns: repeat(3, minmax(0, 1fr)); - align-items: stretch; -} - -.session-card.active { - border-color: rgba(25, 164, 161, 0.6); - box-shadow: 0 0 0 2px rgba(25, 164, 161, 0.15); -} - -.session-card:focus-visible { - outline: 2px solid rgba(25, 164, 161, 0.6); - outline-offset: 2px; -} - -.content { - display: flex; - flex-direction: column; - gap: 20px; -} - -.section-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; -} - -.section-header h1 { - margin: 0 0 4px; - font-size: 28px; -} - -.section-header p { - margin: 0; - color: rgba(230, 237, 246, 0.6); -} - -.section-actions { - display: flex; - align-items: center; - gap: 10px; -} - -.section-stack { - display: flex; - flex-direction: column; - gap: 20px; -} - -.stat-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 16px; -} - -.stat-card { - background: rgba(14, 24, 35, 0.9); - border: 1px solid rgba(255, 255, 255, 0.04); -} - -.stat-label { - margin: 0; - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -.stat-value { - font-size: 24px; - font-weight: 600; - margin: 8px 0; -} - -.stat-helper { - font-size: 12px; - color: rgba(25, 164, 161, 0.9); -} - -.split-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 16px; -} - -.wide-card, -.content-card, -.panel-card { - background: rgba(14, 24, 35, 0.92); - border: 1px solid rgba(255, 255, 255, 0.04); -} - -.card-title-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.progress-row { - display: grid; - grid-template-columns: 120px 1fr; - gap: 12px; - align-items: center; - margin-top: 12px; - font-size: 12px; - color: rgba(230, 237, 246, 0.7); -} - -.activity-list { - display: flex; - flex-direction: column; - gap: 12px; - margin-top: 12px; -} - -.activity-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 12px; - border-radius: 12px; - background: rgba(255, 255, 255, 0.03); - font-size: 13px; -} - -.activity-item span { - display: block; - font-size: 11px; - color: rgba(230, 237, 246, 0.55); -} - -.content-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 16px; -} - -.content-card h3 { - margin-top: 0; -} - -.placeholder-block { - height: 120px; - border-radius: 14px; - background: linear-gradient(130deg, rgba(25, 164, 161, 0.2), rgba(23, 45, 64, 0.9)); -} - -.placeholder-table { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 12px; -} - -.table-row { - display: grid; - grid-template-columns: 1fr auto auto; - gap: 12px; - align-items: center; - padding: 8px 12px; - border-radius: 10px; - background: rgba(255, 255, 255, 0.03); - font-size: 13px; -} - -.table-row.header { - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.08em; - color: rgba(230, 237, 246, 0.5); -} - -.placeholder-tree { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 12px; - font-size: 13px; - color: rgba(230, 237, 246, 0.75); -} - -.placeholder-tree .nested { - margin-left: 16px; -} - -.queue-item { - display: grid; - gap: 8px; - margin-top: 12px; - padding: 10px 12px; - border-radius: 12px; - background: rgba(255, 255, 255, 0.03); - font-size: 13px; -} - -.queue-item span { - display: block; - font-size: 11px; - color: rgba(230, 237, 246, 0.5); -} - -.tab-shell .p-tabview-nav { - background: transparent; - border-color: rgba(255, 255, 255, 0.08); -} - -.tag-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 16px; - margin-top: 12px; -} - -.tag-card { - background: rgba(255, 255, 255, 0.03); -} - -.tag-title { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.settings-row { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 12px; -} - -.token-chip { - display: inline-flex; - align-items: center; - padding: 6px 12px; - border-radius: 999px; - margin-right: 8px; - margin-top: 8px; - background: rgba(25, 164, 161, 0.18); - font-size: 12px; -} - -.right-panel { - display: flex; - flex-direction: column; - gap: 16px; -} - -.panel-card h3 { - margin-top: 0; -} - -.status-row { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 8px; - font-size: 13px; -} - -.summary { - display: flex; - flex-direction: column; - gap: 6px; - flex: 1 1 100%; -} - -.summary-title { - font-size: 14px; - font-weight: 600; - color: rgba(230, 237, 246, 0.95); - margin: 0; -} - -.summary-grid { - display: grid; - grid-template-columns: 140px 1fr; - column-gap: 12px; - row-gap: 6px; - font-size: 13px; -} - -.summary-grid span { - color: var(--text-color-secondary); - font-size: 11px; -} - -.step-actions { - width: 100%; - display: flex; - justify-content: flex-end; -} - -.step-row { - width: 100%; - display: flex; - align-items: center; - gap: 8px; -} - -.step-row .p-inputtext, -.step-row .p-dropdown { - flex: 1 1 220px; - min-width: 0; -} - -.step-row-spacer { - flex: 1 1 auto; -} - -.step-messages { - display: grid; - gap: 6px; - margin-top: 6px; -} - -.p-messages { - margin: 5px 0; -} - -.p-message { - min-height: 30px; - max-height: 30px; - padding: 4px 8px; - font-size: 12px; - overflow: hidden; - display: flex; - align-items: center; -} - -.p-message-summary { - font-size: 12px; - line-height: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: inline-flex; - align-items: center; -} - -.p-message-detail { - font-size: 11px; - line-height: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: inline-flex; - align-items: center; -} - -.p-message-wrapper { - gap: 6px; - align-items: center; - flex-wrap: nowrap; - overflow: hidden; - min-width: 0; - flex: 1 1 auto; -} - -.p-message-text { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; -} - -.is-hidden { - display: none; -} - -.panel-pill { - padding: 10px 12px; - border-radius: 12px; - margin-top: 8px; - background: rgba(255, 255, 255, 0.04); - font-size: 13px; -} - -.capacity-row { - display: grid; - gap: 8px; - margin-top: 10px; -} - -.capacity-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - font-size: 13px; - color: rgba(230, 237, 246, 0.9); -} - -.capacity-meta { - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -.capacity-bar .p-metergroup-meter { - background: linear-gradient( - 90deg, - #39d98a 0%, - #e8d45a 45%, - #f2a94f 65%, - #b68bff 100% - ); - background-size: calc(var(--capacity-scale, 1) * 100%) 100%; - background-position: left center; -} - -.capacity-bar .p-metergroup-labels, -.capacity-bar .p-metergroup-label, -.capacity-bar .p-metergroup-label-type { - display: none; -} - -.bottom-nav { - position: fixed; - bottom: 0; - left: 0; - right: 0; - display: none; - gap: 6px; - padding: 10px 12px 16px; - background: rgba(11, 15, 20, 0.95); - border-top: 1px solid rgba(255, 255, 255, 0.08); - z-index: 20; - grid-template-columns: repeat(6, minmax(0, 1fr)); -} - -.bottom-nav .p-button { - width: 100%; - min-width: 0; - display: grid; - place-items: center; /* bleibt */ - gap: 4px; - background: transparent; - border: none; - color: rgba(230, 237, 246, 0.6); - font-size: 11px; -} - -.bottom-nav .p-button > span { - display: flex; - align-items: center; - justify-content: center; -} - - - -.bottom-nav .p-button.active { - color: #19a4a1; -} - -@media (max-width: 550px) { - /* ❌ nur Text ausblenden */ - .bottom-nav .p-button > span:not(.nav-icon) { - display: none; - } - - /* ✅ Icon bleibt exakt zentriert */ - .bottom-nav .nav-icon { - display: flex; - align-items: center; - justify-content: center; - } -} - - - -@media (max-width: 1100px) { - .layout { - grid-template-columns: 220px minmax(0, 1fr); - } - - .right-panel { - display: none; - } -} - -@media (max-width: 1300px) { - .legacy .boxes-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 980px) { - .legacy .boxes-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 1200px) { - .split-grid { - grid-template-columns: 1fr; - } - - .legacy .boxes-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 900px) { - .topbar-actions { - display: none; - } - - .legacy .boxes-grid { - grid-template-columns: 1fr; - } - - .legacy .boxes-grid .card { - flex-direction: column; - align-items: stretch; - } - - .legacy .boxes-grid .box-actions-row { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; - width: 100%; - } - - .legacy .boxes-grid .box-actions-row > * { - width: 100%; - } - - .sheet-card .session-list { - grid-template-columns: 1fr; - } -} - -@media (max-width: 760px) { - .layout { - grid-template-columns: minmax(0, 1fr); - padding: 16px; - } - - .sidebar { - display: none; - } - - - .section-header { - flex-direction: column; - align-items: flex-start; - } - - .bottom-nav { - display: grid; - grid-template-columns: repeat(6, 1fr); - } -} - -@media (max-width: 720px) { - .sheet-card .session-list { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 520px) { - .sheet-card .session-list { - grid-template-columns: 1fr; - } -} - -.sheet-backdrop { - position: fixed; - inset: 0; - background: rgba(5, 8, 12, 0.6); - display: grid; - align-items: end; - z-index: 50; -} - -.sheet-card { - background: #0f1622; - border-radius: 20px 20px 0 0; - padding: 20px 18px 28px; - border: 1px solid rgba(255, 255, 255, 0.08); - max-height: 80vh; - overflow-y: auto; -} - -.drawer-system { - display: grid; - gap: 12px; -} - -.drawer-tabs { - margin: 12px -18px -28px; - padding: 10px 12px 16px; - border-top: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(11, 15, 20, 0.95); - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 6px; -} - -.drawer-tab { - background: transparent; - border: none; - border-radius: 12px; - color: rgba(230, 237, 246, 0.6); - padding: 8px 10px; - font-size: 11px; - display: grid; - place-items: center; - gap: 4px; - width: 100%; - min-width: 0; - cursor: pointer; -} - -.drawer-tab .nav-icon { - display: inline-flex; - align-items: center; - justify-content: center; - line-height: 1; -} - -.drawer-tab.active { - color: #19a4a1; -} - -.drawer-system { - display: grid; - gap: 12px; -} - -.sheet-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; -} - - -.error { - background: rgba(80, 24, 32, 0.6); - color: #f4a7a7; - border: 1px solid rgba(242, 201, 201, 0.4); - border-radius: 10px; - padding: 10px 12px; - margin-bottom: 16px; -} - -.muted { - color: rgba(230, 237, 246, 0.6); -} - -.status { - background: #0f1520; - color: #e6edf6; - padding: 12px; - border-radius: 12px; - font-size: 12px; - overflow: auto; - border: 1px solid rgba(255, 255, 255, 0.08); - max-width: 100%; - white-space: pre-wrap; - word-break: break-word; -} - -.legacy .card { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 12px; - padding: 12px; - margin-top: 12px; - cursor: pointer; - background: rgba(10, 17, 26, 0.7); -} - -.legacy .card.selected { - border-color: rgba(25, 164, 161, 0.6); - box-shadow: 0 0 0 2px rgba(25, 164, 161, 0.15); -} - -.legacy .card.compact { - padding: 10px; - margin-top: 10px; -} - -.legacy .tags-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; -} - -@media (max-width: 1800px) { - .legacy .tags-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 1500px) { - .legacy .tags-grid { - grid-template-columns: 1fr; - } -} - -.legacy .boxes-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; -} - -.legacy .boxes-grid .card { - margin-top: 0; -} - -@media (max-width: 1300px) { - .legacy .tags-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 1200px) { - .legacy .tags-grid { - grid-template-columns: 1fr; - } - - .legacy .boxes-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 900px) { - .legacy .tags-grid { - grid-template-columns: 1fr; - } - - .legacy .boxes-grid { - grid-template-columns: 1fr; - } -} - -.legacy .stack { - display: flex; - flex-direction: column; - gap: 8px; - align-items: flex-end; -} - -.legacy .stack-inline { - flex-direction: row; - align-items: center; - justify-content: flex-start; - flex-wrap: nowrap; - gap: 6px; -} - -.legacy .tag-actions { - flex-direction: column; - align-items: flex-end; - gap: 6px; -} - -.legacy .tag-actions-row { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: nowrap; - justify-content: flex-end; -} - -.legacy .box-actions { - flex-direction: column; - align-items: flex-end; - gap: 6px; -} - -.legacy .box-actions-row { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: nowrap; - justify-content: flex-end; -} - -.legacy .box-actions .alias-input { - width: 100%; - max-width: none; -} - -@media (max-width: 1800px) { - .legacy .boxes-grid .card { - flex-direction: column; - align-items: stretch; - } - - .legacy .boxes-grid .box-actions-row { - display: grid; - grid-template-columns: 2fr 1fr 1fr; - gap: 8px; - width: 100%; - } - - .legacy .boxes-grid .box-actions-row > * { - width: 100%; - } - - .legacy .tags-grid .card { - flex-direction: column; - align-items: stretch; - } - - .legacy .tags-grid .tag-actions-row { - display: grid; - grid-template-columns: 2fr 1fr 1fr; - gap: 8px; - width: 100%; - } - - .legacy .tags-grid .tag-actions-row > * { - width: 100%; - } -} - -.legacy .tag-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - width: 100%; - flex-wrap: nowrap; -} - -.legacy .tags-grid .tag-row { - display: grid; - grid-template-columns: 1fr 2fr; - align-items: center; - gap: 12px; -} - -.legacy .tags-grid .tag-actions { - width: 100%; - display: flex; - flex-direction: column; - align-items: stretch; - gap: 8px; -} - -.legacy .tags-grid .tag-actions-row { - width: 100%; - display: grid; - grid-template-columns: 2fr 1fr; - align-items: center; - gap: 8px; -} - -.legacy .tags-grid .tag-actions-row .alias-input, -.legacy .tags-grid .tag-actions-row .p-dropdown { - width: 100%; - min-width: 0; -} - -.legacy .tags-grid .tag-actions-row .icon-button { - width: 100%; -} - -.upload-item-meta { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - width: 100%; -} - -.upload-item-name { - text-align: left; - justify-self: start; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 100%; -} - -.upload-item-size { - text-align: right; - white-space: nowrap; -} - -.upload-item-footer { - width: 100%; - display: flex; - justify-content: flex-end; - gap: 12px; - padding-top: 8px; - color: rgba(230, 237, 246, 0.7); - font-size: 12px; -} - -.upload-item-total { - text-align: right; -} - -.upload-item-count { - text-align: right; -} - -@media (max-width: 600px) { - .legacy .tags-grid .tag-row { - grid-template-columns: 1fr; - } - - .legacy .boxes-grid .card { - flex-direction: column; - align-items: stretch; - } - - .legacy .tags-grid .tag-actions-row { - grid-template-columns: 2fr 1fr; - } - - .legacy .boxes-grid .box-actions-row { - grid-template-columns: 2fr 1fr 1fr; - } -} - -.legacy .box-tag-row { - display: flex; - justify-content: space-between; - gap: 16px; - padding: 12px; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 12px; - margin-top: 12px; - background: rgba(10, 17, 26, 0.7); -} - -.legacy .box-tag-actions { - display: flex; - align-items: flex-start; -} - -.legacy .file-list { - margin: 8px 0 0; - padding-left: 18px; - color: rgba(230, 237, 246, 0.7); - font-size: 13px; -} - -.legacy .file-list li { - margin-bottom: 4px; -} - -.legacy .tag-info { - display: flex; - flex-direction: column; - gap: 4px; -} - -.legacy .box-title { - display: flex; - align-items: center; - gap: 8px; -} - -.legacy .alias-input:not(.p-inputtext), -.legacy input:not(.p-inputtext), -.modal-card input:not(.p-inputtext) { - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 8px; - padding: 6px 8px; - font-size: 12px; - min-width: 140px; - background: rgba(255, 255, 255, 0.04); - color: #e6edf6; -} - -.legacy select, -.modal-card select { - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.12); - padding: 8px 10px; - min-width: 180px; - background: rgba(255, 255, 255, 0.04); - color: #e6edf6; -} - -.legacy .tag-matrix { - display: grid; - gap: 8px; - overflow-x: auto; -} - -.legacy .matrix-row { - display: grid; - grid-auto-flow: column; - grid-auto-columns: minmax(120px, 1fr); - gap: 8px; - align-items: center; -} - -.legacy .matrix-row.header { - font-weight: 600; - color: rgba(230, 237, 246, 0.8); -} - -.legacy .matrix-cell { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 10px; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; - background: rgba(255, 255, 255, 0.03); - font-size: 12px; -} - -.legacy .matrix-cell.toggle { - justify-content: center; - cursor: pointer; - width: 100%; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(255, 255, 255, 0.02); - transition: background 0.2s ease, border-color 0.2s ease; -} - -.legacy .matrix-cell.toggle.is-blocked { - border-color: rgba(239, 68, 68, 0.45); - background: rgba(239, 68, 68, 0.08); -} - -.legacy .matrix-cell.toggle.p-message { - width: 100%; - justify-content: center; - background: transparent; - border: 0; - padding: 0; -} - -.legacy .matrix-cell.toggle.p-message .p-message-text { - justify-content: center; - width: 100%; -} - -.legacy .matrix-cell.label { - font-weight: 600; - background: rgba(10, 17, 26, 0.8); -} - -.legacy .meta { - font-size: 12px; - color: rgba(230, 237, 246, 0.55); -} - -.legacy .pill { - background: rgba(25, 164, 161, 0.2); - color: #8fe3df; - padding: 4px 10px; - border-radius: 999px; - font-size: 12px; -} - -.legacy .controls { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 12px; -} - -.legacy .controls-inline { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; -} - -.legacy button:not(.p-button), -.modal-card button:not(.p-button) { - border: 1px solid rgba(255, 255, 255, 0.1); - background: #1b2736; - color: #e6edf6; - padding: 8px 12px; - border-radius: 8px; - cursor: pointer; - font-size: 14px; -} - -.legacy button:not(.p-button):hover, -.modal-card button:not(.p-button):hover { - background: #233144; -} - -.legacy .button-ghost, -.modal-card .button-ghost { - background: transparent; - color: rgba(230, 237, 246, 0.9); - border: 1px solid rgba(255, 255, 255, 0.2); -} - -.legacy .button-ghost:hover, -.modal-card .button-ghost:hover { - background: rgba(255, 255, 255, 0.06); -} - -.icon-button { - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(10, 17, 26, 0.7); - color: #e6edf6; - padding: 6px 8px; - border-radius: 8px; - cursor: pointer; - font-size: 14px; - display: inline-flex; - align-items: center; - justify-content: center; - line-height: 1; -} - -.icon-button:hover { - background: rgba(255, 255, 255, 0.08); -} - -.icon-button.danger { - border-color: rgba(239, 68, 68, 0.55); - color: rgba(248, 113, 113, 0.95); - background: rgba(239, 68, 68, 0.12); -} - -.icon-button.danger:hover { - background: rgba(239, 68, 68, 0.18); -} - -.icon-button.upload { - position: relative; - overflow: hidden; -} - -.icon-button:disabled { - cursor: not-allowed; - opacity: 0.4; -} - -.modal-card .danger { - border: 1px solid rgba(239, 68, 68, 0.55); - background: rgba(239, 68, 68, 0.12); - color: rgba(248, 113, 113, 0.95); -} - -.modal-card .danger:hover { - background: rgba(239, 68, 68, 0.18); -} - -.details-stack > * + * { - margin-top: 12px; -} - -.icon-button.upload input { - position: absolute; - inset: 0; - opacity: 0; - cursor: pointer; -} - - -.panel-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 12px; -} - -.explorer { - display: grid; - grid-template-columns: 260px 1fr; - grid-template-rows: 1fr auto; - gap: 0; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 12px; - overflow-x: auto; - overflow-y: hidden; - background: rgba(8, 12, 18, 0.7); - min-height: 200px; - align-items: stretch; - font-size: 13px; -} - -.explorer-sidebar { - grid-row: 1; - padding: 12px; - display: flex; - flex-direction: column; - gap: 10px; - background: rgba(10, 17, 26, 0.85); - height: 100%; -} - -.sidebar-toolbar { - display: flex; - align-items: center; - justify-content: flex-start; - border: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(255, 255, 255, 0.03); - padding: 0 10px; - min-height: 40px; - gap: 10px; -} - -.sidebar-tree { - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; - background: rgba(10, 17, 26, 0.8); - padding: 8px; - overflow: auto; -} - -.sidebar-search { - flex: 1; - width: 100%; - border: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(255, 255, 255, 0.04); - border-radius: 8px; - padding: 0 10px; - font-size: 13px; - color: #e6edf6; - min-width: 0; - height: 28px; - line-height: 28px; -} - -.sidebar-search:focus { - outline: none; - border-color: rgba(25, 164, 161, 0.6); - box-shadow: 0 0 0 2px rgba(25, 164, 161, 0.2); -} - -.explorer-toolbar, -.sidebar-toolbar { - height: 40px; - min-height: 40px; - box-shadow: 0 1px 2px rgba(16, 18, 22, 0.2); - align-items: center; -} - -.explorer-actions.footer { - padding-top: 6px; -} - -.explorer-main { - grid-row: 1; - display: flex; - flex-direction: column; - gap: 10px; - padding: 12px 14px 14px; - background: rgba(10, 17, 26, 0.7); - height: 100%; -} - -.explorer-toolbar { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; - padding: 0 10px; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; - background: rgba(255, 255, 255, 0.03); -} - -.explorer-toolbar .icon-button, -.explorer-toolbar label.icon-button { - width: 30px; - height: 30px; - padding: 0; -} - -.explorer-toolbar .icon-button svg, -.explorer-toolbar label.icon-button svg { - display: block; -} - -.explorer-path { - display: flex; - flex-wrap: wrap; - gap: 6px; - font-size: 13px; - color: rgba(230, 237, 246, 0.7); - align-items: center; - line-height: 1; -} - -.breadcrumb-root { - font-weight: 600; - color: #e6edf6; -} - -.path-link { - background: transparent; - border: none; - color: #e6edf6; - cursor: pointer; - padding: 0; - font-size: 13px; - font-weight: 600; -} - -.path-link:hover, -.path-link:focus { - background: transparent; - color: #e6edf6; - outline: none; -} - -.toolbar-actions { - margin-left: auto; - display: flex; - align-items: center; - gap: 6px; -} - -.explorer-list { - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; - background: rgba(10, 17, 26, 0.8); - overflow: hidden; -} - -.explorer-row { - display: grid; - grid-template-columns: 2fr repeat(2, minmax(80px, 1fr)); - gap: 12px; - align-items: center; - padding: 10px 12px; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - font-size: 13px; -} - -.explorer-row.has-meta, -.explorer-list.has-meta .explorer-row { - grid-template-columns: 2fr repeat(4, minmax(100px, 1fr)); -} - -.explorer-row.header { - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.08em; - color: rgba(230, 237, 246, 0.5); - background: rgba(255, 255, 255, 0.02); -} - -.explorer-row.footer { - background: rgba(255, 255, 255, 0.02); -} - -.explorer-row.selected { - background: rgba(25, 164, 161, 0.15); -} - -.row-name { - display: flex; - align-items: center; - gap: 8px; -} - -.row-icon { - display: inline-flex; - align-items: center; -} - -.footer-count { - font-weight: 600; - color: rgba(230, 237, 246, 0.9); -} - -.explorer-footer { - grid-column: 1 / -1; - padding: 8px 12px; - font-size: 13px; - color: rgba(230, 237, 246, 0.6); - border-top: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(10, 17, 26, 0.8); -} - -.tree-node { - display: flex; - flex-direction: column; - gap: 4px; -} - -.tree-label { - display: inline-block; - user-select: none; - cursor: pointer; -} - -.depth-1 .tree-row { - margin-left: 16px; -} - -.depth-2 .tree-row { - margin-left: 32px; -} - -.depth-3 .tree-row { - margin-left: 48px; -} - -.depth-4 .tree-row { - margin-left: 64px; -} - -.depth-5 .tree-row { - margin-left: 80px; -} - -.tree-row { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 8px; - border-radius: 8px; - cursor: pointer; - color: rgba(230, 237, 246, 0.75); -} - -.tree-row.active { - background: rgba(25, 164, 161, 0.18); - color: #e6edf6; -} - -.tree-caret { - border: none; - background: transparent; - color: rgba(230, 237, 246, 0.7); - padding: 0; - display: inline-flex; - align-items: center; - justify-content: center; - width: 18px; - flex: 0 0 18px; - cursor: pointer; -} - -.tree-caret.disabled { - width: 18px; - flex: 0 0 18px; -} - -.tree-icon.folder { - color: rgba(25, 164, 161, 0.9); -} - -.tree-label { - flex: 1; -} - -.tree-tag-count { - background: rgba(25, 164, 161, 0.2); - padding: 2px 6px; - border-radius: 999px; - font-size: 11px; -} - -.upload-status { - display: grid; - gap: 8px; - padding: 10px 12px; - margin-bottom: 12px; - border-radius: 10px; - background: rgba(255, 255, 255, 0.04); -} - -.upload-progress { - width: 100%; - height: 6px; - background: rgba(255, 255, 255, 0.1); - border-radius: 999px; - overflow: hidden; -} - -.upload-progress div { - height: 100%; - background: linear-gradient(120deg, #19a4a1, #11709c); -} - - -.modal-backdrop { - position: fixed; - inset: 0; - background: rgba(5, 8, 12, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 90; - backdrop-filter: blur(6px); -} - -.modal-card { - background: #0f1622; - color: #e6edf6; - border-radius: 14px; - padding: 20px; - width: min(520px, 92vw); - border: 1px solid rgba(255, 255, 255, 0.08); - display: grid; - gap: 12px; - position: relative; - z-index: 91; - box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45); - max-height: 90vh; - overflow: auto; - visibility: visible; - opacity: 1; -} - -.modal-card.modal-upload { - width: min(1040px, 96vw); -} - -.modal-card.modal-tag-delete { - width: min(760px, 96vw); - overflow: visible; -} - - -.modal-actions { - display: flex; - justify-content: flex-end; - gap: 8px; - margin-top: 6px; - flex-wrap: nowrap; -} - -.modal-header h3 { - margin: 0; -} - -.modal-body { - display: grid; - gap: 10px; -} - -.modal-label { - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -@media (max-width: 960px) { - .explorer { - grid-template-columns: 1fr; - } - - .explorer-sidebar { - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - } -} - -.player-shell { - display: grid; - gap: 16px; -} - -.player-header { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 16px; - align-items: center; - padding: 14px; - border-radius: 16px; - background: rgba(255, 255, 255, 0.04); -} - -.player-cover { - width: 86px; - height: 86px; - border-radius: 16px; - background: linear-gradient(135deg, #ff7a18, #af002d 60%, #319197); - box-shadow: 0 10px 20px rgba(5, 8, 12, 0.4); -} - -.player-meta h3 { - margin: 0 0 6px; - font-size: 20px; -} - -.player-meta p { - margin: 0 0 6px; - color: rgba(230, 237, 246, 0.6); - font-size: 12px; -} - -.player-sub { - display: flex; - gap: 12px; - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -.player-controls { - display: grid; - grid-auto-flow: column; - gap: 8px; - align-items: center; - justify-content: end; - background: rgba(255, 255, 255, 0.06); - padding: 10px; - border-radius: 14px; -} - -.player-btn { - border: none; - background: transparent; - color: rgba(230, 237, 246, 0.8); - width: 36px; - height: 36px; - border-radius: 50%; - display: grid; - place-items: center; - cursor: pointer; -} - -.player-btn.primary { - background: rgba(230, 237, 246, 0.12); - color: #e6edf6; - width: 42px; - height: 42px; -} - -.player-btn:hover { - background: rgba(255, 255, 255, 0.12); -} - -.volume-wrap { - position: relative; -} - -.volume-popover { - position: absolute; - right: 0; - bottom: 46px; - background: rgba(10, 17, 26, 0.95); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 12px; - padding: 10px 12px; - display: grid; - gap: 8px; - align-items: center; - justify-items: center; - z-index: 5; -} - -.volume-label { - font-size: 11px; - color: rgba(230, 237, 246, 0.7); - min-width: 2ch; - text-align: center; -} - -.player-progress { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 12px; - align-items: center; - font-size: 12px; - color: rgba(230, 237, 246, 0.6); -} - -.player-nfc { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.player-nfc input { - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 8px; - padding: 8px 10px; - background: rgba(255, 255, 255, 0.04); - color: #e6edf6; -} - -.player-nfc button { - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(10, 17, 26, 0.7); - color: #e6edf6; - padding: 8px 12px; - border-radius: 8px; - cursor: pointer; -} - -.player-list { - border-radius: 16px; - border: 1px solid rgba(255, 255, 255, 0.08); - overflow: hidden; -} - -.player-row { - display: grid; - grid-template-columns: 40px 2fr 1fr 80px; - gap: 12px; - align-items: center; - padding: 10px 12px; - font-size: 13px; - background: rgba(10, 17, 26, 0.65); - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - cursor: pointer; - user-select: none; -} - -.player-row.header { - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 11px; - color: rgba(230, 237, 246, 0.5); - background: rgba(255, 255, 255, 0.03); -} - -.player-row.empty { - color: rgba(230, 237, 246, 0.6); -} - -.player-row.active { - background: rgb(45 212 191 / 12%); - color: #f6d08c; -} - -.player-row.active span { - color: inherit; -} - -@media (max-width: 900px) { - .player-header { - grid-template-columns: 1fr; - justify-items: start; - } - - .player-controls { - width: 100%; - justify-content: start; - } -} -.modal-long-button .p-button-label { - white-space: nowrap; -} diff --git a/frontend/vite.config.js b/frontend/vite.config.js deleted file mode 100644 index ee322b1..0000000 --- a/frontend/vite.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import { defineConfig, loadEnv } from 'vite'; -import react from '@vitejs/plugin-react'; - -export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), ''); - const rawTarget = env.VITE_BACKEND_URL || 'http://127.0.0.1:5001'; - const apiTarget = rawTarget.replace(/\/api\/?$/, ''); - - return { - plugins: [react()], - server: { - host: true, - port: 5174, - strictPort: true, - proxy: { - '/api': { - target: apiTarget, - changeOrigin: true, - }, - }, - }, - }; -});