diff --git a/package-lock.json b/package-lock.json index 5576e600f..64e6d1dba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zigbee2mqtt-windfront", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zigbee2mqtt-windfront", - "version": "2.4.3", + "version": "2.5.0", "license": "GPL-3.0-or-later", "devDependencies": { "@biomejs/biome": "^2.3.8", @@ -15,10 +15,10 @@ "@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/free-solid-svg-icons": "^7.1.0", "@fortawesome/react-fontawesome": "^3.1.1", - "@storybook/addon-a11y": "^10.1.5", - "@storybook/addon-docs": "^10.1.5", - "@storybook/react-vite": "^10.1.5", - "@tailwindcss/vite": "^4.1.17", + "@storybook/addon-a11y": "^10.1.7", + "@storybook/addon-docs": "^10.1.7", + "@storybook/react-vite": "^10.1.7", + "@tailwindcss/vite": "^4.1.18", "@tanstack/react-table": "^8.21.3", "@types/file-saver": "^2.0.7", "@types/json-schema": "^7.0.15", @@ -29,7 +29,7 @@ "@virtuoso.dev/masonry": "^1.3.5", "@vitejs/plugin-react": "^5.1.2", "@vitest/coverage-v8": "^4.0.15", - "daisyui": "^5.5.8", + "daisyui": "^5.5.11", "file-saver": "^2.0.5", "i18next": "^25.7.2", "i18next-browser-languagedetector": "^8.2.0", @@ -39,19 +39,20 @@ "react": "^19.2.1", "react-app-polyfill": "^3.0.0", "react-dom": "^19.2.1", - "react-i18next": "^16.4.0", + "react-i18next": "^16.4.1", "react-image": "^4.1.0", "react-router": "^7.10.1", + "react-select": "^5.10.2", "react-virtuoso": "^4.17.0", "reagraph": "^4.30.7", "store2": "^2.14.4", - "storybook": "^10.1.5", + "storybook": "^10.1.7", "tailwindcss": "^4.1.4", "timeago.js": "^4.0.2", "typescript": "^5.9.3", "vite": "^7.2.7", "vite-plugin-compression2": "^2.4.0", - "vitest": "^4.0.14", + "vitest": "^4.0.15", "ws": "^8.18.3", "zigbee2mqtt": "github:Koenkk/zigbee2mqtt#dev-types", "zustand": "^5.0.9" @@ -65,9 +66,9 @@ } }, "node_modules/@acemir/cssom": { - "version": "0.9.28", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.28.tgz", - "integrity": "sha512-LuS6IVEivI75vKN8S04qRD+YySP0RmU/cV8UNukhQZvprxF+76Z43TNo/a08eCodaGhT1Us8etqS1ZRY9/Or0A==", + "version": "0.9.29", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.29.tgz", + "integrity": "sha512-G90x0VW+9nW4dFajtjCoT+NM0scAfH9Mb08IcjgFHYbfiL/lU04dTF9JuVOi3/OH+DJCQdcIseSXkdCB9Ky6JA==", "dev": true, "license": "MIT" }, @@ -765,6 +766,138 @@ "react-dom": ">16.8.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", @@ -2099,9 +2232,9 @@ "license": "MIT" }, "node_modules/@storybook/addon-a11y": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.1.5.tgz", - "integrity": "sha512-dMUrkuQyvDfD6SdvV7F7cbjRrhHN0kqCNhRfg1i1IJuLuck6kiALpx8176KhWBcAkN/0J/1V75n7+F9YU/JlPA==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.1.7.tgz", + "integrity": "sha512-hFN4OXb+sc6ka2dz+m0RKCgWRFf/xJBraYz0jmj8kkdlhMa9DXPQ7b7W1ivuGW8BTANvtgq1N9ggwh3youBBiw==", "dev": true, "license": "MIT", "dependencies": { @@ -2113,20 +2246,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.5" + "storybook": "^10.1.7" } }, "node_modules/@storybook/addon-docs": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.1.5.tgz", - "integrity": "sha512-2FfqFrfEeaKv8OerZCWt1b+dm7N/nizv1G2CnTZfWJ0TKxbPDH6kffAqC9lMnT3xAZjDWiBLdnVx2oouKdmSvw==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.1.7.tgz", + "integrity": "sha512-RNwz5jDjBhjST70BoxUCYVfT2sexTKsDSN2FcnBBJ2/sAtjKbTpX3p4PfFaeFqwhDc+6TCBUTxfO4BsAQXf5jw==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/csf-plugin": "10.1.5", + "@storybook/csf-plugin": "10.1.7", "@storybook/icons": "^2.0.0", - "@storybook/react-dom-shim": "10.1.5", + "@storybook/react-dom-shim": "10.1.7", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -2136,17 +2269,17 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.5" + "storybook": "^10.1.7" } }, "node_modules/@storybook/builder-vite": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.1.5.tgz", - "integrity": "sha512-5alpNa+TQXK1zp9MeovUK/yIUkZqpIFUScUer6cYgidI96Boovn7OXt5oXQ8CqqpzuEtgCvz44TzCmgZoGv41g==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.1.7.tgz", + "integrity": "sha512-UKmym/o20SJFYjbt/X1j39vORXwC1lkGHKD9JlR8UAwkRuGOoEktUIYYfB7cmrOKjdgf3Es8/SIu+lgbWWleew==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "10.1.5", + "@storybook/csf-plugin": "10.1.7", "@vitest/mocker": "3.2.4", "ts-dedent": "^2.0.0" }, @@ -2155,14 +2288,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.1.5", + "storybook": "^10.1.7", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/csf-plugin": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.1.5.tgz", - "integrity": "sha512-v+D7PVRkNUHznfoQg8yqpLWZIIbPddqHDSi1oBGdegF0Kv/lVsGqTZGRLroApsMu7BLwLhpcMID6ofxlfftWKg==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.1.7.tgz", + "integrity": "sha512-mUWM3kFSQpm4At6+OJmmqiezjEdq+y9HD2abuiCVvnTDf7ftoMcv4EbKqf6DM5CXcOpqRDlRxwzEum+hbfh5ig==", "dev": true, "license": "MIT", "dependencies": { @@ -2175,7 +2308,7 @@ "peerDependencies": { "esbuild": "*", "rollup": "*", - "storybook": "^10.1.5", + "storybook": "^10.1.7", "vite": "*", "webpack": "*" }, @@ -2213,14 +2346,14 @@ } }, "node_modules/@storybook/react": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.1.5.tgz", - "integrity": "sha512-M8fR7WVs79fPJHwRZxkz4XzIfzs/bN0heWdZX0D4iRjeIcY4nLM/tyalCcQDrGgrSJbgAAf4xd7KXaZzaZSAqA==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.1.7.tgz", + "integrity": "sha512-QGCQKYj4o17IlKZ4MuzKxm6KCV8kQCie7uZ+EYQ6mETAbYDj9c93DWdgGY0mIbUtuGSPY5pl8uwhr+HvmnkyBA==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "10.1.5", + "@storybook/react-dom-shim": "10.1.7", "react-docgen": "^8.0.2" }, "funding": { @@ -2230,7 +2363,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.1.5", + "storybook": "^10.1.7", "typescript": ">= 4.9.x" }, "peerDependenciesMeta": { @@ -2240,9 +2373,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.1.5.tgz", - "integrity": "sha512-CsXcq26wINUgYP8KnfSuS60B10/Ag34YdcnWIEl9hM5UtTQ65WYJ9fVFqpzfnQrkpgRMd7iQjtmUhCe+4umnHg==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.1.7.tgz", + "integrity": "sha512-cjIoNbWnGiet3vRjswnnh3ioN+X2ZEqDBIV6b+WN8RpGSUs3vg6V2s7G8IzgSfxFDahIqQ6D7yot3aOOlN+qBw==", "dev": true, "license": "MIT", "funding": { @@ -2252,20 +2385,20 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.1.5" + "storybook": "^10.1.7" } }, "node_modules/@storybook/react-vite": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.1.5.tgz", - "integrity": "sha512-27RiCVw5QZ/f9fXS8sGaPHuWkbHSoS66ifeakxHgbkbIXjVI4M6pWB7NUj49MwU1YUMOpB0T8KasvyMZzv/UPA==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.1.7.tgz", + "integrity": "sha512-mqEcS25c1OovoO/G/Sncdvuq5TY+ee0pC3aFblPPYLDjL+60PJJXPhGQ1p3byB3XLeaCs7q7vp3biMDVxtedIQ==", "dev": true, "license": "MIT", "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.6.1", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "10.1.5", - "@storybook/react": "10.1.5", + "@storybook/builder-vite": "10.1.7", + "@storybook/react": "10.1.7", "empathic": "^2.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", @@ -2279,14 +2412,14 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.1.5", + "storybook": "^10.1.7", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@tailwindcss/node": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", - "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2296,37 +2429,37 @@ "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", - "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-x64": "4.1.17", - "@tailwindcss/oxide-freebsd-x64": "4.1.17", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-x64-musl": "4.1.17", - "@tailwindcss/oxide-wasm32-wasi": "4.1.17", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", - "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", "cpu": [ "arm64" ], @@ -2341,9 +2474,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", "cpu": [ "arm64" ], @@ -2358,9 +2491,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", - "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", "cpu": [ "x64" ], @@ -2375,9 +2508,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", - "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", "cpu": [ "x64" ], @@ -2392,9 +2525,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", - "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", "cpu": [ "arm" ], @@ -2409,9 +2542,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", - "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", "cpu": [ "arm64" ], @@ -2426,9 +2559,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", - "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", "cpu": [ "arm64" ], @@ -2443,9 +2576,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", - "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", "cpu": [ "x64" ], @@ -2460,9 +2593,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", - "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", "cpu": [ "x64" ], @@ -2477,9 +2610,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", - "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2495,10 +2628,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.6.0", - "@emnapi/runtime": "^1.6.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.0.7", + "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, @@ -2507,9 +2640,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", - "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", "cpu": [ "arm64" ], @@ -2524,9 +2657,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", - "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", "cpu": [ "x64" ], @@ -2541,15 +2674,15 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", - "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.17", - "@tailwindcss/oxide": "4.1.17", - "tailwindcss": "4.1.17" + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" @@ -2779,9 +2912,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", - "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.0.tgz", + "integrity": "sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==", "dev": true, "license": "MIT", "dependencies": { @@ -2795,6 +2928,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", @@ -2826,6 +2966,16 @@ "@types/react": "*" } }, + "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==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.6", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", @@ -2841,9 +2991,9 @@ "license": "MIT" }, "node_modules/@types/three": { - "version": "0.181.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.181.0.tgz", - "integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", + "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "dev": true, "license": "MIT", "peer": true, @@ -2851,7 +3001,7 @@ "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", - "@types/webxr": "*", + "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" @@ -3272,6 +3422,22 @@ "node": ">=4" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3301,9 +3467,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz", - "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==", + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz", + "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3401,6 +3567,32 @@ "ieee754": "^1.2.1" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camera-controls": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", @@ -3530,6 +3722,33 @@ "dev": true, "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -3768,9 +3987,9 @@ } }, "node_modules/daisyui": { - "version": "5.5.8", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.8.tgz", - "integrity": "sha512-6psL9jIEOFOw68V10j/BKCWcRgx8dh81mmNxShr+g7HDM6UHNoPharlp9zq/PQkHNuGU1ZQsajR3HgpvavbRKQ==", + "version": "5.5.11", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.11.tgz", + "integrity": "sha512-xQ1JIgYbhKe9DKhFtOWAKf91F1mK3HECa5xoINmSHMqW9KjEP+yOWttTN1K7zOwuDmk68CbovCDbMKLVtDmPBQ==", "dev": true, "license": "MIT", "funding": { @@ -3839,6 +4058,49 @@ "node": ">=6" } }, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3902,6 +4164,17 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/draco3d": { "version": "1.5.7", "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", @@ -3974,6 +4247,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -4034,6 +4317,19 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4124,6 +4420,13 @@ "dev": true, "license": "MIT" }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true, + "license": "MIT" + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -4359,6 +4662,16 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hold-event": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/hold-event/-/hold-event-1.1.2.tgz", @@ -4508,6 +4821,23 @@ "dev": true, "license": "MIT" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -4535,6 +4865,13 @@ "node": ">=12" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -4551,6 +4888,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -4561,6 +4914,25 @@ "node": ">=8" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -4575,6 +4947,22 @@ "dev": true, "license": "MIT" }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4753,6 +5141,13 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5050,6 +5445,13 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -5057,6 +5459,19 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -5153,6 +5568,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "dev": true, + "license": "MIT" + }, "node_modules/meshline": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", @@ -5333,6 +5755,25 @@ ], "license": "MIT" }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -5357,6 +5798,38 @@ "mnemonist": "^0.39.2" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -5411,6 +5884,16 @@ "dev": true, "license": "ISC" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -5507,6 +5990,13 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -5535,6 +6025,18 @@ "lie": "^3.0.2" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5631,9 +6133,9 @@ } }, "node_modules/react-i18next": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.4.0.tgz", - "integrity": "sha512-bxVeBA8Ky2UeItNhF4JRxHCFIrpEJHGFG/mOAa4CR0JkqaDEYSLmlEgmC4Os63SBlZ+E5U0YyrNJOSVl2mtVqQ==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.4.1.tgz", + "integrity": "sha512-GzsYomxb1/uE7nlJm0e1qQ8f+W9I3Xirh9VoycZIahk6C8Pmv/9Fd0ek6zjf1FSgtGLElDGqwi/4FOHEGUbsEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5671,9 +6173,9 @@ } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true, "license": "MIT" }, @@ -5733,6 +6235,45 @@ } } }, + "node_modules/react-select": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", + "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.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==", + "dev": true, + "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/react-use-measure": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", @@ -5860,6 +6401,16 @@ "node": ">= 4" } }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -5925,6 +6476,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", @@ -5968,6 +6529,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6077,9 +6651,9 @@ "license": "(MIT OR GPL-2.0)" }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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", "engines": { @@ -6147,9 +6721,9 @@ "license": "MIT" }, "node_modules/storybook": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.1.5.tgz", - "integrity": "sha512-q3xB1pOcmmHUH9LfQNY/BWMGxp3fc1OALJf+F5BXIxHGQUEIizz6V1AbDOngWN9oWzuA8Gdz5rOCe7yelOMWVg==", + "version": "10.1.7", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.1.7.tgz", + "integrity": "sha512-dK1p2LKzAdea60APGo/vMbF+X/D7eVZsv8ijnLVvfMBjScdDBgxfIn025mRtOwqECb/UN9cIpPs5XEWAeLpYMg==", "dev": true, "license": "MIT", "peer": true, @@ -6161,6 +6735,7 @@ "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", + "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.6.2", "use-sync-external-store": "^1.5.0", @@ -6325,6 +6900,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "dev": true, + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6376,9 +6958,9 @@ "license": "MIT" }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "dev": true, "license": "MIT" }, @@ -6740,6 +7322,21 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -7745,6 +8342,22 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -7770,9 +8383,9 @@ "license": "ISC" }, "node_modules/zigbee-herdsman": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/zigbee-herdsman/-/zigbee-herdsman-7.0.4.tgz", - "integrity": "sha512-/MXhrBKfrbpdr/ODetZZpPdXVCovBEmVEoXt5EXJFwrnzx16IrlUiE96Pgf4uLwmoZNJcixQxVq+mAsf/2SCwQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/zigbee-herdsman/-/zigbee-herdsman-7.0.6.tgz", + "integrity": "sha512-d33TLstXp5qKPtkgBLV+McoQ0mLIlER5xywvISPK1iFtiCVSYgz3UUo/nfS/Y7RaKAzG0x18BVqMcTgnqArRYg==", "dev": true, "license": "MIT", "dependencies": { @@ -7789,24 +8402,24 @@ } }, "node_modules/zigbee-herdsman-converters": { - "version": "25.84.0", - "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-25.84.0.tgz", - "integrity": "sha512-5D+Nsr0JKAP5cQzzLo4QPnczksI2e1yIfiFHvz7WvSmCJ8KsR+ztA67R7K/Mcwl8nhlvO6lQQGXHXLJKtIGQQQ==", + "version": "25.86.0", + "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-25.86.0.tgz", + "integrity": "sha512-DqskBtXkXld9bElsB27H6Av7OlxA+T5puBnzv8NucgtFM3yP3gJrfyB8/SGxRUCHB2C8RSb6czPnxTQ0Nh+SIw==", "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "^0.7.0", "semver": "^7.7.3", - "zigbee-herdsman": "^7.0.0" + "zigbee-herdsman": "^7.0.4" }, "engines": { "node": ">=20.15.0" } }, "node_modules/zigbee-herdsman-converters/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "dev": true, "license": "MIT", "dependencies": { @@ -7845,12 +8458,12 @@ }, "node_modules/zigbee2mqtt": { "version": "2.7.1-dev", - "resolved": "git+ssh://git@github.com/Koenkk/zigbee2mqtt.git#4828eb6f2d4a597bdf5841ab9c25a6a7ac9e55a0", + "resolved": "git+ssh://git@github.com/Koenkk/zigbee2mqtt.git#5f679d25991d1602cc676838bca08f3d56081758", "dev": true, "license": "GPL-3.0", "dependencies": { - "zigbee-herdsman": "7.0.4", - "zigbee-herdsman-converters": "25.84.0" + "zigbee-herdsman": "7.0.6", + "zigbee-herdsman-converters": "25.86.0" }, "bin": { "zigbee2mqtt": "cli.js" diff --git a/package.json b/package.json index 79a2e0a6f..2d4ab7630 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zigbee2mqtt-windfront", - "version": "2.4.3", + "version": "2.5.0", "license": "GPL-3.0-or-later", "type": "module", "main": "./index.js", @@ -43,10 +43,10 @@ "@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/free-solid-svg-icons": "^7.1.0", "@fortawesome/react-fontawesome": "^3.1.1", - "@storybook/addon-a11y": "^10.1.5", - "@storybook/addon-docs": "^10.1.5", - "@storybook/react-vite": "^10.1.5", - "@tailwindcss/vite": "^4.1.17", + "@storybook/addon-a11y": "^10.1.7", + "@storybook/addon-docs": "^10.1.7", + "@storybook/react-vite": "^10.1.7", + "@tailwindcss/vite": "^4.1.18", "@tanstack/react-table": "^8.21.3", "@types/file-saver": "^2.0.7", "@types/json-schema": "^7.0.15", @@ -57,7 +57,7 @@ "@virtuoso.dev/masonry": "^1.3.5", "@vitejs/plugin-react": "^5.1.2", "@vitest/coverage-v8": "^4.0.15", - "daisyui": "^5.5.8", + "daisyui": "^5.5.11", "file-saver": "^2.0.5", "i18next": "^25.7.2", "i18next-browser-languagedetector": "^8.2.0", @@ -67,19 +67,20 @@ "react": "^19.2.1", "react-app-polyfill": "^3.0.0", "react-dom": "^19.2.1", - "react-i18next": "^16.4.0", + "react-i18next": "^16.4.1", "react-image": "^4.1.0", "react-router": "^7.10.1", + "react-select": "^5.10.2", "react-virtuoso": "^4.17.0", "reagraph": "^4.30.7", "store2": "^2.14.4", - "storybook": "^10.1.5", + "storybook": "^10.1.7", "tailwindcss": "^4.1.4", "timeago.js": "^4.0.2", "typescript": "^5.9.3", "vite": "^7.2.7", "vite-plugin-compression2": "^2.4.0", - "vitest": "^4.0.14", + "vitest": "^4.0.15", "ws": "^8.18.3", "zigbee2mqtt": "github:Koenkk/zigbee2mqtt#dev-types", "zustand": "^5.0.9" diff --git a/src/components/device-page/AddToGroup.tsx b/src/components/device-page/AddToGroup.tsx index 97671ad92..634c794e6 100644 --- a/src/components/device-page/AddToGroup.tsx +++ b/src/components/device-page/AddToGroup.tsx @@ -43,8 +43,8 @@ const AddToGroup = memo(({ sourceIdx, device, nonMemberGroups }: AddToGroupProps return ( <>

{t(($) => $.add_to_group)}

-
- $.group, { ns: "zigbee" })} value={groupId} groups={nonMemberGroups} onChange={onGroupChange} required /> +
+ $.group, { ns: "zigbee" })} groups={nonMemberGroups} onChange={onGroupChange} required /> $.endpoint, { ns: "zigbee" })} values={endpoints} diff --git a/src/components/device-page/AttributeEditor.tsx b/src/components/device-page/AttributeEditor.tsx index 24d19af72..bd5201642 100644 --- a/src/components/device-page/AttributeEditor.tsx +++ b/src/components/device-page/AttributeEditor.tsx @@ -3,6 +3,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type ChangeEvent, type JSX, memo, useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import type { Zigbee2MQTTAPI } from "zigbee2mqtt"; +import { useShallow } from "zustand/react/shallow"; +import { useAppStore } from "../../store.js"; import type { AttributeDefinition, Device, LogMessage } from "../../types.js"; import { getEndpoints, getObjectFirstKey } from "../../utils.js"; import Button from "../Button.js"; @@ -11,6 +13,7 @@ import InputField from "../form-fields/InputField.js"; import AttributePicker from "../pickers/AttributePicker.js"; import ClusterSinglePicker from "../pickers/ClusterSinglePicker.js"; import EndpointPicker from "../pickers/EndpointPicker.js"; +import type { ClusterGroup } from "../pickers/index.js"; import LastLogResult from "./LastLogResult.js"; export interface AttributeEditorProps { @@ -55,6 +58,7 @@ function AttributeValueInput({ value, onChange, attribute, definition, ...rest } } const AttributeEditor = memo(({ sourceIdx, device, read, write, readReporting, lastLog }: AttributeEditorProps) => { + const bridgeDefinitions = useAppStore(useShallow((state) => state.bridgeDefinitions[sourceIdx])); const [endpoint, setEndpoint] = useState(getObjectFirstKey(device.endpoints) ?? ""); const [cluster, setCluster] = useState(""); const [attributes, setAttributes] = useState([]); @@ -149,21 +153,57 @@ const AttributeEditor = memo(({ sourceIdx, device, read, write, readReporting, l ), [attributes], ); - const availableClusters = useMemo(() => { - const clusters = new Set(); + const availableClusters = useMemo((): ClusterGroup[] => { + const deviceInputs = new Set(); + const deviceCustoms = new Set(); + const otherZcls = new Set(); if (endpoint) { const deviceEndpoint = device.endpoints[Number.parseInt(endpoint, 10)]; + const uniqueClusters = new Set(); + + const customClusters = bridgeDefinitions.custom_clusters[device.ieee_address]; + + if (customClusters) { + for (const key in bridgeDefinitions.custom_clusters[device.ieee_address]) { + if (!uniqueClusters.has(key)) { + uniqueClusters.add(key); + deviceCustoms.add(key); + } + } + } if (deviceEndpoint) { for (const inCluster of deviceEndpoint.clusters.input) { - clusters.add(inCluster); + if (!uniqueClusters.has(inCluster)) { + uniqueClusters.add(inCluster); + deviceInputs.add(inCluster); + } + } + } + + for (const key in bridgeDefinitions.clusters) { + if (!uniqueClusters.has(key)) { + otherZcls.add(key); } } } - return clusters; - }, [device, endpoint]); + return [ + { + name: "custom_clusters", + clusters: deviceCustoms, + }, + { + name: "input_clusters", + clusters: deviceInputs, + }, + { + name: "other_zcl_clusters", + clusters: otherZcls, + }, + ]; + }, [device, endpoint, bridgeDefinitions]); const disableButtons = attributes.length === 0 || cluster === ""; const endpoints = useMemo(() => getEndpoints(device), [device]); diff --git a/src/components/device-page/CommandExecutor.tsx b/src/components/device-page/CommandExecutor.tsx index 5965b2634..d5ebe137a 100644 --- a/src/components/device-page/CommandExecutor.tsx +++ b/src/components/device-page/CommandExecutor.tsx @@ -55,22 +55,10 @@ const CommandExecutor = memo(({ sourceIdx, device, lastLog }: CommandExecutorPro const deviceCustoms = new Set(); const otherZcls = new Set(); - if (deviceEndpoint) { - for (const inCluster of deviceEndpoint.clusters.input) { - uniqueClusters.add(inCluster); - deviceInputs.add(inCluster); - } - - for (const outCluster of deviceEndpoint.clusters.output) { - uniqueClusters.add(outCluster); - deviceOutputs.add(outCluster); - } - } - - const customClusters = bridgeDefinitions.custom_clusters[device.friendly_name]; + const customClusters = bridgeDefinitions.custom_clusters[device.ieee_address]; if (customClusters) { - for (const key in bridgeDefinitions.custom_clusters[device.friendly_name]) { + for (const key in bridgeDefinitions.custom_clusters[device.ieee_address]) { if (!uniqueClusters.has(key)) { uniqueClusters.add(key); deviceCustoms.add(key); @@ -78,6 +66,22 @@ const CommandExecutor = memo(({ sourceIdx, device, lastLog }: CommandExecutorPro } } + if (deviceEndpoint) { + for (const inCluster of deviceEndpoint.clusters.input) { + if (!uniqueClusters.has(inCluster)) { + uniqueClusters.add(inCluster); + deviceInputs.add(inCluster); + } + } + + for (const outCluster of deviceEndpoint.clusters.output) { + if (!uniqueClusters.has(outCluster)) { + uniqueClusters.add(outCluster); + deviceOutputs.add(outCluster); + } + } + } + for (const key in bridgeDefinitions.clusters) { if (!uniqueClusters.has(key)) { otherZcls.add(key); @@ -85,6 +89,10 @@ const CommandExecutor = memo(({ sourceIdx, device, lastLog }: CommandExecutorPro } return [ + { + name: "custom_clusters", + clusters: deviceCustoms, + }, { name: "input_clusters", clusters: deviceInputs, @@ -93,16 +101,12 @@ const CommandExecutor = memo(({ sourceIdx, device, lastLog }: CommandExecutorPro name: "output_clusters", clusters: deviceOutputs, }, - { - name: "custom_clusters", - clusters: deviceCustoms, - }, { name: "other_zcl_clusters", clusters: otherZcls, }, ]; - }, [device.friendly_name, device.endpoints, endpoint, bridgeDefinitions]); + }, [device.ieee_address, device.endpoints, endpoint, bridgeDefinitions]); const onExecute = useCallback(async () => { let commandKey: string | number = Number.parseInt(command, 10); diff --git a/src/components/device-page/HeaderDeviceSelector.tsx b/src/components/device-page/HeaderDeviceSelector.tsx index fe1ab1ed7..0039132e3 100644 --- a/src/components/device-page/HeaderDeviceSelector.tsx +++ b/src/components/device-page/HeaderDeviceSelector.tsx @@ -1,14 +1,11 @@ -import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { type JSX, memo, useMemo } from "react"; +import { memo, type ReactNode, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Link } from "react-router"; -import { useSearch } from "../../hooks/useSearch.js"; +import { useNavigate } from "react-router"; +import Select, { type SingleValue } from "react-select"; +import { REACT_SELECT_DEFAULT_CLASSNAMES } from "../../consts.js"; import type { TabName } from "../../pages/DevicePage.js"; import { API_URLS, useAppStore } from "../../store.js"; -import type { Device } from "../../types.js"; -import DialogDropdown from "../DialogDropdown.js"; -import DebouncedInput from "../form-fields/DebouncedInput.js"; +import type { BaseSelectOption, Device } from "../../types.js"; import SourceDot from "../SourceDot.js"; interface HeaderDeviceSelectorProps { @@ -17,13 +14,27 @@ interface HeaderDeviceSelectorProps { tab?: TabName; } +interface SelectOption extends BaseSelectOption { + name: string; + link: string; +} + const HeaderDeviceSelector = memo(({ currentSourceIdx, currentDevice, tab = "info" }: HeaderDeviceSelectorProps) => { - const [searchTerm, normalizedSearchTerm, setSearchTerm] = useSearch(); const { t } = useTranslation("common"); const devices = useAppStore((state) => state.devices); + const navigate = useNavigate(); + + const onSelectHandler = useCallback( + (option: SingleValue) => { + if (option) { + navigate(option.link); + } + }, + [navigate], + ); - const items = useMemo(() => { - const elements: JSX.Element[] = []; + const options = useMemo(() => { + const elements: SelectOption[] = []; for (let sourceIdx = 0; sourceIdx < API_URLS.length; sourceIdx++) { for (const device of devices[sourceIdx]) { @@ -31,40 +42,43 @@ const HeaderDeviceSelector = memo(({ currentSourceIdx, currentDevice, tab = "inf continue; } - if (normalizedSearchTerm.length > 0 && !device.friendly_name.toLowerCase().includes(normalizedSearchTerm)) { - continue; - } - - elements.push( -
  • - setSearchTerm("")} className="dropdown-item"> + elements.push({ + value: device.friendly_name, + label: ( + <> {device.friendly_name} - -
  • , - ); + + ), + name: `${sourceIdx} ${device.friendly_name}`, + link: `/device/${sourceIdx}/${device.ieee_address}/${tab}`, + }); } } - elements.sort((elA, elB) => elA.key!.localeCompare(elB.key!)); + elements.sort((elA, elB) => elA.name.localeCompare(elB.name)); return elements; - }, [devices, normalizedSearchTerm, currentSourceIdx, currentDevice, tab, setSearchTerm]); + }, [devices, currentSourceIdx, currentDevice, tab]); return ( - - {currentSourceIdx !== undefined && } - {currentDevice ? currentDevice.friendly_name : t(($) => $.unknown_device)} - + + {currentGroup.friendly_name} + + ) : ( + t(($) => $.select_group) + ) } - > - - {items} - + aria-label={t(($) => $.select_group)} + options={options} + isSearchable + onChange={onSelectHandler} + className="min-w-48 sm:w-auto me-2" + classNames={REACT_SELECT_DEFAULT_CLASSNAMES} + /> ); }); diff --git a/src/components/home-page/Activity.tsx b/src/components/home-page/Activity.tsx index 07553e3e0..2392c3f3d 100644 --- a/src/components/home-page/Activity.tsx +++ b/src/components/home-page/Activity.tsx @@ -33,7 +33,7 @@ const Activity = memo(({ devices, maxRows }: ActivityProps) => { key={`${entry.friendlyName}-${entry.sourceIdx}-${entry.activity}-${entry.time}-${i}`} className="flex flex-row gap-1 items-center w-full px-1 rounded-field hover:bg-base-200" > -
    +
    {ieeeAddress ? ( { ) : ( {entry.friendlyName} )} -
    -
    -

    — {entry.activity}

    + — {entry.activity}

    {entry.time}

    diff --git a/src/components/home-page/Hero.tsx b/src/components/home-page/Hero.tsx index 5db183db6..79f53b871 100644 --- a/src/components/home-page/Hero.tsx +++ b/src/components/home-page/Hero.tsx @@ -54,7 +54,7 @@ const Hero = memo(

    {t(($) => $.overview)}

    {MULTI_INSTANCE ? ( -
    +
    {t(($) => $.instances)}
    @@ -77,7 +77,7 @@ const Hero = memo(
    ) : null} -
    +
    {t(($) => $.devices)}
    {totalDevices}
    @@ -99,7 +99,7 @@ const Hero = memo(
    {anyAvailabilityEnabled ? ( -
    +
    {t(($) => $.online, { ns: "availability" })}
    @@ -129,7 +129,7 @@ const Hero = memo(
    ) : null} -
    +
    {t(($) => $.Router, { ns: "zigbee" })}
    {routers}
    @@ -145,7 +145,7 @@ const Hero = memo(
    -
    +
    {t(($) => $.EndDevice, { ns: "zigbee" })}
    {endDevices}
    @@ -162,7 +162,7 @@ const Hero = memo(
    {gpDevices > 0 && ( -
    +
    {t(($) => $.Router, { ns: "zigbee" })} - {t(($) => $.GreenPower, { ns: "zigbee" })} @@ -182,7 +182,7 @@ const Hero = memo(
    )} {lowLqiDevices > 0 && ( -
    +
    {t(($) => $.low_lqi, { ns: "zigbee" })}
    {lowLqiDevices}
    diff --git a/src/components/pickers/AttributePicker.tsx b/src/components/pickers/AttributePicker.tsx index bfe39b9bc..8e730bf4c 100644 --- a/src/components/pickers/AttributePicker.tsx +++ b/src/components/pickers/AttributePicker.tsx @@ -1,20 +1,24 @@ -import { type ChangeEvent, type InputHTMLAttributes, type JSX, memo, useMemo } from "react"; +import { memo, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import Select, { type SingleValue } from "react-select"; import { useShallow } from "zustand/react/shallow"; +import { REACT_SELECT_DEFAULT_CLASSNAMES } from "../../consts.js"; import { useAppStore } from "../../store.js"; -import type { AttributeDefinition, Device } from "../../types.js"; -import SelectField from "../form-fields/SelectField.js"; +import type { AttributeDefinition, BaseSelectOption, Device } from "../../types.js"; import { getClusterAttributes } from "../reporting/index.js"; -interface AttributePickerProps extends Omit, "onChange"> { +interface AttributePickerProps { sourceIdx: number; cluster: string; device: Device; + value: string | number; label?: string; + required?: boolean; + disabled?: boolean; onChange: (attr: string, definition: AttributeDefinition) => void; } -const AttributePicker = memo(({ sourceIdx, cluster, device, onChange, label, ...rest }: AttributePickerProps) => { +const AttributePicker = memo(({ sourceIdx, cluster, device, onChange, value, label, required, disabled }: AttributePickerProps) => { const bridgeDefinitions = useAppStore(useShallow((state) => state.bridgeDefinitions[sourceIdx])); const { t } = useTranslation("zigbee"); @@ -25,33 +29,45 @@ const AttributePicker = memo(({ sourceIdx, cluster, device, onChange, label, ... ); const options = useMemo(() => { - const attrs: JSX.Element[] = []; + const attrs: BaseSelectOption[] = []; for (const key in clusterAttributes) { - attrs.push( - , - ); + attrs.push({ value: key, label: key }); } return attrs; }, [clusterAttributes]); + const selected = useMemo>( + () => (value == null || value === "" ? null : (options.find((o) => o.value === value) ?? null)), + [value, options], + ); + return ( - ): void => onChange(e.target.value, clusterAttributes[e.target.value])} - disabled={options.length === 0} - className="select validator w-64" - {...rest} - > - - {options} - +
    + {label && ( + + {label} + {required ? " *" : ""} + + )} + - {cluster} - - )), - [clusters, onChangeHandler, value, disabled], + const selected = useMemo>( + () => (value == null ? [] : options.filter((o) => value.includes(o.value))), + [value, options], ); return (
    - {label && {label}} -
    {options}
    + {label && ( + + {label} + {required ? " *" : ""} + + )} + $.select_cluster)} + aria-label={label ?? t(($) => $.select_cluster)} + options={options} + value={selected} + isSearchable + isDisabled={disabled} + onChange={(option) => { + if (option != null) { + onChange(option.value); + } + }} + className="min-w-64" + classNames={REACT_SELECT_DEFAULT_CLASSNAMES} + /> +
    ); }); diff --git a/src/components/pickers/DevicePicker.tsx b/src/components/pickers/DevicePicker.tsx index 6feb19a96..b0ac104e4 100644 --- a/src/components/pickers/DevicePicker.tsx +++ b/src/components/pickers/DevicePicker.tsx @@ -1,96 +1,94 @@ -import { type ChangeEvent, type JSX, memo, type SelectHTMLAttributes, useEffect, useMemo, useState } from "react"; +import { memo, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import Select, { type SingleValue } from "react-select"; +import { REACT_SELECT_DEFAULT_CLASSNAMES } from "../../consts.js"; import type { AppState } from "../../store.js"; -import type { Device, Group } from "../../types.js"; -import SelectField from "../form-fields/SelectField.js"; +import type { BaseGroupedOption, BaseSelectOption, Device, Group } from "../../types.js"; -interface DevicePickerProps extends Omit, "onChange"> { +interface DevicePickerProps { devices: AppState["devices"][number]; value: string | number; label?: string; + detail?: string; + required?: boolean; + disabled?: boolean; groups?: Group[]; onChange(device?: Device | Group): void; } -const DevicePicker = memo(({ devices, value, label, onChange, groups = [], ...rest }: DevicePickerProps) => { +const DevicePicker = memo(({ devices, value, label, detail, required, disabled, onChange, groups = [] }: DevicePickerProps) => { const { t } = useTranslation("common"); - const [selectedName, setSelectedName] = useState(""); - useEffect(() => { - if (typeof value === "string") { - const device = devices.find((device) => device.ieee_address === value); + const onSelectHandler = (option: SingleValue): void => { + if (option) { + if (option.value.startsWith("0x") /* ieee */) { + onChange(devices.find((device) => device.ieee_address === option.value)); + } else { + const selectedId = Number.parseInt(option.value, 10); - setSelectedName(device?.friendly_name ?? ""); - } else { - const group = groups.find((g) => value === g.id); - - setSelectedName(group?.friendly_name ?? ""); - } - }, [value, devices, groups]); - - const onSelectHandler = (e: ChangeEvent): void => { - const { value: selectedValue } = e.target; - - if (selectedValue.startsWith("0x") /* ieee */) { - onChange(devices.find((device) => device.ieee_address === selectedValue)); - } else { - const selectedId = Number.parseInt(selectedValue, 10); - - onChange(groups.find((g) => selectedId === g.id)); + onChange(groups.find((g) => selectedId === g.id)); + } } }; const options = useMemo(() => { - const options: JSX.Element[] = []; - const devicesOptions = devices - .map((device) => ( - - )) - .sort((elA, elB) => elA.key!.localeCompare(elB.key!)); + // should always be: [0] devices, [1] groups + const options: BaseGroupedOption[] = []; + const devicesOptions: BaseSelectOption[] = devices + .map((device) => ({ + value: device.ieee_address, + label: `${device.friendly_name} ${device.definition?.model ? `(${device.definition?.model})` : ""}`, + })) + .sort((elA, elB) => elA.label.localeCompare(elB.label)); if (groups?.length) { - const groupOptions = groups - .map((group) => ( - - )) - .sort((elA, elB) => elA.key!.localeCompare(elB.key!)); + const groupOptions: BaseSelectOption[] = groups + .map((group) => ({ value: `${group.id}`, label: group.friendly_name })) + .sort((elA, elB) => elA.label.localeCompare(elB.label)); - options.push( - $.groups)}> - {groupOptions} - , - ); - options.push( - $.devices)}> - {devicesOptions} - , - ); + options.push({ label: t(($) => $.devices), options: devicesOptions }); + options.push({ label: t(($) => $.groups), options: groupOptions }); } else { - options.push(...devicesOptions); + options.push({ label: t(($) => $.devices), options: devicesOptions }); } return options; }, [devices, groups, t]); + const selected = useMemo>(() => { + if (value == null || value === "") { + return null; + } + + if (typeof value === "number") { + return options[1]?.options.find((o) => o.value === `${value}`) ?? null; + } + + return options[0]?.options.find((o) => o.value === value) ?? null; + }, [value, options]); + return ( - - - {options} - +
    + {label && ( + + {label} + {required ? " *" : ""} + + )} + $.select_group)} + aria-label={label ?? t(($) => $.select_group)} + options={options} + isSearchable + isDisabled={disabled} + onChange={onSelectHandler} + className="min-w-64" + classNames={REACT_SELECT_DEFAULT_CLASSNAMES} + /> +
    ); }); diff --git a/src/components/pickers/ScenePicker.tsx b/src/components/pickers/ScenePicker.tsx index d9b65d37d..cf05cfd85 100644 --- a/src/components/pickers/ScenePicker.tsx +++ b/src/components/pickers/ScenePicker.tsx @@ -1,44 +1,44 @@ -import { memo } from "react"; +import { memo, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import type { Scene } from "../../types.js"; -import InputField from "../form-fields/InputField.js"; -import SelectField from "../form-fields/SelectField.js"; +import CreatableSelect from "react-select/creatable"; +import { REACT_SELECT_DEFAULT_CLASSNAMES } from "../../consts.js"; +import type { BaseSelectOption, Scene } from "../../types.js"; type ScenePickerProps = { - value?: Scene; scenes: Scene[]; + disabled?: boolean; onSceneSelected: (sceneId: number) => void; }; -const ScenePicker = memo(({ onSceneSelected, scenes = [], value }: ScenePickerProps) => { +const ScenePicker = memo(({ onSceneSelected, scenes = [], disabled }: ScenePickerProps) => { const { t } = useTranslation("scene"); - return scenes.length > 0 ? ( - $.scene_name)} - value={value?.id ?? ""} - onChange={(e) => !e.target.validationMessage && !!e.target.value && onSceneSelected(Number.parseInt(e.target.value, 10))} - > - - {scenes.map((scene) => ( - - ))} - - ) : ( - $.scene_id)} - type="number" - value={value?.id ?? ""} - onChange={(e) => !e.target.validationMessage && !!e.target.value && onSceneSelected(e.target.valueAsNumber)} - min={0} - max={255} - /> + const options = useMemo( + (): BaseSelectOption[] => + scenes + .map((scene) => ({ value: `${scene.id}`, label: `${scene.id}: ${scene.name}` })) + .sort((elA, elB) => elA.label.localeCompare(elB.label)), + [scenes], + ); + + return ( +
    + $.scene_name)} + aria-label={t(($) => $.scene_name)} + options={options} + isSearchable + isDisabled={disabled} + onChange={(option) => { + if (option != null) { + onSceneSelected(Number.parseInt(option.value, 10)); + } + }} + className="min-w-64" + classNames={REACT_SELECT_DEFAULT_CLASSNAMES} + /> +
    ); }); diff --git a/src/consts.ts b/src/consts.ts index f922aa7a2..d57a6c96d 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -80,3 +80,29 @@ export enum InterviewState { Successful = "SUCCESSFUL", Failed = "FAILED", } + +export const REACT_SELECT_DEFAULT_CLASSNAMES = { + clearIndicator: () => "text-base-content/70 hover:text-error cursor-pointer", + container: () => "w-full !max-w-full", + control: ({ isDisabled }) => `w-full input input-bordered !h-auto ${isDisabled ? "pointer-events-none opacity-50" : ""}`, + dropdownIndicator: ({ isFocused }) => `text-base-content ${isFocused ? "text-primary" : ""}`, + // group: () => "", + groupHeading: () => "px-1 py-2 text-xs font-semibold uppercase text-base-content/70", + indicatorsContainer: () => "flex items-center", + indicatorSeparator: () => "hidden", + input: () => "text-base-content", + // loadingIndicator: () => "", + // loadingMessage: () => "", + menu: () => "!min-w-full !w-fit dropdown-content menu p-0 bg-base-100 text-base-content shadow-md border border-base-300 rounded-box !z-2", + menuList: () => "max-h-60 overflow-auto", + // menuPortal: () => "", + multiValue: () => "p-1 bg-base-200 text-base-content rounded-box", + // multiValueLabel: () => "", + multiValueRemove: () => "p-0 ps-1 text-base-content/50 hover:text-error cursor-pointer", + noOptionsMessage: () => "px-2 py-2 text-base-content/70", + option: ({ isFocused, isSelected }) => + `px-3 py-2 cursor-pointer ${isSelected ? "bg-primary text-primary-content" : isFocused ? "bg-primary/10" : ""}`, + placeholder: () => "text-base-content/60 truncate", + singleValue: () => "text-base-content truncate", + valueContainer: () => "gap-1 py-1 min-w-0", +}; diff --git a/src/i18n/locales/bg.json b/src/i18n/locales/bg.json index d2f3a57c7..485516f6f 100644 --- a/src/i18n/locales/bg.json +++ b/src/i18n/locales/bg.json @@ -386,8 +386,8 @@ "save_description": "Запази описание", "update_description": "Обнови описание", "read_write_attributes": "Четене/писане атрибути на endpoint клъстер", - "input_clusters": "Входни клъстери", - "output_clusters": "Изходни клъстери", + "input_clusters": "Входни клъстери (Server)", + "output_clusters": "Изходни клъстери (Client)", "custom_clusters": "Потребителски клъстери", "other_zcl_clusters": "Други ZCL клъстери", "how_to_add_support": "Как да добавя поддръжка?", diff --git a/src/i18n/locales/ca.json b/src/i18n/locales/ca.json index 44bd00b80..54fd60300 100644 --- a/src/i18n/locales/ca.json +++ b/src/i18n/locales/ca.json @@ -386,8 +386,8 @@ "save_description": "Desa descripció", "update_description": "Actualitza descripció", "read_write_attributes": "Llegir / Escriure atributs del cluster", - "input_clusters": "Clusters entrada", - "output_clusters": "Clusters sortida", + "input_clusters": "Clusters entrada (Server)", + "output_clusters": "Clusters sortida (Client)", "custom_clusters": "Clusters personalitzats", "other_zcl_clusters": "Altres clusters ZCL", "how_to_add_support": "Com afegir suport?", diff --git a/src/i18n/locales/cs.json b/src/i18n/locales/cs.json index e23eb737c..dde046101 100644 --- a/src/i18n/locales/cs.json +++ b/src/i18n/locales/cs.json @@ -386,8 +386,8 @@ "save_description": "Uložit popis", "update_description": "Aktualizovat popis", "read_write_attributes": "Číst / zapisovat vlastnosti (atributy) clusteru endpointu", - "input_clusters": "Vstupní clustery", - "output_clusters": "Výstupní clustery", + "input_clusters": "Vstupní clustery (Server)", + "output_clusters": "Výstupní clustery (Client)", "custom_clusters": "Vlastní clustery", "other_zcl_clusters": "Další ZCL clustery", "how_to_add_support": "Jak přidat podporu?", diff --git a/src/i18n/locales/da.json b/src/i18n/locales/da.json index b8f9e2d81..d4c14b244 100644 --- a/src/i18n/locales/da.json +++ b/src/i18n/locales/da.json @@ -386,8 +386,8 @@ "save_description": "Gem beskrivelse", "update_description": "Opdater beskrivelse", "read_write_attributes": "Læs/Skriv endpoint-klynge attributter", - "input_clusters": "Input klynger", - "output_clusters": "Output klynger", + "input_clusters": "Input klynger (Server)", + "output_clusters": "Output klynger (Client)", "custom_clusters": "Custom klynger", "other_zcl_clusters": "Andre ZCL klynger", "how_to_add_support": "Hvordan tilføje support?", diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index d7e4f88c5..ac1755191 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -386,8 +386,8 @@ "save_description": "Beschreibung speichern", "update_description": "Beschreibung aktualisieren", "read_write_attributes": "Attribute vom Endpoint-Cluster lesen/schreiben", - "input_clusters": "Eingangs-Cluster", - "output_clusters": "Ausgangs-Cluster", + "input_clusters": "Eingangs-Cluster (Server)", + "output_clusters": "Ausgangs-Cluster (Client)", "custom_clusters": "Benutzerdefinierte Cluster", "other_zcl_clusters": "Andere ZCL-Cluster", "how_to_add_support": "Wie füge ich Unterstützung hinzu?", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 6b19d8101..8261b8bb4 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -386,8 +386,8 @@ "save_description": "Save description", "update_description": "Update description", "read_write_attributes": "Read / Write attributes from endpoint cluster", - "input_clusters": "Input clusters", - "output_clusters": "Output clusters", + "input_clusters": "Input clusters (Server)", + "output_clusters": "Output clusters (Client)", "custom_clusters": "Custom clusters", "other_zcl_clusters": "Other ZCL clusters", "how_to_add_support": "How to add support?", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index b76ca1bba..b0132b352 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -386,8 +386,8 @@ "save_description": "Guardar descripción", "update_description": "Actualizar descripción", "read_write_attributes": "Leer / Escribir atributos del cluster del endpoint", - "input_clusters": "Clusters de entrada", - "output_clusters": "Clusters de salida", + "input_clusters": "Clusters de entrada (Server)", + "output_clusters": "Clusters de salida (Client)", "custom_clusters": "Clusters personalizados", "other_zcl_clusters": "Otros clusters ZCL", "how_to_add_support": "¿Cómo añadir soporte?", diff --git a/src/i18n/locales/eu.json b/src/i18n/locales/eu.json index c2c1cde79..a80ea87b5 100644 --- a/src/i18n/locales/eu.json +++ b/src/i18n/locales/eu.json @@ -386,8 +386,8 @@ "save_description": "Gorde deskribapena", "update_description": "Eguneratu deskribapena", "read_write_attributes": "Irakurri/Idatzi endpoint cluster atributuak", - "input_clusters": "Sarrerako clusterrak", - "output_clusters": "Irteteko clusterrak", + "input_clusters": "Sarrerako clusterrak (Server)", + "output_clusters": "Irteteko clusterrak (Client)", "custom_clusters": "Pertsonalizatuak", "other_zcl_clusters": "Beste ZCL clusterrak", "how_to_add_support": "Nola gehitu euskarria?", diff --git a/src/i18n/locales/fi.json b/src/i18n/locales/fi.json index 2c7bcac59..400b7df23 100644 --- a/src/i18n/locales/fi.json +++ b/src/i18n/locales/fi.json @@ -386,8 +386,8 @@ "save_description": "Tallenna kuvaus", "update_description": "Päivitä kuvaus", "read_write_attributes": "Lue/kirjoita endpoint-cluster attribuutteja", - "input_clusters": "Tulo-clusterit", - "output_clusters": "Lähtö-clusterit", + "input_clusters": "Tulo-clusterit (Server)", + "output_clusters": "Lähtö-clusterit (Client)", "custom_clusters": "Mukautetut clusterit", "other_zcl_clusters": "Muut ZCL-clusterit", "how_to_add_support": "Miten lisään tuen?", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index e87cbb374..fcfb4bd1b 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -386,8 +386,8 @@ "save_description": "Enregistrer la description", "update_description": "Mettre à jour la description", "read_write_attributes": "Lire/écrire les attributs du cluster de point de terminaison", - "input_clusters": "Clusters d'entrée", - "output_clusters": "Clusters de sortie", + "input_clusters": "Clusters d'entrée (Server)", + "output_clusters": "Clusters de sortie (Client)", "custom_clusters": "Clusters personnalisés", "other_zcl_clusters": "Autres clusters ZCL", "how_to_add_support": "Comment ajouter la prise en charge ?", diff --git a/src/i18n/locales/hu.json b/src/i18n/locales/hu.json index 402eb1cf3..68046179a 100644 --- a/src/i18n/locales/hu.json +++ b/src/i18n/locales/hu.json @@ -386,8 +386,8 @@ "save_description": "Leírás mentése", "update_description": "Leírás frissítése", "read_write_attributes": "Endpoint cluster attribútumok olvas/írás", - "input_clusters": "Bemeneti clusterek", - "output_clusters": "Kimeneti clusterek", + "input_clusters": "Bemeneti clusterek (Server)", + "output_clusters": "Kimeneti clusterek (Client)", "custom_clusters": "Egyedi clusterek", "other_zcl_clusters": "Egyéb ZCL clusterek", "how_to_add_support": "Hogyan adhatok támogatást?", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index db94fd4ca..576aec212 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -386,8 +386,8 @@ "save_description": "Salva descrizione", "update_description": "Aggiorna descrizione", "read_write_attributes": "Leggi / Scrivi attributi dal cluster endpoint", - "input_clusters": "Cluster input", - "output_clusters": "Cluster output", + "input_clusters": "Cluster input (Server)", + "output_clusters": "Cluster output (Client)", "custom_clusters": "Cluster personalizzati", "other_zcl_clusters": "Altri cluster ZCL", "how_to_add_support": "Come aggiungere supporto?", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index eba89aa02..3bf05ad95 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -386,8 +386,8 @@ "save_description": "説明保存", "update_description": "説明更新", "read_write_attributes": "エンドポイントクラスタの属性 読み/書き", - "input_clusters": "入力クラスタ", - "output_clusters": "出力クラスタ", + "input_clusters": "入力クラスタ (Server)", + "output_clusters": "出力クラスタ (Client)", "custom_clusters": "カスタムクラスタ", "other_zcl_clusters": "その他 ZCL クラスタ", "how_to_add_support": "サポート追加方法?", diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index 583a94bee..f57063d75 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json @@ -386,8 +386,8 @@ "save_description": "설명 저장", "update_description": "설명 업데이트", "read_write_attributes": "엔드포인트 클러스터 속성 읽기/쓰기", - "input_clusters": "입력 클러스터", - "output_clusters": "출력 클러스터", + "input_clusters": "입력 클러스터 (Server)", + "output_clusters": "출력 클러스터 (Client)", "custom_clusters": "사용자 정의 클러스터", "other_zcl_clusters": "기타 ZCL 클러스터", "how_to_add_support": "지원 추가 방법?", diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json index c98634ba2..82c2dedbd 100644 --- a/src/i18n/locales/nl.json +++ b/src/i18n/locales/nl.json @@ -386,8 +386,8 @@ "save_description": "Beschrijving opslaan", "update_description": "Beschrijving bijwerken", "read_write_attributes": "Endpoint-cluster attributen lezen/schrijven", - "input_clusters": "Ingangsclusters", - "output_clusters": "Uitgangsclusters", + "input_clusters": "Ingangsclusters (Server)", + "output_clusters": "Uitgangsclusters (Client)", "custom_clusters": "Aangepaste clusters", "other_zcl_clusters": "Andere ZCL-clusters", "how_to_add_support": "Hoe ondersteuning toevoegen?", diff --git a/src/i18n/locales/no.json b/src/i18n/locales/no.json index 545acf57d..3baec28eb 100644 --- a/src/i18n/locales/no.json +++ b/src/i18n/locales/no.json @@ -386,8 +386,8 @@ "save_description": "Lagre beskrivelse", "update_description": "Oppdater beskrivelse", "read_write_attributes": "Les/skriv endpoint cluster attributter", - "input_clusters": "Inngående clustere", - "output_clusters": "Utgående clustere", + "input_clusters": "Inngående clustere (Server)", + "output_clusters": "Utgående clustere (Client)", "custom_clusters": "Egendefinerte clustere", "other_zcl_clusters": "Andre ZCL-clustere", "how_to_add_support": "Hvordan legge til støtte?", diff --git a/src/i18n/locales/pl.json b/src/i18n/locales/pl.json index 96ac6a619..183c005a3 100644 --- a/src/i18n/locales/pl.json +++ b/src/i18n/locales/pl.json @@ -386,8 +386,8 @@ "save_description": "Zapisz opis", "update_description": "Aktualizuj opis", "read_write_attributes": "Odczyt/zapis atrybutów klastra endpoint", - "input_clusters": "Klastry wejściowe", - "output_clusters": "Klastry wyjściowe", + "input_clusters": "Klastry wejściowe (Server)", + "output_clusters": "Klastry wyjściowe (Client)", "custom_clusters": "Klastry własne", "other_zcl_clusters": "Inne klastry ZCL", "how_to_add_support": "Jak dodać wsparcie?", diff --git a/src/i18n/locales/ptbr.json b/src/i18n/locales/ptbr.json index 5073ab22f..2ca40cdb9 100644 --- a/src/i18n/locales/ptbr.json +++ b/src/i18n/locales/ptbr.json @@ -386,8 +386,8 @@ "save_description": "Salvar descrição", "update_description": "Atualizar descrição", "read_write_attributes": "Ler / Escrever atributos do cluster do endpoint", - "input_clusters": "Clusters de entrada", - "output_clusters": "Clusters de saída", + "input_clusters": "Clusters de entrada (Server)", + "output_clusters": "Clusters de saída (Client)", "custom_clusters": "Clusters personalizados", "other_zcl_clusters": "Outros clusters ZCL", "how_to_add_support": "Como adicionar suporte?", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 9ed2733ac..517581bb1 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -386,8 +386,8 @@ "save_description": "Сохранить описание", "update_description": "Обновить описание", "read_write_attributes": "Чтение/запись атрибутов кластера endpoint", - "input_clusters": "Входные кластеры", - "output_clusters": "Выходные кластеры", + "input_clusters": "Входные кластеры (Server)", + "output_clusters": "Выходные кластеры (Client)", "custom_clusters": "Пользовательские кластеры", "other_zcl_clusters": "Другие ZCL кластеры", "how_to_add_support": "Как добавить поддержку?", diff --git a/src/i18n/locales/sk.json b/src/i18n/locales/sk.json index a8f88df30..8c22c4c2e 100644 --- a/src/i18n/locales/sk.json +++ b/src/i18n/locales/sk.json @@ -386,8 +386,8 @@ "save_description": "Uložiť popis", "update_description": "Aktualizovať popis", "read_write_attributes": "Atribúty na čítanie / zápis z klastra koncového bodu", - "input_clusters": "Vstupné klastre", - "output_clusters": "Výstupné klastre", + "input_clusters": "Vstupné klastre (Server)", + "output_clusters": "Výstupné klastre (Client)", "custom_clusters": "Vlastné klastre", "other_zcl_clusters": "Ostatné ZCL klastre", "how_to_add_support": "Ako pridať podporu?", diff --git a/src/i18n/locales/sv.json b/src/i18n/locales/sv.json index 637ae5ced..dc6e67da1 100644 --- a/src/i18n/locales/sv.json +++ b/src/i18n/locales/sv.json @@ -386,8 +386,8 @@ "save_description": "Spara beskrivning", "update_description": "Uppdatera beskrivning", "read_write_attributes": "Läs/Skriv endpoint-klusterattribut", - "input_clusters": "Ingångskluster", - "output_clusters": "Utgångskluster", + "input_clusters": "Ingångskluster (Server)", + "output_clusters": "Utgångskluster (Client)", "custom_clusters": "Anpassade kluster", "other_zcl_clusters": "Andra ZCL-kluster", "how_to_add_support": "Hur lägger man till stöd?", diff --git a/src/i18n/locales/tr.json b/src/i18n/locales/tr.json index 27ca14632..8a445922d 100644 --- a/src/i18n/locales/tr.json +++ b/src/i18n/locales/tr.json @@ -386,8 +386,8 @@ "save_description": "Açıklamayı kaydet", "update_description": "Açıklamayı güncelle", "read_write_attributes": "Endpoint küme özniteliklerini oku/yaz", - "input_clusters": "Giriş kümeleri", - "output_clusters": "Çıkış kümeleri", + "input_clusters": "Giriş kümeleri (Server)", + "output_clusters": "Çıkış kümeleri (Client)", "custom_clusters": "Özel kümeler", "other_zcl_clusters": "Diğer ZCL kümeleri", "how_to_add_support": "Destek nasıl eklenir?", diff --git a/src/i18n/locales/ua.json b/src/i18n/locales/ua.json index 65959019b..40acded1a 100644 --- a/src/i18n/locales/ua.json +++ b/src/i18n/locales/ua.json @@ -386,8 +386,8 @@ "save_description": "Зберегти опис", "update_description": "Оновити опис", "read_write_attributes": "Читати/писати атрибути endpoint кластера", - "input_clusters": "Вхідні кластери", - "output_clusters": "Вихідні кластери", + "input_clusters": "Вхідні кластери (Server)", + "output_clusters": "Вихідні кластери (Client)", "custom_clusters": "Користувацькі кластери", "other_zcl_clusters": "Інші ZCL кластери", "how_to_add_support": "Як додати підтримку?", diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index 23b303453..20e4b8e46 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -386,8 +386,8 @@ "save_description": "Lưu mô tả", "update_description": "Cập nhật mô tả", "read_write_attributes": "Đọc / Ghi thuộc tính từ cụm điểm cuối", - "input_clusters": "Cụm đầu vào", - "output_clusters": "Cụm đầu ra", + "input_clusters": "Cụm đầu vào (Server)", + "output_clusters": "Cụm đầu ra (Client)", "custom_clusters": "Cụm tùy chỉnh", "other_zcl_clusters": "Các cụm ZCL khác", "how_to_add_support": "Làm thế nào để thêm hỗ trợ?", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index e5e4473d4..daa62d57b 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -386,8 +386,8 @@ "save_description": "保存描述", "update_description": "更新描述", "read_write_attributes": "读/写端点簇属性", - "input_clusters": "输入簇", - "output_clusters": "输出簇", + "input_clusters": "输入簇 (Server)", + "output_clusters": "输出簇 (Client)", "custom_clusters": "自定义簇", "other_zcl_clusters": "其他 ZCL 簇", "how_to_add_support": "如何添加支持?", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 04aec1dd6..3b5116d40 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -386,8 +386,8 @@ "save_description": "儲存描述", "update_description": "更新描述", "read_write_attributes": "讀/寫端點叢集屬性", - "input_clusters": "輸入叢集", - "output_clusters": "輸出叢集", + "input_clusters": "輸入叢集 (Server)", + "output_clusters": "輸出叢集 (Client)", "custom_clusters": "自訂叢集", "other_zcl_clusters": "其他 ZCL 叢集", "how_to_add_support": "如何新增支援?", diff --git a/src/types.ts b/src/types.ts index 0949a7a80..b7b1618eb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -190,4 +190,13 @@ export type AnyColor = RGBColor | XYColor | HueSaturationColor | HexColor; export type ColorFormat = "color_rgb" | "color_xy" | "color_hs" | "hex"; +export interface BaseSelectOption { + readonly value: string; + readonly label: T; +} + +export interface BaseGroupedOption { + readonly label: string; + readonly options: readonly T[]; +} // #endregion