diff --git a/README.md b/README.md
index d2d528f..9e1ee06 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,18 @@ This quick start uses a single standalone agent, so no orchestrator API key is r
- Skitter can run a single agent directly, or bundle multiple agents into an app with the coordinator.
- The built-in harness reads Claude Code `.md` agents and Codex `.toml` agents from `~/.skitter/agents/`.
+## Web Dashboard
+
+`web/` contains an optional React dashboard that connects straight to the broker over MQTT (WebSocket): browse discovered agents, send requests, drive composed apps through chat, and follow workflow runs live. It is a static single-page app with no backend of its own.
+
+```bash
+cd web
+pnpm install
+pnpm dev
+```
+
+The dev server runs at http://localhost:18084/skitter/. Set the broker URL, organization, and unit from the in-app settings (persisted in `localStorage`). See [web/README.md](web/README.md) for details.
+
## Next Steps
- [Usage Guide](docs/usage.md): service management, multi-agent apps, chat, Docker, storage, and limitations
diff --git a/web/.gitignore b/web/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/web/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/web/README.md b/web/README.md
new file mode 100644
index 0000000..4a04920
--- /dev/null
+++ b/web/README.md
@@ -0,0 +1,35 @@
+# Skitter Dashboard
+
+React + TypeScript dashboard for a minimal Skitter A2A-over-MQTT linkage demo.
+
+## What It Does
+
+- Connects to MQTT over WebSocket.
+- Reads retained A2A Agent Cards into a compact target list.
+- Sends direct `SendMessage` requests to agents and composed apps.
+- Drives workflow creation and execution through chat with the `skitter` runtime agent.
+- Keeps debug details out of the primary flow.
+
+## Development
+
+```bash
+pnpm install
+pnpm dev
+```
+
+The Vite dev server runs at http://localhost:18084/skitter/. In local development it proxies `/mqtt` to `ws://localhost:8083` and `/api` to `http://localhost:3000`; override these with the `VITE_PROXY_MQTT` and `VITE_PROXY_API` environment variables.
+
+Default connection settings:
+
+- Broker URL: same origin (works behind the dev proxy or a reverse proxy)
+- Organization: `default`
+- Unit: `default`
+
+The settings dialog persists broker URL / org / unit overrides in `localStorage`.
+
+## Checks
+
+```bash
+pnpm lint
+pnpm build
+```
diff --git a/web/components.json b/web/components.json
new file mode 100644
index 0000000..6226791
--- /dev/null
+++ b/web/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/index.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/web/eslint.config.js b/web/eslint.config.js
new file mode 100644
index 0000000..ef614d2
--- /dev/null
+++ b/web/eslint.config.js
@@ -0,0 +1,22 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..8c3e0b9
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Skitter Dashboard
+
+
+
+
+
+
+
diff --git a/web/package.json b/web/package.json
new file mode 100644
index 0000000..a451e67
--- /dev/null
+++ b/web/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "web",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@fontsource/geist-mono": "^5.2.8",
+ "@fontsource/geist-sans": "^5.2.5",
+ "@radix-ui/react-dialog": "^1.1.16",
+ "@radix-ui/react-scroll-area": "^1.2.11",
+ "@radix-ui/react-select": "^2.3.0",
+ "@tailwindcss/vite": "^4.3.0",
+ "@xyflow/react": "^12.11.1",
+ "allotment": "^1.20.5",
+ "buffer": "^6.0.3",
+ "clsx": "^2.1.1",
+ "i18next": "^26.3.1",
+ "lucide-react": "^1.17.0",
+ "mqtt": "^5.15.1",
+ "radix-ui": "^1.6.0",
+ "react": "^19.2.6",
+ "react-dom": "^19.2.6",
+ "react-i18next": "^17.0.8",
+ "react-markdown": "^10.1.0",
+ "remark-gfm": "^4.0.1",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.6.0",
+ "tailwindcss": "^4.3.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^24.12.3",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.3.0",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.6.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.59.2",
+ "vite": "^8.0.12"
+ }
+}
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
new file mode 100644
index 0000000..0ec8426
--- /dev/null
+++ b/web/pnpm-lock.yaml
@@ -0,0 +1,5459 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@fontsource/geist-mono':
+ specifier: ^5.2.8
+ version: 5.2.8
+ '@fontsource/geist-sans':
+ specifier: ^5.2.5
+ version: 5.2.5
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.16
+ version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-scroll-area':
+ specifier: ^1.2.11
+ version: 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-select':
+ specifier: ^2.3.0
+ version: 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@tailwindcss/vite':
+ specifier: ^4.3.0
+ version: 4.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0))
+ '@xyflow/react':
+ specifier: ^12.11.1
+ version: 12.11.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ allotment:
+ specifier: ^1.20.5
+ version: 1.20.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ buffer:
+ specifier: ^6.0.3
+ version: 6.0.3
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ i18next:
+ specifier: ^26.3.1
+ version: 26.3.1(typescript@6.0.3)
+ lucide-react:
+ specifier: ^1.17.0
+ version: 1.17.0(react@19.2.7)
+ mqtt:
+ specifier: ^5.15.1
+ version: 5.15.1
+ radix-ui:
+ specifier: ^1.6.0
+ version: 1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react:
+ specifier: ^19.2.6
+ version: 19.2.7
+ react-dom:
+ specifier: ^19.2.6
+ version: 19.2.7(react@19.2.7)
+ react-i18next:
+ specifier: ^17.0.8
+ version: 17.0.8(i18next@26.3.1(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)
+ react-markdown:
+ specifier: ^10.1.0
+ version: 10.1.0(@types/react@19.2.17)(react@19.2.7)
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ sonner:
+ specifier: ^2.0.7
+ version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ tailwind-merge:
+ specifier: ^3.6.0
+ version: 3.6.0
+ tailwindcss:
+ specifier: ^4.3.0
+ version: 4.3.0
+ devDependencies:
+ '@eslint/js':
+ specifier: ^10.0.1
+ version: 10.0.1(eslint@10.4.1(jiti@2.7.0))
+ '@types/node':
+ specifier: ^24.12.3
+ version: 24.13.2
+ '@types/react':
+ specifier: ^19.2.14
+ version: 19.2.17
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.17)
+ '@vitejs/plugin-react':
+ specifier: ^6.0.1
+ version: 6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0))
+ eslint:
+ specifier: ^10.3.0
+ version: 10.4.1(jiti@2.7.0)
+ eslint-plugin-react-hooks:
+ specifier: ^7.1.1
+ version: 7.1.1(eslint@10.4.1(jiti@2.7.0))
+ eslint-plugin-react-refresh:
+ specifier: ^0.5.2
+ version: 0.5.2(eslint@10.4.1(jiti@2.7.0))
+ globals:
+ specifier: ^17.6.0
+ version: 17.6.0
+ typescript:
+ specifier: ~6.0.2
+ version: 6.0.3
+ typescript-eslint:
+ specifier: ^8.59.2
+ version: 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ vite:
+ specifier: ^8.0.12
+ version: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)
+
+packages:
+
+ '@babel/code-frame@7.29.7':
+ resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.7':
+ resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.7':
+ resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.7':
+ resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.29.7':
+ resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.29.7':
+ resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.29.7':
+ resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.29.7':
+ resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-string-parser@7.29.7':
+ resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.29.7':
+ resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.29.7':
+ resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.7':
+ resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/runtime@7.29.7':
+ resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.29.7':
+ resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.7':
+ resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.7':
+ resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
+ engines: {node: '>=6.9.0'}
+
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
+
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
+
+ '@emnapi/wasi-threads@1.2.1':
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+
+ '@eslint-community/eslint-utils@4.9.1':
+ resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.23.5':
+ resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/config-helpers@0.6.0':
+ resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/core@1.2.1':
+ resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/js@10.0.1':
+ resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ peerDependencies:
+ eslint: ^10.0.0
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+
+ '@eslint/object-schema@3.0.5':
+ resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/plugin-kit@0.7.2':
+ resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@floating-ui/core@1.7.5':
+ resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
+
+ '@floating-ui/dom@1.7.6':
+ resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
+
+ '@floating-ui/react-dom@2.1.8':
+ resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.11':
+ resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
+
+ '@fontsource/geist-mono@5.2.8':
+ resolution: {integrity: sha512-YqZUb3X42GjRaHkibUNyE8K4Rk9A6I9Ez81YpJYiuJN4ggmTW2ixoQpa9EWCPfXZtIsCQJTyrDoV9OJOZO/UcA==}
+
+ '@fontsource/geist-sans@5.2.5':
+ resolution: {integrity: sha512-anllOHyJbElRs9fV15TeDRqAeb1IKm4bSknPl6ZMoyPTx1BBy7logudcUwpNjmQLkzn4Q0JGQLRCUKJYoyST6A==}
+
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@1.1.5':
+ resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1
+ '@emnapi/runtime': ^1.7.1
+
+ '@oxc-project/types@0.133.0':
+ resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+
+ '@radix-ui/number@1.1.2':
+ resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==}
+
+ '@radix-ui/primitive@1.1.4':
+ resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==}
+
+ '@radix-ui/react-accessible-icon@1.1.10':
+ resolution: {integrity: sha512-TraSwZUqTcVbiDV2/RXzAXC7aeVVXchq0daPFZE7zAxYFaMzjOUggLOfQH9KFLgRizuwVKZO/crveV1eeO3/ZQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-accordion@1.2.14':
+ resolution: {integrity: sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-alert-dialog@1.1.17':
+ resolution: {integrity: sha512-563ygGeyWPrxyVCNp7OV4rE2aIXhFPknpFyo4wbDlcyMMPZ6ySh+zC5WTvY0ZFLgPTg/QB6tA8PyDQyJ2b4cPg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-arrow@1.1.10':
+ resolution: {integrity: sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-arrow@1.1.9':
+ resolution: {integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-aspect-ratio@1.1.10':
+ resolution: {integrity: sha512-kbI7NrqhDeuytYrq7JjAsoXczvL8wgj2tc1MyaYWm+50bMKHCHQtVWCryslx4cCpmCTTkBcwQckE4CmmGV2haQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-avatar@1.2.0':
+ resolution: {integrity: sha512-am/CwltXtmtdtP+5FbYblYDnMa/zuKcMJP1i3/SJMDXXfj2mG+BTqLH2wucqeyyiQMursUtg/5cK+Nh2pCaSOA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-checkbox@1.3.5':
+ resolution: {integrity: sha512-pREzrmNnVwGvYaBoM64huTRK7B3lrTRuwj8A9nwhPiEtMb+yudiWh6zWAqEtP0Dzd5+iBa1Ki7V1pCxV8ExMdA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collapsible@1.1.14':
+ resolution: {integrity: sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collection@1.1.10':
+ resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collection@1.1.9':
+ resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.3':
+ resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context-menu@2.3.1':
+ resolution: {integrity: sha512-XbrxS68W5dyiE4fAb96yvJwSVU5x66B20A99sD5Mk3xSWK/LqeOnx6TZnim1KieMjXS/CTFq8reOAjWxas2G8Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-context@1.1.4':
+ resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.16':
+ resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.17':
+ resolution: {integrity: sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.2':
+ resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.12':
+ resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.13':
+ resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-dropdown-menu@2.1.18':
+ resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.4':
+ resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.10':
+ resolution: {integrity: sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.9':
+ resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-form@0.1.10':
+ resolution: {integrity: sha512-1NfuvctVtX4sU3Mmq/IdrR8UunxiCMiVg3A5UENKhFzxUBeOyaQQ+lmaQaV7Tc8cqvBKsJL3/KGBsixK0D8WFg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-hover-card@1.1.17':
+ resolution: {integrity: sha512-GjZQIEANVkuuWeztlKz6QEHe31ZX2iDfHzcTMCQVZXC0JyQrgfKWSC+LOOEw6aVV64zyjzobIzSA4AU4eKWrHA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.2':
+ resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-label@2.1.10':
+ resolution: {integrity: sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menu@2.1.18':
+ resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menubar@1.1.18':
+ resolution: {integrity: sha512-hX7EGx/oFq6DPY27GQuP/2wP48GHf5LG6r06VgNJlG+znmDS8OfopZcRcGly3L4lsB9FqpmLx6JQSE9P3BUpyw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-navigation-menu@1.2.16':
+ resolution: {integrity: sha512-nJ0SkrSQgudyYhMiYeHA1ayLVuduEJCFLan1RZZN7c9kqzzCFLaU9kuy81uNtqzweM9YaQPgWzxi9MwQ9jZ04g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-one-time-password-field@0.1.10':
+ resolution: {integrity: sha512-GHkcJ+WVj91At+OvUVTD4R3W0/wxw9t/sG5xFUBYXaCbtWiooZX5Md376QjJqgH4VsVyXrbVNHO2O4NYcmjfVg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-password-toggle-field@0.1.5':
+ resolution: {integrity: sha512-fVuA82u0b/fClpbEJv8yp1nU9eSvoSEOERsU/hhf3FXGPIvkmE7oEaHEu8poowoXO39/Va7zq2E0TUcYr1dBRg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popover@1.1.17':
+ resolution: {integrity: sha512-/YSAOdJ7YJvdn7bn5sdSx2egW+SKY+u7O5RyAVs94Ymrg2fg5QTSFPMRkzvhGyFuE4/qsmPBdrwYoZMZh/4f+g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popper@1.3.0':
+ resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popper@1.3.1':
+ resolution: {integrity: sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.11':
+ resolution: {integrity: sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.12':
+ resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.6':
+ resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.5':
+ resolution: {integrity: sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.6':
+ resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-progress@1.1.10':
+ resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-radio-group@1.4.1':
+ resolution: {integrity: sha512-/SSxZdKEo2Eo29FFRKd06EfFDYp8HryKg0WYg7QLXaydPzl52YfSvCH2a3QDBRdtcuwACroJT8UVjQVgOJ7P9A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.13':
+ resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.2.11':
+ resolution: {integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.2.12':
+ resolution: {integrity: sha512-xuafVzQiTCLsyEjakowTdG3OgTXsmO7IdCiO77otIa+z44xoLNs9Do5eg7POFumIOCjtG6djfm6RKUKpUa/csA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-select@2.3.0':
+ resolution: {integrity: sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-select@2.3.1':
+ resolution: {integrity: sha512-w6eDvY78LE9ZUiNnXCA1QVK8RYN7k9galFv09kjVydJqBAgHd7Y9A6h0UJ/6DCZNGZMZrB2ohcSW1Bo9d8+wWA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-separator@1.1.10':
+ resolution: {integrity: sha512-Y6K6jLQCVfCnTL2MEtGxDLffkhNfEfHsEg3Wa8JU+IWdn3EWbLXd3OuOfQRN7p/W/cUce1WyTk3QeuAoDBzN9g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slider@1.4.1':
+ resolution: {integrity: sha512-r91WSpQucNGFKAIxT8FT0H0zyjd5tJlqObLp7LOMV4z49KoDCwjy01w3vDOU4e1wxhF9IgjYco7SB6byOW7Buw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.5':
+ resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-slot@1.3.0':
+ resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-switch@1.3.1':
+ resolution: {integrity: sha512-55bQtCnOB0BohomSHi6qvQXpJEEqUGDm6hRrM0Bph5OXwhSegqkd8IqgBAQkM1IlgUlWZIxpxRcpOEfRIgimyw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.15':
+ resolution: {integrity: sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toast@1.2.17':
+ resolution: {integrity: sha512-uL4kyyWy000pPL43fGGCV5qT6ZchCWEQZOSlkYiPwPt8Hy1iW38RjeptIvz1/SZesrW6Vn58Ct3sV7tfEfiAbw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle-group@1.1.13':
+ resolution: {integrity: sha512-Xb9PLtlvU66F36LiKba6dFswu6V2mDkgidO4fNSbQHQwmZ9ObxMIO17MN/LJ4aWJecVuSVLAHPZjyeMzJrgeiA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle@1.1.12':
+ resolution: {integrity: sha512-AsAVsYNZIlRBsci7BhE+QyQeKd1h6TffJYt+lF0QQkd5OpQ3klfIByPsCb4G0h/Fq6PJwh1FYNluzBFYzhk4+w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toolbar@1.1.13':
+ resolution: {integrity: sha512-Za1l4f6fzTkGgz/iynAMN8iaqiKff2wm2/QwiLmHPtDQreWEBrvSimgQFIekxMUdRPhILM7xdIXxuS/o/DGZag==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tooltip@1.2.10':
+ resolution: {integrity: sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.2':
+ resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.2.3':
+ resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-effect-event@0.0.3':
+ resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-escape-keydown@1.1.2':
+ resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-is-hydrated@0.1.1':
+ resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.2':
+ resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-previous@1.1.2':
+ resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-rect@1.1.2':
+ resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-size@1.1.2':
+ resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-visually-hidden@1.2.5':
+ resolution: {integrity: sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-visually-hidden@1.2.6':
+ resolution: {integrity: sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/rect@1.1.2':
+ resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==}
+
+ '@rolldown/binding-android-arm64@1.0.3':
+ resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.0.3':
+ resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.3':
+ resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.0.3':
+ resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
+ resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
+ resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
+ resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
+ resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
+ resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
+ resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-musl@1.0.3':
+ resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-openharmony-arm64@1.0.3':
+ resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-wasm32-wasi@1.0.3':
+ resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
+ resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
+ resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+
+ '@tailwindcss/node@4.3.0':
+ resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.0':
+ resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/vite@4.3.0':
+ resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
+ '@tybys/wasm-util@0.10.2':
+ resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-drag@3.0.7':
+ resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-selection@3.0.11':
+ resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+
+ '@types/d3-transition@3.0.9':
+ resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
+
+ '@types/d3-zoom@3.0.8':
+ resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
+
+ '@types/debug@4.1.13':
+ resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+
+ '@types/esrecurse@4.3.1':
+ resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
+
+ '@types/estree-jsx@1.0.5':
+ resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@24.13.2':
+ resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.17':
+ resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
+
+ '@types/readable-stream@4.0.23':
+ resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==}
+
+ '@types/unist@2.0.11':
+ resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
+ '@typescript-eslint/eslint-plugin@8.61.0':
+ resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.61.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/parser@8.61.0':
+ resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/project-service@8.61.0':
+ resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/scope-manager@8.61.0':
+ resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.61.0':
+ resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/type-utils@8.61.0':
+ resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/types@8.61.0':
+ resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.61.0':
+ resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/utils@8.61.0':
+ resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.61.0':
+ resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@ungap/structured-clone@1.3.1':
+ resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==}
+
+ '@vitejs/plugin-react@6.0.2':
+ resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
+ babel-plugin-react-compiler: ^1.0.0
+ vite: ^8.0.0
+ peerDependenciesMeta:
+ '@rolldown/plugin-babel':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+
+ '@xyflow/react@12.11.1':
+ resolution: {integrity: sha512-L+zBoLGSXham0MnlY8QqjfR7/C5JNw0zxkaey5aZ5XmCgJBAdH4+WRIu8CR40d3l/BdU635V6YbhBK1jMo8/6Q==}
+ peerDependencies:
+ '@types/react': '>=17'
+ '@types/react-dom': '>=17'
+ react: '>=17'
+ react-dom: '>=17'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@xyflow/system@0.0.78':
+ resolution: {integrity: sha512-lY0z2qP33fUhTva9Vaxrk0lqZta2pkbxB1trHAx1omnJqRtPvDlAQYV2r5fhS6AdpkulYmbNW0svy+A4/t4B/g==}
+
+ abort-controller@3.0.0:
+ resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+ engines: {node: '>=6.5'}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.15.0:
+ resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
+ allotment@1.20.5:
+ resolution: {integrity: sha512-7i4NT7ieXEyAd5lBrXmE7WHz/e7hRuo97+j+TwrPE85ha6kyFURoc76nom0dWSZ1pTKVEAMJy/+f3/Isfu/41A==}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ baseline-browser-mapping@2.10.35:
+ resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ bl@6.1.6:
+ resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==}
+
+ brace-expansion@5.0.6:
+ resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
+ engines: {node: 18 || 20 || >=22}
+
+ broker-factory@3.1.15:
+ resolution: {integrity: sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og==}
+
+ browserslist@4.28.2:
+ resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ buffer@6.0.3:
+ resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+
+ caniuse-lite@1.0.30001797:
+ resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ character-reference-invalid@2.0.1:
+ resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+
+ classcat@5.0.5:
+ resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==}
+
+ classnames@2.5.1:
+ resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ commist@3.2.0:
+ resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==}
+
+ concat-stream@2.0.0:
+ resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
+ engines: {'0': node >= 6.0}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.3.0:
+ resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ electron-to-chromium@1.5.371:
+ resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==}
+
+ enhanced-resolve@5.23.0:
+ resolution: {integrity: sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==}
+ engines: {node: '>=10.13.0'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ eslint-plugin-react-hooks@7.1.1:
+ resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
+
+ eslint-plugin-react-refresh@0.5.2:
+ resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==}
+ peerDependencies:
+ eslint: ^9 || ^10
+
+ eslint-scope@9.1.2:
+ resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@10.4.1:
+ resolution: {integrity: sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@11.2.0:
+ resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-util-is-identifier-name@3.0.0:
+ resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ event-target-shim@5.0.1:
+ resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+ engines: {node: '>=6'}
+
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
+
+ events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-unique-numbers@9.0.27:
+ resolution: {integrity: sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==}
+ engines: {node: '>=18.2.0'}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.4.2:
+ resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@17.6.0:
+ resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==}
+ engines: {node: '>=18'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ hast-util-to-jsx-runtime@2.3.6:
+ resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ help-me@5.0.0:
+ resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
+
+ hermes-estree@0.25.1:
+ resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
+
+ hermes-parser@0.25.1:
+ resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+
+ html-parse-stringify@3.0.1:
+ resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
+
+ html-url-attributes@3.0.1:
+ resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+
+ i18next@26.3.1:
+ resolution: {integrity: sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==}
+ peerDependencies:
+ typescript: ^5 || ^6
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ inline-style-parser@0.2.7:
+ resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
+
+ ip-address@10.2.0:
+ resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
+ engines: {node: '>= 12'}
+
+ is-alphabetical@2.0.1:
+ resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
+
+ is-alphanumerical@2.0.1:
+ resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+
+ is-decimal@2.0.1:
+ resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-hexadecimal@2.0.1:
+ resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
+ js-sdsl@4.3.0:
+ resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.clamp@4.0.3:
+ resolution: {integrity: sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==}
+
+ lodash.debounce@4.0.8:
+ resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lucide-react@1.17.0:
+ resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.3:
+ resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-mdx-expression@2.0.1:
+ resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
+
+ mdast-util-mdx-jsx@3.2.0:
+ resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==}
+
+ mdast-util-mdxjs-esm@2.0.1:
+ resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-hast@13.2.1:
+ resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+
+ mdast-util-to-markdown@2.1.2:
+ resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
+ minimatch@10.2.5:
+ resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ mqtt-packet@9.0.2:
+ resolution: {integrity: sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==}
+
+ mqtt@5.15.1:
+ resolution: {integrity: sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==}
+ engines: {node: '>=16.0.0'}
+ hasBin: true
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.12:
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ node-releases@2.0.47:
+ resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==}
+ engines: {node: '>=18'}
+
+ number-allocator@1.0.14:
+ resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parse-entities@4.0.2:
+ resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.15:
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+ process@0.11.10:
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+ engines: {node: '>= 0.6.0'}
+
+ property-information@7.2.0:
+ resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ radix-ui@1.6.0:
+ resolution: {integrity: sha512-EUEC70O03EgxWMP5aoqfBZ6iLC5bczFagGy7zhSYRt8o5DP7IWNiP3ywetse3L9b8843ExB0OGWZvgbYVJuNeg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ react-dom@19.2.7:
+ resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
+ peerDependencies:
+ react: ^19.2.7
+
+ react-i18next@17.0.8:
+ resolution: {integrity: sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==}
+ peerDependencies:
+ i18next: '>= 26.2.0'
+ react: '>= 16.8.0'
+ react-dom: '*'
+ react-native: '*'
+ typescript: ^5 || ^6
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ typescript:
+ optional: true
+
+ react-markdown@10.1.0:
+ resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
+ peerDependencies:
+ '@types/react': '>=18'
+ react: '>=18'
+
+ react-remove-scroll-bar@2.3.8:
+ resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-remove-scroll@2.7.2:
+ resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-style-singleton@2.2.3:
+ resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react@19.2.7:
+ resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
+ engines: {node: '>=0.10.0'}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readable-stream@4.7.0:
+ resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
+ remark-parse@11.0.0:
+ resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+ remark-rehype@11.1.2:
+ resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
+
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rolldown@1.0.3:
+ resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.8.4:
+ resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ smart-buffer@4.2.0:
+ resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+ engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
+ socks@2.8.9:
+ resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==}
+ engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
+
+ sonner@2.0.7:
+ resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ split2@4.2.0:
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+ engines: {node: '>= 10.x'}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ style-to-js@1.1.21:
+ resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
+
+ style-to-object@1.0.14:
+ resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
+
+ tailwind-merge@3.6.0:
+ resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==}
+
+ tailwindcss@4.3.0:
+ resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
+ ts-api-utils@2.5.0:
+ resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typedarray@0.0.6:
+ resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
+ typescript-eslint@8.61.0:
+ resolution: {integrity: sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.18.2:
+ resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+
+ unified@11.0.5:
+ resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.1.0:
+ resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ use-callback-ref@1.3.3:
+ resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sidecar@1.1.3:
+ resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ usehooks-ts@3.1.1:
+ resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==}
+ engines: {node: '>=16.15.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@8.0.16:
+ resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ '@vitejs/devtools': ^0.1.18
+ esbuild: ^0.27.0 || ^0.28.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ '@vitejs/devtools':
+ optional: true
+ esbuild:
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ void-elements@3.1.0:
+ resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
+ engines: {node: '>=0.10.0'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ worker-factory@7.0.50:
+ resolution: {integrity: sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ==}
+
+ worker-timers-broker@8.0.16:
+ resolution: {integrity: sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==}
+
+ worker-timers-worker@9.0.14:
+ resolution: {integrity: sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==}
+
+ worker-timers@8.0.31:
+ resolution: {integrity: sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==}
+
+ ws@8.21.0:
+ resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zod-validation-error@4.0.2:
+ resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ zod: ^3.25.0 || ^4.0.0
+
+ zod@4.4.3:
+ resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+
+ zustand@4.5.7:
+ resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
+ engines: {node: '>=12.7.0'}
+ peerDependencies:
+ '@types/react': '>=16.8'
+ immer: '>=9.0.6'
+ react: '>=16.8'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@babel/code-frame@7.29.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.29.7
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.7': {}
+
+ '@babel/core@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-compilation-targets': 7.29.7
+ '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
+ '@babel/helpers': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.7':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.29.7':
+ dependencies:
+ '@babel/compat-data': 7.29.7
+ '@babel/helper-validator-option': 7.29.7
+ browserslist: 4.28.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.29.7': {}
+
+ '@babel/helper-module-imports@7.29.7':
+ dependencies:
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+ '@babel/traverse': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.29.7': {}
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
+ '@babel/helper-validator-option@7.29.7': {}
+
+ '@babel/helpers@7.29.7':
+ dependencies:
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/parser@7.29.7':
+ dependencies:
+ '@babel/types': 7.29.7
+
+ '@babel/runtime@7.29.7': {}
+
+ '@babel/template@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/traverse@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-globals': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+
+ '@emnapi/core@1.10.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.10.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(jiti@2.7.0))':
+ dependencies:
+ eslint: 10.4.1(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.23.5':
+ dependencies:
+ '@eslint/object-schema': 3.0.5
+ debug: 4.4.3
+ minimatch: 10.2.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.6.0':
+ dependencies:
+ '@eslint/core': 1.2.1
+
+ '@eslint/core@1.2.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/js@10.0.1(eslint@10.4.1(jiti@2.7.0))':
+ optionalDependencies:
+ eslint: 10.4.1(jiti@2.7.0)
+
+ '@eslint/object-schema@3.0.5': {}
+
+ '@eslint/plugin-kit@0.7.2':
+ dependencies:
+ '@eslint/core': 1.2.1
+ levn: 0.4.1
+
+ '@floating-ui/core@1.7.5':
+ dependencies:
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/dom@1.7.6':
+ dependencies:
+ '@floating-ui/core': 1.7.5
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@floating-ui/dom': 1.7.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
+ '@floating-ui/utils@0.2.11': {}
+
+ '@fontsource/geist-mono@5.2.8': {}
+
+ '@fontsource/geist-sans@5.2.5': {}
+
+ '@humanfs/core@0.19.2':
+ dependencies:
+ '@humanfs/types': 0.15.0
+
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@tybys/wasm-util': 0.10.2
+ optional: true
+
+ '@oxc-project/types@0.133.0': {}
+
+ '@radix-ui/number@1.1.2': {}
+
+ '@radix-ui/primitive@1.1.4': {}
+
+ '@radix-ui/react-accessible-icon@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-accordion@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-alert-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-aspect-ratio@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-avatar@1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-checkbox@1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-collapsible@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-context-menu@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-form@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-hover-card@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-label@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-menubar@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-navigation-menu@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-one-time-password-field@0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-password-toggle-field@0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-popover@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/rect': 1.1.2
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/rect': 1.1.2
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-portal@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-primitive@2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-radio-group@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-scroll-area@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-select@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-select@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ aria-hidden: 1.2.6
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-separator@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-slider@1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/number': 1.1.2
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-slot@1.2.5(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-switch@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-tabs@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toast@1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toggle-group@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toggle@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-toolbar@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/rect': 1.1.2
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ react: 19.2.7
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ '@radix-ui/react-visually-hidden@1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ '@radix-ui/rect@1.1.2': {}
+
+ '@rolldown/binding-android-arm64@1.0.3':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.3':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.0.3':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.3':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.3':
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.3':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
+ optional: true
+
+ '@rolldown/pluginutils@1.0.1': {}
+
+ '@tailwindcss/node@4.3.0':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.23.0
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.3.0
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.0':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-x64': 4.3.0
+ '@tailwindcss/oxide-freebsd-x64': 4.3.0
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.0
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.0
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.0
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.0
+
+ '@tailwindcss/vite@4.3.0(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0))':
+ dependencies:
+ '@tailwindcss/node': 4.3.0
+ '@tailwindcss/oxide': 4.3.0
+ tailwindcss: 4.3.0
+ vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)
+
+ '@tybys/wasm-util@0.10.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-drag@3.0.7':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-selection@3.0.11': {}
+
+ '@types/d3-transition@3.0.9':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-zoom@3.0.8':
+ dependencies:
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-selection': 3.0.11
+
+ '@types/debug@4.1.13':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/esrecurse@4.3.1': {}
+
+ '@types/estree-jsx@1.0.5':
+ dependencies:
+ '@types/estree': 1.0.9
+
+ '@types/estree@1.0.9': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@24.13.2':
+ dependencies:
+ undici-types: 7.18.2
+
+ '@types/react-dom@19.2.3(@types/react@19.2.17)':
+ dependencies:
+ '@types/react': 19.2.17
+
+ '@types/react@19.2.17':
+ dependencies:
+ csstype: 3.2.3
+
+ '@types/readable-stream@4.0.23':
+ dependencies:
+ '@types/node': 24.13.2
+
+ '@types/unist@2.0.11': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 24.13.2
+
+ '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ '@typescript-eslint/parser': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/type-utils': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.61.0
+ eslint: 10.4.1(jiti@2.7.0)
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.61.0
+ debug: 4.4.3
+ eslint: 10.4.1(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.61.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.61.0
+ debug: 4.4.3
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.61.0':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/visitor-keys': 8.61.0
+
+ '@typescript-eslint/tsconfig-utils@8.61.0(typescript@6.0.3)':
+ dependencies:
+ typescript: 6.0.3
+
+ '@typescript-eslint/type-utils@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ debug: 4.4.3
+ eslint: 10.4.1(jiti@2.7.0)
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.61.0': {}
+
+ '@typescript-eslint/typescript-estree@8.61.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/visitor-keys': 8.61.0
+ debug: 4.4.3
+ minimatch: 10.2.5
+ semver: 7.8.4
+ tinyglobby: 0.2.17
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ eslint: 10.4.1(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.61.0':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ eslint-visitor-keys: 5.0.1
+
+ '@ungap/structured-clone@1.3.1': {}
+
+ '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.1
+ vite: 8.0.16(@types/node@24.13.2)(jiti@2.7.0)
+
+ '@xyflow/react@12.11.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
+ dependencies:
+ '@xyflow/system': 0.0.78
+ classcat: 5.0.5
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ zustand: 4.5.7(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+ transitivePeerDependencies:
+ - immer
+
+ '@xyflow/system@0.0.78':
+ dependencies:
+ '@types/d3-drag': 3.0.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-selection': 3.0.11
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-zoom: 3.0.0
+
+ abort-controller@3.0.0:
+ dependencies:
+ event-target-shim: 5.0.1
+
+ acorn-jsx@5.3.2(acorn@8.16.0):
+ dependencies:
+ acorn: 8.16.0
+
+ acorn@8.16.0: {}
+
+ ajv@6.15.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ allotment@1.20.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ classnames: 2.5.1
+ eventemitter3: 5.0.4
+ fast-deep-equal: 3.1.3
+ lodash.clamp: 4.0.3
+ lodash.debounce: 4.0.8
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ usehooks-ts: 3.1.1(react@19.2.7)
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ bail@2.0.2: {}
+
+ balanced-match@4.0.4: {}
+
+ base64-js@1.5.1: {}
+
+ baseline-browser-mapping@2.10.35: {}
+
+ bl@6.1.6:
+ dependencies:
+ '@types/readable-stream': 4.0.23
+ buffer: 6.0.3
+ inherits: 2.0.4
+ readable-stream: 4.7.0
+
+ brace-expansion@5.0.6:
+ dependencies:
+ balanced-match: 4.0.4
+
+ broker-factory@3.1.15:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ fast-unique-numbers: 9.0.27
+ tslib: 2.8.1
+ worker-factory: 7.0.50
+
+ browserslist@4.28.2:
+ dependencies:
+ baseline-browser-mapping: 2.10.35
+ caniuse-lite: 1.0.30001797
+ electron-to-chromium: 1.5.371
+ node-releases: 2.0.47
+ update-browserslist-db: 1.2.3(browserslist@4.28.2)
+
+ buffer-from@1.1.2: {}
+
+ buffer@6.0.3:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ caniuse-lite@1.0.30001797: {}
+
+ ccount@2.0.1: {}
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ character-entities@2.0.2: {}
+
+ character-reference-invalid@2.0.1: {}
+
+ classcat@5.0.5: {}
+
+ classnames@2.5.1: {}
+
+ clsx@2.1.1: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ commist@3.2.0: {}
+
+ concat-stream@2.0.0:
+ dependencies:
+ buffer-from: 1.1.2
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ typedarray: 0.0.6
+
+ convert-source-map@2.0.0: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ csstype@3.2.3: {}
+
+ d3-color@3.1.0: {}
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-ease@3.0.1: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ d3-zoom@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ deep-is@0.1.4: {}
+
+ dequal@2.0.3: {}
+
+ detect-libc@2.1.2: {}
+
+ detect-node-es@1.1.0: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ electron-to-chromium@1.5.371: {}
+
+ enhanced-resolve@5.23.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ eslint-plugin-react-hooks@7.1.1(eslint@10.4.1(jiti@2.7.0)):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/parser': 7.29.7
+ eslint: 10.4.1(jiti@2.7.0)
+ hermes-parser: 0.25.1
+ zod: 4.4.3
+ zod-validation-error: 4.0.2(zod@4.4.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-react-refresh@0.5.2(eslint@10.4.1(jiti@2.7.0)):
+ dependencies:
+ eslint: 10.4.1(jiti@2.7.0)
+
+ eslint-scope@9.1.2:
+ dependencies:
+ '@types/esrecurse': 4.3.1
+ '@types/estree': 1.0.9
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@10.4.1(jiti@2.7.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.23.5
+ '@eslint/config-helpers': 0.6.0
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.2
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ minimatch: 10.2.5
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@11.2.0:
+ dependencies:
+ acorn: 8.16.0
+ acorn-jsx: 5.3.2(acorn@8.16.0)
+ eslint-visitor-keys: 5.0.1
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-util-is-identifier-name@3.0.0: {}
+
+ esutils@2.0.3: {}
+
+ event-target-shim@5.0.1: {}
+
+ eventemitter3@5.0.4: {}
+
+ events@3.3.0: {}
+
+ extend@3.0.2: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-unique-numbers@9.0.27:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ tslib: 2.8.1
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.4.2
+ keyv: 4.5.4
+
+ flatted@3.4.2: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ get-nonce@1.0.1: {}
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@17.6.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ hast-util-to-jsx-runtime@2.3.6:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ hast-util-whitespace: 3.0.0
+ mdast-util-mdx-expression: 2.0.1
+ mdast-util-mdx-jsx: 3.2.0
+ mdast-util-mdxjs-esm: 2.0.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ style-to-js: 1.1.21
+ unist-util-position: 5.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ help-me@5.0.0: {}
+
+ hermes-estree@0.25.1: {}
+
+ hermes-parser@0.25.1:
+ dependencies:
+ hermes-estree: 0.25.1
+
+ html-parse-stringify@3.0.1:
+ dependencies:
+ void-elements: 3.1.0
+
+ html-url-attributes@3.0.1: {}
+
+ i18next@26.3.1(typescript@6.0.3):
+ optionalDependencies:
+ typescript: 6.0.3
+
+ ieee754@1.2.1: {}
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ imurmurhash@0.1.4: {}
+
+ inherits@2.0.4: {}
+
+ inline-style-parser@0.2.7: {}
+
+ ip-address@10.2.0: {}
+
+ is-alphabetical@2.0.1: {}
+
+ is-alphanumerical@2.0.1:
+ dependencies:
+ is-alphabetical: 2.0.1
+ is-decimal: 2.0.1
+
+ is-decimal@2.0.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hexadecimal@2.0.1: {}
+
+ is-plain-obj@4.1.0: {}
+
+ isexe@2.0.0: {}
+
+ jiti@2.7.0: {}
+
+ js-sdsl@4.3.0: {}
+
+ js-tokens@4.0.0: {}
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@2.2.3: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.clamp@4.0.3: {}
+
+ lodash.debounce@4.0.8: {}
+
+ longest-streak@3.1.0: {}
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lucide-react@1.17.0(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ markdown-table@3.0.4: {}
+
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ mdast-util-from-markdown@2.0.3:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.2
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx-expression@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx-jsx@3.2.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ parse-entities: 4.0.2
+ stringify-entities: 4.0.4
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdxjs-esm@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.1
+
+ mdast-util-to-hast@13.2.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.3.1
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+
+ mdast-util-to-markdown@2.1.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ unist-util-visit: 5.1.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.13
+ debug: 4.4.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ minimatch@10.2.5:
+ dependencies:
+ brace-expansion: 5.0.6
+
+ minimist@1.2.8: {}
+
+ mqtt-packet@9.0.2:
+ dependencies:
+ bl: 6.1.6
+ debug: 4.4.3
+ process-nextick-args: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mqtt@5.15.1:
+ dependencies:
+ '@types/readable-stream': 4.0.23
+ '@types/ws': 8.18.1
+ commist: 3.2.0
+ concat-stream: 2.0.0
+ debug: 4.4.3
+ help-me: 5.0.0
+ lru-cache: 10.4.3
+ minimist: 1.2.8
+ mqtt-packet: 9.0.2
+ number-allocator: 1.0.14
+ readable-stream: 4.7.0
+ rfdc: 1.4.1
+ socks: 2.8.9
+ split2: 4.2.0
+ worker-timers: 8.0.31
+ ws: 8.21.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.12: {}
+
+ natural-compare@1.4.0: {}
+
+ node-releases@2.0.47: {}
+
+ number-allocator@1.0.14:
+ dependencies:
+ debug: 4.4.3
+ js-sdsl: 4.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parse-entities@4.0.2:
+ dependencies:
+ '@types/unist': 2.0.11
+ character-entities-legacy: 3.0.0
+ character-reference-invalid: 2.0.1
+ decode-named-character-reference: 1.3.0
+ is-alphanumerical: 2.0.1
+ is-decimal: 2.0.1
+ is-hexadecimal: 2.0.1
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.4: {}
+
+ postcss@8.5.15:
+ dependencies:
+ nanoid: 3.3.12
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ process-nextick-args@2.0.1: {}
+
+ process@0.11.10: {}
+
+ property-information@7.2.0: {}
+
+ punycode@2.3.1: {}
+
+ radix-ui@1.6.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ '@radix-ui/primitive': 1.1.4
+ '@radix-ui/react-accessible-icon': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-accordion': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-alert-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-aspect-ratio': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-avatar': 1.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-checkbox': 1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-context-menu': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-dropdown-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-form': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-hover-card': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-label': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-menubar': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-navigation-menu': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-one-time-password-field': 0.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-password-toggle-field': 0.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popover': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-progress': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-radio-group': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-scroll-area': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-select': 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-separator': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slider': 1.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-switch': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-tabs': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toast': 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toggle-group': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-toolbar': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-tooltip': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7)
+ '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ react-dom@19.2.7(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ scheduler: 0.27.0
+
+ react-i18next@17.0.8(i18next@26.3.1(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3):
+ dependencies:
+ '@babel/runtime': 7.29.7
+ html-parse-stringify: 3.0.1
+ i18next: 26.3.1(typescript@6.0.3)
+ react: 19.2.7
+ use-sync-external-store: 1.6.0(react@19.2.7)
+ optionalDependencies:
+ react-dom: 19.2.7(react@19.2.7)
+ typescript: 6.0.3
+
+ react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@types/react': 19.2.17
+ devlop: 1.1.0
+ hast-util-to-jsx-runtime: 2.3.6
+ html-url-attributes: 3.0.1
+ mdast-util-to-hast: 13.2.1
+ react: 19.2.7
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ unified: 11.0.5
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7)
+ react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7)
+ use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ react@19.2.7: {}
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readable-stream@4.7.0:
+ dependencies:
+ abort-controller: 3.0.0
+ buffer: 6.0.3
+ events: 3.3.0
+ process: 0.11.10
+ string_decoder: 1.3.0
+
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-parse@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ micromark-util-types: 2.0.2
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-rehype@11.1.2:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ mdast-util-to-hast: 13.2.1
+ unified: 11.0.5
+ vfile: 6.0.3
+
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.1.2
+ unified: 11.0.5
+
+ rfdc@1.4.1: {}
+
+ rolldown@1.0.3:
+ dependencies:
+ '@oxc-project/types': 0.133.0
+ '@rolldown/pluginutils': 1.0.1
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.3
+ '@rolldown/binding-darwin-arm64': 1.0.3
+ '@rolldown/binding-darwin-x64': 1.0.3
+ '@rolldown/binding-freebsd-x64': 1.0.3
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.3
+ '@rolldown/binding-linux-arm64-gnu': 1.0.3
+ '@rolldown/binding-linux-arm64-musl': 1.0.3
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.3
+ '@rolldown/binding-linux-s390x-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-musl': 1.0.3
+ '@rolldown/binding-openharmony-arm64': 1.0.3
+ '@rolldown/binding-wasm32-wasi': 1.0.3
+ '@rolldown/binding-win32-arm64-msvc': 1.0.3
+ '@rolldown/binding-win32-x64-msvc': 1.0.3
+
+ safe-buffer@5.2.1: {}
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.8.4: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ smart-buffer@4.2.0: {}
+
+ socks@2.8.9:
+ dependencies:
+ ip-address: 10.2.0
+ smart-buffer: 4.2.0
+
+ sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
+ source-map-js@1.2.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ split2@4.2.0: {}
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ style-to-js@1.1.21:
+ dependencies:
+ style-to-object: 1.0.14
+
+ style-to-object@1.0.14:
+ dependencies:
+ inline-style-parser: 0.2.7
+
+ tailwind-merge@3.6.0: {}
+
+ tailwindcss@4.3.0: {}
+
+ tapable@2.3.3: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ trim-lines@3.0.1: {}
+
+ trough@2.2.0: {}
+
+ ts-api-utils@2.5.0(typescript@6.0.3):
+ dependencies:
+ typescript: 6.0.3
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typedarray@0.0.6: {}
+
+ typescript-eslint@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)
+ eslint: 10.4.1(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@6.0.3: {}
+
+ undici-types@7.18.2: {}
+
+ unified@11.0.5:
+ dependencies:
+ '@types/unist': 3.0.3
+ bail: 2.0.2
+ devlop: 1.1.0
+ extend: 3.0.2
+ is-plain-obj: 4.1.0
+ trough: 2.2.0
+ vfile: 6.0.3
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.1.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ update-browserslist-db@1.2.3(browserslist@4.28.2):
+ dependencies:
+ browserslist: 4.28.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.7
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.17
+
+ use-sync-external-store@1.6.0(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+
+ usehooks-ts@3.1.1(react@19.2.7):
+ dependencies:
+ lodash.debounce: 4.0.8
+ react: 19.2.7
+
+ util-deprecate@1.0.2: {}
+
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
+ vite@8.0.16(@types/node@24.13.2)(jiti@2.7.0):
+ dependencies:
+ lightningcss: 1.32.0
+ picomatch: 4.0.4
+ postcss: 8.5.15
+ rolldown: 1.0.3
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 24.13.2
+ fsevents: 2.3.3
+ jiti: 2.7.0
+
+ void-elements@3.1.0: {}
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ worker-factory@7.0.50:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ fast-unique-numbers: 9.0.27
+ tslib: 2.8.1
+
+ worker-timers-broker@8.0.16:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ broker-factory: 3.1.15
+ fast-unique-numbers: 9.0.27
+ tslib: 2.8.1
+ worker-timers-worker: 9.0.14
+
+ worker-timers-worker@9.0.14:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ tslib: 2.8.1
+ worker-factory: 7.0.50
+
+ worker-timers@8.0.31:
+ dependencies:
+ '@babel/runtime': 7.29.7
+ tslib: 2.8.1
+ worker-timers-broker: 8.0.16
+ worker-timers-worker: 9.0.14
+
+ ws@8.21.0: {}
+
+ yallist@3.1.1: {}
+
+ yocto-queue@0.1.0: {}
+
+ zod-validation-error@4.0.2(zod@4.4.3):
+ dependencies:
+ zod: 4.4.3
+
+ zod@4.4.3: {}
+
+ zustand@4.5.7(@types/react@19.2.17)(react@19.2.7):
+ dependencies:
+ use-sync-external-store: 1.6.0(react@19.2.7)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ react: 19.2.7
+
+ zwitch@2.0.4: {}
diff --git a/web/public/emqx.svg b/web/public/emqx.svg
new file mode 100644
index 0000000..b88fb6e
--- /dev/null
+++ b/web/public/emqx.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/web/public/favicon.ico b/web/public/favicon.ico
new file mode 100644
index 0000000..53ed2ee
Binary files /dev/null and b/web/public/favicon.ico differ
diff --git a/web/src/App.tsx b/web/src/App.tsx
new file mode 100644
index 0000000..077df80
--- /dev/null
+++ b/web/src/App.tsx
@@ -0,0 +1,16 @@
+import { Toaster } from "@/components/ui/sonner";
+import { DashboardShell } from "@/dashboard/components/dashboard-shell";
+import { useSkitterDashboard } from "@/dashboard/use-skitter-dashboard";
+
+function App() {
+ const dashboard = useSkitterDashboard();
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default App;
diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx
new file mode 100644
index 0000000..0d7237d
--- /dev/null
+++ b/web/src/components/ui/badge.tsx
@@ -0,0 +1,21 @@
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+const badgeBase = "inline-flex items-center rounded-[6px] border px-2 py-0.5 text-xs font-medium transition-colors";
+
+const badgeVariants = {
+ default: "border-transparent bg-primary text-primary-foreground",
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
+ outline: "border-border text-foreground",
+ success: "border-online-border bg-online-bg text-online-text",
+ warning: "border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300",
+ muted: "border-border bg-muted text-muted-foreground",
+};
+
+export interface BadgeProps extends React.HTMLAttributes {
+ variant?: keyof typeof badgeVariants;
+}
+
+export function Badge({ className, variant = "default", ...props }: BadgeProps) {
+ return
;
+}
diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx
new file mode 100644
index 0000000..b73d8c4
--- /dev/null
+++ b/web/src/components/ui/button.tsx
@@ -0,0 +1,36 @@
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+const buttonBase =
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[8px] text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
+
+const buttonVariants = {
+ default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+};
+
+const buttonSizes = {
+ default: "h-10 px-4 py-2",
+ sm: "h-8 rounded-[7px] px-3 text-xs",
+ "icon-sm": "size-8",
+};
+
+export interface ButtonProps extends React.ButtonHTMLAttributes {
+ variant?: keyof typeof buttonVariants;
+ size?: keyof typeof buttonSizes;
+}
+
+export function Button({
+ className,
+ variant = "default",
+ size = "default",
+ ...props
+}: ButtonProps) {
+ return (
+
+ );
+}
diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..26e92b0
--- /dev/null
+++ b/web/src/components/ui/dialog.tsx
@@ -0,0 +1,64 @@
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+export function Dialog(props: React.ComponentPropsWithoutRef) {
+ return ;
+}
+
+export function DialogTrigger(props: React.ComponentPropsWithoutRef) {
+ return ;
+}
+
+export function DialogContent({
+ className,
+ children,
+ ...props
+}: React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+ );
+}
+
+export function DialogHeader({ className, ...props }: React.HTMLAttributes) {
+ return
;
+}
+
+export function DialogTitle({
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef) {
+ return ;
+}
+
+export function DialogDescription({
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef) {
+ return (
+
+ );
+}
+
+export function DialogFooter({ className, ...props }: React.HTMLAttributes) {
+ return
;
+}
diff --git a/web/src/components/ui/dropdown-menu.tsx b/web/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..d31ccf2
--- /dev/null
+++ b/web/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,235 @@
+import * as React from "react";
+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
+import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
+
+import { cn } from "@/lib/utils";
+
+function DropdownMenu({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuPortal({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuTrigger({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuContent({
+ className,
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ );
+}
+
+function DropdownMenuGroup({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuItem({
+ className,
+ inset,
+ variant = "default",
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean;
+ variant?: "default" | "destructive";
+}) {
+ return (
+
+ );
+}
+
+function DropdownMenuCheckboxItem({
+ className,
+ children,
+ checked,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
+}
+
+function DropdownMenuRadioGroup({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuRadioItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
+}
+
+function DropdownMenuLabel({
+ className,
+ inset,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean;
+}) {
+ return (
+
+ );
+}
+
+function DropdownMenuSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+function DropdownMenuShortcut({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+ );
+}
+
+function DropdownMenuSub({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function DropdownMenuSubTrigger({
+ className,
+ inset,
+ children,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean;
+}) {
+ return (
+
+ {children}
+
+
+ );
+}
+
+function DropdownMenuSubContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export {
+ DropdownMenu,
+ DropdownMenuPortal,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubTrigger,
+ DropdownMenuSubContent,
+};
diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx
new file mode 100644
index 0000000..eb25603
--- /dev/null
+++ b/web/src/components/ui/input.tsx
@@ -0,0 +1,15 @@
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+export function Input({ className, type, ...props }: React.InputHTMLAttributes) {
+ return (
+
+ );
+}
diff --git a/web/src/components/ui/scroll-area.tsx b/web/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..9d27fcc
--- /dev/null
+++ b/web/src/components/ui/scroll-area.tsx
@@ -0,0 +1,40 @@
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+export function ScrollArea({
+ className,
+ children,
+ ...props
+}: React.ComponentPropsWithoutRef) {
+ return (
+
+
+ {children}
+
+
+
+
+ );
+}
+
+function ScrollBar({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ );
+}
diff --git a/web/src/components/ui/select.tsx b/web/src/components/ui/select.tsx
new file mode 100644
index 0000000..7d174f2
--- /dev/null
+++ b/web/src/components/ui/select.tsx
@@ -0,0 +1,153 @@
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { cn } from "@/lib/utils";
+
+function Select({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function SelectGroup({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function SelectValue({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
+
+function SelectTrigger({
+ className,
+ size = "default",
+ children,
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default";
+}) {
+ return (
+
+ {children}
+
+
+
+
+ );
+}
+
+function SelectContent({
+ className,
+ children,
+ position = "item-aligned",
+ align = "center",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+function SelectItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
+}
+
+function SelectScrollUpButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ );
+}
+
+function SelectScrollDownButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ );
+}
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectTrigger,
+ SelectValue,
+};
diff --git a/web/src/components/ui/sonner.tsx b/web/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..198bb54
--- /dev/null
+++ b/web/src/components/ui/sonner.tsx
@@ -0,0 +1,5 @@
+import { Toaster as Sonner, type ToasterProps } from "sonner";
+
+export function Toaster(props: ToasterProps) {
+ return ;
+}
diff --git a/web/src/components/ui/textarea.tsx b/web/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..717a51d
--- /dev/null
+++ b/web/src/components/ui/textarea.tsx
@@ -0,0 +1,14 @@
+import type * as React from "react";
+import { cn } from "@/lib/utils";
+
+export function Textarea({ className, ...props }: React.TextareaHTMLAttributes) {
+ return (
+
+ );
+}
diff --git a/web/src/dashboard/a2a.ts b/web/src/dashboard/a2a.ts
new file mode 100644
index 0000000..eb55237
--- /dev/null
+++ b/web/src/dashboard/a2a.ts
@@ -0,0 +1,187 @@
+import type { IPublishPacket } from "mqtt";
+import type { AgentCard, DashboardConfig } from "./types";
+
+const IP_BROKER_URL = "ws://162.14.117.182:8083/mqtt";
+const LEGACY_DOMAIN_BROKER_URL = "wss://emqx-device-agent.cloud/mqtt";
+const IP_HOSTNAME = "162.14.117.182";
+
+function sameOriginBrokerUrl() {
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
+ return `${protocol}//${window.location.host}/mqtt`;
+}
+
+function shouldUseSameOriginBroker() {
+ if (typeof window === "undefined") return false;
+ const { hostname, protocol } = window.location;
+ return (
+ protocol === "https:" ||
+ hostname === "localhost" ||
+ hostname === "127.0.0.1" ||
+ hostname === IP_HOSTNAME
+ );
+}
+
+function defaultBrokerUrl() {
+ return shouldUseSameOriginBroker() ? sameOriginBrokerUrl() : IP_BROKER_URL;
+}
+
+export const DEFAULT_CONFIG: DashboardConfig = {
+ brokerUrl: defaultBrokerUrl(),
+ org: "default",
+ unit: "default",
+ username: "",
+ password: "",
+};
+
+function normalizeBrokerUrl(value: unknown) {
+ if (typeof value !== "string") return DEFAULT_CONFIG.brokerUrl;
+ const brokerUrl = value.trim();
+ if (!brokerUrl) return DEFAULT_CONFIG.brokerUrl;
+ if (shouldUseSameOriginBroker() && brokerUrl === IP_BROKER_URL) return DEFAULT_CONFIG.brokerUrl;
+ if (typeof window !== "undefined" && window.location.protocol === "https:" && brokerUrl.startsWith("ws://")) {
+ return DEFAULT_CONFIG.brokerUrl;
+ }
+ if (brokerUrl === LEGACY_DOMAIN_BROKER_URL || brokerUrl.startsWith(`${LEGACY_DOMAIN_BROKER_URL}?`)) {
+ return DEFAULT_CONFIG.brokerUrl;
+ }
+ return brokerUrl;
+}
+
+export const terminalStates = new Set([
+ "TASK_STATE_COMPLETED",
+ "TASK_STATE_FAILED",
+ "TASK_STATE_CANCELED",
+ "TASK_STATE_REJECTED",
+ "TASK_STATE_INPUT_REQUIRED",
+ "TASK_STATE_AUTH_REQUIRED",
+]);
+
+export function topicPrefix() {
+ return "$a2a/v1";
+}
+
+export function discoveryTopic(config: DashboardConfig) {
+ return `${topicPrefix()}/discovery/${config.org}/${config.unit}/+`;
+}
+
+export function requestTopic(config: DashboardConfig, agentId: string) {
+ return `${topicPrefix()}/request/${config.org}/${config.unit}/${agentId}`;
+}
+
+export function replyTopic(config: DashboardConfig, clientId: string) {
+ // clientId is the requester's own agent_id segment; callers append a
+ // per-request reply_suffix → $a2a/v1/reply/{org}/{unit}/{agent_id}/{suffix}.
+ return `${topicPrefix()}/reply/${config.org}/${config.unit}/${clientId}`;
+}
+
+export function eventTopic(config: DashboardConfig) {
+ return `${topicPrefix()}/event/${config.org}/${config.unit}/+`;
+}
+
+export function uuid() {
+ const cryptoApi = globalThis.crypto;
+ if (typeof cryptoApi?.randomUUID === "function") return cryptoApi.randomUUID();
+
+ if (typeof cryptoApi?.getRandomValues === "function") {
+ const bytes = cryptoApi.getRandomValues(new Uint8Array(16));
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
+ return [
+ hex.slice(0, 4).join(""),
+ hex.slice(4, 6).join(""),
+ hex.slice(6, 8).join(""),
+ hex.slice(8, 10).join(""),
+ hex.slice(10, 16).join(""),
+ ].join("-");
+ }
+
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
+ const random = Math.floor(Math.random() * 16);
+ const value = char === "x" ? random : (random & 0x3) | 0x8;
+ return value.toString(16);
+ });
+}
+
+export function parseStoredConfig(): DashboardConfig {
+ try {
+ const raw = localStorage.getItem("skitter.dashboard.config");
+ const stored = raw
+ ? (JSON.parse(raw) as Partial & { language?: unknown })
+ : {};
+ delete stored.language;
+ const config = { ...DEFAULT_CONFIG, ...stored };
+ return { ...config, brokerUrl: normalizeBrokerUrl(config.brokerUrl) };
+ } catch {
+ return { ...DEFAULT_CONFIG };
+ }
+}
+
+export function isAppCard(card: AgentCard) {
+ return Boolean(
+ card.capabilities?.extensions?.some(
+ (extension) => extension.uri === "urn:skitter:app" && extension.params?.tasks,
+ ),
+ );
+}
+
+export function isTaskAgentCard(card: AgentCard) {
+ return Boolean(
+ card.capabilities?.extensions?.some((extension) => extension.uri === "urn:skitter:task-agent"),
+ );
+}
+
+export function decodeCorrelation(packet: IPublishPacket) {
+ const value = packet.properties?.correlationData;
+ if (!value) return "";
+ if (typeof value === "string") return value;
+ if (value instanceof Uint8Array) return new TextDecoder().decode(value);
+ return String(value);
+}
+
+export function getUserProperty(packet: IPublishPacket, key: string) {
+ const props = packet.properties?.userProperties;
+ if (!props) return "";
+ if (Array.isArray(props)) {
+ const found = props.find((item) => Array.isArray(item) && item[0] === key);
+ return found ? String(found[1]) : "";
+ }
+ const value = (props as Record)[key];
+ return Array.isArray(value) ? value[0] : value ?? "";
+}
+
+export function buildA2ARequest(
+ text: string,
+ requestId: string,
+ contextId: string,
+ sender = "dashboard",
+ taskId = uuid(),
+) {
+ return JSON.stringify({
+ jsonrpc: "2.0",
+ id: requestId,
+ method: "SendMessage",
+ params: {
+ message: {
+ messageId: uuid().replaceAll("-", ""),
+ role: "ROLE_USER",
+ parts: [{ text }],
+ taskId,
+ contextId,
+ },
+ metadata: { sender },
+ },
+ });
+}
+
+export function extractPartsText(parts: Array<{ text?: string }> | undefined) {
+ return parts?.map((part) => part.text ?? "").join("") ?? "";
+}
+
+export function parseMaybeJson(text: string) {
+ try {
+ return JSON.parse(text) as unknown;
+ } catch {
+ return text;
+ }
+}
diff --git a/web/src/dashboard/agent-utils.ts b/web/src/dashboard/agent-utils.ts
new file mode 100644
index 0000000..a599816
--- /dev/null
+++ b/web/src/dashboard/agent-utils.ts
@@ -0,0 +1,5 @@
+import type { AgentEntry } from "./types";
+
+export function displayName(agent: AgentEntry | null) {
+ return agent?.card.name ?? agent?.id ?? "";
+}
diff --git a/web/src/dashboard/components/agent-glyph.tsx b/web/src/dashboard/components/agent-glyph.tsx
new file mode 100644
index 0000000..9420e2f
--- /dev/null
+++ b/web/src/dashboard/components/agent-glyph.tsx
@@ -0,0 +1,18 @@
+import { Cpu, Fan, Layers3, LayoutList, LockKeyhole } from "lucide-react";
+import type { AgentEntry } from "../types";
+
+export function AgentGlyph({
+ agent,
+ className,
+}: {
+ agent: AgentEntry | null;
+ className?: string;
+}) {
+ const text = `${agent?.id ?? ""} ${agent?.card.name ?? ""}`.toLowerCase();
+
+ if (agent?.isApp) return ;
+ if (text.includes("lock")) return ;
+ if (text.includes("fan")) return ;
+ if (text.includes("skitter")) return ;
+ return ;
+}
diff --git a/web/src/dashboard/components/app-header.tsx b/web/src/dashboard/components/app-header.tsx
new file mode 100644
index 0000000..1143fc9
--- /dev/null
+++ b/web/src/dashboard/components/app-header.tsx
@@ -0,0 +1,196 @@
+import { Check, Laptop, Moon, RefreshCw, SlidersHorizontal, Sun } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { cn } from "@/lib/utils";
+import type { DashboardConfig, DashboardLanguage, ResolvedTheme, ThemeMode } from "../types";
+import { ConnectionBadge } from "./connection-badge";
+import { SettingsDialog } from "./settings-dialog";
+import type { SkitterDashboardController } from "../use-skitter-dashboard";
+
+interface AppHeaderProps {
+ clientId: string;
+ config: DashboardConfig;
+ activeView: "workflows" | "scenes";
+ connection: SkitterDashboardController["connection"];
+ connectionError: string;
+ connect: () => void;
+ language: SkitterDashboardController["language"];
+ setLanguage: SkitterDashboardController["setLanguage"];
+ theme: SkitterDashboardController["theme"];
+ resolvedTheme: SkitterDashboardController["resolvedTheme"];
+ setTheme: SkitterDashboardController["setTheme"];
+ settingsOpen: boolean;
+ setSettingsOpen: (open: boolean) => void;
+ draftConfig: SkitterDashboardController["draftConfig"];
+ setDraftConfig: SkitterDashboardController["setDraftConfig"];
+ applySettings: () => void;
+}
+
+export function AppHeader({
+ clientId,
+ config,
+ activeView,
+ connection,
+ connectionError,
+ connect,
+ language,
+ setLanguage,
+ theme,
+ resolvedTheme,
+ setTheme,
+ settingsOpen,
+ setSettingsOpen,
+ draftConfig,
+ setDraftConfig,
+ applySettings,
+}: AppHeaderProps) {
+ const { t } = useTranslation();
+ const title = activeView === "scenes" ? t("common.scenes") : t("common.workflows");
+
+ return (
+
+
+
+
+
+
+ Scope {config.org}/{config.unit}
+
+
+ {connectionError ? (
+
+ {connectionError}
+
+ ) : null}
+
+
+
+
+
+
+
+
+ );
+}
+
+interface PreferencesMenuProps {
+ language: DashboardLanguage;
+ setLanguage: (language: DashboardLanguage) => void;
+ theme: ThemeMode;
+ resolvedTheme: ResolvedTheme;
+ setTheme: (theme: ThemeMode) => void;
+}
+
+function PreferencesMenu({
+ language,
+ setLanguage,
+ theme,
+ resolvedTheme,
+ setTheme,
+}: PreferencesMenuProps) {
+ const { t } = useTranslation();
+ const themeLabel =
+ theme === "system"
+ ? `${t("theme.system")} (${t(`theme.${resolvedTheme}`)})`
+ : t(`theme.${theme}`);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t("preferences.language")}
+
+ setLanguage("en")}
+ >
+ {t("preferences.english")}
+
+
+ setLanguage("zh")}
+ >
+ {t("preferences.chinese")}
+
+
+
+
+
+
+ {t("preferences.theme")}
+
+ setTheme("system")}
+ >
+
+ {t("theme.system")}
+
+
+ setTheme("light")}
+ >
+
+ {t("theme.light")}
+
+
+ setTheme("dark")}
+ >
+
+ {t("theme.dark")}
+
+
+
+
+
+ );
+}
+
+function menuItemClass(active: boolean) {
+ return cn("h-8 rounded-[6px] px-2 text-sm", active && "font-medium text-foreground");
+}
+
+function checkClass(active: boolean) {
+ return cn("ml-auto size-4", active ? "opacity-100" : "opacity-0");
+}
diff --git a/web/src/dashboard/components/chat-bubble.tsx b/web/src/dashboard/components/chat-bubble.tsx
new file mode 100644
index 0000000..0b578e4
--- /dev/null
+++ b/web/src/dashboard/components/chat-bubble.tsx
@@ -0,0 +1,57 @@
+import { Loader2 } from "lucide-react";
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+import { useTranslation } from "react-i18next";
+import { cn } from "@/lib/utils";
+import type { ChatMessage } from "../types";
+
+export function ChatBubble({
+ message,
+ activeView,
+}: {
+ message: ChatMessage;
+ activeView: "workflows" | "scenes";
+}) {
+ const { t } = useTranslation();
+ const user = message.role === "user";
+
+ return (
+
+
+ {message.streaming ? (
+
+
+ {message.text || streamingLabel(message.state, activeView, t)}
+
+ ) : (
+
+ {message.text || ""}
+
+ )}
+
+
+ );
+}
+
+function streamingLabel(
+ state: string | undefined,
+ activeView: "workflows" | "scenes",
+ t: ReturnType["t"],
+) {
+ if (activeView === "scenes") {
+ if (state === "TASK_STATE_SUBMITTED" || state === "submitted") return t("chat.sceneStarting");
+ if (state === "TASK_STATE_WORKING") return t("chat.sceneWorking");
+ return t("chat.sceneFallback");
+ }
+
+ if (state === "TASK_STATE_SUBMITTED" || state === "submitted") return t("chat.skitterStarting");
+ if (state === "TASK_STATE_WORKING") return t("chat.skitterWorkflow");
+ return t("chat.skitterFallback");
+}
diff --git a/web/src/dashboard/components/connection-badge.tsx b/web/src/dashboard/components/connection-badge.tsx
new file mode 100644
index 0000000..e899bb3
--- /dev/null
+++ b/web/src/dashboard/components/connection-badge.tsx
@@ -0,0 +1,33 @@
+import { CheckCircle2, CircleOff, Loader2, XCircle } from "lucide-react";
+import type { LucideIcon } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { Badge } from "@/components/ui/badge";
+import type { BadgeProps } from "@/components/ui/badge";
+import { cn } from "@/lib/utils";
+import type { ConnectionState } from "../types";
+
+type ConnectionBadgeItem = {
+ labelKey: string;
+ variant: NonNullable;
+ icon: LucideIcon;
+};
+
+const connectionBadges = {
+ connected: { labelKey: "connection.connected", variant: "success", icon: CheckCircle2 },
+ connecting: { labelKey: "connection.connecting", variant: "warning", icon: Loader2 },
+ disconnected: { labelKey: "connection.disconnected", variant: "muted", icon: CircleOff },
+ error: { labelKey: "connection.error", variant: "warning", icon: XCircle },
+} satisfies Record;
+
+export function ConnectionBadge({ state }: { state: ConnectionState }) {
+ const { t } = useTranslation();
+ const item = connectionBadges[state];
+ const Icon = item.icon;
+
+ return (
+
+
+ {t(item.labelKey)}
+
+ );
+}
diff --git a/web/src/dashboard/components/dashboard-shell.tsx b/web/src/dashboard/components/dashboard-shell.tsx
new file mode 100644
index 0000000..8ef1bfa
--- /dev/null
+++ b/web/src/dashboard/components/dashboard-shell.tsx
@@ -0,0 +1,122 @@
+import { useEffect } from "react";
+import { Allotment, setSashSize } from "allotment";
+import type { SkitterDashboardController } from "../use-skitter-dashboard";
+import { AppHeader } from "./app-header";
+import { DashboardSidebar } from "./dashboard-sidebar";
+import { OrchestrationPanel } from "./orchestration-panel";
+import { RegistryPanel } from "./registry-panel";
+
+const DEFAULT_SPLIT_SIZES = [30, 70];
+const DASHBOARD_SPLIT_SIZES_KEY = "skitter-dashboard-allotment-sizes-v2";
+
+function readStoredSplitSizes() {
+ if (typeof window === "undefined") return DEFAULT_SPLIT_SIZES;
+
+ try {
+ const raw = window.localStorage.getItem(DASHBOARD_SPLIT_SIZES_KEY);
+ if (!raw) return DEFAULT_SPLIT_SIZES;
+
+ const value: unknown = JSON.parse(raw);
+ if (
+ Array.isArray(value) &&
+ value.length === 2 &&
+ value.every((size) => typeof size === "number" && Number.isFinite(size) && size > 0)
+ ) {
+ return value;
+ }
+ } catch {
+ window.localStorage.removeItem(DASHBOARD_SPLIT_SIZES_KEY);
+ }
+
+ return DEFAULT_SPLIT_SIZES;
+}
+
+function storeSplitSizes(sizes: number[]) {
+ if (sizes.length !== 2 || sizes.some((size) => !Number.isFinite(size) || size <= 0)) {
+ return;
+ }
+
+ window.localStorage.setItem(DASHBOARD_SPLIT_SIZES_KEY, JSON.stringify(sizes));
+}
+
+export function DashboardShell({ dashboard }: { dashboard: SkitterDashboardController }) {
+ useEffect(() => {
+ setSashSize(12);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/src/dashboard/components/dashboard-sidebar.tsx b/web/src/dashboard/components/dashboard-sidebar.tsx
new file mode 100644
index 0000000..fe4a1f6
--- /dev/null
+++ b/web/src/dashboard/components/dashboard-sidebar.tsx
@@ -0,0 +1,85 @@
+import { Bot, LayoutList } from "lucide-react";
+import type { ReactNode } from "react";
+import { useTranslation } from "react-i18next";
+import { cn } from "@/lib/utils";
+
+interface DashboardSidebarProps {
+ org: string;
+ unit: string;
+ activeView: "workflows" | "scenes";
+ selectWorkflows: () => void;
+ selectScenes: () => void;
+}
+
+export function DashboardSidebar({
+ org,
+ unit,
+ activeView,
+ selectWorkflows,
+ selectScenes,
+}: DashboardSidebarProps) {
+ const { t } = useTranslation();
+ const baseUrl = import.meta.env.BASE_URL.endsWith("/")
+ ? import.meta.env.BASE_URL
+ : `${import.meta.env.BASE_URL}/`;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function SidebarNavButton({
+ active,
+ children,
+ label,
+ onClick,
+}: {
+ active: boolean;
+ children: ReactNode;
+ label: string;
+ onClick: () => void;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/web/src/dashboard/components/orchestration-panel.tsx b/web/src/dashboard/components/orchestration-panel.tsx
new file mode 100644
index 0000000..1f71b7d
--- /dev/null
+++ b/web/src/dashboard/components/orchestration-panel.tsx
@@ -0,0 +1,143 @@
+import { useMemo } from "react";
+import { Loader2, Send } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { Button } from "@/components/ui/button";
+import { Textarea } from "@/components/ui/textarea";
+import { displayName } from "../agent-utils";
+import type { AgentEntry, ChatSession, ConnectionState } from "../types";
+import { RunOutput } from "./run-output";
+import { WorkflowPreview } from "./workflow-preview";
+
+interface OrchestrationPanelProps {
+ skitterAgent: AgentEntry;
+ activeView: "workflows" | "scenes";
+ callableAgents: AgentEntry[];
+ appAgents: AgentEntry[];
+ taskAgents: AgentEntry[];
+ activeChat: ChatSession | null;
+ chatInput: string;
+ setChatInput: (value: string) => void;
+ sendChat: (text: string) => void;
+ connection: ConnectionState;
+}
+
+export function OrchestrationPanel({
+ skitterAgent,
+ activeView,
+ callableAgents,
+ appAgents,
+ taskAgents,
+ activeChat,
+ chatInput,
+ setChatInput,
+ sendChat,
+ connection,
+}: OrchestrationPanelProps) {
+ const { t } = useTranslation();
+ const isTaskAgentView = activeView === "scenes";
+ const canSend = Boolean(
+ chatInput.trim() &&
+ skitterAgent.id &&
+ !activeChat?.streaming &&
+ (connection === "connected" || skitterAgent.isMock),
+ );
+ const contextTitle = isTaskAgentView ? displayName(skitterAgent) : "";
+ const traceAgents = useMemo(
+ () => [...callableAgents, ...taskAgents],
+ [callableAgents, taskAgents],
+ );
+
+ return (
+
+
+
+ {contextTitle ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+ );
+}
+
+interface PromptComposerProps {
+ chatInput: string;
+ setChatInput: (value: string) => void;
+ canSend: boolean;
+ streaming: boolean;
+ sendChat: (text: string) => void;
+ placeholder: string;
+ sendLabel: string;
+}
+
+function PromptComposer({
+ chatInput,
+ setChatInput,
+ canSend,
+ streaming,
+ sendChat,
+ placeholder,
+ sendLabel,
+}: PromptComposerProps) {
+ function submit() {
+ sendChat(chatInput);
+ setChatInput("");
+ }
+
+ return (
+
+ );
+}
diff --git a/web/src/dashboard/components/registry-panel.tsx b/web/src/dashboard/components/registry-panel.tsx
new file mode 100644
index 0000000..f217af1
--- /dev/null
+++ b/web/src/dashboard/components/registry-panel.tsx
@@ -0,0 +1,576 @@
+import { Children, useMemo, useState } from "react";
+import { Bot, LayoutList, Loader2, Plus, Trash2 } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { toast } from "sonner";
+import type { ReactNode } from "react";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Textarea } from "@/components/ui/textarea";
+import { cn } from "@/lib/utils";
+import { displayName } from "../agent-utils";
+import type { AgentDeviceBinding, AgentEntry } from "../types";
+import { AgentGlyph } from "./agent-glyph";
+import { WorkflowGraphViewer } from "./workflow-graph-viewer";
+
+type AgentSortMode = "newest" | "oldest";
+
+interface RegistryPanelProps {
+ agents: AgentEntry[];
+ apps: AgentEntry[];
+ taskAgents: AgentEntry[];
+ activeView: "workflows" | "scenes";
+ activeSceneId: string;
+ selectScene: (agentId: string) => void;
+ agentDeviceBindings: Map;
+ updateAgentBindingDevice: (agentId: string, deviceId: string) => void;
+ deleteWorkflow: (workflowId: string) => Promise;
+ createMockTaskAgent: (draft: { name: string; description: string }) => void;
+ deleteMockTaskAgent: (agentId: string) => void;
+}
+
+export function RegistryPanel({
+ agents,
+ apps,
+ taskAgents,
+ activeView,
+ activeSceneId,
+ selectScene,
+ agentDeviceBindings,
+ updateAgentBindingDevice,
+ deleteWorkflow,
+ createMockTaskAgent,
+ deleteMockTaskAgent,
+}: RegistryPanelProps) {
+ const { t } = useTranslation();
+ const [agentSortMode, setAgentSortMode] = useState("newest");
+ const sortedAgents = useMemo(
+ () => sortDeviceAgents(agents, agentSortMode),
+ [agents, agentSortMode],
+ );
+
+ return (
+
+
+
+ {activeView === "workflows" ? (
+
+ {apps.map((app) => (
+
+ ))}
+
+ ) : (
+
}
+ >
+ {taskAgents.map((scene) => (
+
selectScene(scene.id)}
+ deleteMockTaskAgent={deleteMockTaskAgent}
+ />
+ ))}
+
+ )}
+
+
+
+ }
+ contentClassName="gap-2"
+ >
+ {sortedAgents.map((agent) => (
+
+ ))}
+
+
+
+
+ );
+}
+
+function sortDeviceAgents(agents: AgentEntry[], mode: AgentSortMode) {
+ return [...agents].sort((left, right) => {
+ if (left.lastSeenAt && !right.lastSeenAt) return -1;
+ if (!left.lastSeenAt && right.lastSeenAt) return 1;
+ if (left.lastSeenAt && right.lastSeenAt && left.lastSeenAt !== right.lastSeenAt) {
+ return mode === "newest"
+ ? right.lastSeenAt - left.lastSeenAt
+ : left.lastSeenAt - right.lastSeenAt;
+ }
+
+ const nameDiff = displayName(left).localeCompare(displayName(right), undefined, {
+ sensitivity: "base",
+ numeric: true,
+ });
+ return nameDiff;
+ });
+}
+
+function AgentSortSelect({
+ value,
+ onChange,
+}: {
+ value: AgentSortMode;
+ onChange: (mode: AgentSortMode) => void;
+}) {
+ const { t } = useTranslation();
+
+ return (
+ onChange(mode as AgentSortMode)}>
+
+
+
+
+
+
+ {t("registry.sortNewest")}
+
+
+ {t("registry.sortOldest")}
+
+
+
+
+ );
+}
+
+function CapabilitySection({
+ title,
+ empty,
+ action,
+ contentClassName,
+ children,
+}: {
+ title: string;
+ empty: string;
+ action?: ReactNode;
+ contentClassName?: string;
+ children: ReactNode;
+}) {
+ const hasChildren = Children.count(children) > 0;
+
+ return (
+
+
+
+ {hasChildren ? (
+ children
+ ) : (
+
+ )}
+
+
+ );
+}
+
+function EmptyListState({ label }: { label: string }) {
+ return (
+
+ {label}
+
+ );
+}
+
+function SceneRow({
+ scene,
+ active,
+ onSelect,
+ deleteMockTaskAgent,
+}: {
+ scene: AgentEntry;
+ active: boolean;
+ onSelect: () => void;
+ deleteMockTaskAgent: (agentId: string) => void;
+}) {
+ const { t } = useTranslation();
+ const name = displayName(scene);
+ const description = scene.card.description || scene.id;
+
+ return (
+
+
+ }
+ name={name}
+ description={description}
+ />
+
+ {scene.isMock ? (
+ deleteMockTaskAgent(scene.id)}
+ >
+
+
+ ) : null}
+
+ );
+}
+
+function CreateTaskAgentDialog({
+ agents,
+ createMockTaskAgent,
+}: {
+ agents: AgentEntry[];
+ createMockTaskAgent: (draft: { name: string; description: string }) => void;
+}) {
+ const { t } = useTranslation();
+ const [open, setOpen] = useState(false);
+ const [name, setName] = useState("");
+ const [description, setDescription] = useState("");
+ const canCreate = Boolean(name.trim() && description.trim());
+
+ function setDialogOpen(nextOpen: boolean) {
+ setOpen(nextOpen);
+ if (!nextOpen) {
+ setName("");
+ setDescription("");
+ }
+ }
+
+ function submit() {
+ if (!canCreate) return;
+ const nextName = name.trim();
+ createMockTaskAgent({ name: nextName, description: description.trim() });
+ toast.success(t("mockTask.created", { name: nextName }));
+ setDialogOpen(false);
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {t("mockTask.title")}
+
+
+
+ {t("mockTask.nameLabel")}
+ setName(event.target.value)}
+ />
+
+
+ {t("mockTask.descriptionLabel")}
+
+
+
{t("mockTask.devicesTitle")}
+ {agents.length ? (
+
+ {agents.slice(0, 4).map((agent) => (
+
+ {displayName(agent)}
+
+ ))}
+
+ ) : (
+
{t("mockTask.noDevices")}
+ )}
+
+
+
+ setDialogOpen(false)}>
+ {t("common.cancel")}
+
+
+ {t("mockTask.create")}
+
+
+
+
+ );
+}
+
+function WorkflowRow({
+ app,
+ agents,
+ deleteWorkflow,
+}: {
+ app: AgentEntry;
+ agents: AgentEntry[];
+ deleteWorkflow: (workflowId: string) => Promise;
+}) {
+ const [deleting, setDeleting] = useState(false);
+ const [open, setOpen] = useState(false);
+ const { t } = useTranslation();
+ const name = displayName(app);
+ const description = app.card.description || app.id;
+
+ async function remove() {
+ if (deleting) return;
+
+ setDeleting(true);
+ try {
+ await deleteWorkflow(app.id);
+ toast.success(t("registry.deleted", { name }));
+ } catch (error) {
+ toast.error(error instanceof Error ? error.message : t("registry.failedDelete", { name }));
+ } finally {
+ setDeleting(false);
+ }
+ }
+
+ return (
+
+
+
+
+ }
+ name={name}
+ description={description}
+ />
+
+
+
+ {deleting ? : }
+
+
+
+
+ {name}
+ {description}
+
+
+
+
+
+
+ );
+}
+
+function AgentRow({
+ agent,
+ binding,
+ updateAgentBindingDevice,
+}: {
+ agent: AgentEntry;
+ binding: AgentDeviceBinding;
+ updateAgentBindingDevice: (agentId: string, deviceId: string) => void;
+}) {
+ const name = displayName(agent);
+ const description = agent.card.description || agent.id;
+
+ return (
+
+
+
+ {description}
+
+
+
+ );
+}
+
+function CapabilityRowContent({
+ icon,
+ name,
+ description,
+}: {
+ icon: ReactNode;
+ name: string;
+ description: string;
+}) {
+ return (
+ <>
+
+ {icon}
+
+
+ {name}
+
+ {description}
+
+
+ >
+ );
+}
+
+function PanelDivider() {
+ return
;
+}
+
+function AgentDeviceSelect({
+ binding,
+ updateAgentBindingDevice,
+}: {
+ binding: AgentDeviceBinding;
+ updateAgentBindingDevice: (agentId: string, deviceId: string) => void;
+}) {
+ const { t } = useTranslation();
+ const devices = binding.loading ? [] : binding.devices;
+ const error = devices.length ? "" : binding.error;
+ const placeholder = binding.loading ? t("registry.loadingDevices") : t("registry.selectDevice");
+
+ return (
+
+
updateAgentBindingDevice(binding.agentId, deviceId)}
+ >
+
+
+
+
+
+ {devices.map((device) => (
+
+ {device.deviceId}
+ {device.online === false ? ` ${t("registry.offline")}` : ""}
+
+ ))}
+
+
+
+ {binding.loading ? (
+
+ ) : null}
+ {error ?
{error}
: null}
+
+ );
+}
+
+function loadingAgentBinding(agent: AgentEntry): AgentDeviceBinding {
+ return {
+ agentId: agent.id,
+ agentName: displayName(agent),
+ devices: [],
+ selectedDeviceId: "",
+ loading: true,
+ };
+}
diff --git a/web/src/dashboard/components/run-output.tsx b/web/src/dashboard/components/run-output.tsx
new file mode 100644
index 0000000..2084b87
--- /dev/null
+++ b/web/src/dashboard/components/run-output.tsx
@@ -0,0 +1,48 @@
+import { ScrollArea } from "@/components/ui/scroll-area";
+import type { AgentEntry, ChatSession } from "../types";
+import { chatTracePanels } from "../workflow-model";
+import { ChatBubble } from "./chat-bubble";
+import { RuntimeProgressPanel } from "./runtime-progress";
+import { WorkflowTrace } from "./workflow-trace";
+
+export function RunOutput({
+ activeChat,
+ traceAgents,
+ workflows,
+ activeView,
+}: {
+ activeChat: ChatSession | null;
+ traceAgents: AgentEntry[];
+ workflows: AgentEntry[];
+ activeView: "workflows" | "scenes";
+}) {
+ const { showTrace, showProgress } = chatTracePanels(activeChat);
+ const showMessages = Boolean(activeChat?.messages.length);
+
+ return (
+
+
+
+ {showTrace || showProgress || showMessages ? (
+ <>
+ {showTrace ? (
+
+ ) : null}
+ {showProgress && activeChat?.runtimeProgress ? (
+
+ ) : null}
+ {activeChat?.messages.map((message) => (
+
+ ))}
+ >
+ ) : null}
+
+
+
+ );
+}
diff --git a/web/src/dashboard/components/runtime-progress.tsx b/web/src/dashboard/components/runtime-progress.tsx
new file mode 100644
index 0000000..47491a7
--- /dev/null
+++ b/web/src/dashboard/components/runtime-progress.tsx
@@ -0,0 +1,158 @@
+import { CheckCircle2, CircleDashed, Loader2, XCircle } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { cn } from "@/lib/utils";
+import type {
+ RuntimeProgress,
+ RuntimeProgressKind,
+ RuntimeProgressPhase,
+} from "../types";
+
+interface RuntimeProgressPanelProps {
+ progress: RuntimeProgress;
+ className?: string;
+}
+
+type ProgressStepId = "submitted" | "building" | "preparing_run" | "registering" | "completed";
+type ProgressStepState = "waiting" | "running" | "completed" | "failed";
+
+const CREATE_STEPS: ProgressStepId[] = ["submitted", "building", "registering", "completed"];
+const RUN_STEPS: ProgressStepId[] = ["submitted", "preparing_run", "completed"];
+const REQUEST_STEPS: ProgressStepId[] = ["submitted", "building", "completed"];
+
+export function RuntimeProgressPanel({ progress, className }: RuntimeProgressPanelProps) {
+ const { t } = useTranslation();
+ const steps = progressSteps(progress.kind);
+ const activeIndex = progressIndex(progress.phase, steps);
+
+ return (
+
+
+
+
{progressTitle(progress.kind, t)}
+
+ {progressDetail(progress, t)}
+
+
+
+
+
+
+ {steps.map((step, index) => (
+
+ ))}
+
+
+ );
+}
+
+function ProgressRow({ label, state }: { label: string; state: ProgressStepState }) {
+ return (
+
+ );
+}
+
+function ProgressBadge({ phase }: { phase: RuntimeProgressPhase }) {
+ const { t } = useTranslation();
+ if (phase === "completed") {
+ return (
+
+ {t("progress.completed")}
+
+ );
+ }
+ if (phase === "failed") {
+ return {t("progress.failed")} ;
+ }
+ return {t("progress.running")} ;
+}
+
+function ProgressIcon({ state }: { state: ProgressStepState }) {
+ if (state === "completed") return ;
+ if (state === "failed") return ;
+ if (state === "running") return ;
+ return ;
+}
+
+function stepState(
+ phase: RuntimeProgressPhase,
+ index: number,
+ activeIndex: number,
+ total: number,
+): ProgressStepState {
+ if (phase === "completed") return "completed";
+ if (phase === "failed") return index === total - 1 ? "failed" : "completed";
+ if (index < activeIndex) return "completed";
+ if (index === activeIndex) return "running";
+ return "waiting";
+}
+
+function progressIndex(phase: RuntimeProgressPhase, steps: ProgressStepId[]) {
+ if (phase === "failed") return steps.length - 1;
+ const index = steps.indexOf(phase);
+ return index >= 0 ? index : 0;
+}
+
+function progressSteps(kind: RuntimeProgressKind) {
+ if (kind === "workflow_run") return RUN_STEPS;
+ if (kind === "workflow_create") return CREATE_STEPS;
+ return REQUEST_STEPS;
+}
+
+function progressTitle(kind: RuntimeProgressKind, t: ReturnType["t"]) {
+ if (kind === "workflow_request") return t("progress.requestTitle");
+ return kind === "workflow_run" ? t("progress.runTitle") : t("progress.createTitle");
+}
+
+function progressSubtitle(
+ kind: RuntimeProgressKind,
+ phase: RuntimeProgressPhase,
+ t: ReturnType["t"],
+) {
+ if (kind === "workflow_request") return t("progress.requestSubtitle");
+ if (kind === "workflow_run") return t("progress.runSubtitle");
+ if (phase === "submitted") return t("progress.createSubmitted");
+ if (phase === "registering") return t("progress.createRegistering");
+ return t("progress.createSubtitle");
+}
+
+function progressDetail(
+ progress: RuntimeProgress,
+ t: ReturnType["t"],
+) {
+ const detail = progress.detail ?? "";
+ if (/planning workflow request/i.test(detail)) {
+ return progress.kind === "workflow_create"
+ ? t("progress.createSubtitle")
+ : t("progress.requestSubtitle");
+ }
+ if (/register|publish/i.test(detail)) {
+ return t("progress.createRegistering");
+ }
+ return detail || progressSubtitle(progress.kind, progress.phase, t);
+}
+
+function progressStepLabel(
+ kind: RuntimeProgressKind,
+ step: ProgressStepId,
+ t: ReturnType["t"],
+) {
+ if (kind === "workflow_request" && step === "building") return t("progress.steps.resolving");
+ return t(`progress.steps.${step}`);
+}
diff --git a/web/src/dashboard/components/settings-dialog.tsx b/web/src/dashboard/components/settings-dialog.tsx
new file mode 100644
index 0000000..43528e4
--- /dev/null
+++ b/web/src/dashboard/components/settings-dialog.tsx
@@ -0,0 +1,117 @@
+import type { Dispatch, SetStateAction } from "react";
+import { Cable } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import type { DashboardConfig } from "../types";
+
+interface SettingsDialogProps {
+ open: boolean;
+ setOpen: (open: boolean) => void;
+ draftConfig: DashboardConfig;
+ setDraftConfig: Dispatch>;
+ applySettings: () => void;
+ clientId: string;
+}
+
+export function SettingsDialog({
+ open,
+ setOpen,
+ draftConfig,
+ setDraftConfig,
+ applySettings,
+ clientId,
+}: SettingsDialogProps) {
+ const { t } = useTranslation();
+ const brokerUrlValid = /^wss?:\/\/.+/i.test(draftConfig.brokerUrl.trim());
+
+ return (
+
+
+
+
+
+
+
+
+ {t("settings.title")}
+ {t("settings.description")}
+
+
+
+ setOpen(false)}>
+ {t("common.cancel")}
+
+
+ {t("common.save")}
+
+
+
+
+ );
+}
diff --git a/web/src/dashboard/components/workflow-graph-viewer.tsx b/web/src/dashboard/components/workflow-graph-viewer.tsx
new file mode 100644
index 0000000..e1dcf65
--- /dev/null
+++ b/web/src/dashboard/components/workflow-graph-viewer.tsx
@@ -0,0 +1,520 @@
+import {
+ Background,
+ BackgroundVariant,
+ Controls,
+ Handle,
+ MiniMap,
+ Position,
+ ReactFlow,
+ useEdgesState,
+ useNodesState,
+ type Edge,
+ type Node,
+ type NodeProps,
+} from "@xyflow/react";
+import { CheckCircle2, CircleDashed, Expand, Flag, Loader2, XCircle } from "lucide-react";
+import { useMemo, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { cn } from "@/lib/utils";
+import { productIdFromAgentId } from "../device-binding";
+import type {
+ AgentEntry,
+ ChatSession,
+ WorkflowState,
+ WorkflowTaskDefinition,
+ WorkflowTraceStep,
+} from "../types";
+import {
+ type WorkflowDefinition,
+ mergedWorkflowSteps,
+ workflowDefinitionFromApp,
+} from "../workflow-model";
+import { AgentGlyph } from "./agent-glyph";
+
+type FlowState = WorkflowTraceStep["state"];
+
+interface FlowTask {
+ id: string;
+ agentId: string;
+ agentName?: string;
+ description: string;
+ needs: string[];
+ hasExplicitNeeds: boolean;
+ terminal: boolean;
+ state: FlowState;
+ result?: string;
+ error?: string;
+}
+
+interface WorkflowGraphViewerProps {
+ activeChat?: ChatSession | null;
+ agents: AgentEntry[];
+ workflows?: AgentEntry[];
+ workflow?: AgentEntry | null;
+ className?: string;
+ expandable?: boolean;
+ expandTitle?: string;
+ expandDescription?: string;
+}
+
+interface FlowNodeData extends Record {
+ task: FlowTask;
+ agent?: AgentEntry;
+ index: number;
+ isStart: boolean;
+}
+
+type FlowNode = Node;
+
+const nodeTypes = {
+ workflowTask: WorkflowTaskNode,
+};
+
+export function WorkflowGraphViewer({
+ activeChat,
+ agents,
+ workflows = [],
+ workflow,
+ className,
+ expandable,
+ expandTitle,
+ expandDescription,
+}: WorkflowGraphViewerProps) {
+ const { t } = useTranslation();
+ const agentsById = useMemo(() => new Map(agents.map((agent) => [agent.id, agent])), [agents]);
+ const workflowFromChat = workflows.find((item) => item.id === activeChat?.workflow?.appId);
+ const selectedWorkflow = workflowFromChat ?? workflow;
+ const definition = useMemo(
+ () => workflowDefinitionFromApp(selectedWorkflow, agentsById),
+ [agentsById, selectedWorkflow],
+ );
+ const tasks = useMemo(
+ () => graphTasks(definition, activeChat, workflows),
+ [activeChat, definition, workflows],
+ );
+ const graph = useMemo(() => buildFlowGraph(tasks, agentsById), [agentsById, tasks]);
+
+ // Changes only on real data changes (structure / task state), never on drag, so it
+ // can drive a controlled re-seed without fighting manual node positions.
+ const graphKey = useMemo(
+ () => tasks.map((task) => `${task.id}:${task.state}:${task.terminal ? 1 : 0}`).join("|"),
+ [tasks],
+ );
+
+ const showMiniMap = tasks.length > 6;
+
+ if (!tasks.length) {
+ return (
+
+ {t("flow.empty")}
+
+ );
+ }
+
+ const canvas = (
+
+
+ {expandable ? (
+
+
+
+ {t("flow.expand")}
+
+
+ ) : null}
+
+ );
+
+ if (!expandable) return canvas;
+
+ return (
+
+ {canvas}
+
+
+ {expandTitle || t("flow.title")}
+ {expandDescription ? (
+ {expandDescription}
+ ) : null}
+
+
+
+
+
+
+ );
+}
+
+function FlowCanvas({
+ graphKey,
+ nodes: seedNodes,
+ edges: seedEdges,
+ showMiniMap,
+}: {
+ graphKey: string;
+ nodes: FlowNode[];
+ edges: Edge[];
+ showMiniMap: boolean;
+}) {
+ const [nodes, setNodes, onNodesChange] = useNodesState(seedNodes);
+ const [edges, setEdges, onEdgesChange] = useEdgesState(seedEdges);
+
+ // Reset to the freshly computed graph whenever the workflow data changes. Guarded by
+ // graphKey (which never changes on drag) so manual node positions are preserved.
+ const [seededKey, setSeededKey] = useState(graphKey);
+ if (seededKey !== graphKey) {
+ setSeededKey(graphKey);
+ setNodes(seedNodes);
+ setEdges(seedEdges);
+ }
+
+ return (
+
+
+
+ {showMiniMap ? (
+
+ ) : null}
+
+ );
+}
+
+function WorkflowTaskNode({ data }: NodeProps) {
+ const { t } = useTranslation();
+ const { task, agent, index, isStart } = data;
+ const title = task.description || task.id;
+ const agentName = agentLabel(agent, task.agentName, task.agentId);
+ const resolved = Boolean(agent?.card.name || task.agentName);
+ const stepLabel = isStart ? t("flow.start") : t("flow.step", { index: index + 1 });
+
+ return (
+
+
+
+
+
+ {isStart ? : null}
+ {stepLabel}
+
+
+
+
+
+
{title}
+
+
+ {agent ? : }
+
+
+ {agentName}
+
+ {task.terminal ? (
+
+ {t("flow.terminal")}
+
+ ) : null}
+
+
+
+
+
+ );
+}
+
+// Resolve a readable agent label, never a raw URN id. Prefer the registry card name,
+// then the workflow-provided name, then the decoded product id from a URN agent id.
+function agentLabel(agent: AgentEntry | undefined, fallbackName: string | undefined, agentId: string) {
+ const name = agent?.card.name?.trim() || fallbackName?.trim();
+ if (name) return name;
+ if (agentId.includes(".p.")) return productIdFromAgentId(agentId) || agentId;
+ return agentId;
+}
+
+function StatusBadge({ state }: { state: FlowState }) {
+ return (
+
+
+
+ );
+}
+
+function graphTasks(
+ definition: WorkflowDefinition | null,
+ activeChat: ChatSession | null | undefined,
+ workflows: AgentEntry[],
+) {
+ const steps = mergedWorkflowSteps(activeChat ?? null, workflows);
+ if (definition) return mergeDefinitionWithTrace(definition.tasks, steps);
+ return traceTasks(steps);
+}
+
+function mergeDefinitionWithTrace(
+ tasks: WorkflowTaskDefinition[],
+ steps: WorkflowTraceStep[],
+): FlowTask[] {
+ const stepsById = new Map(steps.map((step) => [step.id, step]));
+ const usedStepIds = new Set();
+
+ const merged = tasks.map((task) => {
+ const step = stepsById.get(task.id) ?? steps.find((item) => !usedStepIds.has(item.id) && item.agentId === task.agentId);
+ if (step) usedStepIds.add(step.id);
+
+ return {
+ ...task,
+ agentName: task.agentName || step?.agentName,
+ description: step?.description || task.description || task.id,
+ state: step?.state ?? "waiting",
+ result: step?.result,
+ error: step?.error,
+ };
+ });
+
+ // Chain runtime-only steps after the last planned task so they connect into
+ // the graph instead of floating as orphaned "Start" nodes.
+ let previousId = merged.length ? merged[merged.length - 1].id : "";
+ const appended = steps
+ .filter((step) => !usedStepIds.has(step.id))
+ .map((step) => {
+ const task = traceStepTask(step, previousId);
+ previousId = task.id;
+ return task;
+ });
+
+ return [...merged, ...appended];
+}
+
+function traceTasks(steps: WorkflowTraceStep[]) {
+ return steps.map((step, index) => traceStepTask(step, steps[index - 1]?.id));
+}
+
+function traceStepTask(step: WorkflowTraceStep, previousId = ""): FlowTask {
+ return {
+ id: step.id,
+ agentId: step.agentId,
+ agentName: step.agentName,
+ description: step.description || step.id,
+ needs: previousId ? [previousId] : [],
+ hasExplicitNeeds: false,
+ terminal: false,
+ state: step.state,
+ result: step.result,
+ error: step.error,
+ };
+}
+
+function buildFlowGraph(
+ tasks: FlowTask[],
+ agentsById: Map,
+): { nodes: FlowNode[]; edges: Edge[] } {
+ const layout = taskLayout(tasks);
+ const edges = graphEdges(tasks);
+ const incomingTaskIds = new Set(edges.map((edge) => edge.target));
+ const nodes: FlowNode[] = tasks.map((task, index) => ({
+ id: task.id,
+ type: "workflowTask",
+ position: layout.get(task.id) ?? { x: 0, y: 0 },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left,
+ data: {
+ task,
+ agent: agentsById.get(task.agentId),
+ index,
+ isStart: !incomingTaskIds.has(task.id),
+ },
+ }));
+
+ return { nodes, edges };
+}
+
+function graphEdges(tasks: FlowTask[]): Edge[] {
+ const taskIds = new Set(tasks.map((task) => task.id));
+ const hasExplicitNeeds = tasks.some((task) => task.hasExplicitNeeds);
+
+ if (!hasExplicitNeeds) {
+ return tasks.slice(1).map((task, index) => workflowEdge(tasks[index].id, task.id, task.state));
+ }
+
+ return tasks.flatMap((task) =>
+ task.needs
+ .filter((need) => taskIds.has(need))
+ .map((need) => workflowEdge(need, task.id, task.state)),
+ );
+}
+
+function workflowEdge(source: string, target: string, targetState: FlowState): Edge {
+ const settled = targetState === "completed" || targetState === "failed";
+
+ return {
+ id: edgeId(source, target),
+ source,
+ target,
+ type: "default",
+ animated: !settled,
+ style: {
+ stroke: workflowEdgeStroke(targetState),
+ strokeWidth: 1.5,
+ },
+ };
+}
+
+function edgeId(source: string, target: string) {
+ return `edge:${source.length}:${source}:${target.length}:${target}`;
+}
+
+function taskLayout(tasks: FlowTask[]) {
+ const layers = taskLayers(tasks);
+ const grouped = new Map();
+ for (const task of tasks) {
+ const layer = layers.get(task.id) ?? 0;
+ grouped.set(layer, [...(grouped.get(layer) ?? []), task]);
+ }
+
+ const positions = new Map();
+ const xGap = 380;
+ const yGap = 188;
+
+ for (const [layer, layerTasks] of grouped) {
+ const totalHeight = (layerTasks.length - 1) * yGap;
+ layerTasks.forEach((task, row) => {
+ positions.set(task.id, {
+ x: layer * xGap,
+ y: row * yGap - totalHeight / 2,
+ });
+ });
+ }
+
+ return positions;
+}
+
+function taskLayers(tasks: FlowTask[]) {
+ const taskIds = new Set(tasks.map((task) => task.id));
+ const hasExplicitNeeds = tasks.some((task) => task.hasExplicitNeeds);
+ if (!hasExplicitNeeds) return new Map(tasks.map((task, index) => [task.id, index]));
+
+ const layers = new Map();
+ const pending = new Set(tasks.map((task) => task.id));
+ const taskById = new Map(tasks.map((task) => [task.id, task]));
+
+ for (let pass = 0; pass < tasks.length && pending.size; pass += 1) {
+ for (const id of [...pending]) {
+ const task = taskById.get(id);
+ if (!task) continue;
+
+ const upstream = task.needs.filter((need) => taskIds.has(need));
+ if (upstream.some((need) => !layers.has(need))) continue;
+
+ const layer = upstream.length
+ ? Math.max(...upstream.map((need) => layers.get(need) ?? 0)) + 1
+ : 0;
+ layers.set(id, layer);
+ pending.delete(id);
+ }
+ }
+
+ for (const id of pending) {
+ layers.set(id, layers.size);
+ }
+
+ return layers;
+}
+
+function StateIcon({ state }: { state: WorkflowState | FlowState }) {
+ if (state === "completed") return ;
+ if (state === "failed") return ;
+ if (state === "waiting" || state === "idle") return ;
+ return ;
+}
+
+function workflowEdgeStroke(state: FlowState) {
+ if (state === "failed") return "var(--destructive)";
+ if (state === "running") return "var(--brand-500)";
+ if (state === "completed") return "var(--online-solid)";
+ return "color-mix(in srgb, var(--muted-foreground) 50%, transparent)";
+}
+
+function statusBadgeClass(state: WorkflowState | FlowState) {
+ if (state === "completed") return "border-online-solid/25 bg-online-bg text-online-solid";
+ if (state === "failed") return "border-destructive/25 bg-destructive/10 text-destructive";
+ if (state === "running") return "border-brand-500/25 bg-brand-500/10 text-brand-500";
+ return "border-border bg-muted/50 text-muted-foreground";
+}
+
+function miniMapNodeColor(node: Node) {
+ const state = (node.data as FlowNodeData | undefined)?.task?.state;
+ if (!state) return "var(--muted-foreground)";
+ if (state === "failed") return "var(--destructive)";
+ if (state === "running") return "var(--brand-500)";
+ if (state === "completed") return "var(--online-solid)";
+ return "color-mix(in srgb, var(--muted-foreground) 40%, transparent)";
+}
diff --git a/web/src/dashboard/components/workflow-preview.tsx b/web/src/dashboard/components/workflow-preview.tsx
new file mode 100644
index 0000000..a7ff1aa
--- /dev/null
+++ b/web/src/dashboard/components/workflow-preview.tsx
@@ -0,0 +1,62 @@
+import { useTranslation } from "react-i18next";
+import { cn } from "@/lib/utils";
+import type { AgentEntry, ChatSession } from "../types";
+import { chatTracePanels } from "../workflow-model";
+import { RuntimeProgressPanel } from "./runtime-progress";
+import { WorkflowTrace } from "./workflow-trace";
+
+interface WorkflowPreviewProps {
+ traceAgents: AgentEntry[];
+ workflows: AgentEntry[];
+ activeView: "workflows" | "scenes";
+ activeChat: ChatSession | null;
+ className?: string;
+}
+
+export function WorkflowPreview({
+ traceAgents,
+ workflows,
+ activeView,
+ activeChat,
+ className,
+}: WorkflowPreviewProps) {
+ const { t } = useTranslation();
+ const { showTrace: hasTrace, showProgress: hasProgress } = chatTracePanels(activeChat);
+ const isTaskAgentView = activeView === "scenes";
+ const title = hasProgress ? t("preview.progress") : t("preview.tracePanel");
+
+ return (
+
+
+
+ {title}
+
+
+
+
+ {hasTrace ? (
+
+ ) : hasProgress && activeChat?.runtimeProgress ? (
+
+ ) : (
+
+ {isTaskAgentView ? t("preview.a2aTraceEmpty") : t("preview.workflowTraceEmpty")}
+
+ )}
+
+
+ );
+}
diff --git a/web/src/dashboard/components/workflow-trace.tsx b/web/src/dashboard/components/workflow-trace.tsx
new file mode 100644
index 0000000..920c565
--- /dev/null
+++ b/web/src/dashboard/components/workflow-trace.tsx
@@ -0,0 +1,408 @@
+import { CheckCircle2, CircleDashed, Loader2, XCircle } from "lucide-react";
+import type { TFunction } from "i18next";
+import { useTranslation } from "react-i18next";
+import { Badge } from "@/components/ui/badge";
+import { cn } from "@/lib/utils";
+import { displayName } from "../agent-utils";
+import type { AgentEntry, ChatSession, WorkflowTraceStep } from "../types";
+import { mergedWorkflowSteps } from "../workflow-model";
+import { AgentGlyph } from "./agent-glyph";
+import { WorkflowGraphViewer } from "./workflow-graph-viewer";
+
+interface WorkflowTraceProps {
+ activeChat: ChatSession | null;
+ agents: AgentEntry[];
+ workflows?: AgentEntry[];
+ className?: string;
+}
+
+export function WorkflowTrace({
+ activeChat,
+ agents,
+ workflows = [],
+ className,
+}: WorkflowTraceProps) {
+ const { t } = useTranslation();
+ const isProcessing = Boolean(activeChat?.streaming);
+ const isTaskAgentChat = Boolean(activeChat?.targetId && activeChat.targetId !== "skitter");
+ const steps = mergedWorkflowSteps(activeChat, workflows);
+ const hasRuntimeTrace = Boolean(activeChat?.workflow || steps.length || isProcessing);
+
+ if (!hasRuntimeTrace) return null;
+
+ const agentsById = new Map(agents.map((agent) => [agent.id, agent]));
+ const state = traceState(activeChat, steps);
+
+ if (isTaskAgentChat) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+ {runtimeTraceLabel(activeChat, steps, workflows, t)}
+
+
+
+
+ {steps.length > 0 ? (
+
+ ) : null}
+
+ {steps.length > 0 ?
: null}
+
+
+
+ );
+}
+
+function SceneTrace({
+ activeChat,
+ agentsById,
+ className,
+ state,
+ steps,
+ t,
+}: {
+ activeChat: ChatSession | null;
+ agentsById: Map;
+ className?: string;
+ state: string;
+ steps: WorkflowTraceStep[];
+ t: TFunction;
+}) {
+ return (
+
+
+
+
{t("trace.a2aSteps")}
+
+ {runtimeTraceLabel(activeChat, steps, [], t)}
+
+
+
+
+
+
+ {steps.length > 0 ? (
+ steps.map((step, index) => (
+
+ ))
+ ) : (
+
+ )}
+
+
+ );
+}
+
+function SceneStepRow({
+ agent,
+ description,
+ index,
+ name,
+ result,
+ state,
+ t,
+}: {
+ agent?: AgentEntry;
+ description: string;
+ index: number;
+ name: string;
+ result?: string;
+ state: string;
+ t: TFunction;
+}) {
+ return (
+
+
+
+
+
+
+
{index}
+
+ {agent ? : }
+
+
{name}
+
+
+ {traceDescription(description, t)}
+
+ {result ? (
+
+ {compactSnippet(result, 140)}
+
+ ) : null}
+
+
+ );
+}
+
+function RuntimeTrace({
+ agentsById,
+ steps,
+ t,
+}: {
+ agentsById: Map;
+ steps: WorkflowTraceStep[];
+ t: TFunction;
+}) {
+ return (
+
+ {steps.length > 0 ? (
+
+ {steps.map((step, index) => (
+
+ ))}
+
+ ) : (
+
+ )}
+
+ );
+}
+
+function TraceStepRow({
+ isLast,
+ name,
+ agent,
+ description,
+ state,
+ result,
+ error,
+ t,
+}: {
+ isLast: boolean;
+ name: string;
+ agent?: AgentEntry;
+ description: string;
+ state: string;
+ result?: string;
+ error?: string;
+ t: TFunction;
+}) {
+ const resultSnippet = error || compactSnippet(result, 140);
+ const stepTitle = traceDescription(description, t);
+
+ return (
+
+
+ {!isLast ? : null}
+
+
+
+
+
+
+
+
+
{stepTitle}
+
+
+ {agent ? : }
+
+
{name}
+
+
+ {state !== "completed" ?
: null}
+
+
+ {resultSnippet ? (
+
+ {resultSnippet}
+
+ ) : null}
+
+
+ );
+}
+
+function PreparingTrace({ message }: { message: string }) {
+ return (
+
+ );
+}
+
+function TraceStateBadge({ state, t }: { state: string; t: TFunction }) {
+ const badgeClassName = "shrink-0 whitespace-nowrap";
+ if (state === "completed") {
+ return (
+
+ {t("trace.completed")}
+
+ );
+ }
+ if (state === "failed") {
+ return (
+
+ {t("trace.failed")}
+
+ );
+ }
+ if (state === "waiting") {
+ return (
+
+ {t("trace.waiting")}
+
+ );
+ }
+ if (state === "running") {
+ return (
+
+ {t("trace.running")}
+
+ );
+ }
+ return (
+
+ {state}
+
+ );
+}
+
+function StateIcon({ state }: { state: string }) {
+ if (state === "completed") return ;
+ if (state === "failed") return ;
+ if (state === "waiting") return ;
+ return ;
+}
+
+function runtimeTraceLabel(
+ activeChat: ChatSession | null,
+ steps: WorkflowTraceStep[],
+ workflows: AgentEntry[],
+ t: TFunction,
+) {
+ if (activeChat?.targetId && activeChat.targetId !== "skitter") {
+ const completed = steps.filter((step) => step.state === "completed").length;
+ const total = steps.length;
+ if (total > 0) return t("trace.a2aStepsCompleted", { completed, total });
+ return t("trace.preparingA2A");
+ }
+
+ const workflow = activeChat?.workflow;
+ if (!workflow?.appId) return t("trace.preparingCallFlow");
+ const completed = steps.filter((step) => step.state === "completed").length;
+ const total = steps.length;
+ const description = workflows.find((item) => item.id === workflow.appId)?.card.description;
+ if (total > 0) {
+ const progress = t("trace.stepsCompleted", { completed, total });
+ return description ? `${progress} · ${description}` : progress;
+ }
+ return description || t("trace.preparingCallFlow");
+}
+
+function traceFlowTitle(
+ activeChat: ChatSession | null,
+ workflows: AgentEntry[],
+ t: TFunction,
+) {
+ const app = workflows.find((item) => item.id === activeChat?.workflow?.appId);
+ return (app ? displayName(app) : "") || t("flow.title");
+}
+
+function traceState(activeChat: ChatSession | null, steps: WorkflowTraceStep[]) {
+ if (activeChat?.workflow?.state) return activeChat.workflow.state;
+ if (activeChat?.streaming) return "running";
+ if (steps.some((step) => step.state === "failed")) return "failed";
+ if (steps.length > 0) return "completed";
+ return "running";
+}
+
+function displayTraceAgentName(step: WorkflowTraceStep, agentsById: Map) {
+ const agent = agentsById.get(step.agentId);
+ return agent ? displayName(agent) : step.agentName || step.agentId || step.id;
+}
+
+function traceDescription(value: string, t: TFunction) {
+ const normalized = value.replace(/\s+/g, " ").trim();
+ if (/interpret user request/i.test(normalized)) return t("trace.planTask");
+ return compactSnippet(normalized, 90) ?? normalized;
+}
+
+function compactSnippet(value?: string, limit = 260) {
+ if (!value) return undefined;
+ const normalized = value.replace(/\s+/g, " ").trim();
+ if (!normalized) return undefined;
+ return normalized.length > limit ? `${normalized.slice(0, limit - 3)}...` : normalized;
+}
diff --git a/web/src/dashboard/device-binding.ts b/web/src/dashboard/device-binding.ts
new file mode 100644
index 0000000..81e6b74
--- /dev/null
+++ b/web/src/dashboard/device-binding.ts
@@ -0,0 +1,145 @@
+import type {
+ DeviceBindingRequest,
+ DeviceOption,
+} from "./types";
+
+export function parseDeviceBindingRequest(value: unknown): DeviceBindingRequest | null {
+ const parsed = typeof value === "string" ? parseJsonFragment(value) : value;
+ if (!isRecord(parsed) || !isRecord(parsed.device_binding_required)) return null;
+
+ const data = parsed.device_binding_required;
+ const appId = String(data.app_id ?? "").trim();
+ const workflowName = String(data.workflow_name ?? "").trim();
+ const prompt = String(data.prompt ?? "").trim();
+ const rawTasks = Array.isArray(data.tasks) ? data.tasks : [];
+ const tasks = rawTasks
+ .filter(isRecord)
+ .map((task) => ({
+ id: String(task.id ?? "").trim(),
+ agentId: String(task.agent_id ?? task.agent ?? "").trim(),
+ agentName: String(task.agent_name ?? "").trim() || undefined,
+ description: String(task.description ?? "").trim(),
+ needs: [],
+ hasExplicitNeeds: false,
+ terminal: false,
+ }))
+ .filter((task) => task.id && task.agentId);
+
+ if (!appId || !workflowName || !prompt || tasks.length === 0) return null;
+ return { appId, workflowName, prompt, tasks };
+}
+
+export function parseDeviceOptions(value: unknown): DeviceOption[] {
+ const parsed = typeof value === "string" ? parseJsonFragment(value) : value;
+ return rawDeviceList(parsed)
+ .filter(isRecord)
+ .map(parseDeviceOption)
+ .filter(isDeviceOption);
+}
+
+export function productIdFromAgentId(agentId: string) {
+ const productSegment = agentId.split(".p.")[1]?.split(".")[0] ?? "";
+ if (!productSegment) return "";
+ return decodeBase64Url(productSegment) || productSegment;
+}
+
+export async function fetchAgentDevices(agentId: string): Promise {
+ const productId = productIdFromAgentId(agentId);
+ if (!productId) throw new Error("Cannot resolve product ID");
+
+ const response = await fetch(`/api/products/${encodeURIComponent(productId)}/devices`, {
+ headers: { Accept: "application/json" },
+ });
+ if (!response.ok) throw new Error(`Device API returned ${response.status}`);
+
+ return parseDeviceOptions(await response.json());
+}
+
+export function bindingPrompt(prompt: string, workflowName: string, bindings: Array<{
+ description: string;
+ agentName: string;
+ device: DeviceOption;
+}>) {
+ const lines = bindings.map(
+ (binding) =>
+ `- ${binding.description} via ${binding.agentName}: device_id=${binding.device.deviceId}, device_name=${binding.device.name}`,
+ );
+
+ return `${prompt}
+
+Confirmed device bindings for workflow "${workflowName}":
+${lines.join("\n")}
+
+Use exactly these device_id values for the matching workflow steps. Do not choose other devices.`;
+}
+
+function parseJsonFragment(text: string): unknown {
+ let normalized = text.trim();
+ if (normalized.startsWith("```")) {
+ normalized = normalized.replace(/^```[a-zA-Z0-9_-]*\s*/, "").replace(/\s*```$/, "").trim();
+ }
+
+ try {
+ return JSON.parse(normalized);
+ } catch {
+ const match = normalized.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
+ if (!match) return null;
+ try {
+ return JSON.parse(match[1]);
+ } catch {
+ return null;
+ }
+ }
+}
+
+function isRecord(value: unknown): value is Record {
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
+}
+
+function rawDeviceList(value: unknown) {
+ if (Array.isArray(value)) return value;
+ if (isRecord(value) && Array.isArray(value.devices)) return value.devices;
+ return [];
+}
+
+function parseDeviceOption(device: Record): DeviceOption | null {
+ const deviceId = String(device.device_id ?? device.id ?? "").trim();
+ if (!deviceId) return null;
+
+ const option: DeviceOption = {
+ deviceId,
+ name: deviceName(device, deviceId),
+ };
+ const online = deviceOnline(device);
+ if (online !== undefined) option.online = online;
+ if (isRecord(device.state)) option.state = device.state;
+
+ return option;
+}
+
+function deviceName(device: Record, fallback: string) {
+ return typeof device.name === "string" && device.name.trim() ? device.name.trim() : fallback;
+}
+
+function deviceOnline(device: Record) {
+ if (typeof device.online === "boolean") return device.online;
+ if (device.status === "online") return true;
+ if (device.status === "offline") return false;
+ return undefined;
+}
+
+function isDeviceOption(value: DeviceOption | null): value is DeviceOption {
+ return value !== null;
+}
+
+function decodeBase64Url(value: string) {
+ try {
+ const normalized = value.replaceAll("-", "+").replaceAll("_", "/");
+ const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
+ const binary = atob(padded);
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
+ return new TextDecoder().decode(bytes).trim();
+ } catch {
+ return "";
+ }
+}
diff --git a/web/src/dashboard/i18n.ts b/web/src/dashboard/i18n.ts
new file mode 100644
index 0000000..2146aa2
--- /dev/null
+++ b/web/src/dashboard/i18n.ts
@@ -0,0 +1,381 @@
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import type { DashboardLanguage } from "./types";
+
+const CONFIG_STORAGE_KEY = "skitter.dashboard.config";
+const LANGUAGE_STORAGE_KEY = "skitter.dashboard.language";
+
+function isLanguage(value: unknown): value is DashboardLanguage {
+ return value === "en" || value === "zh";
+}
+
+function browserLanguage(): DashboardLanguage {
+ return navigator.language.toLowerCase().startsWith("zh") ? "zh" : "en";
+}
+
+function initialLanguage(): DashboardLanguage {
+ try {
+ const storedLanguage = localStorage.getItem(LANGUAGE_STORAGE_KEY);
+ if (isLanguage(storedLanguage)) return storedLanguage;
+
+ const raw = localStorage.getItem(CONFIG_STORAGE_KEY);
+ if (!raw) return browserLanguage();
+
+ const parsed = JSON.parse(raw) as { language?: unknown };
+ return isLanguage(parsed.language) ? parsed.language : browserLanguage();
+ } catch {
+ return browserLanguage();
+ }
+}
+
+export const resources = {
+ en: {
+ translation: {
+ common: {
+ cancel: "Cancel",
+ save: "Save",
+ workflow: "Scene",
+ workflows: "Scenes",
+ scenes: "Tasks",
+ skitter: "Skitter",
+ a2a: "A2A",
+ },
+ settings: {
+ title: "MQTT Config",
+ trigger: "MQTT Config",
+ description: "Network MQTT WebSocket connection used by this dashboard.",
+ brokerUrl: "Broker URL",
+ brokerUrlInvalid: "Enter a WebSocket URL starting with ws:// or wss://",
+ org: "Organization",
+ unit: "Unit",
+ username: "Username",
+ password: "Password",
+ clientId: "MQTT Client ID",
+ },
+ preferences: {
+ trigger: "Language and theme",
+ title: "Preferences",
+ language: "Language",
+ english: "English",
+ chinese: "中文",
+ theme: "Theme",
+ },
+ header: {
+ reconnect: "Reconnect",
+ theme: "Theme: {{theme}} (effective: {{resolvedTheme}})",
+ },
+ connection: {
+ connected: "Connected",
+ connecting: "Connecting",
+ disconnected: "Disconnected",
+ error: "Error",
+ },
+ theme: {
+ system: "System",
+ light: "Light",
+ dark: "Dark",
+ },
+ registry: {
+ savedWorkflows: "Saved",
+ noWorkflows: "No saved scenes. Create one in chat.",
+ taskAgents: "Task agents",
+ noScenes: "No task agents. Use Create to add one.",
+ registeredDeviceAgents: "Device Agent Cards",
+ noOnlineAgents: "No registered device agents",
+ deleted: "Deleted {{name}}",
+ failedDelete: "Failed to delete {{name}}",
+ delete: "Delete {{name}}",
+ loadingDevices: "Loading devices",
+ selectDevice: "Select device",
+ offline: "offline",
+ sort: "Sort",
+ sortNewest: "Newest",
+ sortOldest: "Oldest",
+ },
+ chat: {
+ workflowPlaceholder: "Type a goal to create or run...",
+ scenePlaceholder: "Ask this task to coordinate devices...",
+ send: "Send message",
+ sceneStarting: "Task agent is handling the request...",
+ sceneWorking: "Task agent is coordinating devices...",
+ sceneFallback: "Task agent is working...",
+ skitterStarting: "Skitter is handling the request...",
+ skitterWorkflow: "Skitter is running...",
+ skitterFallback: "Skitter is working...",
+ },
+ preview: {
+ sceneTrace: "Task trace",
+ workflowTrace: "Scene trace",
+ tracePanel: "Trace",
+ a2aTraceEmpty: "No trace",
+ workflowTraceEmpty: "No trace",
+ preparingCallFlow: "Preparing call flow",
+ progress: "Progress",
+ workflowSession: "Scene session",
+ },
+ flow: {
+ title: "Scene flow",
+ empty: "No workflow graph",
+ open: "View {{name}} workflow",
+ expand: "Fullscreen",
+ start: "Start",
+ step: "Step {{index}}",
+ terminal: "terminal",
+ },
+ progress: {
+ requestTitle: "Processing",
+ createTitle: "Creating",
+ runTitle: "Preparing run",
+ requestSubtitle: "Skitter is matching the request to a saved scene.",
+ createSubmitted: "Skitter received the request.",
+ createSubtitle: "Skitter is designing the scene.",
+ createRegistering: "Skitter is saving the scene.",
+ runSubtitle: "Skitter is preparing the selected item.",
+ running: "running",
+ completed: "completed",
+ failed: "failed",
+ steps: {
+ submitted: "Send request",
+ building: "Design scene",
+ resolving: "Match scene",
+ registering: "Save scene",
+ preparing_run: "Prepare execution",
+ completed: "Ready",
+ },
+ },
+ trace: {
+ workflowCallFlow: "Scene call flow",
+ callFlow: "Call flow",
+ a2aCallFlow: "A2A call flow",
+ a2aSteps: "A2A steps",
+ scenePlanning: "Task agent is planning the A2A call flow.",
+ skitterPreparing: "Skitter is checking the request and preparing the call flow.",
+ step: "Step {{index}}",
+ completed: "completed",
+ running: "running",
+ failed: "failed",
+ waiting: "waiting",
+ a2aStepsCompleted: "{{completed}}/{{total}} A2A steps completed",
+ stepsCompleted: "{{completed}}/{{total}} steps completed",
+ preparingA2A: "Preparing A2A call flow",
+ preparingCallFlow: "Preparing call flow",
+ workflowSession: "Scene session",
+ planTask: "Plan task",
+ },
+ status: {
+ runningWorkflow: "Running {{name}}...",
+ bindDevices: "Bind devices for {{name}} in the left panel, then ask Skitter to run it again.",
+ a2aFailed: "A2A request failed",
+ mqttDisconnected: "MQTT client is not connected",
+ coordinatorTimeout: "Coordinator did not reply before timeout",
+ agentTimeout: "Agent did not reply before timeout",
+ noDevices: "No registered devices",
+ a2aTimeout: "A2A request timed out",
+ },
+ fallback: {
+ skitterDescription: "Scene coordinator",
+ scenesName: "Tasks",
+ scenesDescription: "Task agents",
+ waitingSceneDiscovery: "Waiting for task agent discovery",
+ },
+ mockTask: {
+ title: "Create task agent",
+ create: "Create task agent",
+ createShort: "Create",
+ created: "Created {{name}}",
+ delete: "Delete {{name}}",
+ nameLabel: "Agent name",
+ namePlaceholder: "Example: Smart Cockpit Coordinator",
+ descriptionLabel: "Description",
+ descriptionPlaceholder: "Example: Coordinate cockpit scene mode, range, and microclimate devices.",
+ devicesTitle: "Available device agents",
+ noDevices: "No registered device agents are online yet.",
+ stepPlan: "Plan task",
+ stepPlanResult: "Parsed user intent and prepared the device coordination plan.",
+ stepDevices: "Match device agents",
+ stepDevicesResult: "Selected available device agents and prepared A2A calls.",
+ mockReply:
+ "### {{name}}\n\nTask agent created from the dashboard preview.\n\n- Interprets the user request\n- Selects matching device agents\n- Coordinates execution through A2A",
+ },
+ },
+ },
+ zh: {
+ translation: {
+ common: {
+ cancel: "取消",
+ save: "保存",
+ workflow: "场景",
+ workflows: "场景",
+ scenes: "任务",
+ skitter: "Skitter",
+ a2a: "A2A",
+ },
+ settings: {
+ title: "MQTT 配置",
+ trigger: "MQTT 配置",
+ description: "Dashboard 使用的网络层 MQTT WebSocket 连接。",
+ brokerUrl: "Broker URL",
+ brokerUrlInvalid: "请输入以 ws:// 或 wss:// 开头的 WebSocket 地址",
+ org: "组织",
+ unit: "单元",
+ username: "用户名",
+ password: "密码",
+ clientId: "MQTT Client ID",
+ },
+ preferences: {
+ trigger: "语言和主题",
+ title: "偏好",
+ language: "语言",
+ english: "English",
+ chinese: "中文",
+ theme: "主题",
+ },
+ header: {
+ reconnect: "重新连接",
+ theme: "主题:{{theme}}(当前:{{resolvedTheme}})",
+ },
+ connection: {
+ connected: "已连接",
+ connecting: "连接中",
+ disconnected: "已断开",
+ error: "错误",
+ },
+ theme: {
+ system: "跟随系统",
+ light: "浅色",
+ dark: "深色",
+ },
+ registry: {
+ savedWorkflows: "已保存",
+ noWorkflows: "暂无场景,可在对话中创建。",
+ taskAgents: "任务 Agent",
+ noScenes: "暂无任务 Agent,可点击创建。",
+ registeredDeviceAgents: "已注册设备 Agent",
+ noOnlineAgents: "没有注册设备 Agent",
+ deleted: "已删除 {{name}}",
+ failedDelete: "删除 {{name}} 失败",
+ delete: "删除 {{name}}",
+ loadingDevices: "加载设备中",
+ selectDevice: "选择设备",
+ offline: "离线",
+ sort: "排序",
+ sortNewest: "最近上报",
+ sortOldest: "最早上报",
+ },
+ chat: {
+ workflowPlaceholder: "输入目标,创建或运行...",
+ scenePlaceholder: "让这个任务协调设备...",
+ send: "发送消息",
+ sceneStarting: "任务 Agent 正在处理请求...",
+ sceneWorking: "任务 Agent 正在协调设备...",
+ sceneFallback: "任务 Agent 正在处理...",
+ skitterStarting: "Skitter 正在处理请求...",
+ skitterWorkflow: "Skitter 正在运行...",
+ skitterFallback: "Skitter 正在处理...",
+ },
+ preview: {
+ sceneTrace: "任务链路",
+ workflowTrace: "场景链路",
+ tracePanel: "链路",
+ a2aTraceEmpty: "暂无链路",
+ workflowTraceEmpty: "暂无链路",
+ preparingCallFlow: "正在准备调用链路",
+ progress: "进度",
+ workflowSession: "场景会话",
+ },
+ flow: {
+ title: "场景流程图",
+ empty: "暂无流程图",
+ open: "查看 {{name}} 流程图",
+ expand: "全屏查看",
+ start: "开始",
+ step: "步骤 {{index}}",
+ terminal: "终点",
+ },
+ progress: {
+ requestTitle: "正在处理",
+ createTitle: "正在创建",
+ runTitle: "准备运行",
+ requestSubtitle: "Skitter 正在匹配场景。",
+ createSubmitted: "Skitter 已收到请求。",
+ createSubtitle: "Skitter 正在设计场景。",
+ createRegistering: "Skitter 正在保存场景。",
+ runSubtitle: "Skitter 正在准备选中项。",
+ running: "进行中",
+ completed: "已完成",
+ failed: "失败",
+ steps: {
+ submitted: "发送请求",
+ building: "设计场景",
+ resolving: "匹配场景",
+ registering: "保存场景",
+ preparing_run: "准备执行",
+ completed: "就绪",
+ },
+ },
+ trace: {
+ workflowCallFlow: "场景调用链路",
+ callFlow: "调用链路",
+ a2aCallFlow: "A2A 调用链路",
+ a2aSteps: "A2A 步骤",
+ scenePlanning: "任务 Agent 正在规划 A2A 调用链路。",
+ skitterPreparing: "Skitter 正在检查请求并准备调用链路。",
+ step: "步骤 {{index}}",
+ completed: "已完成",
+ running: "进行中",
+ failed: "失败",
+ waiting: "等待中",
+ a2aStepsCompleted: "{{completed}}/{{total}} 个 A2A 步骤已完成",
+ stepsCompleted: "{{completed}}/{{total}} 个步骤已完成",
+ preparingA2A: "正在准备 A2A 调用链路",
+ preparingCallFlow: "正在准备调用链路",
+ workflowSession: "场景会话",
+ planTask: "规划任务",
+ },
+ status: {
+ runningWorkflow: "正在运行 {{name}}...",
+ bindDevices: "先在左侧为 {{name}} 绑定设备,然后再让 Skitter 运行一次。",
+ a2aFailed: "A2A 请求失败",
+ mqttDisconnected: "MQTT 客户端未连接",
+ coordinatorTimeout: "Coordinator 响应超时",
+ agentTimeout: "Agent 响应超时",
+ noDevices: "没有注册设备",
+ a2aTimeout: "A2A 请求超时",
+ },
+ fallback: {
+ skitterDescription: "场景协调",
+ scenesName: "任务",
+ scenesDescription: "任务 Agent",
+ waitingSceneDiscovery: "等待任务 Agent 发现",
+ },
+ mockTask: {
+ title: "创建任务 Agent",
+ create: "创建任务 Agent",
+ createShort: "创建",
+ created: "已创建 {{name}}",
+ delete: "删除 {{name}}",
+ nameLabel: "Agent 名称",
+ namePlaceholder: "例如:智能驾驶舱",
+ descriptionLabel: "描述",
+ descriptionPlaceholder: "例如:协调座舱场景、续航与微气候设备,自动完成驾驶舱体验编排。",
+ devicesTitle: "可用设备 Agent",
+ noDevices: "当前没有在线的已注册设备 Agent。",
+ stepPlan: "规划任务",
+ stepPlanResult: "解析用户意图,并准备设备协调计划。",
+ stepDevices: "匹配设备 Agent",
+ stepDevicesResult: "选择可用设备 Agent,并准备 A2A 调用。",
+ mockReply:
+ "### {{name}}\n\n这是从 Dashboard 创建的任务 Agent 预览。\n\n- 理解座舱场景请求\n- 选择匹配的设备 Agent\n- 通过 A2A 协调座舱设备执行",
+ },
+ },
+ },
+} as const;
+
+void i18n.use(initReactI18next).init({
+ resources,
+ lng: initialLanguage(),
+ fallbackLng: "en",
+ interpolation: { escapeValue: false },
+});
+
+export default i18n;
diff --git a/web/src/dashboard/types.ts b/web/src/dashboard/types.ts
new file mode 100644
index 0000000..9005d2c
--- /dev/null
+++ b/web/src/dashboard/types.ts
@@ -0,0 +1,143 @@
+export type ConnectionState = "disconnected" | "connecting" | "connected" | "error";
+export type ReplyRole = "user" | "agent";
+export type DashboardLanguage = "en" | "zh";
+export type ThemeMode = "system" | "light" | "dark";
+export type ResolvedTheme = "light" | "dark";
+
+export interface DashboardConfig {
+ brokerUrl: string;
+ org: string;
+ unit: string;
+ username: string;
+ password: string;
+}
+
+export interface AgentCard {
+ name?: string;
+ description?: string;
+ version?: string;
+ capabilities?: {
+ extensions?: Array<{ uri?: string; description?: string; params?: Record }>;
+ };
+}
+
+export interface AgentEntry {
+ id: string;
+ card: AgentCard;
+ status: "online" | "offline" | "unknown";
+ isApp: boolean;
+ isTaskAgent: boolean;
+ lastSeenAt?: number;
+ isMock?: boolean;
+}
+
+export interface WorkflowTaskDefinition {
+ id: string;
+ agentId: string;
+ description: string;
+ agentName?: string;
+ needs: string[];
+ hasExplicitNeeds: boolean;
+ terminal: boolean;
+}
+
+export interface DeviceOption {
+ deviceId: string;
+ name: string;
+ online?: boolean;
+ state?: Record;
+}
+
+export interface DeviceBindingRequest {
+ appId: string;
+ workflowName: string;
+ prompt: string;
+ tasks: WorkflowTaskDefinition[];
+}
+
+export interface StaticWorkflowBindingStep {
+ taskId: string;
+ agentId: string;
+ agentName: string;
+ description: string;
+ devices: DeviceOption[];
+ selectedDeviceId: string;
+ loading: boolean;
+ error?: string;
+}
+
+export interface StaticWorkflowBinding {
+ workflowId: string;
+ workflowName: string;
+ steps: StaticWorkflowBindingStep[];
+}
+
+export interface AgentDeviceBinding {
+ agentId: string;
+ agentName: string;
+ devices: DeviceOption[];
+ selectedDeviceId: string;
+ loading: boolean;
+ error?: string;
+}
+
+export interface ChatMessage {
+ id: string;
+ role: ReplyRole;
+ text: string;
+ streaming?: boolean;
+ state?: string;
+}
+
+export type WorkflowState = "idle" | "running" | "completed" | "failed";
+
+export type RuntimeProgressKind = "workflow_request" | "workflow_create" | "workflow_run";
+export type RuntimeProgressPhase =
+ | "submitted"
+ | "building"
+ | "preparing_run"
+ | "registering"
+ | "completed"
+ | "failed";
+
+export interface RuntimeProgress {
+ kind: RuntimeProgressKind;
+ phase: RuntimeProgressPhase;
+ detail?: string;
+ startedAt: string;
+ completedAt?: string;
+}
+
+export interface WorkflowTraceStep {
+ id: string;
+ sessionId: string;
+ agentId: string;
+ agentName?: string;
+ description: string;
+ state: "waiting" | "running" | "completed" | "failed";
+ result?: string;
+ error?: string;
+ timestamp?: string;
+}
+
+export interface WorkflowSession {
+ sessionId: string;
+ appId: string;
+ contextId: string;
+ state: WorkflowState;
+ startedAt?: string;
+ completedAt?: string;
+}
+
+export interface ChatSession {
+ id: string;
+ targetId: string;
+ targetName: string;
+ contextId: string;
+ requestTaskId?: string;
+ runtimeProgress?: RuntimeProgress;
+ workflow?: WorkflowSession;
+ traceSteps: WorkflowTraceStep[];
+ messages: ChatMessage[];
+ streaming: boolean;
+}
diff --git a/web/src/dashboard/use-skitter-dashboard.ts b/web/src/dashboard/use-skitter-dashboard.ts
new file mode 100644
index 0000000..7c7c23e
--- /dev/null
+++ b/web/src/dashboard/use-skitter-dashboard.ts
@@ -0,0 +1,1557 @@
+import { Buffer } from "buffer";
+import type { IClientOptions, IPublishPacket, MqttClient } from "mqtt";
+import mqtt from "mqtt";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ buildA2ARequest,
+ decodeCorrelation,
+ discoveryTopic,
+ eventTopic,
+ extractPartsText,
+ getUserProperty,
+ isAppCard,
+ isTaskAgentCard,
+ parseMaybeJson,
+ parseStoredConfig,
+ replyTopic,
+ requestTopic,
+ terminalStates,
+ topicPrefix,
+ uuid,
+} from "./a2a";
+import { displayName } from "./agent-utils";
+import {
+ bindingPrompt,
+ fetchAgentDevices,
+ parseDeviceBindingRequest,
+} from "./device-binding";
+import type {
+ AgentCard,
+ AgentDeviceBinding,
+ AgentEntry,
+ ChatMessage,
+ ChatSession,
+ ConnectionState,
+ DashboardConfig,
+ DashboardLanguage,
+ DeviceBindingRequest,
+ DeviceOption,
+ ResolvedTheme,
+ RuntimeProgress,
+ RuntimeProgressKind,
+ RuntimeProgressPhase,
+ StaticWorkflowBinding,
+ ThemeMode,
+ WorkflowState,
+ WorkflowTraceStep,
+} from "./types";
+import { bindableWorkflowTasks } from "./workflow-model";
+
+interface PendingChat {
+ type: "chat";
+ sessionId: string;
+ agentMessageId: string;
+ taskId: string;
+ artifact: string;
+ timer: number;
+}
+
+interface PendingRuntime {
+ type: "runtime";
+ artifact: string;
+ resolve: (value: unknown) => void;
+ reject: (error: Error) => void;
+ timer: number;
+}
+
+type PendingRequest = PendingChat | PendingRuntime;
+
+interface ConfirmedDeviceBinding {
+ description: string;
+ agentName: string;
+ device: DeviceOption;
+}
+
+interface BindingRetryTarget {
+ sessionId: string;
+ agentMessageId: string;
+}
+
+interface MockTaskAgentDraft {
+ name: string;
+ description: string;
+}
+
+type DashboardView = "workflows" | "scenes";
+const CHAT_REQUEST_TIMEOUT_MS = 120000;
+const LANGUAGE_STORAGE_KEY = "skitter.dashboard.language";
+const THEME_STORAGE_KEY = "skitter.dashboard.theme";
+const MOCK_TASK_AGENTS_KEY = "skitter.dashboard.mockTaskAgents.v2";
+
+function bindingStorageKey(config: DashboardConfig) {
+ return `skitter.dashboard.workflowBindings.${config.org}.${config.unit}`;
+}
+
+function agentBindingStorageKey(config: DashboardConfig) {
+ return `skitter.dashboard.agentDeviceBindings.${config.org}.${config.unit}`;
+}
+
+function getSystemTheme(): ResolvedTheme {
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+}
+
+function getStoredThemeMode(): ThemeMode {
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
+ if (stored === "system" || stored === "light" || stored === "dark") return stored;
+ return "system";
+}
+
+function browserLanguage(): DashboardLanguage {
+ return navigator.language.toLowerCase().startsWith("zh") ? "zh" : "en";
+}
+
+function getStoredLanguage(): DashboardLanguage {
+ const stored = localStorage.getItem(LANGUAGE_STORAGE_KEY);
+ if (stored === "en" || stored === "zh") return stored;
+
+ try {
+ const raw = localStorage.getItem("skitter.dashboard.config");
+ const legacyConfig = raw ? (JSON.parse(raw) as { language?: unknown }) : {};
+ if (legacyConfig.language === "en" || legacyConfig.language === "zh") {
+ return legacyConfig.language;
+ }
+ } catch {
+ return browserLanguage();
+ }
+
+ return browserLanguage();
+}
+
+function loadSelectedDeviceIds(config: DashboardConfig) {
+ try {
+ const raw = localStorage.getItem(bindingStorageKey(config));
+ if (!raw) return new Map>();
+ const parsed = JSON.parse(raw) as Record>;
+ return new Map(Object.entries(parsed));
+ } catch {
+ return new Map>();
+ }
+}
+
+function saveSelectedDeviceIds(config: DashboardConfig, bindings: Map>) {
+ localStorage.setItem(bindingStorageKey(config), JSON.stringify(Object.fromEntries(bindings)));
+}
+
+function loadSelectedAgentDeviceIds(config: DashboardConfig) {
+ try {
+ const raw = localStorage.getItem(agentBindingStorageKey(config));
+ if (!raw) return new Map();
+ const parsed = JSON.parse(raw) as Record;
+ return new Map(Object.entries(parsed));
+ } catch {
+ return new Map();
+ }
+}
+
+function saveSelectedAgentDeviceIds(config: DashboardConfig, bindings: Map) {
+ localStorage.setItem(agentBindingStorageKey(config), JSON.stringify(Object.fromEntries(bindings)));
+}
+
+function loadMockTaskAgents(): AgentEntry[] {
+ try {
+ const raw = localStorage.getItem(MOCK_TASK_AGENTS_KEY);
+ if (!raw) return [];
+ const parsed = JSON.parse(raw) as AgentEntry[];
+ return parsed.filter((agent) => agent?.id && agent?.card?.name);
+ } catch {
+ return [];
+ }
+}
+
+function saveMockTaskAgents(agents: AgentEntry[]) {
+ localStorage.setItem(MOCK_TASK_AGENTS_KEY, JSON.stringify(agents));
+}
+
+function slugifyMockAgentName(value: string) {
+ return value
+ .trim()
+ .toLowerCase()
+ .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-")
+ .replace(/^-+|-+$/g, "");
+}
+
+function uniqueMockAgentId(base: string, taken: Set) {
+ const prefix = base || "task-agent";
+ let candidate = prefix;
+ let index = 2;
+ while (taken.has(candidate)) {
+ candidate = `${prefix}-${index}`;
+ index += 1;
+ }
+ return candidate;
+}
+
+function buildMockTaskAgent(id: string, draft: MockTaskAgentDraft): AgentEntry {
+ return {
+ id,
+ card: {
+ name: draft.name,
+ description: draft.description,
+ capabilities: {
+ extensions: [
+ {
+ uri: "urn:skitter:task-agent",
+ description: "Dashboard-created task agent preview",
+ params: { kind: "mock-task-agent" },
+ },
+ ],
+ },
+ },
+ status: "online",
+ isApp: false,
+ isTaskAgent: true,
+ isMock: true,
+ };
+}
+
+function confirmedBindings(binding: StaticWorkflowBinding | undefined): ConfirmedDeviceBinding[] | null {
+ if (!binding || binding.steps.length === 0) return null;
+ const selected = binding.steps.map((step) => {
+ const device = step.devices.find((item) => item.deviceId === step.selectedDeviceId);
+ if (!device) return null;
+ return {
+ description: step.description,
+ agentName: step.agentName,
+ device,
+ };
+ });
+ if (selected.some((item) => !item)) return null;
+ return selected as ConfirmedDeviceBinding[];
+}
+
+function resultError(result: unknown) {
+ if (!result || typeof result !== "object" || !("error" in result)) return "";
+ const error = (result as { error?: unknown }).error;
+ return typeof error === "string" ? error : "";
+}
+
+function selectedDeviceForAgent(
+ agentId: string,
+ workflowBindings: Map,
+) {
+ for (const binding of workflowBindings.values()) {
+ const step = binding.steps.find((item) => item.agentId === agentId && item.selectedDeviceId);
+ if (step) return step.selectedDeviceId;
+ }
+ return "";
+}
+
+function initialRuntimeProgress(kind: RuntimeProgressKind): RuntimeProgress {
+ return {
+ kind,
+ phase: "submitted",
+ startedAt: new Date().toISOString(),
+ };
+}
+
+function progressKindFromText(text: string): RuntimeProgressKind | undefined {
+ if (/preparing to run/i.test(text)) return "workflow_run";
+ if (/created workflow/i.test(text)) return "workflow_create";
+ if (/registering workflow/i.test(text)) return "workflow_create";
+ return undefined;
+}
+
+function progressPhaseFromStatus(
+ state: string,
+ text: string,
+): RuntimeProgressPhase | undefined {
+ if (state === "TASK_STATE_COMPLETED") return "completed";
+ if (terminalStates.has(state)) return "failed";
+ if (/registering workflow/i.test(text)) return "registering";
+ if (/preparing to run/i.test(text)) return "preparing_run";
+ if (/planning workflow request/i.test(text)) return "building";
+ if (state === "TASK_STATE_SUBMITTED" || state === "submitted") return "submitted";
+ return undefined;
+}
+
+function matchingDeviceId(devices: DeviceOption[], ...deviceIds: Array) {
+ for (const deviceId of deviceIds) {
+ if (deviceId && devices.some((device) => device.deviceId === deviceId)) return deviceId;
+ }
+ return "";
+}
+
+function appBasePath() {
+ const base = import.meta.env.BASE_URL.replace(/\/+$/, "");
+ return base === "" || base === "/" ? "" : base;
+}
+
+function stripAppBase(pathname: string) {
+ const base = appBasePath();
+ if (!base) return pathname;
+ if (pathname === base) return "/";
+ if (pathname.startsWith(`${base}/`)) return pathname.slice(base.length) || "/";
+ return pathname;
+}
+
+function withAppBase(pathname: string) {
+ return `${appBasePath()}${pathname}`;
+}
+
+function readViewFromUrl(): DashboardView {
+ if (typeof window === "undefined") return "workflows";
+ const path = stripAppBase(window.location.pathname).replace(/\/+$/, "");
+ if (path === "/scenes" || path.startsWith("/scenes/")) return "scenes";
+ return "workflows";
+}
+
+function readSceneFromUrl() {
+ if (typeof window === "undefined") return "";
+ const match = stripAppBase(window.location.pathname).match(/^\/scenes\/([^/]+)$/);
+ return match?.[1] ? decodeURIComponent(match[1]) : "";
+}
+
+function writeViewToUrl(
+ view: DashboardView,
+ targetId = "",
+ replace = false,
+) {
+ if (typeof window === "undefined") return;
+ const url = new URL(window.location.href);
+ const path =
+ view === "workflows"
+ ? "/workflows"
+ : targetId
+ ? `/scenes/${encodeURIComponent(targetId)}`
+ : "/scenes";
+ url.pathname = withAppBase(path);
+ url.search = "";
+
+ const next = `${url.pathname}${url.search}${url.hash}`;
+ const current = `${window.location.pathname}${window.location.search}${window.location.hash}`;
+ if (next === current) return;
+
+ if (replace) window.history.replaceState(null, "", next);
+ else window.history.pushState(null, "", next);
+}
+
+export function useSkitterDashboard() {
+ const { i18n, t } = useTranslation();
+ const clientId = useMemo(() => `dash-${uuid().slice(0, 8)}`, []);
+ const clientRef = useRef(null);
+ const pendingRef = useRef>(new Map());
+ const selectedDeviceIdsRef = useRef>>(new Map());
+ const selectedAgentDeviceIdsRef = useRef>(new Map());
+ const workflowBindingsRef = useRef>(new Map());
+ const loadedAgentDevicesRef = useRef>(new Set());
+ const bindingRetryRef = useRef(null);
+
+ const [config, setConfig] = useState(() => parseStoredConfig());
+ const [draftConfig, setDraftConfig] = useState(() => parseStoredConfig());
+ const [language, setLanguage] = useState(getStoredLanguage);
+ const [theme, setTheme] = useState(getStoredThemeMode);
+ const [systemTheme, setSystemTheme] = useState(getSystemTheme);
+ const [connection, setConnection] = useState("disconnected");
+ const [connectionError, setConnectionError] = useState("");
+ const [settingsOpen, setSettingsOpen] = useState(false);
+ const [agents, setAgents] = useState>(new Map());
+ const [mockTaskAgents, setMockTaskAgents] = useState(loadMockTaskAgents);
+ const [chatSessions, setChatSessions] = useState>(new Map());
+ const [activeChatId, setActiveChatId] = useState("");
+ const [activeView, setActiveView] = useState(() => readViewFromUrl());
+ const [activeSceneId, setActiveSceneId] = useState(() => readSceneFromUrl());
+ const [chatInput, setChatInput] = useState("");
+ const [agentDeviceBindings, setAgentDeviceBindings] = useState>(
+ new Map(),
+ );
+ const [queuedBindingRequest, setQueuedBindingRequest] = useState(
+ null,
+ );
+
+ const agentList = useMemo(
+ () => {
+ const merged = new Map(agents);
+ for (const agent of mockTaskAgents) {
+ if (!merged.has(agent.id)) merged.set(agent.id, agent);
+ }
+
+ return [...merged.values()].sort((a, b) => {
+ if (a.isApp !== b.isApp) return a.isApp ? -1 : 1;
+ if (a.id === "skitter") return -1;
+ if (b.id === "skitter") return 1;
+ if (a.isMock !== b.isMock) return a.isMock ? 1 : -1;
+ return displayName(a).localeCompare(displayName(b));
+ });
+ },
+ [agents, mockTaskAgents],
+ );
+
+ const skitterAgent = useMemo(
+ () =>
+ agents.get("skitter") ?? {
+ id: "skitter",
+ card: {
+ name: "Skitter",
+ description: t("fallback.skitterDescription"),
+ },
+ status: connection === "connected" ? "online" : "unknown",
+ isApp: false,
+ isTaskAgent: false,
+ },
+ [agents, connection, t],
+ );
+
+ const appAgents = useMemo(() => agentList.filter((agent) => agent.isApp), [agentList]);
+ const taskAgents = useMemo(
+ () => agentList.filter((agent) => agent.isTaskAgent && agent.status === "online"),
+ [agentList],
+ );
+
+ const callableAgents = useMemo(
+ () =>
+ agentList.filter(
+ (agent) => !agent.isApp && !agent.isTaskAgent && agent.id !== "skitter",
+ ),
+ [agentList],
+ );
+
+ const selectedAgent = useMemo(() => {
+ if (activeView === "workflows") return skitterAgent;
+ if (!activeSceneId) {
+ return {
+ id: "",
+ card: {
+ name: t("fallback.scenesName"),
+ description: t("fallback.scenesDescription"),
+ },
+ status: "unknown",
+ isApp: false,
+ isTaskAgent: true,
+ };
+ }
+
+ return (
+ agents.get(activeSceneId) ??
+ mockTaskAgents.find((agent) => agent.id === activeSceneId) ?? {
+ id: activeSceneId,
+ card: {
+ name: activeSceneId,
+ description: t("fallback.waitingSceneDiscovery"),
+ },
+ status: "unknown",
+ isApp: false,
+ isTaskAgent: true,
+ }
+ );
+ }, [activeSceneId, activeView, agents, mockTaskAgents, skitterAgent, t]);
+
+ const activeChat = activeChatId ? chatSessions.get(activeChatId) ?? null : null;
+ const resolvedTheme: ResolvedTheme = theme === "system" ? systemTheme : theme;
+
+ useEffect(() => {
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ const handleThemeChange = () => {
+ setSystemTheme(mediaQuery.matches ? "dark" : "light");
+ };
+
+ mediaQuery.addEventListener("change", handleThemeChange);
+ return () => mediaQuery.removeEventListener("change", handleThemeChange);
+ }, []);
+
+ useEffect(() => {
+ document.documentElement.classList.toggle("dark", resolvedTheme === "dark");
+ localStorage.setItem(THEME_STORAGE_KEY, theme);
+ }, [resolvedTheme, theme]);
+
+ useEffect(() => {
+ localStorage.setItem(LANGUAGE_STORAGE_KEY, language);
+ if (i18n.language !== language) void i18n.changeLanguage(language);
+ }, [i18n, language]);
+
+ useEffect(() => {
+ if (activeView !== "scenes") return;
+ const nextTaskAgent =
+ taskAgents.find((agent) => agent.id === activeSceneId) ?? taskAgents[0];
+ if (!nextTaskAgent) {
+ setActiveSceneId("");
+ setActiveChatId("");
+ return;
+ }
+ if (nextTaskAgent.id === activeSceneId) return;
+
+ setActiveSceneId(nextTaskAgent.id);
+ const session = [...chatSessions.values()].find(
+ (item) => item.targetId === nextTaskAgent.id,
+ );
+ setActiveChatId(session?.id ?? "");
+ }, [activeSceneId, activeView, chatSessions, taskAgents]);
+
+ useEffect(() => {
+ writeViewToUrl(activeView, activeSceneId, true);
+ }, [activeSceneId, activeView]);
+
+ useEffect(() => {
+ function handlePopState() {
+ const view = readViewFromUrl();
+ const sceneId = readSceneFromUrl();
+ const targetId = view === "workflows" ? "skitter" : sceneId;
+ setActiveView(view);
+ setActiveSceneId(sceneId);
+ const existing = [...chatSessions.values()].find((session) => session.targetId === targetId);
+ setActiveChatId(existing?.id ?? "");
+ }
+
+ window.addEventListener("popstate", handlePopState);
+ return () => window.removeEventListener("popstate", handlePopState);
+ }, [chatSessions]);
+
+ useEffect(() => {
+ selectedDeviceIdsRef.current = loadSelectedDeviceIds(config);
+ selectedAgentDeviceIdsRef.current = loadSelectedAgentDeviceIds(config);
+ workflowBindingsRef.current = new Map();
+ loadedAgentDevicesRef.current.clear();
+ setAgentDeviceBindings(new Map());
+ }, [config]);
+
+ const updateChatMessage = useCallback(
+ (sessionId: string, messageId: string, updater: (message: ChatMessage) => ChatMessage) => {
+ setChatSessions((prev) => {
+ const session = prev.get(sessionId);
+ if (!session) return prev;
+
+ const next = new Map(prev);
+ next.set(sessionId, {
+ ...session,
+ messages: session.messages.map((message) =>
+ message.id === messageId ? updater(message) : message,
+ ),
+ });
+ return next;
+ });
+ },
+ [],
+ );
+
+ const finishChat = useCallback((sessionId: string, messageId: string, failed: boolean) => {
+ setChatSessions((prev) => {
+ const session = prev.get(sessionId);
+ if (!session) return prev;
+
+ const next = new Map(prev);
+ next.set(sessionId, {
+ ...session,
+ streaming: false,
+ messages: session.messages.map((message) =>
+ message.id === messageId
+ ? { ...message, streaming: false, state: failed ? "failed" : "completed" }
+ : message,
+ ),
+ });
+ return next;
+ });
+ }, []);
+
+ const updateRuntimeProgress = useCallback(
+ (
+ sessionId: string,
+ updater: (progress: RuntimeProgress) => RuntimeProgress,
+ ) => {
+ setChatSessions((prev) => {
+ const session = prev.get(sessionId);
+ if (!session?.runtimeProgress) return prev;
+
+ const next = new Map(prev);
+ next.set(sessionId, {
+ ...session,
+ runtimeProgress: updater(session.runtimeProgress),
+ });
+ return next;
+ });
+ },
+ [],
+ );
+
+ const handleEvent = useCallback((payloadText: string) => {
+ let payload: {
+ event?: string;
+ session_id?: string;
+ task_id?: string;
+ timestamp?: string;
+ data?: {
+ app_id?: string;
+ context_id?: string;
+ request_task_id?: string;
+ node_id?: string;
+ agent?: string;
+ description?: string;
+ result?: string;
+ error?: string;
+ };
+ };
+
+ try {
+ payload = JSON.parse(payloadText);
+ } catch {
+ return;
+ }
+
+ const requestTaskId = payload.data?.request_task_id;
+ if (!requestTaskId) return;
+
+ setChatSessions((prev) => {
+ const session = [...prev.values()].find((item) => item.requestTaskId === requestTaskId);
+ if (!session) return prev;
+
+ const next = new Map(prev);
+ const event = payload.event ?? "";
+ const workflowState: WorkflowState =
+ event === "session_completed"
+ ? "completed"
+ : event === "session_failed"
+ ? "failed"
+ : "running";
+ const workflow =
+ payload.session_id || payload.data?.app_id
+ ? {
+ sessionId: payload.session_id ?? session.workflow?.sessionId ?? "",
+ appId: payload.data?.app_id ?? session.workflow?.appId ?? "",
+ contextId: payload.data?.context_id ?? session.workflow?.contextId ?? session.contextId,
+ state: workflowState,
+ startedAt: session.workflow?.startedAt ?? payload.timestamp,
+ completedAt:
+ event === "session_completed" || event === "session_failed"
+ ? payload.timestamp
+ : session.workflow?.completedAt,
+ }
+ : session.workflow;
+
+ let traceSteps = session.traceSteps;
+ if (event === "task_started" || event === "task_completed" || event === "task_failed") {
+ const stepId = payload.task_id || payload.data?.node_id || "task";
+ const state =
+ event === "task_completed" ? "completed" : event === "task_failed" ? "failed" : "running";
+ const nextStep: WorkflowTraceStep = {
+ id: stepId,
+ sessionId: payload.session_id ?? "",
+ agentId: payload.data?.agent ?? "",
+ description: payload.data?.description ?? stepId,
+ state,
+ result: payload.data?.result,
+ error: payload.data?.error,
+ timestamp: payload.timestamp,
+ };
+
+ const existing = traceSteps.findIndex((step) => step.id === stepId);
+ traceSteps =
+ existing >= 0
+ ? traceSteps.map((step, index) => (index === existing ? { ...step, ...nextStep } : step))
+ : [...traceSteps, nextStep];
+ }
+
+ next.set(session.id, {
+ ...session,
+ runtimeProgress: workflow ? undefined : session.runtimeProgress,
+ workflow,
+ traceSteps,
+ });
+ return next;
+ });
+ }, []);
+
+ const handleReply = useCallback(
+ (payloadText: string, packet: IPublishPacket) => {
+ const correlation = decodeCorrelation(packet);
+ if (!correlation) return;
+
+ const pending = pendingRef.current.get(correlation);
+ if (!pending) return;
+
+ let payload: {
+ error?: { message?: string };
+ result?: {
+ statusUpdate?: {
+ status?: { state?: string; message?: { parts?: Array<{ text?: string }> } };
+ metadata?: {
+ type?: string;
+ agent?: string;
+ description?: string;
+ result?: string;
+ error?: string;
+ };
+ };
+ artifactUpdate?: {
+ append?: boolean;
+ artifact?: { parts?: Array<{ text?: string }> };
+ };
+ };
+ };
+
+ try {
+ payload = JSON.parse(payloadText);
+ } catch {
+ return;
+ }
+
+ if (payload.error) {
+ const message = payload.error.message ?? t("status.a2aFailed");
+ if (pending.type === "chat") {
+ window.clearTimeout(pending.timer);
+ updateRuntimeProgress(pending.sessionId, (progress) => ({
+ ...progress,
+ phase: "failed",
+ detail: message,
+ completedAt: new Date().toISOString(),
+ }));
+ updateChatMessage(pending.sessionId, pending.agentMessageId, (item) => ({
+ ...item,
+ text: message,
+ streaming: false,
+ state: "failed",
+ }));
+ finishChat(pending.sessionId, pending.agentMessageId, true);
+ } else {
+ window.clearTimeout(pending.timer);
+ pending.reject(new Error(message));
+ }
+ pendingRef.current.delete(correlation);
+ return;
+ }
+
+ const result = payload.result;
+ if (!result) return;
+
+ if (result.artifactUpdate) {
+ const text = extractPartsText(result.artifactUpdate.artifact?.parts);
+ pending.artifact = result.artifactUpdate.append ? `${pending.artifact}${text}` : text;
+ }
+
+ if (result.statusUpdate) {
+ const state = result.statusUpdate.status?.state ?? "";
+ const text = extractPartsText(result.statusUpdate.status?.message?.parts);
+
+ if (pending.type === "chat") {
+ const phase = progressPhaseFromStatus(state, text);
+ if (phase) {
+ updateRuntimeProgress(pending.sessionId, (progress) => ({
+ ...progress,
+ kind: progressKindFromText(text) ?? progress.kind,
+ phase,
+ detail: text || progress.detail,
+ completedAt: terminalStates.has(state)
+ ? new Date().toISOString()
+ : progress.completedAt,
+ }));
+ }
+
+ const metadata = result.statusUpdate.metadata;
+ if (metadata?.type === "task_step" && metadata.agent) {
+ const stepId = `${metadata.agent}-${Date.now()}`;
+ const nextStep: WorkflowTraceStep = {
+ id: stepId,
+ sessionId: "",
+ agentId: metadata.agent,
+ description: metadata.description || text || metadata.agent,
+ state: metadata.error ? "failed" : "completed",
+ result: metadata.result,
+ error: metadata.error,
+ timestamp: new Date().toISOString(),
+ };
+
+ setChatSessions((prev) => {
+ const session = prev.get(pending.sessionId);
+ if (!session) return prev;
+ const next = new Map(prev);
+ next.set(pending.sessionId, {
+ ...session,
+ traceSteps: [...session.traceSteps, nextStep],
+ });
+ return next;
+ });
+ }
+
+ if (terminalStates.has(state) && state === "TASK_STATE_COMPLETED") {
+ const bindingRequest = parseDeviceBindingRequest(pending.artifact);
+ if (bindingRequest) {
+ bindingRetryRef.current = {
+ sessionId: pending.sessionId,
+ agentMessageId: pending.agentMessageId,
+ };
+ setQueuedBindingRequest(bindingRequest);
+ const hasSavedBindings = confirmedBindings(
+ workflowBindingsRef.current.get(bindingRequest.appId),
+ );
+ updateChatMessage(pending.sessionId, pending.agentMessageId, (item) => ({
+ ...item,
+ text: hasSavedBindings
+ ? t("status.runningWorkflow", { name: bindingRequest.workflowName })
+ : t("status.bindDevices", { name: bindingRequest.workflowName }),
+ streaming: false,
+ state,
+ }));
+ updateRuntimeProgress(pending.sessionId, (progress) => ({
+ ...progress,
+ kind: "workflow_create",
+ phase: "completed",
+ detail: hasSavedBindings
+ ? t("status.runningWorkflow", { name: bindingRequest.workflowName })
+ : t("status.bindDevices", { name: bindingRequest.workflowName }),
+ completedAt: new Date().toISOString(),
+ }));
+ finishChat(pending.sessionId, pending.agentMessageId, false);
+ pendingRef.current.delete(correlation);
+ return;
+ }
+ }
+
+ const isTerminal = terminalStates.has(state);
+ const finalText = pending.artifact || text;
+ updateChatMessage(pending.sessionId, pending.agentMessageId, (item) => ({
+ ...item,
+ text: isTerminal ? finalText || item.text : text || item.text,
+ streaming: !isTerminal,
+ state,
+ }));
+ if (isTerminal) {
+ updateRuntimeProgress(pending.sessionId, (progress) => ({
+ ...progress,
+ kind: progressKindFromText(finalText || text) ?? progress.kind,
+ phase: state === "TASK_STATE_COMPLETED" ? "completed" : "failed",
+ detail: finalText || text || progress.detail,
+ completedAt: new Date().toISOString(),
+ }));
+ window.clearTimeout(pending.timer);
+ finishChat(pending.sessionId, pending.agentMessageId, state !== "TASK_STATE_COMPLETED");
+ pendingRef.current.delete(correlation);
+ }
+ } else if (terminalStates.has(state)) {
+ window.clearTimeout(pending.timer);
+ if (state === "TASK_STATE_COMPLETED") {
+ pending.resolve(parseMaybeJson(pending.artifact));
+ } else {
+ pending.reject(new Error(text || state));
+ }
+ pendingRef.current.delete(correlation);
+ }
+ }
+ },
+ [finishChat, t, updateChatMessage, updateRuntimeProgress],
+ );
+
+ const handleMessage = useCallback(
+ (topic: string, payload: Buffer, packet: IPublishPacket) => {
+ const payloadText = payload.toString("utf8");
+ const discoveryPrefix = `${topicPrefix()}/discovery/${config.org}/${config.unit}/`;
+
+ if (topic.startsWith(discoveryPrefix)) {
+ const id = topic.slice(discoveryPrefix.length);
+ if (!payloadText) {
+ setAgents((prev) => {
+ const next = new Map(prev);
+ next.delete(id);
+ return next;
+ });
+ return;
+ }
+
+ try {
+ const card = JSON.parse(payloadText) as AgentCard;
+ const status = getUserProperty(packet, "a2a-status");
+ const lastSeenAt = Date.now();
+ setAgents((prev) => {
+ const next = new Map(prev);
+ next.set(id, {
+ id,
+ card,
+ status: status === "offline" ? "offline" : status === "online" ? "online" : "unknown",
+ isApp: isAppCard(card),
+ isTaskAgent: isTaskAgentCard(card),
+ lastSeenAt,
+ });
+ return next;
+ });
+ } catch {
+ return;
+ }
+ }
+
+ if (topic.startsWith(`${topicPrefix()}/event/${config.org}/${config.unit}/`)) {
+ handleEvent(payloadText);
+ }
+
+ if (topic.startsWith(`${topicPrefix()}/reply/${config.org}/${config.unit}/${clientId}/`)) {
+ handleReply(payloadText, packet);
+ }
+ },
+ [clientId, config.org, config.unit, handleEvent, handleReply],
+ );
+
+ // Route messages through a ref so that callback churn (e.g. the i18n `t`
+ // function changing on language switch) never recreates `connect` and tears
+ // down the live MQTT connection. Only broker/topic config changes reconnect.
+ const handleMessageRef = useRef(handleMessage);
+ useEffect(() => {
+ handleMessageRef.current = handleMessage;
+ }, [handleMessage]);
+
+ const connect = useCallback(() => {
+ if (clientRef.current) {
+ clientRef.current.end(true);
+ clientRef.current = null;
+ }
+
+ loadedAgentDevicesRef.current.clear();
+ setAgentDeviceBindings(new Map());
+ setConnection("connecting");
+ setConnectionError("");
+
+ const options: IClientOptions = {
+ // Transport binding requires MQTT Client ID as {org_id}/{unit_id}/{agent_id}.
+ clientId: `${config.org}/${config.unit}/${clientId}`,
+ protocolVersion: 5,
+ clean: true,
+ reconnectPeriod: 2500,
+ connectTimeout: 5000,
+ };
+ if (config.username) options.username = config.username;
+ if (config.password) options.password = config.password;
+
+ const client = mqtt.connect(config.brokerUrl, options);
+ clientRef.current = client;
+
+ client.on("connect", () => {
+ setConnection("connected");
+ const replyBase = replyTopic(config, clientId);
+ client.subscribe(discoveryTopic(config), { qos: 1 });
+ client.subscribe(eventTopic(config), { qos: 1 });
+ client.subscribe(replyBase, { qos: 1 });
+ client.subscribe(`${replyBase}/#`, { qos: 1 });
+ });
+ client.on("close", () => setConnection((current) => (current === "error" ? "error" : "disconnected")));
+ client.on("error", (error) => {
+ setConnection("error");
+ setConnectionError(error.message);
+ });
+ client.on("message", (topic, payload, packet) =>
+ handleMessageRef.current(topic, payload, packet),
+ );
+ }, [clientId, config]);
+
+ useEffect(() => {
+ connect();
+ const pendingRequests = pendingRef.current;
+
+ return () => {
+ clientRef.current?.end(true);
+ clientRef.current = null;
+ pendingRequests.forEach((pending) => {
+ window.clearTimeout(pending.timer);
+ });
+ pendingRequests.clear();
+ };
+ }, [connect]);
+
+ const publishA2A = useCallback(
+ (
+ targetId: string,
+ text: string,
+ contextId: string,
+ pending: PendingRequest,
+ taskId = uuid(),
+ ) => {
+ const client = clientRef.current;
+ if (!client || connection !== "connected") {
+ throw new Error(t("status.mqttDisconnected"));
+ }
+
+ const requestId = `dash-${uuid().slice(0, 8)}`;
+ pendingRef.current.set(requestId, pending);
+ client.publish(
+ requestTopic(config, targetId),
+ buildA2ARequest(text, requestId, contextId, "dashboard", taskId),
+ {
+ qos: 1,
+ properties: {
+ responseTopic: `${replyTopic(config, clientId)}/${requestId}`,
+ correlationData: Buffer.from(requestId),
+ },
+ },
+ );
+ return { requestId, taskId };
+ },
+ [clientId, config, connection, t],
+ );
+
+ const runtimeQuery = useCallback(
+ (text: string) =>
+ new Promise((resolve, reject) => {
+ let correlation = "";
+ const timer = window.setTimeout(() => {
+ if (correlation) pendingRef.current.delete(correlation);
+ reject(new Error(t("status.coordinatorTimeout")));
+ }, 20000);
+
+ try {
+ const request = publishA2A("skitter", text, uuid(), {
+ type: "runtime",
+ artifact: "",
+ resolve,
+ reject,
+ timer,
+ });
+ correlation = request.requestId;
+ } catch (error) {
+ window.clearTimeout(timer);
+ reject(error instanceof Error ? error : new Error(String(error)));
+ }
+ }),
+ [publishA2A, t],
+ );
+
+ useEffect(() => {
+ if (connection !== "connected") return;
+
+ setAgentDeviceBindings((prev) => {
+ const next = new Map();
+ for (const agent of callableAgents) {
+ const existing = prev.get(agent.id);
+ next.set(agent.id, {
+ agentId: agent.id,
+ agentName: displayName(agent),
+ devices: existing?.devices ?? [],
+ selectedDeviceId:
+ existing?.selectedDeviceId ||
+ selectedAgentDeviceIdsRef.current.get(agent.id) ||
+ selectedDeviceForAgent(agent.id, workflowBindingsRef.current),
+ loading: existing?.loading ?? true,
+ error: existing?.error,
+ });
+ }
+ return next;
+ });
+
+ for (const agent of callableAgents) {
+ if (loadedAgentDevicesRef.current.has(agent.id)) continue;
+ loadedAgentDevicesRef.current.add(agent.id);
+
+ void fetchAgentDevices(agent.id)
+ .then((devices) => {
+ setAgentDeviceBindings((prev) => {
+ const binding = prev.get(agent.id);
+ if (!binding) return prev;
+ const next = new Map(prev);
+ next.set(agent.id, {
+ ...binding,
+ devices,
+ selectedDeviceId: matchingDeviceId(devices, binding.selectedDeviceId),
+ loading: false,
+ error: devices.length ? undefined : t("status.noDevices"),
+ });
+ return next;
+ });
+ })
+ .catch((error) => {
+ setAgentDeviceBindings((prev) => {
+ const binding = prev.get(agent.id);
+ if (!binding) return prev;
+ const next = new Map(prev);
+ next.set(agent.id, {
+ ...binding,
+ loading: false,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ return next;
+ });
+ });
+ }
+ }, [callableAgents, connection, t]);
+
+ useEffect(() => {
+ if (connection !== "connected") return;
+
+ const next = new Map();
+ for (const app of appAgents) {
+ const tasks = bindableWorkflowTasks(app, agents);
+ if (!tasks.length) continue;
+
+ const existing = workflowBindingsRef.current.get(app.id);
+ const selected = selectedDeviceIdsRef.current.get(app.id) ?? {};
+ next.set(app.id, {
+ workflowId: app.id,
+ workflowName: displayName(app),
+ steps: tasks.map((task) => {
+ const current = existing?.steps.find((step) => step.taskId === task.id);
+ const agent = agents.get(task.agentId);
+ const agentBinding = agentDeviceBindings.get(task.agentId);
+ const devices = agentBinding?.devices ?? current?.devices ?? [];
+ return {
+ taskId: task.id,
+ agentId: task.agentId,
+ agentName: task.agentName || agentBinding?.agentName || (agent ? displayName(agent) : task.agentId),
+ description: task.description,
+ devices,
+ selectedDeviceId: matchingDeviceId(
+ devices,
+ current?.selectedDeviceId,
+ selected[task.id],
+ agentBinding?.selectedDeviceId,
+ selectedAgentDeviceIdsRef.current.get(task.agentId),
+ ),
+ loading: agentBinding?.loading ?? current?.loading ?? true,
+ error: agentBinding?.error ?? current?.error,
+ };
+ }),
+ });
+ }
+ workflowBindingsRef.current = next;
+ }, [agentDeviceBindings, agents, appAgents, connection]);
+
+ const sendChatMessage = useCallback(
+ (wireText: string, visibleText = wireText) => {
+ const trimmed = wireText.trim();
+ const visible = visibleText.trim();
+ if (!trimmed || !visible) return;
+ const targetId = selectedAgent.id || (activeView === "workflows" ? "skitter" : "");
+ if (!targetId) return;
+ const targetName = displayName(selectedAgent) || targetId;
+ const existing = [...chatSessions.values()].find((session) => session.targetId === targetId);
+ const sessionId = existing?.id ?? uuid();
+ const contextId = existing?.contextId ?? uuid();
+ const agentMessageId = uuid();
+ const taskId = uuid();
+
+ setChatSessions((prev) => {
+ const next = new Map(prev);
+ const current =
+ next.get(sessionId) ??
+ ({
+ id: sessionId,
+ targetId,
+ targetName,
+ contextId,
+ traceSteps: [],
+ messages: [],
+ streaming: false,
+ } satisfies ChatSession);
+ next.set(sessionId, {
+ ...current,
+ requestTaskId: taskId,
+ runtimeProgress: targetId === "skitter" ? initialRuntimeProgress("workflow_request") : undefined,
+ workflow: undefined,
+ traceSteps: [],
+ streaming: true,
+ messages: [
+ ...current.messages,
+ { id: uuid(), role: "user", text: visible },
+ { id: agentMessageId, role: "agent", text: "", streaming: true, state: "submitted" },
+ ],
+ });
+ return next;
+ });
+ setActiveChatId(sessionId);
+
+ if (selectedAgent.isMock) {
+ window.setTimeout(() => {
+ setChatSessions((prev) => {
+ const session = prev.get(sessionId);
+ if (!session) return prev;
+
+ const next = new Map(prev);
+ next.set(sessionId, {
+ ...session,
+ streaming: false,
+ traceSteps: [
+ {
+ id: `${taskId}-plan`,
+ sessionId,
+ agentId: targetId,
+ description: t("mockTask.stepPlan"),
+ state: "completed",
+ result: t("mockTask.stepPlanResult"),
+ timestamp: new Date().toISOString(),
+ },
+ {
+ id: `${taskId}-devices`,
+ sessionId,
+ agentId: targetId,
+ description: t("mockTask.stepDevices"),
+ state: "completed",
+ result: t("mockTask.stepDevicesResult"),
+ timestamp: new Date().toISOString(),
+ },
+ ],
+ messages: session.messages.map((message) =>
+ message.id === agentMessageId
+ ? {
+ ...message,
+ text: t("mockTask.mockReply", { name: targetName }),
+ streaming: false,
+ state: "completed",
+ }
+ : message,
+ ),
+ });
+ return next;
+ });
+ }, 450);
+ return;
+ }
+
+ let correlation = "";
+ const timer = window.setTimeout(() => {
+ if (correlation) pendingRef.current.delete(correlation);
+ updateChatMessage(sessionId, agentMessageId, (message) => ({
+ ...message,
+ text: t("status.a2aTimeout"),
+ streaming: false,
+ state: "failed",
+ }));
+ finishChat(sessionId, agentMessageId, true);
+ }, CHAT_REQUEST_TIMEOUT_MS);
+
+ try {
+ const request = publishA2A(
+ targetId,
+ trimmed,
+ contextId,
+ {
+ type: "chat",
+ sessionId,
+ agentMessageId,
+ taskId,
+ artifact: "",
+ timer,
+ },
+ taskId,
+ );
+ correlation = request.requestId;
+ } catch (error) {
+ window.clearTimeout(timer);
+ updateChatMessage(sessionId, agentMessageId, (message) => ({
+ ...message,
+ text: error instanceof Error ? error.message : String(error),
+ streaming: false,
+ state: "failed",
+ }));
+ finishChat(sessionId, agentMessageId, true);
+ }
+ },
+ [activeView, chatSessions, finishChat, publishA2A, selectedAgent, t, updateChatMessage],
+ );
+
+ const createMockTaskAgent = useCallback(
+ (draft: MockTaskAgentDraft) => {
+ const name = draft.name.trim();
+ const description = draft.description.trim();
+ if (!name || !description) return;
+
+ const taken = new Set([...agents.keys(), ...mockTaskAgents.map((agent) => agent.id)]);
+ const id = uniqueMockAgentId(slugifyMockAgentName(name), taken);
+ const agent = buildMockTaskAgent(id, { name, description });
+
+ setMockTaskAgents((current) => {
+ const next = [agent, ...current];
+ saveMockTaskAgents(next);
+ return next;
+ });
+ setActiveView("scenes");
+ setActiveSceneId(id);
+ setActiveChatId("");
+ writeViewToUrl("scenes", id);
+ },
+ [agents, mockTaskAgents],
+ );
+
+ const deleteMockTaskAgent = useCallback(
+ (agentId: string) => {
+ setMockTaskAgents((current) => {
+ const next = current.filter((agent) => agent.id !== agentId);
+ saveMockTaskAgents(next);
+ return next;
+ });
+ setChatSessions((current) => {
+ const next = new Map(current);
+ for (const [sessionId, session] of next) {
+ if (session.targetId === agentId) next.delete(sessionId);
+ }
+ return next;
+ });
+ if (activeSceneId === agentId) {
+ setActiveSceneId("");
+ setActiveChatId("");
+ writeViewToUrl("scenes");
+ }
+ },
+ [activeSceneId],
+ );
+
+ const retryChatMessage = useCallback(
+ (target: BindingRetryTarget | null, wireText: string, statusText: string) => {
+ if (!target) {
+ sendChatMessage(wireText, statusText);
+ return;
+ }
+
+ const session = chatSessions.get(target.sessionId);
+ if (!session) {
+ sendChatMessage(wireText, statusText);
+ return;
+ }
+
+ const taskId = uuid();
+ setChatSessions((prev) => {
+ const current = prev.get(target.sessionId);
+ if (!current) return prev;
+
+ const next = new Map(prev);
+ next.set(target.sessionId, {
+ ...current,
+ requestTaskId: taskId,
+ runtimeProgress: initialRuntimeProgress("workflow_run"),
+ workflow: undefined,
+ traceSteps: [],
+ streaming: true,
+ messages: current.messages.map((message) =>
+ message.id === target.agentMessageId
+ ? { ...message, text: statusText, streaming: true, state: "submitted" }
+ : message,
+ ),
+ });
+ return next;
+ });
+ setActiveChatId(target.sessionId);
+
+ let correlation = "";
+ const timer = window.setTimeout(() => {
+ if (correlation) pendingRef.current.delete(correlation);
+ updateChatMessage(target.sessionId, target.agentMessageId, (message) => ({
+ ...message,
+ text: t("status.a2aTimeout"),
+ streaming: false,
+ state: "failed",
+ }));
+ finishChat(target.sessionId, target.agentMessageId, true);
+ }, CHAT_REQUEST_TIMEOUT_MS);
+
+ try {
+ const request = publishA2A(
+ "skitter",
+ wireText,
+ session.contextId,
+ {
+ type: "chat",
+ sessionId: target.sessionId,
+ agentMessageId: target.agentMessageId,
+ taskId,
+ artifact: "",
+ timer,
+ },
+ taskId,
+ );
+ correlation = request.requestId;
+ } catch (error) {
+ window.clearTimeout(timer);
+ updateChatMessage(target.sessionId, target.agentMessageId, (message) => ({
+ ...message,
+ text: error instanceof Error ? error.message : String(error),
+ streaming: false,
+ state: "failed",
+ }));
+ finishChat(target.sessionId, target.agentMessageId, true);
+ }
+ },
+ [chatSessions, finishChat, publishA2A, sendChatMessage, t, updateChatMessage],
+ );
+
+ useEffect(() => {
+ if (!queuedBindingRequest) return;
+ const retryTarget = bindingRetryRef.current;
+ bindingRetryRef.current = null;
+ const savedBindings = confirmedBindings(workflowBindingsRef.current.get(queuedBindingRequest.appId));
+ if (savedBindings?.length) {
+ const wireText = bindingPrompt(
+ queuedBindingRequest.prompt,
+ queuedBindingRequest.workflowName,
+ savedBindings,
+ );
+ setQueuedBindingRequest(null);
+ retryChatMessage(
+ retryTarget,
+ wireText,
+ t("status.runningWorkflow", { name: queuedBindingRequest.workflowName }),
+ );
+ return;
+ }
+
+ if (retryTarget) {
+ updateChatMessage(retryTarget.sessionId, retryTarget.agentMessageId, (message) => ({
+ ...message,
+ text: t("status.bindDevices", { name: queuedBindingRequest.workflowName }),
+ streaming: false,
+ state: "completed",
+ }));
+ }
+ setQueuedBindingRequest(null);
+ }, [queuedBindingRequest, retryChatMessage, t, updateChatMessage]);
+
+ const sendChat = useCallback(
+ (text: string) => {
+ const trimmed = text.trim();
+ if (!trimmed) return;
+
+ sendChatMessage(trimmed);
+ },
+ [sendChatMessage],
+ );
+
+ const updateAgentBindingDevice = useCallback(
+ (agentId: string, deviceId: string) => {
+ setAgentDeviceBindings((prev) => {
+ const binding = prev.get(agentId);
+ if (!binding) return prev;
+ const next = new Map(prev);
+ next.set(agentId, { ...binding, selectedDeviceId: deviceId });
+ return next;
+ });
+
+ const selectedAgents = new Map(selectedAgentDeviceIdsRef.current);
+ if (deviceId) selectedAgents.set(agentId, deviceId);
+ else selectedAgents.delete(agentId);
+ selectedAgentDeviceIdsRef.current = selectedAgents;
+ saveSelectedAgentDeviceIds(config, selectedAgents);
+
+ const next = new Map();
+ const selectedByWorkflow = new Map(selectedDeviceIdsRef.current);
+ let changed = false;
+
+ for (const [workflowId, binding] of workflowBindingsRef.current) {
+ let workflowChanged = false;
+ const selected = { ...(selectedByWorkflow.get(workflowId) ?? {}) };
+ const steps = binding.steps.map((step) => {
+ if (step.agentId !== agentId) return step;
+
+ workflowChanged = true;
+ changed = true;
+ if (deviceId) selected[step.taskId] = deviceId;
+ else delete selected[step.taskId];
+ return { ...step, selectedDeviceId: deviceId };
+ });
+
+ if (workflowChanged) selectedByWorkflow.set(workflowId, selected);
+ next.set(workflowId, workflowChanged ? { ...binding, steps } : binding);
+ }
+
+ if (!changed) return;
+ workflowBindingsRef.current = next;
+ selectedDeviceIdsRef.current = selectedByWorkflow;
+ saveSelectedDeviceIds(config, selectedByWorkflow);
+ },
+ [config],
+ );
+
+ const deleteWorkflow = useCallback(
+ async (workflowId: string) => {
+ const result = await runtimeQuery(`delete app ${workflowId}`);
+ const error = resultError(result);
+ if (error) throw new Error(error);
+
+ const next = new Map(workflowBindingsRef.current);
+ next.delete(workflowId);
+ workflowBindingsRef.current = next;
+
+ const selected = new Map(selectedDeviceIdsRef.current);
+ selected.delete(workflowId);
+ selectedDeviceIdsRef.current = selected;
+ saveSelectedDeviceIds(config, selected);
+ },
+ [config, runtimeQuery],
+ );
+
+ const selectWorkflows = useCallback(() => {
+ setActiveView("workflows");
+ setActiveSceneId("");
+ const existing = [...chatSessions.values()].find((session) => session.targetId === "skitter");
+ setActiveChatId(existing?.id ?? "");
+ writeViewToUrl("workflows");
+ }, [chatSessions]);
+
+ const selectScenes = useCallback(() => {
+ const taskAgentId = activeSceneId || taskAgents[0]?.id || "";
+ setActiveView("scenes");
+ setActiveSceneId(taskAgentId);
+ const existing = [...chatSessions.values()].find(
+ (session) => session.targetId === taskAgentId,
+ );
+ setActiveChatId(existing?.id ?? "");
+ writeViewToUrl("scenes", taskAgentId);
+ }, [activeSceneId, chatSessions, taskAgents]);
+
+ const selectScene = useCallback(
+ (targetId: string) => {
+ setActiveView("scenes");
+ setActiveSceneId(targetId);
+ const existing = [...chatSessions.values()].find((session) => session.targetId === targetId);
+ setActiveChatId(existing?.id ?? "");
+ writeViewToUrl("scenes", targetId);
+ },
+ [chatSessions],
+ );
+
+ const openSettings = useCallback(
+ (open: boolean) => {
+ // Start every edit from the active config so a prior Cancel never leaves
+ // stale unsaved edits in the draft.
+ if (open) setDraftConfig(config);
+ setSettingsOpen(open);
+ },
+ [config],
+ );
+
+ const applySettings = useCallback(() => {
+ const brokerUrl = draftConfig.brokerUrl.trim();
+ if (!/^wss?:\/\/.+/i.test(brokerUrl)) return; // guarded by the Save button too
+ const next = { ...draftConfig, brokerUrl };
+ localStorage.setItem("skitter.dashboard.config", JSON.stringify(next));
+ setAgents(new Map());
+ setConfig(next);
+ setSettingsOpen(false);
+ }, [draftConfig]);
+
+ return {
+ activeChat,
+ appAgents,
+ applySettings,
+ agentDeviceBindings,
+ callableAgents,
+ chatInput,
+ clientId,
+ config,
+ connect,
+ connection,
+ connectionError,
+ draftConfig,
+ language,
+ taskAgents,
+ resolvedTheme,
+ selectedAgent,
+ activeView,
+ activeSceneId,
+ sendChat,
+ selectWorkflows,
+ selectScenes,
+ selectScene,
+ setChatInput,
+ setDraftConfig,
+ setLanguage,
+ setSettingsOpen: openSettings,
+ setTheme,
+ updateAgentBindingDevice,
+ deleteWorkflow,
+ createMockTaskAgent,
+ deleteMockTaskAgent,
+ settingsOpen,
+ theme,
+ };
+}
+
+export type SkitterDashboardController = ReturnType;
diff --git a/web/src/dashboard/workflow-model.ts b/web/src/dashboard/workflow-model.ts
new file mode 100644
index 0000000..20a1ec0
--- /dev/null
+++ b/web/src/dashboard/workflow-model.ts
@@ -0,0 +1,149 @@
+import { displayName } from "./agent-utils";
+import type { AgentEntry, ChatSession, WorkflowTaskDefinition, WorkflowTraceStep } from "./types";
+
+const APP_EXTENSION_URI = "urn:skitter:app";
+
+/** Which of the trace / progress panels a chat should show in the output column. */
+export function chatTracePanels(activeChat: ChatSession | null) {
+ const showTrace = Boolean(activeChat?.workflow || activeChat?.traceSteps.length);
+ const showProgress = Boolean(activeChat?.runtimeProgress && !showTrace);
+ return { showTrace, showProgress };
+}
+
+export interface WorkflowDefinition {
+ workflowId: string;
+ workflowName: string;
+ description: string;
+ tasks: WorkflowTaskDefinition[];
+}
+
+export function workflowDefinitionFromApp(
+ app: AgentEntry | null | undefined,
+ agents: Map = new Map(),
+): WorkflowDefinition | null {
+ if (!app) return null;
+
+ const tasks = workflowTasksFromApp(app, agents);
+ if (!tasks.length) return null;
+
+ return {
+ workflowId: app.id,
+ workflowName: displayName(app),
+ description: app.card.description ?? "",
+ tasks,
+ };
+}
+
+export function workflowTasksFromApp(
+ app: AgentEntry,
+ agents: Map = new Map(),
+): WorkflowTaskDefinition[] {
+ const tasks = app.card.capabilities?.extensions
+ ?.find((extension) => extension.uri === APP_EXTENSION_URI)
+ ?.params?.tasks;
+
+ if (!Array.isArray(tasks)) return [];
+
+ return tasks
+ .filter((task): task is Record => Boolean(task) && typeof task === "object")
+ .map((task) => workflowTaskFromMetadata(task, agents))
+ .filter((task) => task.id && task.agentId);
+}
+
+export function bindableWorkflowTasks(
+ app: AgentEntry,
+ agents: Map,
+): WorkflowTaskDefinition[] {
+ return workflowTasksFromApp(app, agents).filter((task) => {
+ const agent = agents.get(task.agentId);
+ return agent && !agent.isApp && !agent.isTaskAgent;
+ });
+}
+
+function plannedWorkflowSteps(
+ activeChat: ChatSession | null,
+ workflows: AgentEntry[],
+): WorkflowTraceStep[] {
+ const workflow = workflows.find((item) => item.id === activeChat?.workflow?.appId);
+ const definition = workflowDefinitionFromApp(workflow);
+ if (!definition) return [];
+
+ return definition.tasks.map((task) => ({
+ id: task.id,
+ sessionId: activeChat?.workflow?.sessionId ?? "",
+ agentId: task.agentId,
+ agentName: task.agentName,
+ description: task.description || task.id,
+ state: "waiting",
+ }));
+}
+
+export function mergedWorkflowSteps(
+ activeChat: ChatSession | null,
+ workflows: AgentEntry[],
+) {
+ const actualSteps = [...(activeChat?.traceSteps ?? [])].sort(compareTraceSteps);
+ const plannedSteps =
+ activeChat?.targetId && activeChat.targetId !== "skitter"
+ ? []
+ : plannedWorkflowSteps(activeChat, workflows);
+
+ return plannedSteps.length ? mergeTraceSteps(plannedSteps, actualSteps) : actualSteps;
+}
+
+function workflowTaskFromMetadata(
+ task: Record,
+ agents: Map,
+): WorkflowTaskDefinition {
+ const agentId = String(task.agent ?? task.agent_id ?? "").trim();
+ const agent = agents.get(agentId);
+ const agentName = String(task.agentName ?? task.agent_name ?? "").trim();
+
+ return {
+ id: String(task.id ?? "").trim(),
+ agentId,
+ agentName: agentName || (agent ? displayName(agent) : undefined),
+ description: String(task.description ?? "").trim(),
+ needs: stringList(task.needs),
+ hasExplicitNeeds: Array.isArray(task.needs),
+ terminal: Boolean(task.terminal),
+ };
+}
+
+function mergeTraceSteps(
+ plannedSteps: WorkflowTraceStep[],
+ actualSteps: WorkflowTraceStep[],
+) {
+ const usedActualIds = new Set();
+
+ const merged = plannedSteps.map((plannedStep) => {
+ const actual =
+ actualSteps.find((step) => !usedActualIds.has(step.id) && step.id === plannedStep.id) ??
+ actualSteps.find((step) => !usedActualIds.has(step.id) && step.agentId === plannedStep.agentId);
+
+ if (!actual) return plannedStep;
+
+ usedActualIds.add(actual.id);
+ return {
+ ...plannedStep,
+ ...actual,
+ id: plannedStep.id,
+ agentId: plannedStep.agentId,
+ agentName: plannedStep.agentName,
+ description: actual.description || plannedStep.description,
+ };
+ });
+
+ const appendedActual = actualSteps.filter((step) => !usedActualIds.has(step.id));
+ return [...merged, ...appendedActual];
+}
+
+function stringList(value: unknown) {
+ if (!Array.isArray(value)) return [];
+ return value.map((item) => String(item).trim()).filter(Boolean);
+}
+
+function compareTraceSteps(a: WorkflowTraceStep, b: WorkflowTraceStep) {
+ if (!a.timestamp || !b.timestamp) return 0;
+ return a.timestamp.localeCompare(b.timestamp);
+}
diff --git a/web/src/index.css b/web/src/index.css
new file mode 100644
index 0000000..7c3137c
--- /dev/null
+++ b/web/src/index.css
@@ -0,0 +1,250 @@
+@import "tailwindcss";
+@import "@xyflow/react/dist/style.css";
+@import "@fontsource/geist-sans/400.css";
+@import "@fontsource/geist-sans/500.css";
+@import "@fontsource/geist-sans/600.css";
+@import "@fontsource/geist-sans/700.css";
+@import "@fontsource/geist-mono/400.css";
+@import "@fontsource/geist-mono/500.css";
+
+@custom-variant dark (&:is(.dark *));
+
+:root {
+ color-scheme: light;
+ --background: #ffffff;
+ --foreground: #0f172a;
+ --card: #ffffff;
+ --card-foreground: #0f172a;
+ --popover: #ffffff;
+ --popover-foreground: #0f172a;
+ --primary: #5e4eff;
+ --primary-foreground: #ffffff;
+ --secondary: #f8fafc;
+ --secondary-foreground: #0f172a;
+ --muted: #f1f5f9;
+ --muted-foreground: #64748b;
+ --accent: #f8fafc;
+ --accent-foreground: #0f172a;
+ --destructive: #ef4444;
+ --destructive-foreground: #ffffff;
+ --border: #e5e7eb;
+ --input: #e5e7eb;
+ --ring: #5e4eff;
+ --radius: 0.75rem;
+ --brand-500: #5e4eff;
+ --online-bg: #dcfce7;
+ --online-border: rgba(22, 163, 74, 0.3);
+ --online-text: #166534;
+ --online-solid: #16a34a;
+}
+
+.dark {
+ color-scheme: dark;
+ --background: #09090b;
+ --foreground: #ededed;
+ --card: #141416;
+ --card-foreground: #ededed;
+ --popover: #141416;
+ --popover-foreground: #ededed;
+ --primary: #5e4eff;
+ --primary-foreground: #ffffff;
+ --secondary: #1e1e22;
+ --secondary-foreground: #ededed;
+ --muted: #1e1e22;
+ --muted-foreground: #9a9a9a;
+ --accent: #1e1e22;
+ --accent-foreground: #ededed;
+ --destructive: #ef4444;
+ --destructive-foreground: #ffffff;
+ --border: rgba(255, 255, 255, 0.08);
+ --input: rgba(255, 255, 255, 0.08);
+ --ring: #8b80ff;
+ --online-bg: rgba(22, 163, 74, 0.1);
+ --online-border: rgba(22, 163, 74, 0.3);
+ --online-text: #4ade80;
+ --online-solid: #16a34a;
+}
+
+@theme inline {
+ --font-sans:
+ "Geist Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
+ "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
+ sans-serif;
+ --font-mono:
+ "Geist Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, ui-monospace,
+ monospace;
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-destructive-foreground: var(--destructive-foreground);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-brand-500: var(--brand-500);
+ --color-online-bg: var(--online-bg);
+ --color-online-border: var(--online-border);
+ --color-online-text: var(--online-text);
+ --color-online-solid: var(--online-solid);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+}
+
+* {
+ border-color: var(--border);
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ background: var(--background);
+ color: var(--foreground);
+ font-family: var(--font-sans);
+ font-feature-settings:
+ "rlig" 1,
+ "calt" 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+:root:lang(zh),
+:root:lang(zh-CN) {
+ --font-sans:
+ "Geist Sans", -apple-system, BlinkMacSystemFont, "PingFang SC",
+ "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", "WenQuanYi Micro Hei",
+ "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+button,
+input,
+textarea,
+select {
+ font: inherit;
+}
+
+button:not(:disabled),
+a[href],
+summary,
+select:not(:disabled),
+input[type="checkbox"]:not(:disabled),
+input[type="radio"]:not(:disabled),
+[role="button"]:not([aria-disabled="true"]),
+label:has(input[type="checkbox"]:not(:disabled)),
+label:has(input[type="radio"]:not(:disabled)) {
+ cursor: pointer;
+}
+
+code,
+pre {
+ font-family: var(--font-mono);
+}
+
+.skitter-split-view {
+ --focus-border: var(--border);
+ --separator-border: var(--border);
+ --sash-hover-size: 1px;
+ height: 100%;
+ width: 100%;
+}
+
+.skitter-split-view .sash {
+ background: transparent;
+ transition: background-color 120ms ease;
+}
+
+.skitter-split-view .sash::before {
+ opacity: 1;
+}
+
+.skitter-split-view .sash-hover::before,
+.skitter-split-view .sash-active::before {
+ background: var(--border);
+}
+
+.skitter-registry-scroll [data-radix-scroll-area-viewport] > div {
+ display: block !important;
+ min-width: 0 !important;
+ width: 100% !important;
+ max-width: 100% !important;
+}
+
+.workflow-flow {
+ --xy-background-color: transparent;
+ --xy-background-pattern-dots-color-default: color-mix(in srgb, var(--muted-foreground) 20%, transparent);
+ --xy-controls-button-background-color-default: var(--background);
+ --xy-controls-button-background-color-hover-default: var(--muted);
+ --xy-controls-button-color-default: var(--muted-foreground);
+ --xy-controls-button-border-color-default: transparent;
+ --xy-node-border-default: transparent;
+ --xy-node-boxshadow-default: none;
+ --xy-edge-stroke-default: var(--border);
+ --xy-edge-stroke-width-default: 2;
+ --xy-minimap-background-color-default: color-mix(in srgb, var(--background) 92%, transparent);
+}
+
+.workflow-flow .react-flow__handle {
+ width: 9px;
+ height: 9px;
+ min-width: 0;
+ min-height: 0;
+ border: 2px solid var(--background);
+ background: color-mix(in srgb, var(--muted-foreground) 55%, transparent);
+ opacity: 1;
+ pointer-events: none;
+}
+
+.workflow-flow .react-flow__edge-path {
+ shape-rendering: geometricPrecision;
+}
+
+.workflow-flow .react-flow__controls {
+ overflow: hidden;
+ gap: 2px;
+ padding: 3px;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--background) 88%, transparent);
+ backdrop-filter: blur(6px);
+ box-shadow: 0 10px 28px -12px rgb(15 23 42 / 0.28);
+}
+
+.workflow-flow .react-flow__controls-button {
+ width: 26px;
+ height: 26px;
+ border: none;
+ border-radius: 7px;
+}
+
+.workflow-flow .react-flow__controls-button:hover {
+ color: var(--foreground);
+}
+
+.workflow-flow .react-flow__controls-button svg {
+ fill: currentColor;
+}
+
+.workflow-flow .react-flow__minimap {
+ overflow: hidden;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ box-shadow: 0 10px 28px -12px rgb(15 23 42 / 0.28);
+}
+
+.workflow-flow .react-flow__minimap-mask {
+ fill: color-mix(in srgb, var(--muted-foreground) 14%, transparent);
+ stroke: color-mix(in srgb, var(--brand-500) 40%, transparent);
+ stroke-width: 2;
+}
diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts
new file mode 100644
index 0000000..a5ef193
--- /dev/null
+++ b/web/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/web/src/main.tsx b/web/src/main.tsx
new file mode 100644
index 0000000..0d4e057
--- /dev/null
+++ b/web/src/main.tsx
@@ -0,0 +1,12 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import 'allotment/dist/style.css'
+import './index.css'
+import '@/dashboard/i18n'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json
new file mode 100644
index 0000000..35fcf3d
--- /dev/null
+++ b/web/tsconfig.app.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM"],
+ "module": "esnext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "ignoreDeprecations": "6.0",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/web/tsconfig.json b/web/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/web/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json
new file mode 100644
index 0000000..d3c52ea
--- /dev/null
+++ b/web/tsconfig.node.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023"],
+ "module": "esnext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/web/vite.config.ts b/web/vite.config.ts
new file mode 100644
index 0000000..1d1d752
--- /dev/null
+++ b/web/vite.config.ts
@@ -0,0 +1,33 @@
+import path from "node:path";
+import tailwindcss from "@tailwindcss/vite";
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// Dev-proxy targets default to localhost; override per environment via env vars.
+const mqttProxyTarget = process.env.VITE_PROXY_MQTT ?? "ws://localhost:8083";
+const apiProxyTarget = process.env.VITE_PROXY_API ?? "http://localhost:3000";
+
+export default defineConfig({
+ base: "/skitter/",
+ plugins: [react(), tailwindcss()],
+ server: {
+ port: 18084,
+ strictPort: true,
+ proxy: {
+ "/mqtt": {
+ target: mqttProxyTarget,
+ ws: true,
+ changeOrigin: true,
+ },
+ "/api": {
+ target: apiProxyTarget,
+ changeOrigin: true,
+ },
+ },
+ },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
+});