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 (
-
- );
-}
-
-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 ? : }
-
- ) : (
-
- )}
-
-
-
-
{node.name}
- {tagCount > 0 && (
-
- {tagCount}
-
- )}
-
- {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"
- />
- handlePlayerCommand('nfc_on', { uid: nfcUid })} />
- handlePlayerCommand('nfc_off', { uid: nfcUid })} />
-
-
-
- #
- 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}
-
-
handlePair(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,
- }));
- }}
- />
- }
- onClick={(event) => {
- event.stopPropagation();
- handleSaveBoxAlias(box.box_id);
- }}
- title="Alias speichern"
- aria-label="Alias speichern"
- />
- }
- onClick={(event) => {
- event.stopPropagation();
- handleShowBoxDetails(box.box_id);
- }}
- title="Details"
- aria-label="Details"
- />
- }
- onClick={(event) => {
- event.stopPropagation();
- setBoxDeleteTarget(box.box_id);
- }}
- title="Unpair"
- aria-label="Unpair"
- />
-
-
-
- ))}
-
- )}
-
-
- );
- }
-
- 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)}
-
-
-
-
-
}
- onClick={handleGoUp}
- title="Hoch"
- aria-label="Hoch"
- />
-
- setCurrentPath('')}
- role="button"
- tabIndex={0}
- onKeyDown={(event) => {
- if (event.key === 'Enter') setCurrentPath('');
- }}
- >
- media
-
- {buildBreadcrumb(currentPath).map((crumb) => (
- setCurrentPath(crumb.path)}
- role="button"
- tabIndex={0}
- onKeyDown={(event) => {
- if (event.key === 'Enter') setCurrentPath(crumb.path);
- }}
- >
- / {crumb.name}
-
- ))}
-
-
- }
- onClick={() => setActiveModal('new-folder')}
- title="Neuer Ordner"
- aria-label="Neuer Ordner"
- />
- }
- onClick={() => setActiveModal('rename')}
- title="Umbenennen"
- disabled={selectedPaths.length !== 1}
- aria-label="Umbenennen"
- />
- }
- onClick={() => setActiveModal('delete')}
- title="Loeschen"
- disabled={selectedPaths.length === 0}
- aria-label="Loeschen"
- />
- }
- onClick={() => {
- setMoveTarget('');
- setActiveModal('move');
- }}
- title="Verschieben"
- disabled={selectedPaths.length === 0}
- aria-label="Verschieben"
- />
- }
- title="Upload"
- onClick={(event) => {
- event.preventDefault();
- event.stopPropagation();
- setUploadAfterCreate(true);
- setPendingUploadFiles([]);
- setNewFolderName('');
- setActiveModal('new-folder');
- }}
- aria-label="Upload"
- />
-
-
-
-
- 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.
- )}
-
-
- {
- setImportTargetUid(tag.uid);
- setActiveModal('import-tag');
- }}
- disabled={!tag.media_exists}
- />
-
-
- ))}
-
- )}
-
-
- 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,
- }))
- }
- />
- }
- onClick={() => handleSaveTagAlias(tag.uid)}
- title="Alias speichern"
- aria-label="Alias speichern"
- />
-
-
-
- setDbTagMedia((prev) => ({
- ...prev,
- [tag.uid]: event.value,
- }))
- }
- options={collectTopLevelFolders(mediaTree).map((folderPath) => ({
- label: folderPath,
- value: folderPath,
- }))}
- placeholder="Medienordner waehlen"
- filter
- className="p-inputtext-sm"
- />
- }
- onClick={() => {
- setTagDeleteTarget(tag.uid);
- setActiveModal('tag-delete');
- }}
- title="Loeschen"
- aria-label="Loeschen"
- />
-
-
-
-
- ))}
-
- )}
-
-
-
- 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 (
-
-
-
-
-
-
-
-
-
-
- {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"
- />
- {
- setTagUidMode('new');
- setScanTagUid(generateTagId());
- }}
- />
-
- {
- const uid = scanTagUid.trim();
- if (!isValidTagUid(uid)) {
- setTagUidError(
- 'UID ungueltig. Bitte 10 Zeichen (a-z, 0-9) oder TAG_ + 8 Zeichen verwenden.'
- );
- return;
- }
- if (tagUidError && tagUidError !== autoReplaceMsg) {
- setTagUidError('');
- }
- setTagStepOneDone(true);
- setTagStepMax(1);
- setTagStep(1);
- }}
- />
-
-
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}
- />
-
- {
- setTagStepTwoDone(true);
- setTagStepMax(2);
- setTagStep(2);
- }}
- />
-
-
-
-
-
-
-
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)
-
-
- )}
- >
- )}
-
- {
- setActiveModal('');
- setUploadAfterCreate(false);
- setPendingUploadFiles([]);
- setUploadSize(0);
- setUploadNameError('');
- setTagDeleteTarget('');
- }}
- />
- {
- if (uploadAfterCreate) {
- setActiveModal('');
- }
- handleCreateFolder();
- }}
- />
-
-
-
- )}
-
- {activeModal === 'rename' && (
-
{
- setActiveModal('');
- setRenameName('');
- }}
- >
-
event.stopPropagation()}
- ref={modalRef}
- >
-
Umbenennen
-
setRenameName(event.target.value)}
- placeholder="Neuer Name"
- className="p-inputtext-sm"
- />
-
- {
- setActiveModal('');
- setRenameName('');
- }}
- />
-
-
-
-
- )}
-
- {activeModal === 'delete' && (
-
{
- setActiveModal('');
- }}
- >
-
event.stopPropagation()}
- ref={modalRef}
- >
-
Eintraege loeschen
-
{selectedPaths.length} Eintraege werden geloescht.
-
- setActiveModal('')} />
-
-
-
-
- )}
-
- {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"
- />
-
- {
- setActiveModal('');
- setMoveTarget('');
- }}
- />
-
-
-
-
- )}
-
- {activeModal === 'tag-delete' && (
-
{
- setActiveModal('');
- setTagDeleteTarget('');
- }}
- >
-
event.stopPropagation()}
- ref={modalRef}
- >
-
Tag entfernen
-
- Soll der Tag komplett geloescht werden oder nur die Medienzuweisung?
-
-
- {
- setActiveModal('');
- setTagDeleteTarget('');
- }}
- />
- {
- if (!tagDeleteTarget) return;
- await handleClearTagMedia(tagDeleteTarget);
- setTagDeleteTarget('');
- setActiveModal('');
- }}
- />
- {
- if (!tagDeleteTarget) return;
- await handleClearTagMedia(tagDeleteTarget);
- await handleDeleteTag(tagDeleteTarget);
- setTagDeleteTarget('');
- setActiveModal('');
- }}
- />
-
-
-
- )}
-
- {boxDeleteTarget && (
-
setBoxDeleteTarget('')}
- >
-
event.stopPropagation()}
- ref={modalRef}
- >
-
Box entfernen
-
Soll die Box wirklich entkoppelt werden?
-
- setBoxDeleteTarget('')}
- />
- {
- if (!boxDeleteTarget) return;
- await handleUnpair(boxDeleteTarget);
- setBoxDeleteTarget('');
- }}
- />
-
-
-
- )}
-
- {boxDetailsOpen && (
-
{
- setBoxDetailsOpen(false);
- setBoxDetailsError('');
- setBoxDetailsData(null);
- }}
- role="presentation"
- >
-
event.stopPropagation()}>
-
-
Box Details
- {
- setBoxDetailsOpen(false);
- setBoxDetailsError('');
- setBoxDetailsData(null);
- }}
- aria-label="Schliessen"
- />
-
-
- {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 && (
-
- )}
-
-
- {
- setActiveModal('');
- setImportTargetFolder('');
- setImportTargetUid('');
- }}
- />
-
-
-
-
- )}
-
- {showSessionSheet && (
-
setShowSessionSheet(false)}
- >
-
event.stopPropagation()}
- >
-
- ToolHub
- setShowSessionSheet(false)}
- aria-label="Schliessen"
- />
-
- {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 (
-
{
- setSelectedId(box.box_id);
- setShowSessionSheet(false);
- }}
- >
-
-
- {box.alias || box.box_id}
- {isActive && Aktiv}
-
-
-
- );
- })}
-
- >
- )}
- {drawerTab === 'system' && (
-
{systemPanels}
- )}
-
- setDrawerTab('boxes')}
- >
-
-
-
- Boxen
-
- setDrawerTab('system')}
- >
-
-
-
- System
-
-
-
-
- )}
-
- );
-}
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,
- },
- },
- },
- };
-});