diff --git a/bun.lock b/bun.lock
deleted file mode 100644
index db5831282..000000000
--- a/bun.lock
+++ /dev/null
@@ -1,2558 +0,0 @@
-{
- "lockfileVersion": 1,
- "workspaces": {
- "": {
- "name": "21st-desktop",
- "dependencies": {
- "@ai-sdk/react": "^3.0.14",
- "@anthropic-ai/claude-agent-sdk": "0.2.25",
- "@git-diff-view/react": "^0.0.35",
- "@git-diff-view/shiki": "^0.0.36",
- "@modelcontextprotocol/sdk": "^1.25.3",
- "@monaco-editor/react": "^4.7.0",
- "@radix-ui/react-accordion": "^1.2.12",
- "@radix-ui/react-alert-dialog": "^1.1.15",
- "@radix-ui/react-checkbox": "^1.3.3",
- "@radix-ui/react-collapsible": "^1.1.12",
- "@radix-ui/react-context-menu": "^2.2.16",
- "@radix-ui/react-dialog": "^1.1.15",
- "@radix-ui/react-dropdown-menu": "^2.1.16",
- "@radix-ui/react-hover-card": "^1.1.15",
- "@radix-ui/react-icons": "^1.3.2",
- "@radix-ui/react-label": "^2.1.8",
- "@radix-ui/react-popover": "^1.1.15",
- "@radix-ui/react-progress": "^1.1.8",
- "@radix-ui/react-select": "^2.2.6",
- "@radix-ui/react-slot": "^1.2.4",
- "@radix-ui/react-switch": "^1.2.6",
- "@radix-ui/react-tabs": "^1.1.13",
- "@radix-ui/react-tooltip": "^1.2.8",
- "@sentry/electron": "^7.5.0",
- "@tailwindcss/typography": "^0.5.19",
- "@tanstack/react-query": "^5.90.10",
- "@tanstack/react-virtual": "^3.13.18",
- "@trpc/client": "^11.7.1",
- "@trpc/react-query": "^11.7.1",
- "@trpc/server": "^11.7.1",
- "@xterm/addon-canvas": "^0.7.0",
- "@xterm/addon-fit": "^0.11.0",
- "@xterm/addon-search": "^0.16.0",
- "@xterm/addon-serialize": "^0.14.0",
- "@xterm/addon-web-links": "^0.12.0",
- "@xterm/addon-webgl": "^0.19.0",
- "ai": "^6.0.14",
- "async-mutex": "^0.5.0",
- "better-sqlite3": "^11.8.1",
- "chokidar": "^5.0.0",
- "class-variance-authority": "^0.7.1",
- "clsx": "^2.1.1",
- "date-fns": "^3.6.0",
- "drizzle-orm": "^0.45.1",
- "electron-log": "^5.4.3",
- "electron-updater": "^6.7.3",
- "gray-matter": "^4.0.3",
- "jotai": "^2.11.1",
- "jsonc-parser": "^3.3.1",
- "lucide-react": "^0.468.0",
- "mermaid": "^11.12.2",
- "monaco-editor": "^0.55.1",
- "motion": "^11.15.0",
- "next-themes": "^0.4.4",
- "node-pty": "^1.1.0",
- "pidtree": "^0.6.0",
- "posthog-js": "^1.239.1",
- "posthog-node": "^5.20.0",
- "react": "19.2.1",
- "react-dom": "19.2.1",
- "react-hotkeys-hook": "^4.6.1",
- "react-icons": "^5.5.0",
- "react-zoom-pan-pinch": "^3.7.0",
- "remark-breaks": "^4.0.0",
- "remark-gfm": "^4.0.1",
- "shiki": "^1.24.4",
- "simple-git": "^3.28.0",
- "sonner": "^1.7.1",
- "streamdown": "^2.0.1",
- "superjson": "^2.2.2",
- "tailwind-merge": "^2.6.0",
- "tailwindcss-animate": "^1.0.7",
- "trpc-electron": "^0.1.2",
- "unique-names-generator": "^4.7.1",
- "xterm": "^5.3.0",
- "zod": "^3.24.1",
- "zustand": "^5.0.3",
- },
- "devDependencies": {
- "@electron-toolkit/preload": "^3.0.1",
- "@electron-toolkit/utils": "^4.0.0",
- "@types/better-sqlite3": "^7.6.13",
- "@types/node": "^20.17.50",
- "@types/react": "^19.0.7",
- "@types/react-dom": "^19.0.3",
- "@vitejs/plugin-react": "^4.3.4",
- "@welldone-software/why-did-you-render": "^10.0.1",
- "autoprefixer": "^10.4.20",
- "drizzle-kit": "^0.31.8",
- "electron": "33.4.5",
- "electron-builder": "^25.1.8",
- "electron-rebuild": "^3.2.9",
- "electron-vite": "^3.0.0",
- "postcss": "^8.5.1",
- "tailwindcss": "^3.4.17",
- "typescript": "^5.4.5",
- "vite": "^6.3.4",
- },
- },
- },
- "packages": {
- "7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="],
-
- "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.4", "@ai-sdk/provider-utils": "4.0.8", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-WOHGHclkcDN3VYJ3xBafa38uhmW+xRV8Hbe9LtEJKej3+qU3Hy1SUcptxsqfVACo1H1Nt5xRX7rEvoCHlwC8tw=="],
-
- "@ai-sdk/provider": ["@ai-sdk/provider@3.0.4", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5KXyBOSEX+l67elrEa+wqo/LSsSTtrPj9Uoh3zMbe/ceQX4ucHI3b9nUEfNkGF3Ry1svv90widAt+aiKdIJasQ=="],
-
- "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.8", "", { "dependencies": { "@ai-sdk/provider": "3.0.4", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ns9gN7MmpI8vTRandzgz+KK/zNMLzhrriiKECMt4euLtQFSBgNfydtagPOX4j4pS1/3KvHF6RivhT3gNQgBZsg=="],
-
- "@ai-sdk/react": ["@ai-sdk/react@3.0.47", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.8", "ai": "6.0.45", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-H+RKDWrJCybang8RxC4WkEzdgaXD/KrlmRKq6gfJ5fNv0pmcGSjIHi3PJpEPa5zOymblesDWxh8KM0u/ToMo7w=="],
-
- "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
-
- "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
-
- "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.25", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-YIP3I40+XSkC3zE1Z8KRQY02VA7UfofFamF1cFrLe7FbtCnjpslyDl9coGBh2DAi9xj2yQcKZZf751jEWpB+dQ=="],
-
- "@apm-js-collab/code-transformer": ["@apm-js-collab/code-transformer@0.8.2", "", {}, "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA=="],
-
- "@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="],
-
- "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
-
- "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="],
-
- "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="],
-
- "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="],
-
- "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
-
- "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
-
- "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
-
- "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
-
- "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="],
-
- "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
-
- "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
-
- "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
-
- "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
-
- "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
-
- "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
-
- "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
-
- "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
-
- "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
-
- "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="],
-
- "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
-
- "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="],
-
- "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.0.3", "", { "dependencies": { "@chevrotain/gast": "11.0.3", "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ=="],
-
- "@chevrotain/gast": ["@chevrotain/gast@11.0.3", "", { "dependencies": { "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q=="],
-
- "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.0.3", "", {}, "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA=="],
-
- "@chevrotain/types": ["@chevrotain/types@11.0.3", "", {}, "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="],
-
- "@chevrotain/utils": ["@chevrotain/utils@11.0.3", "", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="],
-
- "@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="],
-
- "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
-
- "@electron-toolkit/preload": ["@electron-toolkit/preload@3.0.2", "", { "peerDependencies": { "electron": ">=13.0.0" } }, "sha512-TWWPToXd8qPRfSXwzf5KVhpXMfONaUuRAZJHsKthKgZR/+LqX1dZVSSClQ8OTAEduvLGdecljCsoT2jSshfoUg=="],
-
- "@electron-toolkit/utils": ["@electron-toolkit/utils@4.0.0", "", { "peerDependencies": { "electron": ">=13.0.0" } }, "sha512-qXSntwEzluSzKl4z5yFNBknmPGjPa3zFhE4mp9+h0cgokY5ornAeP+CJQDBhKsL1S58aOQfcwkD3NwLZCl+64g=="],
-
- "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
-
- "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],
-
- "@electron/notarize": ["@electron/notarize@2.5.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", "promise-retry": "^2.0.1" } }, "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A=="],
-
- "@electron/osx-sign": ["@electron/osx-sign@1.3.1", "", { "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", "fs-extra": "^10.0.0", "isbinaryfile": "^4.0.8", "minimist": "^1.2.6", "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", "electron-osx-sign": "bin/electron-osx-sign.js" } }, "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw=="],
-
- "@electron/rebuild": ["@electron/rebuild@3.6.1", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "fs-extra": "^10.0.0", "got": "^11.7.0", "node-abi": "^3.45.0", "node-api-version": "^0.2.0", "node-gyp": "^9.0.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^6.0.5", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w=="],
-
- "@electron/universal": ["@electron/universal@2.0.1", "", { "dependencies": { "@electron/asar": "^3.2.7", "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", "dir-compare": "^4.2.0", "fs-extra": "^11.1.1", "minimatch": "^9.0.3", "plist": "^3.1.0" } }, "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA=="],
-
- "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
-
- "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
-
- "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
-
- "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
-
- "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
-
- "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
-
- "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
-
- "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
-
- "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
-
- "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
-
- "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
-
- "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
-
- "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
-
- "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
-
- "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
-
- "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
-
- "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
-
- "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
-
- "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
-
- "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
-
- "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
-
- "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
-
- "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
-
- "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
-
- "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
-
- "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
-
- "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
-
- "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
-
- "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
-
- "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
-
- "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="],
-
- "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
-
- "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="],
-
- "@git-diff-view/core": ["@git-diff-view/core@0.0.35", "", { "dependencies": { "@git-diff-view/lowlight": "^0.0.35", "fast-diff": "^1.3.0", "highlight.js": "^11.11.0", "lowlight": "^3.3.0" } }, "sha512-cdH3BopR6AWUW+6hP78zGyryKxR9JkPgryd1JN78i5k+F9Eo4x/4S23ZF1VZnrpPlGLrSuYfiAZ0ho5m+pTuKg=="],
-
- "@git-diff-view/lowlight": ["@git-diff-view/lowlight@0.0.35", "", { "dependencies": { "@types/hast": "^3.0.0", "highlight.js": "^11.11.0", "lowlight": "^3.3.0" } }, "sha512-MVpOxrNn1oHVOTOWUjxLbbf1W4OtVHjj6CHxwJbBRg9ZWZdShBINjuEgHVMSGB6vZuHKfwruRfXw8XxV3aF8zw=="],
-
- "@git-diff-view/react": ["@git-diff-view/react@0.0.35", "", { "dependencies": { "@git-diff-view/core": "^0.0.35", "@types/hast": "^3.0.0", "fast-diff": "^1.3.0", "highlight.js": "^11.11.0", "lowlight": "^3.3.0", "reactivity-store": "^0.3.12", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-jIzESQ/oB2FZgEGK1urvAqju89cThv5+enEC4r2atDX15W5xMDNsDBe8FT2U74zBj8JkgROaiGmbF93KIJrWpg=="],
-
- "@git-diff-view/shiki": ["@git-diff-view/shiki@0.0.36", "", { "dependencies": { "@types/hast": "^3.0.0", "shiki": "^3.9.2" } }, "sha512-t6LGKISEvE0q7u2AR1rq2RLgCcccRttuKE617D1MiHecO0Xd/xvsMhqxW4PgDMkMadhbzWnt6IhBbrdgqBKPwQ=="],
-
- "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
-
- "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
-
- "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
-
- "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
-
- "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
-
- "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
-
- "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
-
- "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
-
- "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
-
- "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
-
- "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
-
- "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
-
- "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
-
- "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
-
- "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
-
- "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
-
- "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
-
- "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
-
- "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
-
- "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
-
- "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
-
- "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
-
- "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
-
- "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
-
- "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
-
- "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
-
- "@kwsites/file-exists": ["@kwsites/file-exists@1.1.1", "", { "dependencies": { "debug": "^4.1.1" } }, "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw=="],
-
- "@kwsites/promise-deferred": ["@kwsites/promise-deferred@1.1.1", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="],
-
- "@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="],
-
- "@malept/flatpak-bundler": ["@malept/flatpak-bundler@0.4.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", "lodash": "^4.17.15", "tmp-promise": "^3.0.2" } }, "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q=="],
-
- "@mermaid-js/parser": ["@mermaid-js/parser@0.6.3", "", { "dependencies": { "langium": "3.3.1" } }, "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA=="],
-
- "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="],
-
- "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="],
-
- "@monaco-editor/react": ["@monaco-editor/react@4.7.0", "", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="],
-
- "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
-
- "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
-
- "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
-
- "@npmcli/fs": ["@npmcli/fs@2.1.2", "", { "dependencies": { "@gar/promisify": "^1.1.3", "semver": "^7.3.5" } }, "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ=="],
-
- "@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="],
-
- "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
-
- "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="],
-
- "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw=="],
-
- "@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="],
-
- "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.208.0", "@opentelemetry/otlp-transformer": "0.208.0", "@opentelemetry/sdk-logs": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg=="],
-
- "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA=="],
-
- "@opentelemetry/instrumentation-amqplib": ["@opentelemetry/instrumentation-amqplib@0.55.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5ULoU8p+tWcQw5PDYZn8rySptGSLZHNX/7srqo2TioPnAAcvTy6sQFQXsNPrAnyRRtYGMetXVyZUy5OaX1+IfA=="],
-
- "@opentelemetry/instrumentation-connect": ["@opentelemetry/instrumentation-connect@0.52.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg=="],
-
- "@opentelemetry/instrumentation-dataloader": ["@opentelemetry/instrumentation-dataloader@0.26.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-P2BgnFfTOarZ5OKPmYfbXfDFjQ4P9WkQ1Jji7yH5/WwB6Wm/knynAoA1rxbjWcDlYupFkyT0M1j6XLzDzy0aCA=="],
-
- "@opentelemetry/instrumentation-express": ["@opentelemetry/instrumentation-express@0.57.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg=="],
-
- "@opentelemetry/instrumentation-fs": ["@opentelemetry/instrumentation-fs@0.28.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g=="],
-
- "@opentelemetry/instrumentation-generic-pool": ["@opentelemetry/instrumentation-generic-pool@0.52.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ=="],
-
- "@opentelemetry/instrumentation-graphql": ["@opentelemetry/instrumentation-graphql@0.56.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q=="],
-
- "@opentelemetry/instrumentation-hapi": ["@opentelemetry/instrumentation-hapi@0.55.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-prqAkRf9e4eEpy4G3UcR32prKE8NLNlA90TdEU1UsghOTg0jUvs40Jz8LQWFEs5NbLbXHYGzB4CYVkCI8eWEVQ=="],
-
- "@opentelemetry/instrumentation-http": ["@opentelemetry/instrumentation-http@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/instrumentation": "0.208.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ=="],
-
- "@opentelemetry/instrumentation-ioredis": ["@opentelemetry/instrumentation-ioredis@0.56.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/redis-common": "^0.38.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-XSWeqsd3rKSsT3WBz/JKJDcZD4QYElZEa0xVdX8f9dh4h4QgXhKRLorVsVkK3uXFbC2sZKAS2Ds+YolGwD83Dg=="],
-
- "@opentelemetry/instrumentation-kafkajs": ["@opentelemetry/instrumentation-kafkajs@0.18.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-KCL/1HnZN5zkUMgPyOxfGjLjbXjpd4odDToy+7c+UsthIzVLFf99LnfIBE8YSSrYE4+uS7OwJMhvhg3tWjqMBg=="],
-
- "@opentelemetry/instrumentation-knex": ["@opentelemetry/instrumentation-knex@0.53.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.33.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-xngn5cH2mVXFmiT1XfQ1aHqq1m4xb5wvU6j9lSgLlihJ1bXzsO543cpDwjrZm2nMrlpddBf55w8+bfS4qDh60g=="],
-
- "@opentelemetry/instrumentation-koa": ["@opentelemetry/instrumentation-koa@0.57.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.36.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" } }, "sha512-3JS8PU/D5E3q295mwloU2v7c7/m+DyCqdu62BIzWt+3u9utjxC9QS7v6WmUNuoDN3RM+Q+D1Gpj13ERo+m7CGg=="],
-
- "@opentelemetry/instrumentation-lru-memoizer": ["@opentelemetry/instrumentation-lru-memoizer@0.53.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw=="],
-
- "@opentelemetry/instrumentation-mongodb": ["@opentelemetry/instrumentation-mongodb@0.61.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ=="],
-
- "@opentelemetry/instrumentation-mongoose": ["@opentelemetry/instrumentation-mongoose@0.55.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5afj0HfF6aM6Nlqgu6/PPHFk8QBfIe3+zF9FGpX76jWPS0/dujoEYn82/XcLSaW5LPUDW8sni+YeK0vTBNri+w=="],
-
- "@opentelemetry/instrumentation-mysql": ["@opentelemetry/instrumentation-mysql@0.54.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@types/mysql": "2.15.27" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bqC1YhnwAeWmRzy1/Xf9cDqxNG2d/JDkaxnqF5N6iJKN1eVWI+vg7NfDkf52/Nggp3tl1jcC++ptC61BD6738A=="],
-
- "@opentelemetry/instrumentation-mysql2": ["@opentelemetry/instrumentation-mysql2@0.55.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@opentelemetry/sql-common": "^0.41.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-0cs8whQG55aIi20gnK8B7cco6OK6N+enNhW0p5284MvqJ5EPi+I1YlWsWXgzv/V2HFirEejkvKiI4Iw21OqDWg=="],
-
- "@opentelemetry/instrumentation-pg": ["@opentelemetry/instrumentation-pg@0.61.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@opentelemetry/sql-common": "^0.41.2", "@types/pg": "8.15.6", "@types/pg-pool": "2.0.6" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-UeV7KeTnRSM7ECHa3YscoklhUtTQPs6V6qYpG283AB7xpnPGCUCUfECFT9jFg6/iZOQTt3FHkB1wGTJCNZEvPw=="],
-
- "@opentelemetry/instrumentation-redis": ["@opentelemetry/instrumentation-redis@0.57.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/redis-common": "^0.38.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bCxTHQFXzrU3eU1LZnOZQ3s5LURxQPDlU3/upBzlWY77qOI1GZuGofazj3jtzjctMJeBEJhNwIFEgRPBX1kp/Q=="],
-
- "@opentelemetry/instrumentation-tedious": ["@opentelemetry/instrumentation-tedious@0.27.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", "@types/tedious": "^4.0.14" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jRtyUJNZppPBjPae4ZjIQ2eqJbcRaRfJkr0lQLHFmOU/no5A6e9s1OHLd5XZyZoBJ/ymngZitanyRRA5cniseA=="],
-
- "@opentelemetry/instrumentation-undici": ["@opentelemetry/instrumentation-undici@0.19.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/semantic-conventions": "^1.24.0" }, "peerDependencies": { "@opentelemetry/api": "^1.7.0" } }, "sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ=="],
-
- "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="],
-
- "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="],
-
- "@opentelemetry/redis-common": ["@opentelemetry/redis-common@0.38.2", "", {}, "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA=="],
-
- "@opentelemetry/resources": ["@opentelemetry/resources@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g=="],
-
- "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="],
-
- "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="],
-
- "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ=="],
-
- "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.39.0", "", {}, "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg=="],
-
- "@opentelemetry/sql-common": ["@opentelemetry/sql-common@0.41.2", "", { "dependencies": { "@opentelemetry/core": "^2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0" } }, "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ=="],
-
- "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
-
- "@posthog/core": ["@posthog/core@1.13.0", "", { "dependencies": { "cross-spawn": "^7.0.6" } }, "sha512-knjncrk7qRmssFRbGzBl1Tunt21GRpe0Wv+uVelyL0Rh7PdQUsgguulzXFTps8hA6wPwTU4kq85qnbAJ3eH6Wg=="],
-
- "@posthog/types": ["@posthog/types@1.333.0", "", {}, "sha512-9Wg/2ez+EZh6NmtOjhtYSkBHz/yIq8WMS0QSIizUoggh35hHVg4BTMXl3rz/tPearJNKU/8oRjEyuZ0OYTEDOA=="],
-
- "@prisma/instrumentation": ["@prisma/instrumentation@6.19.0", "", { "dependencies": { "@opentelemetry/instrumentation": ">=0.52.0 <1" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "sha512-QcuYy25pkXM8BJ37wVFBO7Zh34nyRV1GOb2n3lPkkbRYfl4hWl3PTcImP41P0KrzVXfa/45p6eVCos27x3exIg=="],
-
- "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
-
- "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
-
- "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
-
- "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
-
- "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
-
- "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
-
- "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
-
- "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
-
- "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
-
- "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
-
- "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
-
- "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
-
- "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="],
-
- "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="],
-
- "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
-
- "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="],
-
- "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="],
-
- "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
-
- "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
-
- "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
-
- "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="],
-
- "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
-
- "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
-
- "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
-
- "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="],
-
- "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
-
- "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
-
- "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
-
- "@radix-ui/react-icons": ["@radix-ui/react-icons@1.3.2", "", { "peerDependencies": { "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" } }, "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g=="],
-
- "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
-
- "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
-
- "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
-
- "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="],
-
- "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
-
- "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
-
- "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
-
- "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
-
- "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="],
-
- "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
-
- "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
-
- "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="],
-
- "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
-
- "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="],
-
- "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="],
-
- "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
-
- "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
-
- "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
-
- "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
-
- "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
-
- "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
-
- "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
-
- "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
-
- "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
-
- "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
-
- "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
-
- "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.56.0", "", { "os": "android", "cpu": "arm" }, "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw=="],
-
- "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.56.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q=="],
-
- "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.56.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w=="],
-
- "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.56.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g=="],
-
- "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.56.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ=="],
-
- "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.56.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg=="],
-
- "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A=="],
-
- "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw=="],
-
- "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ=="],
-
- "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA=="],
-
- "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg=="],
-
- "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA=="],
-
- "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw=="],
-
- "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg=="],
-
- "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew=="],
-
- "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ=="],
-
- "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.56.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ=="],
-
- "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw=="],
-
- "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA=="],
-
- "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.56.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA=="],
-
- "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.56.0", "", { "os": "none", "cpu": "arm64" }, "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ=="],
-
- "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.56.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing=="],
-
- "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.56.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg=="],
-
- "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ=="],
-
- "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g=="],
-
- "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.34.0", "", { "dependencies": { "@sentry/core": "10.34.0" } }, "sha512-0YNr60rGHyedmwkO0lbDBjNx2KAmT3kWamjaqu7Aw+jsESoPLgt+fzaTVvUBvkftBDui2PeTSzXm/nqzssctYg=="],
-
- "@sentry-internal/feedback": ["@sentry-internal/feedback@10.34.0", "", { "dependencies": { "@sentry/core": "10.34.0" } }, "sha512-wgGnq+iNxsFSOe9WX/FOvtoItSTjgLJJ4dQkVYtcVM6WGBVIg4wgNYfECCnRNztUTPzpZHLjC9r+4Pym451DDQ=="],
-
- "@sentry-internal/replay": ["@sentry-internal/replay@10.34.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.34.0", "@sentry/core": "10.34.0" } }, "sha512-Vmea0GcOg57z/S1bVSj3saFcRvDqdLzdy4wd9fQMpMgy5OCbTlo7lxVUndKzbcZnanma6zF6VxwnWER1WuN9RA=="],
-
- "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.34.0", "", { "dependencies": { "@sentry-internal/replay": "10.34.0", "@sentry/core": "10.34.0" } }, "sha512-XWH/9njtgMD+LLWjc4KKgBpb+dTCkoUEIFDxcvzG/87d+jirmzf0+r8EfpLwKG+GrqNiiGRV39zIqu0SfPl+cw=="],
-
- "@sentry/browser": ["@sentry/browser@10.34.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.34.0", "@sentry-internal/feedback": "10.34.0", "@sentry-internal/replay": "10.34.0", "@sentry-internal/replay-canvas": "10.34.0", "@sentry/core": "10.34.0" } }, "sha512-8WCsAXli5Z+eIN8dMY8KGQjrS3XgUp1np/pjdeWNrVPVR8q8XpS34qc+f+y/LFrYQC9bs2Of5aIBwRtDCIvRsg=="],
-
- "@sentry/core": ["@sentry/core@10.34.0", "", {}, "sha512-4FFpYBMf0VFdPcsr4grDYDOR87mRu6oCfb51oQjU/Pndmty7UgYo0Bst3LEC/8v0SpytBtzXq+Wx/fkwulBesg=="],
-
- "@sentry/electron": ["@sentry/electron@7.6.0", "", { "dependencies": { "@sentry/browser": "10.34.0", "@sentry/core": "10.34.0", "@sentry/node": "10.34.0" }, "peerDependencies": { "@sentry/node-native": "10.34.0" }, "optionalPeers": ["@sentry/node-native"] }, "sha512-ueW3Coa0BtOQFPaf+QaI3mBHMi/t7CkZnuzZ6PNoVpHe6CgYfCtNdE7H1BpMsCpG1FhEAgCLBJtpaMKyQBFdzQ=="],
-
- "@sentry/node": ["@sentry/node@10.34.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.2.0", "@opentelemetry/core": "^2.2.0", "@opentelemetry/instrumentation": "^0.208.0", "@opentelemetry/instrumentation-amqplib": "0.55.0", "@opentelemetry/instrumentation-connect": "0.52.0", "@opentelemetry/instrumentation-dataloader": "0.26.0", "@opentelemetry/instrumentation-express": "0.57.0", "@opentelemetry/instrumentation-fs": "0.28.0", "@opentelemetry/instrumentation-generic-pool": "0.52.0", "@opentelemetry/instrumentation-graphql": "0.56.0", "@opentelemetry/instrumentation-hapi": "0.55.0", "@opentelemetry/instrumentation-http": "0.208.0", "@opentelemetry/instrumentation-ioredis": "0.56.0", "@opentelemetry/instrumentation-kafkajs": "0.18.0", "@opentelemetry/instrumentation-knex": "0.53.0", "@opentelemetry/instrumentation-koa": "0.57.0", "@opentelemetry/instrumentation-lru-memoizer": "0.53.0", "@opentelemetry/instrumentation-mongodb": "0.61.0", "@opentelemetry/instrumentation-mongoose": "0.55.0", "@opentelemetry/instrumentation-mysql": "0.54.0", "@opentelemetry/instrumentation-mysql2": "0.55.0", "@opentelemetry/instrumentation-pg": "0.61.0", "@opentelemetry/instrumentation-redis": "0.57.0", "@opentelemetry/instrumentation-tedious": "0.27.0", "@opentelemetry/instrumentation-undici": "0.19.0", "@opentelemetry/resources": "^2.2.0", "@opentelemetry/sdk-trace-base": "^2.2.0", "@opentelemetry/semantic-conventions": "^1.37.0", "@prisma/instrumentation": "6.19.0", "@sentry/core": "10.34.0", "@sentry/node-core": "10.34.0", "@sentry/opentelemetry": "10.34.0", "import-in-the-middle": "^2.0.1", "minimatch": "^9.0.0" } }, "sha512-bEOyH97HuVtWZYAZ5mp0NhYNc+n6QCfiKuLee2P75n2kt4cIPTGvLOSdUwwjllf795uOdKZJuM1IUN0W+YMcVg=="],
-
- "@sentry/node-core": ["@sentry/node-core@10.34.0", "", { "dependencies": { "@apm-js-collab/tracing-hooks": "^0.3.1", "@sentry/core": "10.34.0", "@sentry/opentelemetry": "10.34.0", "import-in-the-middle": "^2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/instrumentation": ">=0.57.1 <1", "@opentelemetry/resources": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/semantic-conventions": "^1.37.0" } }, "sha512-FrGfC8GzD1cnZDO3zwQ4cjyoY1ZwNHvZbXSvXRYxpjhXidZhvaPurjgLRSB0xGaFgoemmOp1ufsx/w6fQOGA6Q=="],
-
- "@sentry/opentelemetry": ["@sentry/opentelemetry@10.34.0", "", { "dependencies": { "@sentry/core": "10.34.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0 || ^2.2.0", "@opentelemetry/semantic-conventions": "^1.37.0" } }, "sha512-uKuULBOmdVu3bYdD8doMLqKgN0PP3WWtI7Shu11P9PVrhSNT4U9yM9Z6v1aFlQcbrgyg3LynZuXs8lyjt90UbA=="],
-
- "@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="],
-
- "@shikijs/engine-javascript": ["@shikijs/engine-javascript@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "oniguruma-to-es": "^2.2.0" } }, "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A=="],
-
- "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1" } }, "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA=="],
-
- "@shikijs/langs": ["@shikijs/langs@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2" } }, "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ=="],
-
- "@shikijs/themes": ["@shikijs/themes@1.29.2", "", { "dependencies": { "@shikijs/types": "1.29.2" } }, "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g=="],
-
- "@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
-
- "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
-
- "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="],
-
- "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
-
- "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="],
-
- "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
-
- "@tanstack/query-core": ["@tanstack/query-core@5.90.19", "", {}, "sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA=="],
-
- "@tanstack/react-query": ["@tanstack/react-query@5.90.19", "", { "dependencies": { "@tanstack/query-core": "5.90.19" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ=="],
-
- "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.18", "", { "dependencies": { "@tanstack/virtual-core": "3.13.18" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A=="],
-
- "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.18", "", {}, "sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg=="],
-
- "@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
-
- "@trpc/client": ["@trpc/client@11.8.1", "", { "peerDependencies": { "@trpc/server": "11.8.1", "typescript": ">=5.7.2" } }, "sha512-L/SJFGanr9xGABmuDoeXR4xAdHJmsXsiF9OuH+apecJ+8sUITzVT1EPeqp0ebqA6lBhEl5pPfg3rngVhi/h60Q=="],
-
- "@trpc/react-query": ["@trpc/react-query@11.8.1", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.8.1", "@trpc/server": "11.8.1", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-0Vu55ld/oINb4U6nIPPi7eZMhxUop6K+4QUK90RVsfSD5r+957sM80M4c8bjh/JBZUxMFv9JOhxxlWcrgHxHow=="],
-
- "@trpc/server": ["@trpc/server@11.8.1", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA=="],
-
- "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
-
- "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
-
- "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
-
- "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
-
- "@types/better-sqlite3": ["@types/better-sqlite3@7.6.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="],
-
- "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="],
-
- "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
-
- "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="],
-
- "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
-
- "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="],
-
- "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="],
-
- "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="],
-
- "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
-
- "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="],
-
- "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="],
-
- "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="],
-
- "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="],
-
- "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="],
-
- "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
-
- "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="],
-
- "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="],
-
- "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="],
-
- "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="],
-
- "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="],
-
- "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
-
- "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
-
- "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="],
-
- "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="],
-
- "@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="],
-
- "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
-
- "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="],
-
- "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="],
-
- "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
-
- "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
-
- "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="],
-
- "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
-
- "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="],
-
- "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="],
-
- "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
-
- "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
-
- "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
-
- "@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="],
-
- "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
-
- "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
-
- "@types/http-cache-semantics": ["@types/http-cache-semantics@4.0.4", "", {}, "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="],
-
- "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="],
-
- "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
-
- "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
-
- "@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="],
-
- "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="],
-
- "@types/pg": ["@types/pg@8.15.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="],
-
- "@types/pg-pool": ["@types/pg-pool@2.0.6", "", { "dependencies": { "@types/pg": "*" } }, "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ=="],
-
- "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="],
-
- "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="],
-
- "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
-
- "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
-
- "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="],
-
- "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
-
- "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
-
- "@types/verror": ["@types/verror@1.10.11", "", {}, "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg=="],
-
- "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
-
- "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
-
- "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="],
-
- "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
-
- "@vue/reactivity": ["@vue/reactivity@3.5.27", "", { "dependencies": { "@vue/shared": "3.5.27" } }, "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ=="],
-
- "@vue/shared": ["@vue/shared@3.5.27", "", {}, "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ=="],
-
- "@welldone-software/why-did-you-render": ["@welldone-software/why-did-you-render@10.0.1", "", { "dependencies": { "lodash": "^4" }, "peerDependencies": { "react": "^19" } }, "sha512-tMgGkt30iVYeLMUKExNmtm019QgyjLtA7lwB0QAizYNEuihlCG2eoAWBBaz/bDeI7LeqAJ9msC6hY3vX+JB97g=="],
-
- "@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
-
- "@xterm/addon-canvas": ["@xterm/addon-canvas@0.7.0", "", { "peerDependencies": { "@xterm/xterm": "^5.0.0" } }, "sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw=="],
-
- "@xterm/addon-fit": ["@xterm/addon-fit@0.11.0", "", {}, "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g=="],
-
- "@xterm/addon-search": ["@xterm/addon-search@0.16.0", "", {}, "sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA=="],
-
- "@xterm/addon-serialize": ["@xterm/addon-serialize@0.14.0", "", {}, "sha512-uteyTU1EkrQa2Ux6P/uFl2fzmXI46jy5uoQMKEOM0fKTyiW7cSn0WrFenHm5vO5uEXX/GpwW/FgILvv3r0WbkA=="],
-
- "@xterm/addon-web-links": ["@xterm/addon-web-links@0.12.0", "", {}, "sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw=="],
-
- "@xterm/addon-webgl": ["@xterm/addon-webgl@0.19.0", "", {}, "sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A=="],
-
- "@xterm/xterm": ["@xterm/xterm@5.5.0", "", {}, "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="],
-
- "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="],
-
- "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
-
- "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
-
- "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
-
- "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
-
- "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
-
- "ai": ["ai@6.0.45", "", { "dependencies": { "@ai-sdk/gateway": "3.0.19", "@ai-sdk/provider": "3.0.4", "@ai-sdk/provider-utils": "4.0.8", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8QFU1cFY25Z7M72gpw4pzkvyEXK4X1oO3ZRUQYofU4hs742rt+NMAmgNn2uZo/cMtEH5Dt1Vlu/fMQXmLnRoZQ=="],
-
- "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
-
- "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
-
- "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="],
-
- "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
-
- "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
-
- "app-builder-bin": ["app-builder-bin@5.0.0-alpha.10", "", {}, "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw=="],
-
- "app-builder-lib": ["app-builder-lib@25.1.8", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.1", "@electron/rebuild": "3.6.1", "@electron/universal": "2.0.1", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "chromium-pickle-js": "^0.2.0", "config-file-ts": "0.2.8-rc1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "25.1.7", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.0", "resedit": "^1.7.0", "sanitize-filename": "^1.6.3", "semver": "^7.3.8", "tar": "^6.1.12", "temp-file": "^3.4.0" }, "peerDependencies": { "dmg-builder": "25.1.8", "electron-builder-squirrel-windows": "25.1.8" } }, "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg=="],
-
- "aproba": ["aproba@2.1.0", "", {}, "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew=="],
-
- "archiver": ["archiver@5.3.2", "", { "dependencies": { "archiver-utils": "^2.1.0", "async": "^3.2.4", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", "readdir-glob": "^1.1.2", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" } }, "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw=="],
-
- "archiver-utils": ["archiver-utils@2.1.0", "", { "dependencies": { "glob": "^7.1.4", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^2.0.0" } }, "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw=="],
-
- "are-we-there-yet": ["are-we-there-yet@3.0.1", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" } }, "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg=="],
-
- "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
-
- "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
-
- "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
-
- "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="],
-
- "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="],
-
- "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
-
- "async-exit-hook": ["async-exit-hook@2.0.1", "", {}, "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="],
-
- "async-mutex": ["async-mutex@0.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA=="],
-
- "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
-
- "at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
-
- "autoprefixer": ["autoprefixer@10.4.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="],
-
- "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
-
- "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
-
- "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
-
- "baseline-browser-mapping": ["baseline-browser-mapping@2.9.17", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ=="],
-
- "better-sqlite3": ["better-sqlite3@11.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
-
- "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
-
- "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
-
- "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
-
- "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
-
- "bluebird-lst": ["bluebird-lst@1.0.9", "", { "dependencies": { "bluebird": "^3.5.5" } }, "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw=="],
-
- "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
-
- "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
-
- "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
-
- "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
-
- "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
-
- "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
-
- "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
-
- "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
-
- "builder-util": ["builder-util@25.1.7", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.10", "bluebird-lst": "^1.0.9", "builder-util-runtime": "9.2.10", "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0" } }, "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww=="],
-
- "builder-util-runtime": ["builder-util-runtime@9.2.10", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw=="],
-
- "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
-
- "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
-
- "cacache": ["cacache@16.1.3", "", { "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", "fs-minipass": "^2.1.0", "glob": "^8.0.1", "infer-owner": "^1.0.4", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^9.0.0", "tar": "^6.1.11", "unique-filename": "^2.0.0" } }, "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ=="],
-
- "cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="],
-
- "cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="],
-
- "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
-
- "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
-
- "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
-
- "caniuse-lite": ["caniuse-lite@1.0.30001765", "", {}, "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ=="],
-
- "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
-
- "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
-
- "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
-
- "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
-
- "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
-
- "chevrotain": ["chevrotain@11.0.3", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", "@chevrotain/regexp-to-ast": "11.0.3", "@chevrotain/types": "11.0.3", "@chevrotain/utils": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw=="],
-
- "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="],
-
- "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
-
- "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
-
- "chromium-pickle-js": ["chromium-pickle-js@0.2.0", "", {}, "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw=="],
-
- "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
-
- "cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
-
- "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
-
- "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
-
- "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
-
- "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
-
- "cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="],
-
- "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
-
- "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
-
- "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="],
-
- "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
-
- "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="],
-
- "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
-
- "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
-
- "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
-
- "compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="],
-
- "compress-commons": ["compress-commons@4.1.2", "", { "dependencies": { "buffer-crc32": "^0.2.13", "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg=="],
-
- "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
-
- "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
-
- "config-file-ts": ["config-file-ts@0.2.8-rc1", "", { "dependencies": { "glob": "^10.3.12", "typescript": "^5.4.3" } }, "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg=="],
-
- "console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="],
-
- "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
-
- "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
-
- "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
-
- "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
-
- "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
-
- "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
-
- "core-js": ["core-js@3.48.0", "", {}, "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ=="],
-
- "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
-
- "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
-
- "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
-
- "crc": ["crc@3.8.0", "", { "dependencies": { "buffer": "^5.1.0" } }, "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ=="],
-
- "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
-
- "crc32-stream": ["crc32-stream@4.0.3", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" } }, "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw=="],
-
- "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
-
- "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
-
- "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
-
- "cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="],
-
- "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
-
- "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="],
-
- "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="],
-
- "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
-
- "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="],
-
- "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="],
-
- "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="],
-
- "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
-
- "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="],
-
- "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
-
- "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="],
-
- "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="],
-
- "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="],
-
- "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
-
- "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="],
-
- "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="],
-
- "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
-
- "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
-
- "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="],
-
- "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
-
- "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
-
- "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="],
-
- "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="],
-
- "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="],
-
- "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="],
-
- "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
-
- "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
-
- "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
-
- "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
-
- "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
-
- "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
-
- "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
-
- "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="],
-
- "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
-
- "dagre-d3-es": ["dagre-d3-es@7.0.13", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q=="],
-
- "date-fns": ["date-fns@3.6.0", "", {}, "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww=="],
-
- "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
-
- "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
-
- "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
-
- "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
-
- "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
-
- "defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="],
-
- "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
-
- "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
-
- "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
-
- "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
-
- "delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="],
-
- "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
-
- "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
-
- "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
-
- "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="],
-
- "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
-
- "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
-
- "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
-
- "dir-compare": ["dir-compare@4.2.0", "", { "dependencies": { "minimatch": "^3.0.5", "p-limit": "^3.1.0 " } }, "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ=="],
-
- "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
-
- "dmg-builder": ["dmg-builder@25.1.8", "", { "dependencies": { "app-builder-lib": "25.1.8", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ=="],
-
- "dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="],
-
- "dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
-
- "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
-
- "dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
-
- "drizzle-kit": ["drizzle-kit@0.31.8", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="],
-
- "drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="],
-
- "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
-
- "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
-
- "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
-
- "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
-
- "electron": ["electron@33.4.5", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^20.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-rbDc4QOqfMT1uopUG+KcaMKzKgFAXAzN3wNIdgErnB1tUnpgTxwFv1BDN/exCl1vaVWBeM9YtbO5PgbGZeq7xw=="],
-
- "electron-builder": ["electron-builder@25.1.8", "", { "dependencies": { "app-builder-lib": "25.1.8", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "chalk": "^4.1.2", "dmg-builder": "25.1.8", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig=="],
-
- "electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@25.1.8", "", { "dependencies": { "app-builder-lib": "25.1.8", "archiver": "^5.3.1", "builder-util": "25.1.7", "fs-extra": "^10.1.0" } }, "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg=="],
-
- "electron-log": ["electron-log@5.4.3", "", {}, "sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ=="],
-
- "electron-publish": ["electron-publish@25.1.7", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "chalk": "^4.1.2", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg=="],
-
- "electron-rebuild": ["electron-rebuild@3.2.9", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "fs-extra": "^10.0.0", "got": "^11.7.0", "lzma-native": "^8.0.5", "node-abi": "^3.0.0", "node-api-version": "^0.1.4", "node-gyp": "^9.0.0", "ora": "^5.1.0", "semver": "^7.3.5", "tar": "^6.0.5", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/src/cli.js" } }, "sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw=="],
-
- "electron-to-chromium": ["electron-to-chromium@1.5.277", "", {}, "sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw=="],
-
- "electron-updater": ["electron-updater@6.7.3", "", { "dependencies": { "builder-util-runtime": "9.5.1", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-EgkT8Z9noqXKbwc3u5FkJA+r48jwZ5DTUiOkJMOTEEH//n5Am6wfQGz7nvSFEA2oIAMv9jRzn5JKTyWeSKOPgg=="],
-
- "electron-vite": ["electron-vite@3.1.0", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-arrow-functions": "^7.25.9", "cac": "^6.7.14", "esbuild": "^0.25.1", "magic-string": "^0.30.17", "picocolors": "^1.1.1" }, "peerDependencies": { "@swc/core": "^1.0.0", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["@swc/core"], "bin": { "electron-vite": "bin/electron-vite.js" } }, "sha512-M7aAzaRvSl5VO+6KN4neJCYLHLpF/iWo5ztchI/+wMxIieDZQqpbCYfaEHHHPH6eupEzfvZdLYdPdmvGqoVe0Q=="],
-
- "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
-
- "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
-
- "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="],
-
- "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
-
- "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
-
- "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
-
- "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="],
-
- "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
-
- "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
-
- "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
-
- "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
-
- "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
-
- "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
-
- "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
-
- "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
-
- "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
-
- "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
-
- "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
-
- "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
-
- "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
-
- "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
-
- "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
-
- "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
-
- "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
-
- "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
-
- "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
-
- "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
-
- "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
-
- "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
-
- "extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="],
-
- "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
-
- "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
-
- "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
-
- "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
-
- "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
-
- "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
-
- "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
-
- "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
-
- "fflate": ["fflate@0.4.8", "", {}, "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="],
-
- "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
-
- "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
-
- "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
-
- "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
-
- "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
-
- "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
-
- "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
-
- "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="],
-
- "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
-
- "framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="],
-
- "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
-
- "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
-
- "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
-
- "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
-
- "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
-
- "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
-
- "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
-
- "gauge": ["gauge@4.0.4", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg=="],
-
- "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
-
- "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
-
- "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
-
- "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
-
- "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
-
- "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
-
- "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
-
- "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
-
- "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
-
- "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
-
- "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="],
-
- "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
-
- "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
-
- "got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="],
-
- "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
-
- "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
-
- "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
-
- "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
-
- "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
-
- "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
-
- "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
-
- "has-unicode": ["has-unicode@2.0.1", "", {}, "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="],
-
- "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
-
- "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
-
- "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
-
- "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
-
- "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="],
-
- "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
-
- "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
-
- "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
-
- "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
-
- "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
-
- "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
-
- "hono": ["hono@4.11.5", "", {}, "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g=="],
-
- "hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
-
- "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
-
- "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
-
- "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
-
- "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
-
- "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
-
- "http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="],
-
- "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
-
- "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
-
- "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="],
-
- "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
-
- "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
-
- "import-in-the-middle": ["import-in-the-middle@2.0.5", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-0InH9/4oDCBRzWXhpOqusspLBrVfK1vPvbn9Wxl8DAQ8yyx5fWJRETICSwkiAMaYntjJAMBP1R4B6cQnEUYVEA=="],
-
- "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
-
- "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
-
- "infer-owner": ["infer-owner@1.0.4", "", {}, "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="],
-
- "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
-
- "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
-
- "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
-
- "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
-
- "internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
-
- "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
-
- "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
-
- "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
-
- "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
-
- "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
-
- "is-ci": ["is-ci@3.0.1", "", { "dependencies": { "ci-info": "^3.2.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ=="],
-
- "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
-
- "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
-
- "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
-
- "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
-
- "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
-
- "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
-
- "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
-
- "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="],
-
- "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="],
-
- "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
-
- "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
-
- "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
-
- "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
-
- "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
-
- "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
-
- "isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
-
- "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
-
- "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
-
- "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="],
-
- "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
-
- "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
-
- "jotai": ["jotai@2.16.2", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-DH0lBiTXvewsxtqqwjDW6Hg9JPTDnq9LcOsXSFWCAUEt+qj5ohl9iRVX9zQXPPHKLXCdH+5mGvM28fsXMl17/g=="],
-
- "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
- "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
-
- "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
-
- "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
-
- "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
-
- "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
-
- "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
-
- "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="],
-
- "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
-
- "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
-
- "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
-
- "katex": ["katex@0.16.27", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw=="],
-
- "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
-
- "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
-
- "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
-
- "langium": ["langium@3.3.1", "", { "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.0.8" } }, "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w=="],
-
- "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="],
-
- "lazy-val": ["lazy-val@1.0.5", "", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="],
-
- "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="],
-
- "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
-
- "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
-
- "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
-
- "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
-
- "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
-
- "lodash.difference": ["lodash.difference@4.5.0", "", {}, "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="],
-
- "lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="],
-
- "lodash.flatten": ["lodash.flatten@4.4.0", "", {}, "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="],
-
- "lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="],
-
- "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
-
- "lodash.union": ["lodash.union@4.6.0", "", {}, "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="],
-
- "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
-
- "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
-
- "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
-
- "lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
-
- "lowlight": ["lowlight@3.3.0", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="],
-
- "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
-
- "lucide-react": ["lucide-react@0.468.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA=="],
-
- "lzma-native": ["lzma-native@8.0.6", "", { "dependencies": { "node-addon-api": "^3.1.0", "node-gyp-build": "^4.2.1", "readable-stream": "^3.6.0" }, "bin": { "lzmajs": "bin/lzmajs" } }, "sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA=="],
-
- "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
-
- "make-fetch-happen": ["make-fetch-happen@10.2.1", "", { "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "socks-proxy-agent": "^7.0.0", "ssri": "^9.0.0" } }, "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w=="],
-
- "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
-
- "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
-
- "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="],
-
- "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
-
- "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
-
- "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
-
- "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.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.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
-
- "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
-
- "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
-
- "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
-
- "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
-
- "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
-
- "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
-
- "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
-
- "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
-
- "mdast-util-newline-to-break": ["mdast-util-newline-to-break@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-find-and-replace": "^3.0.0" } }, "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog=="],
-
- "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
-
- "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
-
- "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
-
- "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
-
- "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
-
- "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
-
- "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
-
- "mermaid": ["mermaid@11.12.2", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.3", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.13", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w=="],
-
- "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
-
- "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
-
- "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
-
- "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
-
- "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
-
- "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
-
- "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
-
- "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
-
- "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
-
- "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
-
- "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
-
- "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
-
- "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
-
- "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
-
- "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
-
- "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
-
- "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
-
- "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
-
- "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
-
- "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
-
- "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
-
- "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
-
- "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
-
- "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
-
- "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
-
- "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
-
- "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
-
- "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
-
- "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
-
- "mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="],
-
- "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
-
- "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
-
- "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
-
- "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
-
- "minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
-
- "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
-
- "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
-
- "minipass-collect": ["minipass-collect@1.0.2", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA=="],
-
- "minipass-fetch": ["minipass-fetch@2.1.2", "", { "dependencies": { "minipass": "^3.1.6", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA=="],
-
- "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="],
-
- "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
-
- "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="],
-
- "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
-
- "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
-
- "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
-
- "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
-
- "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
-
- "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="],
-
- "motion": ["motion@11.18.2", "", { "dependencies": { "framer-motion": "^11.18.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-JLjvFDuFr42NFtcVoMAyC2sEjnpA8xpy6qWPyzQvCloznAyQ8FIXioxWfHiLtgYhoVpfUqSWpn1h9++skj9+Wg=="],
-
- "motion-dom": ["motion-dom@11.18.1", "", { "dependencies": { "motion-utils": "^11.18.1" } }, "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw=="],
-
- "motion-utils": ["motion-utils@11.18.1", "", {}, "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA=="],
-
- "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
-
- "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
-
- "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
-
- "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
-
- "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
-
- "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
-
- "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="],
-
- "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
-
- "node-api-version": ["node-api-version@0.1.4", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g=="],
-
- "node-gyp": ["node-gyp@9.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.0.3", "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ=="],
-
- "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
-
- "node-pty": ["node-pty@1.1.0", "", { "dependencies": { "node-addon-api": "^7.1.0" } }, "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg=="],
-
- "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
-
- "nopt": ["nopt@6.0.0", "", { "dependencies": { "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g=="],
-
- "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
-
- "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="],
-
- "npmlog": ["npmlog@6.0.2", "", { "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg=="],
-
- "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
-
- "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
-
- "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
-
- "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
-
- "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
-
- "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
-
- "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
-
- "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
-
- "oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
-
- "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
-
- "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="],
-
- "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
-
- "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
-
- "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
-
- "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
-
- "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
-
- "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
-
- "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
-
- "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
-
- "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
-
- "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
-
- "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
-
- "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
-
- "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
-
- "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
-
- "pe-library": ["pe-library@0.4.1", "", {}, "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw=="],
-
- "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
-
- "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
-
- "pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
-
- "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
-
- "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
-
- "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
-
- "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
-
- "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
-
- "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
-
- "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
-
- "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
-
- "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="],
-
- "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="],
-
- "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
-
- "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
-
- "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
-
- "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="],
-
- "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
-
- "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
-
- "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
-
- "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
-
- "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
-
- "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
-
- "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
-
- "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
-
- "posthog-js": ["posthog-js@1.333.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.208.0", "@opentelemetry/exporter-logs-otlp-http": "^0.208.0", "@opentelemetry/resources": "^2.2.0", "@opentelemetry/sdk-logs": "^0.208.0", "@posthog/core": "1.13.0", "@posthog/types": "1.333.0", "core-js": "^3.38.1", "dompurify": "^3.3.1", "fflate": "^0.4.8", "preact": "^10.28.0", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^4.2.4" } }, "sha512-c7vquERMedjuGE2GnaDDJW/V1BIMMQG7BlYKrH0z8O7fc3WpEsQ/IyQ+9aD9+DLxlDCFpzrwgoxVDWi9K37mdA=="],
-
- "posthog-node": ["posthog-node@5.24.1", "", { "dependencies": { "@posthog/core": "1.13.0" } }, "sha512-1+wsosb5fjuor9zpp3h2uq0xKYY7rDz8gpw/10Scz8Ob/uVNrsHSwGy76D9rgt4cfyaEgpJwyYv+hPi2+YjWtw=="],
-
- "preact": ["preact@10.28.2", "", {}, "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA=="],
-
- "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
-
- "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
-
- "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
-
- "promise-inflight": ["promise-inflight@1.0.1", "", {}, "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="],
-
- "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="],
-
- "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
-
- "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
-
- "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
-
- "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
-
- "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
- "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
-
- "query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="],
-
- "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
-
- "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="],
-
- "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
-
- "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
-
- "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
-
- "react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="],
-
- "react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="],
-
- "react-hotkeys-hook": ["react-hotkeys-hook@4.6.2", "", { "peerDependencies": { "react": ">=16.8.1", "react-dom": ">=16.8.1" } }, "sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q=="],
-
- "react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
-
- "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
-
- "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
-
- "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
-
- "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
-
- "react-zoom-pan-pinch": ["react-zoom-pan-pinch@3.7.0", "", { "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA=="],
-
- "reactivity-store": ["reactivity-store@0.3.12", "", { "dependencies": { "@vue/reactivity": "~3.5.22", "@vue/shared": "~3.5.22", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Idz9EL4dFUtQbHySZQzckWOTUfqjdYpUtNW0iOysC32mG7IjiUGB77QrsyR5eAWBkRiS9JscF6A3fuQAIy+LrQ=="],
-
- "read-binary-file-arch": ["read-binary-file-arch@1.0.6", "", { "dependencies": { "debug": "^4.3.4" }, "bin": { "read-binary-file-arch": "cli.js" } }, "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg=="],
-
- "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
-
- "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
-
- "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
-
- "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
-
- "regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
-
- "regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
-
- "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
-
- "rehype-harden": ["rehype-harden@1.1.7", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-j5DY0YSK2YavvNGV+qBHma15J9m0WZmRe8posT5AtKDS6TNWtMVTo6RiqF8SidfcASYz8f3k2J/1RWmq5zTXUw=="],
-
- "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
-
- "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="],
-
- "remark-breaks": ["remark-breaks@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-newline-to-break": "^2.0.0", "unified": "^11.0.0" } }, "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ=="],
-
- "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
-
- "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
-
- "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="],
-
- "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
-
- "remend": ["remend@1.1.0", "", {}, "sha512-JENGyuIhTwzUfCarW43X4r9cehoqTo9QyYxfNDZSud2AmqeuWjZ5pfybasTa4q0dxTJAj5m8NB+wR+YueAFpxQ=="],
-
- "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
-
- "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
-
- "require-in-the-middle": ["require-in-the-middle@8.0.1", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="],
-
- "resedit": ["resedit@1.7.2", "", { "dependencies": { "pe-library": "^0.4.1" } }, "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA=="],
-
- "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
-
- "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="],
-
- "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
-
- "responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="],
-
- "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
-
- "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
-
- "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
-
- "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
-
- "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
-
- "robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
-
- "rollup": ["rollup@4.56.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.56.0", "@rollup/rollup-android-arm64": "4.56.0", "@rollup/rollup-darwin-arm64": "4.56.0", "@rollup/rollup-darwin-x64": "4.56.0", "@rollup/rollup-freebsd-arm64": "4.56.0", "@rollup/rollup-freebsd-x64": "4.56.0", "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", "@rollup/rollup-linux-arm-musleabihf": "4.56.0", "@rollup/rollup-linux-arm64-gnu": "4.56.0", "@rollup/rollup-linux-arm64-musl": "4.56.0", "@rollup/rollup-linux-loong64-gnu": "4.56.0", "@rollup/rollup-linux-loong64-musl": "4.56.0", "@rollup/rollup-linux-ppc64-gnu": "4.56.0", "@rollup/rollup-linux-ppc64-musl": "4.56.0", "@rollup/rollup-linux-riscv64-gnu": "4.56.0", "@rollup/rollup-linux-riscv64-musl": "4.56.0", "@rollup/rollup-linux-s390x-gnu": "4.56.0", "@rollup/rollup-linux-x64-gnu": "4.56.0", "@rollup/rollup-linux-x64-musl": "4.56.0", "@rollup/rollup-openbsd-x64": "4.56.0", "@rollup/rollup-openharmony-arm64": "4.56.0", "@rollup/rollup-win32-arm64-msvc": "4.56.0", "@rollup/rollup-win32-ia32-msvc": "4.56.0", "@rollup/rollup-win32-x64-gnu": "4.56.0", "@rollup/rollup-win32-x64-msvc": "4.56.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg=="],
-
- "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
-
- "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
-
- "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
-
- "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
-
- "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
-
- "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
-
- "sanitize-filename": ["sanitize-filename@1.6.3", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg=="],
-
- "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="],
-
- "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
-
- "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
-
- "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
-
- "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
-
- "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
-
- "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="],
-
- "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
-
- "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
-
- "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
-
- "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
-
- "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
-
- "shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="],
-
- "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
-
- "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
-
- "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
-
- "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
-
- "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
- "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
-
- "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
-
- "simple-git": ["simple-git@3.30.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" } }, "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg=="],
-
- "simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="],
-
- "slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="],
-
- "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
-
- "socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
-
- "socks-proxy-agent": ["socks-proxy-agent@7.0.0", "", { "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" } }, "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww=="],
-
- "sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="],
-
- "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
-
- "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
-
- "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
-
- "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
-
- "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
-
- "ssri": ["ssri@9.0.1", "", { "dependencies": { "minipass": "^3.1.1" } }, "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q=="],
-
- "stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="],
-
- "state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="],
-
- "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
-
- "streamdown": ["streamdown@2.1.0", "", { "dependencies": { "clsx": "^2.1.1", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "marked": "^17.0.1", "rehype-harden": "^1.1.7", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.1.0", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-u9gWd0AmjKg1d+74P44XaPlGrMeC21oDOSIhjGNEYMAttDMzCzlJO6lpTyJ9JkSinQQF65YcK4eOd3q9iTvULw=="],
-
- "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
-
- "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="],
-
- "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
-
- "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
-
- "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
-
- "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
-
- "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
-
- "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="],
-
- "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
-
- "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
-
- "swr": ["swr@2.3.8", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w=="],
-
- "tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
-
- "tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="],
-
- "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
-
- "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
-
- "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
-
- "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
-
- "temp-file": ["temp-file@3.4.0", "", { "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg=="],
-
- "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
-
- "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
-
- "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
-
- "tiny-typed-emitter": ["tiny-typed-emitter@2.1.0", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="],
-
- "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
-
- "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
-
- "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
-
- "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="],
-
- "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
-
- "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
-
- "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
-
- "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
-
- "trpc-electron": ["trpc-electron@0.1.2", "", { "peerDependencies": { "@trpc/client": ">=11.0.0", "@trpc/server": ">=11.0.0", "electron": ">19.0.0" } }, "sha512-sQpWBwQWzsgrERugjzUpPqY/+/n8NxkUq6YssQ5+5rALkvGCWq45T5Dreiwm2kh91dZMFlALTyMd8PhB0vgbIg=="],
-
- "truncate-utf8-bytes": ["truncate-utf8-bytes@1.0.2", "", { "dependencies": { "utf8-byte-length": "^1.0.1" } }, "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ=="],
-
- "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
-
- "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
-
- "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
-
- "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
-
- "type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
-
- "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
-
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
-
- "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
-
- "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
-
- "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
-
- "unique-filename": ["unique-filename@2.0.1", "", { "dependencies": { "unique-slug": "^3.0.0" } }, "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A=="],
-
- "unique-names-generator": ["unique-names-generator@4.7.1", "", {}, "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow=="],
-
- "unique-slug": ["unique-slug@3.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w=="],
-
- "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
-
- "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
-
- "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
-
- "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
-
- "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
-
- "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
-
- "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
-
- "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
-
- "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
-
- "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
-
- "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
-
- "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
-
- "utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
-
- "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
-
- "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
-
- "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
-
- "verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
-
- "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
-
- "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="],
-
- "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
-
- "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
-
- "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
-
- "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
-
- "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
-
- "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
-
- "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
-
- "vscode-uri": ["vscode-uri@3.0.8", "", {}, "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="],
-
- "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
-
- "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
-
- "web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="],
-
- "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
-
- "wide-align": ["wide-align@1.1.5", "", { "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg=="],
-
- "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
-
- "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
-
- "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
-
- "xterm": ["xterm@5.3.0", "", {}, "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="],
-
- "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
-
- "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
-
- "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
-
- "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
-
- "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
-
- "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
-
- "zip-stream": ["zip-stream@4.1.1", "", { "dependencies": { "archiver-utils": "^3.0.4", "compress-commons": "^4.1.2", "readable-stream": "^3.6.0" } }, "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ=="],
-
- "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
-
- "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
-
- "zustand": ["zustand@5.0.10", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg=="],
-
- "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
-
- "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
-
- "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
-
- "@chevrotain/cst-dts-gen/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
-
- "@chevrotain/gast/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
-
- "@develar/schema-utils/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
-
- "@electron/asar/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="],
-
- "@electron/asar/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
-
- "@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
-
- "@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
-
- "@electron/notarize/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
-
- "@electron/osx-sign/isbinaryfile": ["isbinaryfile@4.0.10", "", {}, "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw=="],
-
- "@electron/rebuild/node-api-version": ["node-api-version@0.2.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q=="],
-
- "@electron/universal/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="],
-
- "@electron/universal/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
-
- "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
-
- "@git-diff-view/shiki/shiki": ["shiki@3.21.0", "", { "dependencies": { "@shikijs/core": "3.21.0", "@shikijs/engine-javascript": "3.21.0", "@shikijs/engine-oniguruma": "3.21.0", "@shikijs/langs": "3.21.0", "@shikijs/themes": "3.21.0", "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w=="],
-
- "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
-
- "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
-
- "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
-
- "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
-
- "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/instrumentation-http/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/otlp-exporter-base/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/otlp-transformer/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
-
- "@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
-
- "@opentelemetry/sdk-logs/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
-
- "@opentelemetry/sdk-metrics/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
- "@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
-
- "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
-
- "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
-
- "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
-
- "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
-
- "@sentry/node/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
-
- "@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
-
- "accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
-
- "ajv-keywords/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
-
- "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
- "archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
-
- "body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
-
- "cacache/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="],
-
- "cacache/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
-
- "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
-
- "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
-
- "config-file-ts/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
-
- "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="],
-
- "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
-
- "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
-
- "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
-
- "dir-compare/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
-
- "dmg-license/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
-
- "electron-updater/builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="],
-
- "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
- "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
-
- "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
-
- "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
-
- "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
-
- "hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
-
- "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="],
-
- "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
-
- "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
-
- "lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
-
- "lzma-native/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="],
-
- "make-fetch-happen/http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],
-
- "make-fetch-happen/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
-
- "make-fetch-happen/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
-
- "make-fetch-happen/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "matcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
-
- "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
- "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "monaco-editor/dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="],
-
- "monaco-editor/marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="],
-
- "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
-
- "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
-
- "path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
-
- "raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
-
- "readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
-
- "roarr/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
-
- "socks-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
-
- "ssri/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
- "streamdown/marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
-
- "streamdown/tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
-
- "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
-
- "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
-
- "zip-stream/archiver-utils": ["archiver-utils@3.0.4", "", { "dependencies": { "glob": "^7.2.3", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw=="],
-
- "@develar/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
-
- "@electron/asar/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
-
- "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
-
- "@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
-
- "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/core": ["@shikijs/core@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/langs": ["@shikijs/langs@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/themes": ["@shikijs/themes@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/types": ["@shikijs/types@3.21.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA=="],
-
- "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
-
- "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
-
- "ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
-
- "archiver-utils/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "archiver-utils/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
-
- "cacache/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
-
- "config-file-ts/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
-
- "config-file-ts/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
-
- "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="],
-
- "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
-
- "dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
-
- "dmg-license/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
-
- "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
-
- "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
-
- "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
-
- "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
-
- "make-fetch-happen/http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
-
- "make-fetch-happen/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
-
- "tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
- "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
-
- "tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
-
- "@git-diff-view/shiki/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
- }
-}
diff --git a/bun.lockb b/bun.lockb
index 6601c57c8..2f16a73c3 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/docs/features/system-tray.md b/docs/features/system-tray.md
new file mode 100644
index 000000000..bcb1e9f8c
--- /dev/null
+++ b/docs/features/system-tray.md
@@ -0,0 +1,77 @@
+# System Tray
+
+The app includes a persistent system tray icon that allows quick access to profiles and workspaces even when all windows are closed.
+
+## Platform Support
+
+- **macOS**: Menu bar icon (template image, adapts to light/dark mode)
+- **Windows**: System tray icon (bottom-right)
+- **Linux**: System tray icon (desktop environment dependent)
+
+## Features
+
+### Left Click
+- Restores or creates the main window
+- If windows are minimized, brings them to front
+- If no windows exist, creates a new one
+
+### Right Click Menu
+
+```
+├── Show 1Code (macOS only)
+├── ─────────
+├── Profiles ⊳
+│ ├── ✓ Active Profile
+│ └── Other Profiles
+├── ─────────
+├── Workspaces ⊳
+│ └── Project List
+├── ─────────
+├── New Workspace...
+├── Settings...
+├── ─────────
+└── Quit
+```
+
+### Profile Switching
+- Click any profile to switch immediately
+- All windows reload with new profile
+- Active profile shown with checkmark
+
+### Workspace Navigation
+- Click workspace to open new chat with that workspace selected
+- Creates window if needed
+- Sets workspace and reloads
+
+### Background Mode
+- App continues running when all windows are closed
+- Only way to quit is via tray menu or Cmd+Q
+- Windows can be restored by clicking tray icon
+
+## Implementation
+
+### Files
+- `src/main/lib/tray.ts` - Tray initialization and menu building
+- `src/renderer/lib/tray-events.ts` - Renderer event listeners
+- `resources/icons/tray-*.png` - Tray icon assets
+
+### Menu Updates
+Menu rebuilds when:
+- Profiles are added/removed/switched
+- Projects are added/removed
+- App starts
+
+## Troubleshooting
+
+### Tray icon not appearing (macOS)
+- Check System Settings → Control Center → Menu Bar Only
+- Ensure "Show in Menu Bar" is enabled for the app
+
+### Tray icon not appearing (Linux)
+- Ensure desktop environment supports system tray
+- Install system tray extensions if using GNOME
+- Check if `libappindicator` is installed
+
+### App won't quit
+- Use tray menu → Quit
+- Or press Cmd+Q from menu bar (macOS)
diff --git a/docs/plans/2025-01-31-multiple-model-profiles-design.md b/docs/plans/2025-01-31-multiple-model-profiles-design.md
new file mode 100644
index 000000000..a03db1ad1
--- /dev/null
+++ b/docs/plans/2025-01-31-multiple-model-profiles-design.md
@@ -0,0 +1,188 @@
+# Multiple Model Profiles - Design Document
+
+**Date:** 2025-01-31
+**Status:** Approved
+**Author:** AI Brainstorming Session
+
+## Overview
+
+Enable users to create and manage multiple Claude API proxy configurations (profiles), each with their own `ANTHROPIC_BASE_URL`, `ANTHROPIC_API_KEY`, and model name. Users can quickly switch between profiles from the settings UI and directly from the active chat interface.
+
+## Problem Statement
+
+Currently, the app supports only a single "Override Model" configuration. Users with multiple proxy configurations (e.g., local proxy, cloud proxy, different API keys) must manually edit these settings each time they want to switch. This is cumbersome and error-prone.
+
+## Solution
+
+Replace the single "Override Model" card with a profile list UI that supports:
+- Creating, editing, deleting, and switching between multiple profiles
+- Quick profile selector in the chat header for fast switching
+- Auto-detection of CLI config to suggest creating a profile
+- Legacy migration from existing single config
+
+## Section 1: UI Layout
+
+### Model Profiles List
+
+The "Override Model" section in [agents-models-tab.tsx](../src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx) will be replaced with a list of profile cards.
+
+**ProfileRow Component Structure:**
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Profile Name [Active] [⋮] │
+│ Model: claude-3-7-sonnet-20250219 │
+│ Proxy: http://localhost:8080 │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+- **Profile Name**: User-defined name (e.g., "Local Proxy", "Cloud Gateway")
+- **Active Badge**: Shows which profile is currently active
+- **Actions Menu (⋮)**: Edit, Delete, Set Active
+
+**Empty State:**
+- When no profiles exist (excluding offline), show "Add your first proxy configuration"
+- Button to open the add form
+
+## Section 2: Add/Edit Form & CRUD Operations
+
+### Inline Add/Edit Form
+
+When user clicks "Add Profile" or edits an existing profile, an inline form expands:
+
+```tsx
+interface ProfileFormState {
+ mode: 'add' | 'edit'
+ profileId?: string // set when editing
+ fields: {
+ name: string
+ model: string
+ token: string
+ baseUrl: string
+ }
+}
+```
+
+**Form Fields:**
+- Name (text input, required)
+- Model (text input, placeholder: "claude-3-7-sonnet-20250219")
+- API Token (password input, placeholder: "sk-ant-...")
+- Base URL (text input, placeholder: "https://api.anthropic.com")
+
+**Validation:**
+- All fields required
+- Token should start with "sk-ant-" (warning only, not blocking)
+- Base URL should be a valid URL format
+
+### CRUD Operations Table
+
+| Operation | Action | Atoms Updated |
+|-----------|--------|---------------|
+| **Add** | Push new profile to `modelProfilesAtom` | `modelProfilesAtom`, `activeProfileIdAtom` (if first) |
+| **Edit** | Find by ID and update in `modelProfilesAtom` | `modelProfilesAtom` |
+| **Delete** | Filter out by ID from `modelProfilesAtom` | `modelProfilesAtom`, `activeProfileIdAtom` (if deleting active) |
+| **Set Active** | Update `activeProfileIdAtom` | `activeProfileIdAtom` |
+
+**Delete Behavior:**
+- If deleting the active profile, auto-select the next available profile
+- If no profiles remain, set `activeProfileIdAtom` to `null` (use default)
+- Show confirmation dialog for delete
+
+## Section 3: Integration
+
+### Existing System Integration
+
+The `activeConfigAtom` in [atoms/index.ts](../src/renderer/lib/atoms/index.ts) already handles the priority chain:
+
+```typescript
+// Priority chain:
+// 1. Auto-offline (if enabled and no internet)
+// 2. Active profile (if selected)
+// 3. Legacy single config (backwards compat)
+// 4. None (use Claude Code default)
+```
+
+**No changes needed to `activeConfigAtom`** - it already reads from `activeProfileIdAtom` and `modelProfilesAtom`.
+
+### CLI Config Auto-Detection
+
+The existing [cli-config-detected-page.tsx](../src/renderer/features/onboarding/cli-config-detected-page.tsx) detects environment variables. Extend it to offer creating a profile:
+
+**Flow:**
+1. User has `ANTHROPIC_BASE_URL` and/or `ANTHROPIC_API_KEY` in env
+2. App shows "Configuration Detected" page
+3. User clicks "Use Existing Configuration"
+4. **NEW**: Prompt "Save as a profile?" with Yes/No
+5. If Yes, open inline form with detected values pre-filled
+
+### Legacy Migration
+
+On app startup, check if `customClaudeConfigAtom` has values:
+
+```typescript
+// One-time migration
+useEffect(() => {
+ const legacyConfig = get(customClaudeConfigAtom)
+ const profiles = get(modelProfilesAtom)
+
+ // Only migrate if legacy has values and profiles is default (only offline)
+ if (normalizeCustomClaudeConfig(legacyConfig) && profiles.length === 1) {
+ const migratedProfile: ModelProfile = {
+ id: crypto.randomUUID(),
+ name: "Migrated Config",
+ config: legacyConfig,
+ }
+ set(modelProfilesAtom, [OFFLINE_PROFILE, migratedProfile])
+ set(activeProfileIdAtom, migratedProfile.id)
+ // Clear legacy
+ set(customClaudeConfigAtom, EMPTY_CONFIG)
+ }
+}, [])
+```
+
+### Quick Selector in Chat
+
+Add a dropdown in the chat header ([active-chat.tsx](../src/renderer/features/agents/main/active-chat.tsx)) for fast profile switching:
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ [New Chat] [Profile: Local Proxy ▼] [Settings ⚙] │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Implementation:**
+- Use `DropdownMenu` with `RadioGroup` for selection
+- Show active profile with checkmark
+- clicking updates `activeProfileIdAtom`
+- Profile change takes effect on next message
+
+## Files to Modify
+
+1. **[agents-models-tab.tsx](../src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx)** - Replace Override Model section with profile list
+2. **[cli-config-detected-page.tsx](../src/renderer/features/onboarding/cli-config-detected-page.tsx)** - Add "Save as profile" option
+3. **[active-chat.tsx](../src/renderer/features/agents/main/active-chat.tsx)** - Add quick profile selector to header
+4. **[atoms/index.ts](../src/renderer/lib/atoms/index.ts)** - Add migration logic (atoms already in place)
+
+## Data Types (Already Defined)
+
+```typescript
+export type ModelProfile = {
+ id: string
+ name: string
+ config: CustomClaudeConfig
+ isOffline?: boolean
+}
+
+export type CustomClaudeConfig = {
+ model: string
+ token: string
+ baseUrl: string
+}
+```
+
+## Future Enhancements (Out of Scope)
+
+- Per-chat profile selection
+- Profile import/export
+- Connection testing before saving
+- Profile templates (presets for common proxies)
diff --git a/docs/plans/2025-01-31-multiple-model-profiles-implementation.md b/docs/plans/2025-01-31-multiple-model-profiles-implementation.md
new file mode 100644
index 000000000..438680b63
--- /dev/null
+++ b/docs/plans/2025-01-31-multiple-model-profiles-implementation.md
@@ -0,0 +1,751 @@
+# Multiple Model Profiles - Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Enable users to create, manage, and switch between multiple Claude API proxy configurations (profiles) with custom base URLs, API keys, and model names.
+
+**Architecture:** Replace the single "Override Model" form with a profile list UI. Use existing `modelProfilesAtom` and `activeProfileIdAtom` for state. The `activeConfigAtom` already handles the priority chain - no changes needed there.
+
+**Tech Stack:** React 19, Jotai atoms, TypeScript, Tailwind CSS, Radix UI components, sonner toasts
+
+---
+
+## Task 1: Create ProfileRow Component
+
+**Files:**
+- Create: `src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx`
+
+**Step 1: Write the component**
+
+```tsx
+import { Check, MoreHorizontal, Pencil, Trash2 } from "lucide-react"
+import { type ModelProfile } from "../../../../lib/atoms"
+import { Badge } from "../../ui/badge"
+import { Button } from "../../ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "../../ui/dropdown-menu"
+
+interface ProfileRowProps {
+ profile: ModelProfile
+ isActive: boolean
+ onActivate: () => void
+ onEdit: () => void
+ onDelete: () => void
+}
+
+export function ModelProfileRow({
+ profile,
+ isActive,
+ onActivate,
+ onEdit,
+ onDelete,
+}: ProfileRowProps) {
+ return (
+
+
+
+
+ {profile.name}
+ {isActive && (
+
+ Active
+
+ )}
+
+
+ Model: {profile.config.model || "Not set"}
+
+
+ Proxy: {profile.config.baseUrl || "Default (api.anthropic.com)"}
+
+
+
+
+
+ {!isActive && (
+
+ Use
+
+ )}
+
+
+
+
+
+
+
+
+
+ Edit
+
+
+
+ Delete
+
+
+
+
+
+ )
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx
+git commit -m "feat: add ModelProfileRow component for profile list"
+```
+
+---
+
+## Task 2: Create ProfileForm Component (Inline Add/Edit)
+
+**Files:**
+- Create: `src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx`
+
+**Step 1: Write the component**
+
+```tsx
+import { X } from "lucide-react"
+import { useEffect, useState } from "react"
+import { type CustomClaudeConfig, type ModelProfile } from "../../../../lib/atoms"
+import { Button } from "../../ui/button"
+import { Input } from "../../ui/input"
+import { Label } from "../../ui/label"
+
+const EMPTY_CONFIG: CustomClaudeConfig = {
+ model: "",
+ token: "",
+ baseUrl: "",
+}
+
+interface ProfileFormProps {
+ mode: "add" | "edit"
+ existingProfile?: ModelProfile
+ onSave: (profile: Omit) => void
+ onCancel: () => void
+}
+
+export function ModelProfileForm({
+ mode,
+ existingProfile,
+ onSave,
+ onCancel,
+}: ProfileFormProps) {
+ const [name, setName] = useState(existingProfile?.name || "")
+ const [model, setModel] = useState(existingProfile?.config.model || "")
+ const [token, setToken] = useState(existingProfile?.config.token || "")
+ const [baseUrl, setBaseUrl] = useState(existingProfile?.config.baseUrl || "")
+
+ const isValid = name.trim() && model.trim() && token.trim() && baseUrl.trim()
+
+ const handleSave = () => {
+ if (!isValid) return
+
+ onSave({
+ name: name.trim(),
+ config: {
+ model: model.trim(),
+ token: token.trim(),
+ baseUrl: baseUrl.trim(),
+ },
+ })
+ }
+
+ return (
+
+
+
+ {mode === "add" ? "Add Profile" : "Edit Profile"}
+
+
+
+
+
+
+
+
+ )
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx
+git commit -m "feat: add ModelProfileForm component for add/edit"
+```
+
+---
+
+## Task 3: Create ModelProfilesSection Component
+
+**Files:**
+- Create: `src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx`
+
+**Step 1: Write the component**
+
+```tsx
+import { useAtom } from "jotai"
+import { Plus } from "lucide-react"
+import { useState } from "react"
+import { toast } from "sonner"
+import {
+ activeProfileIdAtom,
+ modelProfilesAtom,
+ type ModelProfile,
+} from "../../../../lib/atoms"
+import { Button } from "../../ui/button"
+import { ModelProfileForm } from "./model-profile-form"
+import { ModelProfileRow } from "./model-profile-row"
+
+type FormState = {
+ mode: "add" | "edit" | null
+ editingProfile?: ModelProfile
+}
+
+export function ModelProfilesSection() {
+ const [profiles, setProfiles] = useAtom(modelProfilesAtom)
+ const [activeProfileId, setActiveProfileId] = useAtom(activeProfileIdAtom)
+ const [formState, setFormState] = useState({ mode: null })
+
+ // Filter out offline profile from display
+ const customProfiles = profiles.filter((p) => !p.isOffline)
+
+ const handleAddProfile = (profileData: Omit) => {
+ const newProfile: ModelProfile = {
+ ...profileData,
+ id: crypto.randomUUID(),
+ }
+
+ setProfiles([...profiles, newProfile])
+
+ // If this is the first custom profile, auto-activate it
+ if (customProfiles.length === 0) {
+ setActiveProfileId(newProfile.id)
+ }
+
+ setFormState({ mode: null })
+ toast.success("Profile added")
+ }
+
+ const handleEditProfile = (profileData: Omit) => {
+ if (!formState.editingProfile) return
+
+ setProfiles(
+ profiles.map((p) =>
+ p.id === formState.editingProfile!.id
+ ? { ...profileData, id: p.id }
+ : p,
+ ),
+ )
+
+ setFormState({ mode: null })
+ toast.success("Profile updated")
+ }
+
+ const handleDeleteProfile = (profileId: string) => {
+ const confirmed = window.confirm(
+ "Are you sure you want to delete this profile?",
+ )
+ if (!confirmed) return
+
+ const newProfiles = profiles.filter((p) => p.id !== profileId)
+ setProfiles(newProfiles)
+
+ // If we deleted the active profile, clear the active ID
+ if (activeProfileId === profileId) {
+ const remainingCustom = newProfiles.filter((p) => !p.isOffline)
+ setActiveProfileId(remainingCustom[0]?.id || null)
+ }
+
+ toast.success("Profile deleted")
+ }
+
+ const handleStartEdit = (profile: ModelProfile) => {
+ setFormState({ mode: "edit", editingProfile: profile })
+ }
+
+ const handleCancelForm = () => {
+ setFormState({ mode: null })
+ }
+
+ return (
+
+ {customProfiles.length === 0 && !formState.mode ? (
+
+
+ No custom profiles yet. Add your first proxy configuration.
+
+
setFormState({ mode: "add" })}
+ >
+
+ Add Profile
+
+
+ ) : (
+ <>
+ {customProfiles.map((profile) => (
+
setActiveProfileId(profile.id)}
+ onEdit={() => handleStartEdit(profile)}
+ onDelete={() => handleDeleteProfile(profile.id)}
+ />
+ ))}
+
+ {formState.mode && (
+
+ )}
+
+ {!formState.mode && (
+
+
setFormState({ mode: "add" })}
+ >
+
+ Add Profile
+
+
+ )}
+ >
+ )}
+
+ )
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx
+git commit -m "feat: add ModelProfilesSection with CRUD operations"
+```
+
+---
+
+## Task 4: Integrate ModelProfilesSection into AgentsModelsTab
+
+**Files:**
+- Modify: `src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx:463-599`
+
+**Step 1: Replace the Override Model section with ModelProfilesSection**
+
+Find the existing "Override Model" section (around line 463) and replace it with:
+
+```tsx
+ {/* Model Profiles */}
+
+
+
+
+ Model Profiles
+
+
+ Manage your proxy configurations
+
+
+
+
+
+
+```
+
+**Step 2: Add the import at the top of the file**
+
+Add to imports section:
+```tsx
+import { ModelProfilesSection } from "./model-profiles-section"
+```
+
+**Step 3: Remove the old Override Model UI code**
+
+Delete the entire "Override Model" section div that contains:
+- The header with "Override Model" title and Reset button
+- The form with Model name, API token, and Base URL inputs
+- All associated state (model, baseUrl, token, handleBlurSave, handleReset, etc.)
+
+**Step 4: Clean up unused code**
+
+Remove these unused imports and state from the component:
+- `storedConfig, setStoredConfig, model, baseUrl, token` state variables
+- `handleBlurSave`, `handleReset` handlers
+- `savedConfigRef` ref
+- `useEffect` hooks for syncing config state
+
+**Step 5: Commit**
+
+```bash
+git add src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx
+git commit -m "feat: integrate ModelProfilesSection into settings tab"
+```
+
+---
+
+## Task 5: Add Quick Profile Selector to Chat Header
+
+**Files:**
+- Modify: `src/renderer/features/agents/main/active-chat.tsx`
+
+**Step 1: Find the chat header section**
+
+Locate the header area in active-chat.tsx that contains the chat title and controls. Look for elements like the model badge or settings button.
+
+**Step 2: Add the profile selector dropdown**
+
+Insert this component near the header controls:
+
+```tsx
+import { useAtom } from "jotai"
+import { ChevronDown } from "lucide-react"
+import { activeProfileIdAtom, modelProfilesAtom } from "../../../../lib/atoms"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "../../../components/ui/dropdown-menu"
+import { Badge } from "../../../components/ui/badge"
+
+// Inside the component, add:
+function ModelProfileSelector() {
+ const [profiles] = useAtom(modelProfilesAtom)
+ const [activeProfileId, setActiveProfileId] = useAtom(activeProfileIdAtom)
+
+ const activeProfile = profiles.find((p) => p.id === activeProfileId)
+ const customProfiles = profiles.filter((p) => !p.isOffline)
+
+ if (customProfiles.length === 0) return null
+
+ return (
+
+
+
+
+ {activeProfile ? activeProfile.name : "Default"}
+
+
+
+
+
+ setActiveProfileId(null)}>
+ Default (no profile)
+ {!activeProfileId && Active }
+
+ {customProfiles.map((profile) => (
+ setActiveProfileId(profile.id)}
+ >
+ {profile.name}
+ {activeProfileId === profile.id && (
+ Active
+ )}
+
+ ))}
+
+
+ )
+}
+```
+
+**Step 3: Place the selector in the header**
+
+Add ` ` in the header, likely next to the settings button or model indicator.
+
+**Step 4: Commit**
+
+```bash
+git add src/renderer/features/agents/main/active-chat.tsx
+git commit -m "feat: add quick profile selector to chat header"
+```
+
+---
+
+## Task 6: Add CLI Config "Save as Profile" Prompt
+
+**Files:**
+- Modify: `src/renderer/features/onboarding/cli-config-detected-page.tsx:29-37`
+
+**Step 1: Modify handleUseExistingConfig to prompt for saving**
+
+Replace the existing `handleUseExistingConfig` function with:
+
+```tsx
+ const handleUseExistingConfig = () => {
+ const cliConfigData = cliConfig
+ if (!cliConfigData) return
+
+ // Prompt user to save as profile
+ const shouldSave = window.confirm(
+ "Would you like to save this configuration as a profile for easy switching later?",
+ )
+
+ if (shouldSave) {
+ // Get profiles from atom
+ const profiles = get(modelProfilesAtom)
+ const setProfiles = set(modelProfilesAtom)
+ const setActiveProfileId = set(activeProfileIdAtom)
+
+ const newProfile: ModelProfile = {
+ id: crypto.randomUUID(),
+ name: "Detected CLI Config",
+ config: {
+ model: cliConfigData.model || "claude-3-7-sonnet-20250219",
+ token: cliConfigData.apiKey || "",
+ baseUrl: cliConfigData.baseUrl || "https://api.anthropic.com",
+ },
+ }
+
+ setProfiles([...profiles, newProfile])
+ setActiveProfileId(newProfile.id)
+ }
+
+ // Mark CLI config detected as shown
+ setCliConfigDetectedShown(true)
+ setBillingMethod("api-key")
+ setApiKeyOnboardingCompleted(true)
+ }
+```
+
+**Step 2: Add necessary imports**
+
+Add to imports:
+```tsx
+import { modelProfilesAtom, activeProfileIdAtom, type ModelProfile } from "../../lib/atoms"
+import { useSetAtom, useAtom } from "jotai"
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/renderer/features/onboarding/cli-config-detected-page.tsx
+git commit -m "feat: add 'save as profile' option to CLI config detection"
+```
+
+---
+
+## Task 7: Add Legacy Config Migration
+
+**Files:**
+- Modify: `src/renderer/lib/atoms/index.ts`
+
+**Step 1: Add migration logic after atom definitions**
+
+Add this code after the `customClaudeConfigAtom` definition (around line 271):
+
+```tsx
+// Migration: convert legacy single config to profile
+// This runs once when the module loads
+if (typeof window !== "undefined") {
+ const legacyKey = "agents:claude-custom-config"
+ const migrationFlagKey = "agents:legacy-config-migrated"
+
+ const legacyValue = localStorage.getItem(legacyKey)
+ const hasMigrated = localStorage.getItem(migrationFlagKey)
+
+ if (legacyValue && !hasMigrated) {
+ try {
+ const legacyConfig: CustomClaudeConfig = JSON.parse(legacyValue)
+
+ // Only migrate if has actual values
+ if (
+ legacyConfig.model?.trim() ||
+ legacyConfig.token?.trim() ||
+ legacyConfig.baseUrl?.trim()
+ ) {
+ const profilesKey = "agents:model-profiles"
+ const activeProfileKey = "agents:active-profile-id"
+
+ const existingProfiles = localStorage.getItem(profilesKey)
+ const profiles: ModelProfile[] = existingProfiles
+ ? JSON.parse(existingProfiles)
+ : [OFFLINE_PROFILE]
+
+ // Create migrated profile
+ const migratedProfile: ModelProfile = {
+ id: crypto.randomUUID(),
+ name: "Migrated Config",
+ config: {
+ model: legacyConfig.model || "",
+ token: legacyConfig.token || "",
+ baseUrl: legacyConfig.baseUrl || "",
+ },
+ }
+
+ // Add profile and set as active
+ const updatedProfiles = [...profiles, migratedProfile]
+ localStorage.setItem(profilesKey, JSON.stringify(updatedProfiles))
+ localStorage.setItem(activeProfileKey, JSON.stringify(migratedProfile.id))
+
+ // Clear legacy config
+ localStorage.setItem(
+ legacyKey,
+ JSON.stringify({ model: "", token: "", baseUrl: "" }),
+ )
+
+ // Mark as migrated
+ localStorage.setItem(migrationFlagKey, "true")
+ console.log("[atoms] Migrated legacy config to profile system")
+ }
+ } catch (e) {
+ console.error("[atoms] Failed to migrate legacy config:", e)
+ }
+ }
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/lib/atoms/index.ts
+git commit -m "feat: add legacy config migration to profile system"
+```
+
+---
+
+## Testing Checklist
+
+After completing all tasks, verify:
+
+1. **Settings UI:**
+ - [ ] Can add a new profile with all fields
+ - [ ] Can edit an existing profile
+ - [ ] Can delete a profile
+ - [ ] Active profile shows badge
+ - [ ] Clicking "Use" activates a profile
+ - [ ] Empty state shows "Add Profile" button
+
+2. **Chat Header:**
+ - [ ] Profile selector appears when profiles exist
+ - [ ] Can switch profiles from dropdown
+ - [ ] Active profile is highlighted
+ - [ ] "Default" option clears active profile
+
+3. **CLI Config Detection:**
+ - [ ] Prompt appears when using existing config
+ - [ ] Choosing "Yes" creates a profile
+ - [ ] Profile is auto-activated
+
+4. **Legacy Migration:**
+ - [ ] Existing `customClaudeConfigAtom` values migrate to profile
+ - [ ] Migrated profile is set as active
+ - [ ] Legacy config is cleared after migration
+
+5. **Integration:**
+ - [ ] Active profile config is used by `activeConfigAtom`
+ - [ ] Switching profiles affects next message
+ - [ ] Offline profile still works for fallback
+
+---
+
+## Files Modified Summary
+
+| File | Action |
+|------|--------|
+| `src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx` | Create |
+| `src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx` | Create |
+| `src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx` | Create |
+| `src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx` | Modify |
+| `src/renderer/features/agents/main/active-chat.tsx` | Modify |
+| `src/renderer/features/onboarding/cli-config-detected-page.tsx` | Modify |
+| `src/renderer/lib/atoms/index.ts` | Modify |
+
+---
+
+## Related Docs
+
+- Design: [docs/plans/2025-01-31-multiple-model-profiles-design.md](./2025-01-31-multiple-model-profiles-design.md)
+- Atoms reference: `src/renderer/lib/atoms/index.ts:222-368`
diff --git a/docs/plans/2025-01-31-per-workspace-model-profiles-design.md b/docs/plans/2025-01-31-per-workspace-model-profiles-design.md
new file mode 100644
index 000000000..ed80c85ed
--- /dev/null
+++ b/docs/plans/2025-01-31-per-workspace-model-profiles-design.md
@@ -0,0 +1,650 @@
+# Per-Workspace Model Profiles
+
+**Date:** 2025-01-31
+**Status:** Approved
+**Author:** AI Assistant
+
+## Overview
+
+Currently, model profiles are stored globally in localStorage. All chats share the same active profile. This design changes model profiles to be **per-chat (workspace)**, allowing each workspace to use a different model profile.
+
+## Goals
+
+1. Each chat/workspace can independently select a model profile
+2. Each profile contains a list of available models with display names
+3. Model selector shows `DisplayName (modelId)` format (e.g., "Opus (glm-4.7)")
+4. New chats inherit the last-used profile
+5. Profile can be changed from chat input area and right-click context menu
+
+## Non-Goals
+
+- Per-project profiles (may add later as enhancement)
+- Syncing profiles across devices
+- Profile import/export
+
+---
+
+## Data Model Changes
+
+### 1. Extend `ModelProfile` Type
+
+**File:** `src/renderer/lib/atoms/index.ts`
+
+```typescript
+// NEW: Model mapping within a profile
+export type ModelMapping = {
+ id: string // Internal reference ID (e.g., "opus", "sonnet", "haiku")
+ displayName: string // User-facing name (e.g., "Opus", "Sonnet 3.5")
+ modelId: string // Actual model ID sent to API (e.g., "glm-4.7", "claude-3-opus-20240229")
+ supportsThinking?: boolean // Whether this model supports extended thinking
+}
+
+// UPDATED: Add models array to profile
+export type ModelProfile = {
+ id: string
+ name: string // Profile name (e.g., "My OpenRouter Proxy")
+ config: CustomClaudeConfig // Base URL, token, default model
+ models: ModelMapping[] // NEW: Available models for this profile
+ isOffline?: boolean // Mark as offline/Ollama profile
+}
+```
+
+### 2. Database Schema Changes
+
+**File:** `src/main/lib/db/schema/index.ts`
+
+Add two new columns to `chats` table:
+
+```typescript
+export const chats = sqliteTable("chats", {
+ // ... existing fields ...
+
+ // NEW: Model profile reference
+ // References profile ID from localStorage modelProfilesAtom
+ // null = use lastUsedProfileId (global default)
+ modelProfileId: text("model_profile_id"),
+
+ // NEW: Selected model within the profile
+ // References ModelMapping.id from the profile's models array
+ // null = use first model in profile
+ selectedModelId: text("selected_model_id"),
+})
+```
+
+### 3. Migration
+
+**File:** `drizzle/XXXX_add_model_profile_to_chats.sql`
+
+```sql
+ALTER TABLE chats ADD COLUMN model_profile_id TEXT;
+ALTER TABLE chats ADD COLUMN selected_model_id TEXT;
+```
+
+### 4. Rename Global Atom
+
+**File:** `src/renderer/lib/atoms/index.ts`
+
+```typescript
+// RENAME: activeProfileIdAtom → lastUsedProfileIdAtom
+// This now serves as:
+// 1. Default for new chats
+// 2. Fallback for chats with null modelProfileId
+// 3. Updated whenever user changes profile on any chat
+
+export const lastUsedProfileIdAtom = atomWithStorage(
+ "agents:last-used-profile-id", // New storage key
+ null,
+ undefined,
+ { getOnInit: true },
+)
+
+// Migration: read old key, write to new key, delete old
+if (typeof window !== "undefined") {
+ const oldKey = "agents:active-profile-id"
+ const newKey = "agents:last-used-profile-id"
+ const oldValue = localStorage.getItem(oldKey)
+ if (oldValue !== null && localStorage.getItem(newKey) === null) {
+ localStorage.setItem(newKey, oldValue)
+ localStorage.removeItem(oldKey)
+ }
+}
+```
+
+### 5. Type Exports
+
+**File:** `src/main/lib/db/schema/index.ts`
+
+```typescript
+// Update Chat type to include new fields
+export type Chat = typeof chats.$inferSelect
+// Chat now includes: modelProfileId: string | null, selectedModelId: string | null
+```
+
+---
+
+## UI Components
+
+### 1. Profile Selector (New Component)
+
+**File:** `src/renderer/features/agents/ui/profile-selector.tsx`
+
+A dropdown component for selecting model profile.
+
+```typescript
+interface ProfileSelectorProps {
+ chatId: string
+ currentProfileId: string | null // null = using default
+ onProfileChange: (profileId: string) => void
+}
+```
+
+**Behavior:**
+- Displays current profile name (or "Default" if null)
+- Dropdown shows all profiles from `modelProfilesAtom`
+- Checkmark on currently selected profile
+- On select: calls `onProfileChange` which updates DB and atoms
+
+**Visual Design:**
+```
+┌──────────────────┐
+│ My Proxy ▼ │ ← Compact button with dropdown arrow
+└──────────────────┘
+
+Dropdown open:
+┌──────────────────────┐
+│ ✓ My Proxy │ ← Current selection
+│ OpenRouter │
+│ Local Ollama │
+│ ─────────────────── │
+│ Manage Profiles... │ ← Opens settings
+└──────────────────────┘
+```
+
+### 2. Updated Model Selector
+
+**File:** `src/renderer/features/agents/main/chat-input-area.tsx`
+
+Update existing model dropdown to:
+1. Source models from current profile's `models` array (not global `CLAUDE_MODELS`)
+2. Display format: `DisplayName (modelId)` (e.g., "Opus (glm-4.7)")
+3. Store selection in `chat.selectedModelId`
+
+**Visual Design:**
+```
+┌─────────────────────────┐
+│ ❈ Opus (glm-4.7) ▼ │
+└─────────────────────────┘
+
+Dropdown open:
+┌─────────────────────────────┐
+│ ✓ Opus (glm-4.7) │
+│ Sonnet (claude-3-sonnet) │
+│ Haiku (claude-3-haiku) │
+└─────────────────────────────┘
+```
+
+### 3. Chat Input Area Layout
+
+**File:** `src/renderer/features/agents/main/chat-input-area.tsx`
+
+Update `PromptInputActions` layout:
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Plan, @ for context, / for commands │
+├─────────────────────────────────────────────────────────────────┤
+│ [My Proxy ▼] ❈ Opus (glm-4.7) ▼ 🧠 Thinking 📍 │ ○ 📎 ⬆ │
+│ └─ NEW ──┘ └──── UPDATED ────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+Component order (left to right):
+1. **Profile Selector** (NEW) - dropdown to select profile
+2. **Model Selector** (UPDATED) - shows models from selected profile
+3. **Thinking Toggle** (existing)
+4. **Mode Toggle** (existing)
+5. **Context Indicator** (existing)
+6. **Attach Button** (existing)
+7. **Send Button** (existing)
+
+### 4. Chat Right-Click Context Menu
+
+**File:** `src/renderer/features/sidebar/components/chat-item.tsx` (or equivalent)
+
+Add "Model Profile" submenu to existing context menu:
+
+```
+┌─────────────────────────┐
+│ Rename │
+│ Archive │
+│ ─────────────────────── │
+│ Model Profile ▸ │ ← NEW submenu
+│ ─────────────────────── │
+│ Delete │
+└─────────────────────────┘
+
+Submenu:
+┌─────────────────────────┐
+│ ✓ My Proxy │
+│ OpenRouter │
+│ Local Ollama │
+│ ─────────────────────── │
+│ Manage Profiles... │
+└─────────────────────────┘
+```
+
+### 5. Model Profile Form Update
+
+**File:** `src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx`
+
+Add models configuration to profile form:
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Profile Name: [My OpenRouter Proxy ] │
+│ Base URL: [https://api.openrouter.ai ] │
+│ API Token: [sk-or-... ] │
+│ │
+│ ─────────────── Models ─────────────────────────────── │
+│ │
+│ ┌─────────────────────────────────────────────────────┐ │
+│ │ Display Name Model ID Thinking [×] │ │
+│ │ [Opus ] [glm-4.7 ] [✓] │ │
+│ │ [Sonnet ] [claude-3-sonnet ] [✓] │ │
+│ │ [Haiku ] [claude-3-haiku ] [ ] │ │
+│ └─────────────────────────────────────────────────────┘ │
+│ │
+│ [+ Add Model] │
+│ │
+│ [Cancel] [Save Profile] │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## State Management
+
+### 1. New Atoms/Hooks
+
+**File:** `src/renderer/features/agents/atoms/index.ts`
+
+```typescript
+// Get effective profile ID for a chat (with fallback)
+export function useEffectiveProfileId(chat: Chat | null): string | null {
+ const lastUsedProfileId = useAtomValue(lastUsedProfileIdAtom)
+ return chat?.modelProfileId ?? lastUsedProfileId
+}
+
+// Get effective model ID for a chat (with fallback)
+export function useEffectiveModelId(
+ chat: Chat | null,
+ profile: ModelProfile | null
+): string | null {
+ if (!profile) return null
+ const chatModelId = chat?.selectedModelId
+ // If chat has a model selected and it exists in profile, use it
+ if (chatModelId && profile.models.some(m => m.id === chatModelId)) {
+ return chatModelId
+ }
+ // Otherwise use first model in profile
+ return profile.models[0]?.id ?? null
+}
+
+// Get full model info for display
+export function useCurrentModel(
+ chat: Chat | null,
+ profiles: ModelProfile[]
+): { profile: ModelProfile | null; model: ModelMapping | null } {
+ const effectiveProfileId = useEffectiveProfileId(chat)
+ const profile = profiles.find(p => p.id === effectiveProfileId) ?? null
+ const effectiveModelId = useEffectiveModelId(chat, profile)
+ const model = profile?.models.find(m => m.id === effectiveModelId) ?? null
+ return { profile, model }
+}
+```
+
+### 2. tRPC Router Updates
+
+**File:** `src/main/lib/trpc/routers/chats.ts`
+
+```typescript
+// Update chat's model profile
+updateModelProfile: publicProcedure
+ .input(z.object({
+ chatId: z.string(),
+ modelProfileId: z.string().nullable(),
+ }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+ db.update(chats)
+ .set({
+ modelProfileId: input.modelProfileId,
+ updatedAt: new Date(),
+ })
+ .where(eq(chats.id, input.chatId))
+ .run()
+ return { success: true }
+ }),
+
+// Update chat's selected model
+updateSelectedModel: publicProcedure
+ .input(z.object({
+ chatId: z.string(),
+ selectedModelId: z.string().nullable(),
+ }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+ db.update(chats)
+ .set({
+ selectedModelId: input.selectedModelId,
+ updatedAt: new Date(),
+ })
+ .where(eq(chats.id, input.chatId))
+ .run()
+ return { success: true }
+ }),
+```
+
+### 3. Chat Creation Update
+
+**File:** `src/main/lib/trpc/routers/chats.ts`
+
+Update `create` procedure to accept optional profile:
+
+```typescript
+create: publicProcedure
+ .input(z.object({
+ projectId: z.string(),
+ name: z.string().optional(),
+ modelProfileId: z.string().nullable().optional(), // NEW
+ selectedModelId: z.string().nullable().optional(), // NEW
+ }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+ const [chat] = db.insert(chats).values({
+ projectId: input.projectId,
+ name: input.name,
+ modelProfileId: input.modelProfileId ?? null, // NEW
+ selectedModelId: input.selectedModelId ?? null, // NEW
+ }).returning().all()
+ return chat
+ }),
+```
+
+---
+
+## Behavior Specifications
+
+### 1. Profile Change Flow
+
+When user changes profile (from input area or context menu):
+
+```
+1. User selects new profile from dropdown
+2. Call trpc.chats.updateModelProfile({ chatId, modelProfileId: newProfileId })
+3. Update lastUsedProfileIdAtom (so new chats inherit this)
+4. Model dropdown reloads with new profile's models array
+5. Auto-select first model from new profile (or try to match by displayName)
+6. If model changed, call trpc.chats.updateSelectedModel({ chatId, selectedModelId })
+7. UI updates immediately (optimistic)
+```
+
+### 2. Model Change Flow
+
+When user changes model within current profile:
+
+```
+1. User selects new model from dropdown
+2. Call trpc.chats.updateSelectedModel({ chatId, selectedModelId: newModelId })
+3. UI updates immediately (optimistic)
+4. Next message uses new model
+```
+
+### 3. New Chat Creation Flow
+
+When user creates a new chat:
+
+```
+1. Get lastUsedProfileId from atom
+2. Get lastUsedModelId from profile (could store in atom too)
+3. Create chat with:
+ - modelProfileId: lastUsedProfileId
+ - selectedModelId: lastUsedModelId (or first model in profile)
+4. Chat immediately shows correct profile and model
+```
+
+### 4. Sending Message Flow
+
+When sending a message to Claude:
+
+```typescript
+// In claude.ts router
+const chat = await db.select().from(chats).where(eq(chats.id, chatId)).get()
+const profiles = /* get from IPC or passed in */
+
+// Get effective profile
+const effectiveProfileId = chat.modelProfileId ?? lastUsedProfileId
+const profile = profiles.find(p => p.id === effectiveProfileId)
+
+if (!profile) {
+ // No profile configured - use Claude Code default (OAuth)
+ return await sendWithDefaultAuth(message)
+}
+
+// Get effective model
+const effectiveModelId = chat.selectedModelId ?? profile.models[0]?.id
+const model = profile.models.find(m => m.id === effectiveModelId)
+
+// Build config for Claude SDK
+const config = {
+ baseUrl: profile.config.baseUrl,
+ apiKey: profile.config.token,
+ model: model?.modelId ?? profile.config.model,
+ // Extended thinking support
+ supportsThinking: model?.supportsThinking ?? true,
+}
+
+return await sendWithCustomConfig(message, config)
+```
+
+### 5. Fallback Chain
+
+When resolving which profile/model to use:
+
+```
+Profile Resolution:
+1. chat.modelProfileId (if set and profile exists)
+2. lastUsedProfileIdAtom (if set and profile exists)
+3. First non-offline profile in modelProfilesAtom
+4. null (use Claude Code OAuth default)
+
+Model Resolution (within profile):
+1. chat.selectedModelId (if set and model exists in profile)
+2. First model in profile.models array
+3. profile.config.model (legacy fallback)
+```
+
+---
+
+## Migration Strategy
+
+### 1. Existing Profiles
+
+Existing profiles in localStorage don't have `models` array. Add migration:
+
+```typescript
+// In atoms/index.ts or a migration hook
+const migrateProfiles = (profiles: ModelProfile[]): ModelProfile[] => {
+ return profiles.map(profile => {
+ if (profile.models && profile.models.length > 0) {
+ return profile // Already migrated
+ }
+
+ // Generate default models from legacy config
+ const defaultModel: ModelMapping = {
+ id: 'default',
+ displayName: 'Default',
+ modelId: profile.config.model || 'claude-3-opus',
+ supportsThinking: true,
+ }
+
+ return {
+ ...profile,
+ models: [defaultModel],
+ }
+ })
+}
+```
+
+### 2. Existing Chats
+
+Existing chats have `modelProfileId: null` and `selectedModelId: null`. This is fine - they'll use the fallback chain (lastUsedProfileId → first profile → default).
+
+### 3. LocalStorage Key Rename
+
+Migrate `activeProfileIdAtom` to `lastUsedProfileIdAtom`:
+
+```typescript
+// Run once on app start
+if (typeof window !== "undefined") {
+ const oldKey = "agents:active-profile-id"
+ const newKey = "agents:last-used-profile-id"
+ const oldValue = localStorage.getItem(oldKey)
+ if (oldValue !== null && localStorage.getItem(newKey) === null) {
+ localStorage.setItem(newKey, oldValue)
+ localStorage.removeItem(oldKey)
+ }
+}
+```
+
+---
+
+## Implementation Tasks
+
+### Phase 1: Data Model (Backend)
+
+- [ ] **1.1** Add `ModelMapping` type to atoms
+- [ ] **1.2** Update `ModelProfile` type to include `models: ModelMapping[]`
+- [ ] **1.3** Add migration for existing profiles (add default models array)
+- [ ] **1.4** Add `modelProfileId` and `selectedModelId` columns to chats schema
+- [ ] **1.5** Create Drizzle migration file
+- [ ] **1.6** Rename `activeProfileIdAtom` → `lastUsedProfileIdAtom` with migration
+- [ ] **1.7** Update Chat type exports
+
+### Phase 2: tRPC API
+
+- [ ] **2.1** Add `chats.updateModelProfile` mutation
+- [ ] **2.2** Add `chats.updateSelectedModel` mutation
+- [ ] **2.3** Update `chats.create` to accept modelProfileId and selectedModelId
+- [ ] **2.4** Update `chats.getById` to return new fields
+
+### Phase 3: UI Components
+
+- [ ] **3.1** Create `ProfileSelector` component
+- [ ] **3.2** Update model selector to show `DisplayName (modelId)` format
+- [ ] **3.3** Update model selector to source from profile's models array
+- [ ] **3.4** Integrate ProfileSelector into ChatInputArea (left of model selector)
+- [ ] **3.5** Add "Model Profile" submenu to chat context menu
+- [ ] **3.6** Update ModelProfileForm to include models configuration
+
+### Phase 4: State & Logic
+
+- [ ] **4.1** Create `useEffectiveProfileId` hook
+- [ ] **4.2** Create `useEffectiveModelId` hook
+- [ ] **4.3** Create `useCurrentModel` hook
+- [ ] **4.4** Update ChatInputArea to use new hooks
+- [ ] **4.5** Update profile change handler (update DB + lastUsedProfileIdAtom)
+- [ ] **4.6** Update model change handler (update DB)
+- [ ] **4.7** Update new chat creation to inherit from lastUsedProfileIdAtom
+
+### Phase 5: Claude SDK Integration
+
+- [ ] **5.1** Update `claude.ts` router to resolve effective profile
+- [ ] **5.2** Update message sending to use profile's model config
+- [ ] **5.3** Pass `supportsThinking` from model to SDK
+
+### Phase 6: Testing
+
+- [ ] **6.1** Test profile switching in chat input area
+- [ ] **6.2** Test profile switching via context menu
+- [ ] **6.3** Test model switching within profile
+- [ ] **6.4** Test new chat inherits correct profile
+- [ ] **6.5** Test fallback when profile is deleted
+- [ ] **6.6** Test migration of existing profiles
+- [ ] **6.7** Test migration of existing chats
+
+---
+
+## File Changes Summary
+
+| File | Change Type | Description |
+|------|-------------|-------------|
+| `src/renderer/lib/atoms/index.ts` | Modify | Add ModelMapping type, update ModelProfile, rename atom |
+| `src/main/lib/db/schema/index.ts` | Modify | Add modelProfileId, selectedModelId to chats |
+| `drizzle/XXXX_add_model_profile.sql` | Create | Migration for new columns |
+| `src/main/lib/trpc/routers/chats.ts` | Modify | Add mutations, update create |
+| `src/renderer/features/agents/ui/profile-selector.tsx` | Create | New profile selector component |
+| `src/renderer/features/agents/main/chat-input-area.tsx` | Modify | Add ProfileSelector, update model display |
+| `src/renderer/features/agents/main/new-chat-form.tsx` | Modify | Pass profile to chat creation |
+| `src/renderer/features/sidebar/components/chat-item.tsx` | Modify | Add profile submenu to context menu |
+| `src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx` | Modify | Add models configuration UI |
+| `src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx` | Modify | Update to use new profile structure |
+| `src/main/lib/trpc/routers/claude.ts` | Modify | Resolve effective profile for messages |
+| `src/renderer/features/agents/atoms/index.ts` | Modify | Add hooks for profile/model resolution |
+
+---
+
+## Risks & Mitigations
+
+| Risk | Impact | Mitigation |
+|------|--------|------------|
+| Existing profiles break | High | Add migration to add default models array |
+| Performance with many profiles | Low | Profiles stored in localStorage, fast lookup |
+| Profile deleted while in use | Medium | Fallback chain handles gracefully |
+| Model ID mismatch after profile edit | Medium | Re-select first model if current not found |
+
+---
+
+## Future Enhancements
+
+1. **Per-project default profile** - Projects could have a default profile that new chats inherit
+2. **Profile sync** - Sync profiles across devices via cloud storage
+3. **Profile import/export** - Share profile configs as JSON files
+4. **Model usage stats** - Track which models are used most per profile
+
+---
+
+## Appendix: Example Profile Data
+
+```json
+{
+ "id": "abc123",
+ "name": "My OpenRouter Proxy",
+ "config": {
+ "model": "glm-4.7",
+ "token": "sk-or-v1-xxxx",
+ "baseUrl": "https://openrouter.ai/api/v1"
+ },
+ "models": [
+ {
+ "id": "opus",
+ "displayName": "Opus",
+ "modelId": "glm-4.7",
+ "supportsThinking": true
+ },
+ {
+ "id": "sonnet",
+ "displayName": "Sonnet",
+ "modelId": "claude-3-sonnet-20240229",
+ "supportsThinking": true
+ },
+ {
+ "id": "haiku",
+ "displayName": "Haiku",
+ "modelId": "claude-3-haiku-20240307",
+ "supportsThinking": false
+ }
+ ],
+ "isOffline": false
+}
+```
diff --git a/docs/plans/2025-01-31-unified-model-selector.md b/docs/plans/2025-01-31-unified-model-selector.md
new file mode 100644
index 000000000..2dcd74f7c
--- /dev/null
+++ b/docs/plans/2025-01-31-unified-model-selector.md
@@ -0,0 +1,86 @@
+# Unified Model Selector with Custom Profile Support
+
+## Problem
+When a custom model profile is active, the model selector in the chat input is disabled and shows "Custom Model". Users cannot switch between Default/Opus/Sonnet/Haiku options.
+
+## Goal
+Keep the model selector (Default, Opus, Sonnet, Haiku) always enabled. When a custom profile is active, these selections map to the profile's configured models:
+
+| UI Selection | Without Profile | With Profile |
+|--------------|-----------------|--------------|
+| Default | claude-3-7-sonnet | profile.model (ANTHROPIC_MODEL) |
+| Opus | opus | profile.defaultOpusModel |
+| Sonnet | sonnet | profile.defaultSonnetModel |
+| Haiku | haiku | profile.defaultHaikuModel |
+
+## Implementation
+
+### 1. Update `chat-input-area.tsx`
+
+**File:** `src/renderer/features/agents/main/chat-input-area.tsx`
+
+Changes:
+- Remove `disabled={hasCustomClaudeConfig}` from the model dropdown (line 1370)
+- Remove condition that forces dropdown closed: `open={hasCustomClaudeConfig ? false : isModelDropdownOpen}` → `open={isModelDropdownOpen}` (line 1361)
+- Add "Default" option to the model list
+- Show profile name in parentheses when profile is active (e.g., "Sonnet (Z.AI Proxy)")
+
+### 2. Update `models.ts`
+
+**File:** `src/renderer/features/agents/lib/models.ts`
+
+Add "default" model option:
+```typescript
+export const CLAUDE_MODELS = [
+ { id: "default", name: "Default" },
+ { id: "opus", name: "Opus" },
+ { id: "sonnet", name: "Sonnet" },
+ { id: "haiku", name: "Haiku" },
+]
+```
+
+### 3. Update `atoms/index.ts` (agents feature)
+
+**File:** `src/renderer/features/agents/atoms/index.ts`
+
+Update MODEL_ID_MAP to include "default":
+```typescript
+export const MODEL_ID_MAP: Record = {
+ default: "default", // Will use profile's main model
+ opus: "opus",
+ sonnet: "sonnet",
+ haiku: "haiku",
+}
+```
+
+### 4. Backend already supports this
+
+The tRPC schema in `claude.ts` already accepts:
+- `model: z.string().optional()` - the selected model (opus/sonnet/haiku/default)
+- `customConfig.defaultOpusModel`, `customConfig.defaultSonnetModel`, `customConfig.defaultHaikuModel`
+
+The Claude SDK uses these as environment variables:
+- ANTHROPIC_MODEL
+- ANTHROPIC_DEFAULT_OPUS_MODEL
+- ANTHROPIC_DEFAULT_SONNET_MODEL
+- ANTHROPIC_DEFAULT_HAIKU_MODEL
+
+## Files to Modify
+
+1. `src/renderer/features/agents/main/chat-input-area.tsx` - Enable dropdown, add Default option
+2. `src/renderer/features/agents/lib/models.ts` - Add "default" to CLAUDE_MODELS
+3. `src/renderer/features/agents/atoms/index.ts` - Add "default" to MODEL_ID_MAP
+
+## Testing
+
+1. Create a custom profile with:
+ - Model: glm-4.7
+ - Default Opus Model: glm-4.7
+ - Default Sonnet Model: glm-4.7
+ - Default Haiku Model: glm-4.5-air
+
+2. Set profile as active
+
+3. Verify model selector shows Default/Opus/Sonnet/Haiku options
+
+4. Select each option and verify the correct model is used in the request
diff --git a/docs/plans/2025-02-02-websocket-chat-relay.md b/docs/plans/2025-02-02-websocket-chat-relay.md
new file mode 100644
index 000000000..87878b4dc
--- /dev/null
+++ b/docs/plans/2025-02-02-websocket-chat-relay.md
@@ -0,0 +1,1068 @@
+# WebSocket Chat Relay Implementation Plan (Push-Based Sync)
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Simplify web-desktop communication with push-based data sync. Web client subscribes to resources, server pushes data proactively.
+
+**Architecture:** Server pushes data on subscription events - no polling, no direct tRPC queries from web.
+
+**Tech Stack:** WebSocket (ws), tRPC, Electron IPC, Observable pattern
+
+---
+
+## Target Flow (Push-Based)
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ AUTH SUCCESS │
+├─────────────────────────────────────────────────────────────┤
+│ Web → Server: auth with PIN │
+│ Server → Web: auth_success + { projects, modelProfiles } │
+└─────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────┐
+│ SUBSCRIBE TO PROJECT │
+├─────────────────────────────────────────────────────────────┤
+│ Web → Server: subscribe(projectId) │
+│ Server → Web: subscribed + { chats: [] } │
+│ Server → Web: (push) new chat created │
+└─────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────┐
+│ SUBSCRIBE TO CHAT │
+├─────────────────────────────────────────────────────────────┤
+│ Web → Server: subscribe(chatId) │
+│ Server → Web: subscribed + { messages: [] } │
+│ Server → Web: (push) new message from Claude │
+└─────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────┐
+│ SEND MESSAGE │
+├─────────────────────────────────────────────────────────────┤
+│ Web → Server: send(chatId, message) │
+│ Server → Desktop: claude.sendMessage() │
+│ Server → Web: (push via subscription) response │
+└─────────────────────────────────────────────────────────────┘
+```
+
+**Benefits:**
+- Single channel for all communication
+- Server pushes data proactively (no polling)
+- Desktop controls everything (security)
+- Simpler client code
+- Real-time sync guaranteed
+
+---
+
+## Task 1: Define New Message Protocol
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Define message types**
+
+```typescript
+// Resource types for subscription
+type ResourceType = "project" | "chat" | "modelProfiles"
+
+interface WSRequest {
+ id: string
+ type: "auth" | "send" | "subscribe" | "unsubscribe"
+ resource?: ResourceType // What to subscribe to
+ resourceId?: string // ID of the resource (projectId/chatId)
+ message?: any // For send (chat message content)
+ pin?: string // For auth
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "subscribed" | "error" | "data"
+ resource?: ResourceType // Which resource this data is for
+ resourceId?: string // ID of the resource
+ data?: unknown
+ error?: string
+}
+```
+
+**Step 2: Add resource subscription tracking**
+
+```typescript
+// Track subscriptions per client
+interface ClientSubscription {
+ resource: ResourceType
+ resourceId: string
+ ws: WebSocket
+}
+
+interface AuthenticatedClient {
+ ws: WebSocket
+ id: string
+ subscriptions: Map // key: `${resource}:${resourceId}`
+}
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "refactor: define new WebSocket message protocol"
+```
+
+---
+
+## Task 2: Auth Success - Push Initial Data
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Fetch projects and model profiles on auth**
+
+```typescript
+// In handleMessage(), auth success section
+if (message.pin && validatePin(message.pin)) {
+ const clientId = randomUUID()
+ const newClient: AuthenticatedClient = {
+ ws,
+ id: clientId,
+ subscriptions: new Map(),
+ }
+ authenticatedClients.set(ws, newClient)
+ addClient(clientId)
+
+ // Fetch initial data
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+ const caller = router.createCaller({
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ })
+
+ // Get projects and model profiles in parallel
+ const [projects, modelProfiles] = await Promise.all([
+ caller.projects.list(),
+ caller.modelProfiles.list().catch(() => []),
+ ])
+
+ send(ws, {
+ id: message.id,
+ type: "auth_success",
+ data: {
+ projects,
+ modelProfiles,
+ },
+ })
+
+ console.log(`[WS] Client authenticated: ${clientId}, sent ${projects.length} projects`)
+ return
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat: send projects and model profiles on auth"
+```
+
+---
+
+## Task 3: Subscribe to Project - Push Chat List
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Add project subscription handler**
+
+```typescript
+// Subscribe to project (get chats)
+if (message.type === "subscribe" && message.resource === "project" && message.resourceId) {
+ const projectId = message.resourceId
+ const client = authenticatedClients.get(ws)
+
+ if (!client) {
+ send(ws, { id: message.id, type: "error", error: "Not authenticated" })
+ return
+ }
+
+ console.log(`[WS] Subscribing to project: ${projectId}`)
+
+ // Fetch chats for this project
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+ const caller = router.createCaller({
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ })
+
+ const chats = await caller.chats.list({ projectId })
+
+ // Store subscription
+ const subKey = `project:${projectId}`
+ client.subscriptions.set(subKey, {
+ resource: "project",
+ resourceId: projectId,
+ ws,
+ })
+
+ // Send initial data
+ send(ws, {
+ id: message.id,
+ type: "subscribed",
+ resource: "project",
+ resourceId: projectId,
+ data: { chats },
+ })
+
+ console.log(`[WS] Subscribed to project ${projectId}, sent ${chats.length} chats`)
+ return
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat: add project subscription with chat list"
+```
+
+---
+
+## Task 4: Subscribe to Chat - Push Message List + Stream Updates
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Add chat subscription handler with message history**
+
+```typescript
+// Subscribe to chat (get messages + stream)
+if (message.type === "subscribe" && message.resource === "chat" && message.resourceId) {
+ const subChatId = message.resourceId
+ const client = authenticatedClients.get(ws)
+
+ if (!client) {
+ send(ws, { id: message.id, type: "error", error: "Not authenticated" })
+ return
+ }
+
+ console.log(`[WS] Subscribing to chat: ${subChatId}`)
+
+ // Check if shared subscription exists
+ let sharedSub = sharedChatSubscriptions.get(subChatId)
+
+ if (!sharedSub) {
+ // Create new shared subscription
+ console.log(`[WS] Creating new shared subscription for ${subChatId}`)
+
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+ const claudeRouter = (router as any).claude
+ const chatProcedure = claudeRouter.chat
+ const subscriptionFn = chatProcedure._def.resolver
+
+ const ctx = {
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ }
+
+ const observable = subscriptionFn({
+ input: { subChatId },
+ type: 'subscription',
+ ctx,
+ path: 'claude.chat',
+ })
+
+ if (typeof observable?.subscribe !== 'function') {
+ throw new Error(`chat subscription did not return Observable`)
+ }
+
+ // Subscribe and broadcast
+ const subscription = observable.subscribe({
+ next: (data: any) => {
+ if (sharedSub) {
+ console.log(`[WS] Broadcasting to ${sharedSub.clients.size} clients for ${subChatId}:`, data.type)
+ for (const [clientWs] of sharedSub.clients) {
+ send(clientWs, {
+ id: null, // Streaming data doesn't have request ID
+ type: "data",
+ resource: "chat",
+ resourceId: subChatId,
+ data: data,
+ })
+ }
+ // Also notify IPC listeners (desktop)
+ for (const listener of sharedSub.listeners) {
+ try {
+ listener(data)
+ } catch (err) {
+ console.error(`[WS] Error in IPC listener:`, err)
+ }
+ }
+ }
+ },
+ error: (err: any) => {
+ console.error(`[WS] Chat subscription error:`, err)
+ if (sharedSub) {
+ for (const [clientWs] of sharedSub.clients) {
+ send(clientWs, {
+ id: null,
+ type: "error",
+ resource: "chat",
+ resourceId: subChatId,
+ error: err.message,
+ })
+ }
+ }
+ },
+ complete: () => {
+ console.log(`[WS] Chat subscription complete for ${subChatId}`)
+ if (sharedSub) {
+ for (const [clientWs] of sharedSub.clients) {
+ send(clientWs, {
+ id: null,
+ type: "data",
+ resource: "chat",
+ resourceId: subChatId,
+ data: { completed: true },
+ })
+ }
+ }
+ }
+ })
+
+ sharedSub = {
+ observable,
+ subscription,
+ clients: new Map(),
+ listeners: sharedChatSubscriptions.get(subChatId)?.listeners || new Set(),
+ }
+ sharedChatSubscriptions.set(subChatId, sharedSub)
+ }
+
+ // Add client to shared subscription
+ sharedSub.clients.set(ws, message.id)
+
+ // Store subscription in client
+ const subKey = `chat:${subChatId}`
+ client.subscriptions.set(subKey, {
+ resource: "chat",
+ resourceId: subChatId,
+ ws,
+ })
+
+ // Send subscribed confirmation (messages will come via stream)
+ send(ws, {
+ id: message.id,
+ type: "subscribed",
+ resource: "chat",
+ resourceId: subChatId,
+ })
+
+ // Clean up on disconnect
+ ws.on("close", () => {
+ if (sharedSub) {
+ sharedSub.clients.delete(ws)
+ if (sharedSub.clients.size === 0 && sharedSub.listeners.size === 0) {
+ sharedSub.subscription.unsubscribe()
+ sharedChatSubscriptions.delete(subChatId)
+ }
+ }
+ })
+
+ console.log(`[WS] Subscribed to chat ${subChatId}`)
+ return
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat: add chat subscription with message streaming"
+```
+
+---
+
+## Task 5: Send Message Handler
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Add send message handler**
+
+```typescript
+// Send chat message
+if (message.type === "send" && message.resourceId && message.message) {
+ const subChatId = message.resourceId
+ const client = authenticatedClients.get(ws)
+
+ if (!client) {
+ send(ws, { id: message.id, type: "error", error: "Not authenticated" })
+ return
+ }
+
+ try {
+ console.log(`[WS] Sending message to chat ${subChatId}`)
+
+ // Forward to tRPC via desktop's router
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+ const caller = router.createCaller({
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ })
+
+ // Call claude.sendMessage
+ await caller.claude.sendMessage({
+ subChatId,
+ message: message.message,
+ })
+
+ // Send success (actual response will come through subscription)
+ send(ws, {
+ id: message.id,
+ type: "data",
+ resource: "chat",
+ resourceId: subChatId,
+ data: { sent: true },
+ })
+
+ return
+ } catch (error) {
+ send(ws, {
+ id: message.id,
+ type: "error",
+ error: error instanceof Error ? error.message : "Failed to send message",
+ })
+ return
+ }
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat: add send message handler"
+```
+
+---
+
+## Task 6: Unsubscribe Handler
+
+**Files:**
+- Modify: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Add unsubscribe handler**
+
+```typescript
+// Unsubscribe from resource
+if (message.type === "unsubscribe" && message.resource && message.resourceId) {
+ const { resource, resourceId } = message
+ const client = authenticatedClients.get(ws)
+
+ if (!client) {
+ send(ws, { id: message.id, type: "error", error: "Not authenticated" })
+ return
+ }
+
+ const subKey = `${resource}:${resourceId}`
+ const subscription = client.subscriptions.get(subKey)
+
+ if (subscription) {
+ client.subscriptions.delete(subKey)
+
+ // If chat, remove from shared subscription
+ if (resource === "chat") {
+ const sharedSub = sharedChatSubscriptions.get(resourceId)
+ if (sharedSub) {
+ sharedSub.clients.delete(ws)
+ if (sharedSub.clients.size === 0 && sharedSub.listeners.size === 0) {
+ sharedSub.subscription.unsubscribe()
+ sharedChatSubscriptions.delete(resourceId)
+ }
+ }
+ }
+
+ send(ws, {
+ id: message.id,
+ type: "data",
+ data: { unsubscribed: true },
+ })
+
+ console.log(`[WS] Unsubscribed from ${resource}:${resourceId}`)
+ }
+
+ return
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat: add unsubscribe handler"
+```
+
+---
+
+## Task 7: Update Client-Side ws-link
+
+**Files:**
+- Modify: `src/renderer/lib/remote-transport/ws-link.ts`
+
+**Step 1: Update message types**
+
+```typescript
+type ResourceType = "project" | "chat" | "modelProfiles"
+
+type WSMessageType = "auth" | "send" | "subscribe" | "unsubscribe"
+
+interface WSRequest {
+ id: string
+ type: WSMessageType
+ resource?: ResourceType
+ resourceId?: string
+ message?: any
+ pin?: string
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "subscribed" | "error" | "data"
+ resource?: ResourceType
+ resourceId?: string
+ data?: unknown
+ error?: string
+}
+```
+
+**Step 2: Add auth data handler**
+
+```typescript
+// Store initial data from auth
+let initialData: {
+ projects: any[]
+ modelProfiles: any[]
+} | null = null
+
+// In ws.onmessage, after auth success:
+if (message.type === "auth_success") {
+ console.log("[ws-link] Authentication successful")
+ isAuthenticated = true
+ initialData = message.data as { projects: any[], modelProfiles: any[] }
+ connectionPromise = null
+ resolve()
+ return
+}
+```
+
+**Step 3: Add subscribe to project function**
+
+```typescript
+/**
+ * Subscribe to a project (receives chat list)
+ */
+export async function subscribeToProject(projectId: string): Promise {
+ await connect()
+
+ return new Promise((resolve, reject) => {
+ const id = `sub-project-${Date.now()}`
+ const request: WSRequest = { id, type: "subscribe", resource: "project", resourceId: projectId }
+
+ const timeout = setTimeout(() => {
+ subscriptionHandlers.delete(id)
+ reject(new Error("Subscribe timeout"))
+ }, 10000)
+
+ // One-time handler for initial data
+ subscriptionHandlers.set(id, {
+ onData: (data: any) => {
+ clearTimeout(timeout)
+ subscriptionHandlers.delete(id)
+ resolve(data.chats || [])
+ },
+ onError: (error) => {
+ clearTimeout(timeout)
+ subscriptionHandlers.delete(id)
+ reject(error)
+ },
+ onComplete: () => {
+ clearTimeout(timeout)
+ subscriptionHandlers.delete(id)
+ },
+ })
+
+ ws!.send(JSON.stringify(request))
+ })
+}
+```
+
+**Step 4: Add subscribe to chat function**
+
+```typescript
+/**
+ * Subscribe to a chat (receives messages + streams updates)
+ */
+export function subscribeToChat(chatId: string, onData: (data: any) => void): () => void {
+ const id = `sub-chat-${Date.now()}`
+
+ const handler: SubscriptionHandler = {
+ onData: (data) => {
+ console.log("[ws-link] Chat data received:", chatId, data.type)
+ onData(data)
+ },
+ onError: (error) => {
+ console.error("[ws-link] Chat error:", chatId, error)
+ },
+ onComplete: () => {
+ console.log("[ws-link] Chat complete:", chatId)
+ },
+ }
+
+ subscriptionHandlers.set(id, handler)
+
+ connect().then(() => {
+ const request: WSRequest = { id, type: "subscribe", resource: "chat", resourceId: chatId }
+ ws!.send(JSON.stringify(request))
+ console.log("[ws-link] Subscribe chat request sent:", chatId)
+ }).catch((error) => {
+ console.error("[ws-link] Failed to connect:", error)
+ subscriptionHandlers.delete(id)
+ })
+
+ return () => {
+ console.log("[ws-link] Unsubscribing from chat:", chatId)
+ subscriptionHandlers.delete(id)
+ if (ws?.readyState === WebSocket.OPEN) {
+ const unsubscribeRequest: WSRequest = {
+ id: `unsub-${Date.now()}`,
+ type: "unsubscribe",
+ resource: "chat",
+ resourceId: chatId,
+ }
+ ws.send(JSON.stringify(unsubscribeRequest))
+ }
+ }
+}
+```
+
+**Step 5: Add send message function**
+
+```typescript
+/**
+ * Send a message to a chat
+ */
+export async function sendChatMessage(chatId: string, message: any): Promise {
+ await connect()
+
+ return new Promise((resolve, reject) => {
+ const id = `send-${Date.now()}`
+ const request: WSRequest = { id, type: "send", resourceId: chatId, message }
+
+ const timeout = setTimeout(() => {
+ pendingRequests.delete(id)
+ reject(new Error("Send timeout"))
+ }, 30000)
+
+ pendingRequests.set(id, {
+ resolve: (data) => {
+ clearTimeout(timeout)
+ resolve(data)
+ },
+ reject: (error) => {
+ clearTimeout(timeout)
+ reject(error)
+ },
+ })
+
+ ws!.send(JSON.stringify(request))
+ })
+}
+```
+
+**Step 6: Add helper to get initial data**
+
+```typescript
+/**
+ * Get initial data from auth (projects, model profiles)
+ */
+export function getInitialData() {
+ return initialData
+}
+```
+
+**Step 7: Update onmessage to handle resource-based messages**
+
+```typescript
+ws.onmessage = (event) => {
+ const message: WSResponse = JSON.parse(event.data)
+
+ // Handle auth response
+ if (message.type === "auth_success") {
+ console.log("[ws-link] Authentication successful")
+ isAuthenticated = true
+ initialData = message.data as { projects: any[], modelProfiles: any[] }
+ connectionPromise = null
+ resolve()
+ return
+ }
+
+ // ... rest of handlers (auth_failed, auth_required) ...
+
+ // Handle subscription data (streaming)
+ if (!message.id && message.type === "data" && message.resource) {
+ // This is streaming data from active subscription
+ const subKey = `${message.resource}:${message.resourceId}`
+ // Find all handlers for this resource and notify them
+ for (const [subId, handler] of subscriptionHandlers) {
+ if (subId.includes(message.resource!)) {
+ handler.onData(message.data)
+ }
+ }
+ return
+ }
+
+ // Handle response to specific request
+ if (message.id) {
+ const subHandler = subscriptionHandlers.get(message.id)
+ if (subHandler) {
+ if (message.type === "subscribed" || (message.type === "data" && message.data !== undefined)) {
+ subHandler.onData(message.data)
+ } else if (message.type === "error") {
+ subHandler.onError(new Error(message.error || "Error"))
+ } else if (message.type === "data") {
+ subHandler.onComplete()
+ subscriptionHandlers.delete(message.id)
+ }
+ return
+ }
+
+ // Handle pending requests
+ const pending = pendingRequests.get(message.id)
+ if (pending) {
+ pendingRequests.delete(message.id)
+ if (message.type === "error") {
+ pending.reject(new Error(message.error || "Unknown error"))
+ } else {
+ pending.resolve(message.data)
+ }
+ }
+ }
+}
+```
+
+**Step 8: Commit**
+
+```bash
+git add src/renderer/lib/remote-transport/ws-link.ts
+git commit -m "refactor: update ws-link for push-based protocol"
+```
+
+---
+
+## Task 8: Create Resource Data Store
+
+**Files:**
+- Create: `src/renderer/lib/stores/remote-data-store.ts`
+
+**Step 1: Create Zustand store for remote data**
+
+```typescript
+import { create } from 'zustand'
+import { subscribeToProject, subscribeToChat, sendChatMessage, getInitialData } from '../remote-transport/ws-link'
+
+interface RemoteDataState {
+ // Data
+ projects: any[]
+ modelProfiles: any[]
+ projectChats: Map // projectId -> chats
+
+ // Actions
+ loadInitialData: () => void
+ subscribeToProject: (projectId: string) => Promise
+ subscribeToChat: (chatId: string, onData: (data: any) => void) => () => void
+ sendMessage: (chatId: string, message: any) => Promise
+}
+
+export const useRemoteDataStore = create((set, get) => ({
+ projects: [],
+ modelProfiles: [],
+ projectChats: new Map(),
+
+ loadInitialData: () => {
+ const data = getInitialData()
+ if (data) {
+ set({ projects: data.projects, modelProfiles: data.modelProfiles })
+ }
+ },
+
+ subscribeToProject: async (projectId: string) => {
+ const chats = await subscribeToProject(projectId)
+ set((state) => {
+ const newMap = new Map(state.projectChats)
+ newMap.set(projectId, chats)
+ return { projectChats: newMap }
+ })
+ },
+
+ subscribeToChat: (chatId: string, onData: (data: any) => void) => {
+ return subscribeToChat(chatId, onData)
+ },
+
+ sendMessage: async (chatId: string, message: any) => {
+ return sendChatMessage(chatId, message)
+ },
+}))
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/lib/stores/remote-data-store.ts
+git commit -m "feat: create remote data store"
+```
+
+---
+
+## Task 9: Update UI to Use New Store
+
+**Files:**
+- Modify: `src/renderer/features/sidebar/agents-sidebar.tsx`
+- Modify: `src/renderer/features/agents/main/active-chat.tsx`
+
+**Step 1: Update sidebar to use remote data store**
+
+```typescript
+import { useRemoteDataStore } from "@/lib/stores/remote-data-store"
+
+export function AgentsSidebar() {
+ const { projects, loadInitialData } = useRemoteDataStore()
+
+ useEffect(() => {
+ // Load initial data on mount
+ loadInitialData()
+ }, [])
+
+ // Render projects from store instead of tRPC query
+ // ...
+}
+```
+
+**Step 2: Update active chat to use new transport**
+
+```typescript
+import { useRemoteDataStore } from "@/lib/stores/remote-data-store"
+
+export function ActiveChat() {
+ const { sendMessage, subscribeToChat } = useRemoteDataStore()
+
+ useEffect(() => {
+ if (chatId) {
+ const unsubscribe = subscribeToChat(chatId, (data) => {
+ // Handle incoming message data
+ console.log("Received chat data:", data)
+ })
+ return unsubscribe
+ }
+ }, [chatId])
+
+ const handleSend = async (message: string) => {
+ await sendMessage(chatId, { message })
+ }
+
+ // ...
+}
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/renderer/features/sidebar/agents-sidebar.tsx
+git add src/renderer/features/agents/main/active-chat.tsx
+git commit -m "refactor: use remote data store in UI"
+```
+
+---
+
+## Task 10: Remove tRPC Functions from Web App
+
+**Files:**
+- Modify: `src/renderer/lib/trpc.ts`
+- Modify: `src/renderer/contexts/TRPCProvider.tsx`
+- Modify: `src/renderer/features/sidebar/agents-sidebar.tsx`
+- Modify: `src/renderer/features/agents/ui/agents-content.tsx`
+
+**Step 1: Remove wsLink from tRPC client configuration**
+
+In `src/renderer/lib/trpc.ts`, conditionally add wsLink only for desktop:
+
+```typescript
+import { wsLink } from "./remote-transport/ws-link"
+
+// In createTRPCClient(), only add wsLink for desktop
+const links = []
+
+// Always add HTTP link (for desktop IPC and web initial load)
+links.push(httpBatchLink)
+
+// Only add WebSocket link for desktop (web uses WebSocket transport directly)
+if (isDesktopApp) {
+ links.push(wsLink())
+}
+
+export const trpcClient = createTRPCClient({
+ links,
+})
+```
+
+**Step 2: Remove tRPC queries from web sidebar**
+
+In `src/renderer/features/sidebar/agents-sidebar.tsx`, remove tRPC queries for remote mode:
+
+```typescript
+import { useRemoteDataStore } from "@/lib/stores/remote-data-store"
+import { isDesktopApp } from "@/lib/utils/platform"
+
+export function AgentsSidebar() {
+ const isRemoteMode = !isDesktopApp()
+
+ // Desktop: use tRPC queries
+ // Web: use remote data store (populated via WebSocket)
+ const { projects, loadInitialData } = useRemoteDataStore()
+
+ const { data: localProjects } = trpc.projects.list.useQuery(
+ undefined,
+ // Only enable tRPC query for desktop
+ { enabled: isDesktopApp }
+ )
+
+ // Use appropriate data source
+ const projects = isDesktopApp ? localProjects : remoteProjects
+
+ useEffect(() => {
+ // Load initial data from WebSocket for web
+ if (isRemoteMode) {
+ loadInitialData()
+ }
+ }, [isRemoteMode, loadInitialData])
+
+ // ... rest of component
+}
+```
+
+**Step 3: Remove tRPC queries from agents content**
+
+In `src/renderer/features/agents/ui/agents-content.tsx`:
+
+```typescript
+// Only use tRPC for desktop
+const { data: projects } = trpc.projects.list.useQuery(
+ undefined,
+ { enabled: isDesktopApp() }
+)
+
+// For web, data comes from remoteDataStore
+```
+
+**Step 4: Update TRPCProvider to handle web mode**
+
+In `src/renderer/contexts/TRPCProvider.tsx`:
+
+```typescript
+// Ensure tRPC provider still works for desktop IPC
+// Web mode won't use tRPC queries, but provider should still be available
+// for any shared types/utilities
+```
+
+**Step 5: Verify no remaining tRPC calls in web mode**
+
+Search for remaining tRPC usage:
+```bash
+grep -r "trpc\." src/renderer/features --include="*.tsx" --include="*.ts"
+```
+
+All usages should be guarded with `isDesktopApp()` check.
+
+**Step 6: Commit**
+
+```bash
+git add src/renderer/lib/trpc.ts
+git add src/renderer/contexts/TRPCProvider.tsx
+git add src/renderer/features/sidebar/agents-sidebar.tsx
+git add src/renderer/features/agents/ui/agents-content.tsx
+git commit -m "refactor: remove tRPC queries from web mode
+
+- WebSocket transport handles all data for web
+- Desktop continues using tRPC for IPC
+- All web data flows through push-based WebSocket protocol"
+```
+
+---
+
+## Task 11: Test Complete Flow
+
+**Files:**
+- Test: Manual testing
+
+**Step 1: Test auth + initial data**
+
+```bash
+bun run dev
+```
+
+Open web app, enter PIN.
+
+**Expected:**
+- Auth success
+- Projects list appears
+- Model profiles loaded
+
+**Step 2: Test project subscription**
+
+Click on a project.
+
+**Expected:**
+- Chat list for project appears
+
+**Step 3: Test chat subscription**
+
+Click on a chat.
+
+**Expected:**
+- Message history loads
+- Real-time updates work
+
+**Step 4: Test send message**
+
+Send a message from web.
+
+**Expected:**
+- Message appears in web
+- Message appears in desktop
+- Response appears in both
+
+**Step 5: Test sync**
+
+Send message from desktop.
+
+**Expected:**
+- Message appears in web immediately
+
+---
+
+## Testing Checklist
+
+- [ ] Auth sends projects + model profiles
+- [ ] Project subscription sends chat list
+- [ ] Chat subscription sends message history
+- [ ] Chat subscription streams new messages
+- [ ] Send message works from web
+- [ ] Send message works from desktop
+- [ ] Messages sync between web and desktop
+- [ ] Unsubscribe works
+- [ ] Reconnection works
+- [ ] No memory leaks
+
+---
+
+## Rollback Plan
+
+If issues arise:
+```bash
+git revert HEAD # Undo last commit
+# Or
+git reset --hard
+```
diff --git a/docs/plans/2026-01-31-system-tray.md b/docs/plans/2026-01-31-system-tray.md
new file mode 100644
index 000000000..b2db6a10f
--- /dev/null
+++ b/docs/plans/2026-01-31-system-tray.md
@@ -0,0 +1,1220 @@
+# System Tray Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Add persistent system tray support with quick access to profiles and workspaces, allowing the app to continue running when all windows are closed.
+
+**Architecture:** Use Electron's `Tray` API to create a system tray icon with dynamic context menu. Modify app lifecycle to prevent quit on window close. Add IPC handlers for profile switching and workspace navigation from tray menu.
+
+**Tech Stack:** Electron Tray API, IPC (main ↔ renderer), existing tRPC for data fetching
+
+---
+
+## Task 1: Setup System Tray Infrastructure
+
+**Files:**
+- Create: `src/main/lib/tray.ts`
+- Modify: `src/main/index.ts`
+
+**Step 1: Create tray module with basic initialization**
+
+Create `src/main/lib/tray.ts`:
+
+```typescript
+import { app, Menu, Tray, nativeImage } from "electron"
+import { join } from "path"
+import { IS_DEV } from "../constants"
+
+let tray: Tray | null = null
+
+/**
+ * Get path to tray icon based on platform and theme
+ * Returns template image for macOS (auto-adapts to light/dark mode)
+ */
+function getTrayIconPath(): string {
+ const iconName = process.platform === "darwin"
+ ? "tray-iconTemplate.png" // macOS template (auto dark mode)
+ : "tray-icon.png" // Windows/Linux
+
+ if (IS_DEV) {
+ return join(app.getAppPath(), "resources", "icons", iconName)
+ }
+ return join(process.resourcesPath, "icons", iconName)
+}
+
+/**
+ * Initialize system tray
+ * Should be called after app is ready
+ */
+export function initTray(): void {
+ if (tray) {
+ console.log("[Tray] Already initialized")
+ return
+ }
+
+ const iconPath = getTrayIconPath()
+ console.log("[Tray] Creating tray with icon:", iconPath)
+
+ tray = new Tray(iconPath)
+ tray.setToolTip("1Code")
+
+ // Build initial menu
+ buildTrayMenu()
+
+ console.log("[Tray] Initialized successfully")
+}
+
+/**
+ * Build and set the tray context menu
+ * Called on init and whenever menu needs to update
+ */
+export function buildTrayMenu(): void {
+ if (!tray) {
+ console.warn("[Tray] Cannot build menu - tray not initialized")
+ return
+ }
+
+ const template: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: "Quit",
+ click: () => {
+ app.quit()
+ },
+ },
+ ]
+
+ const contextMenu = Menu.buildFromTemplate(template)
+ tray.setContextMenu(contextMenu)
+}
+
+/**
+ * Destroy the tray icon
+ * Should be called on app quit
+ */
+export function destroyTray(): void {
+ if (tray) {
+ tray.destroy()
+ tray = null
+ console.log("[Tray] Destroyed")
+ }
+}
+
+/**
+ * Get the tray instance (for testing or advanced use)
+ */
+export function getTray(): Tray | null {
+ return tray
+}
+```
+
+**Step 2: Add tray initialization to main process**
+
+Modify `src/main/index.ts` - add import at top:
+
+```typescript
+import { initTray, destroyTray } from "./lib/tray"
+```
+
+**Step 3: Initialize tray after app ready**
+
+In `src/main/index.ts`, find the `app.whenReady()` section (around line 548) and add after menu initialization (after line 804):
+
+```typescript
+ // Initialize system tray
+ initTray()
+```
+
+**Step 4: Cleanup tray on quit**
+
+In `src/main/index.ts`, find the `app.on("before-quit")` handler or add one if it doesn't exist:
+
+```typescript
+// Clean up before quit
+app.on("before-quit", () => {
+ console.log("[App] before-quit event")
+ destroyTray()
+})
+```
+
+**Step 5: Test basic tray initialization**
+
+Run: `pnpm dev`
+Expected: App starts with tray icon visible in menu bar (macOS) or system tray (Windows/Linux). Right-click shows "Quit" menu item.
+
+**Step 6: Commit**
+
+```bash
+git add src/main/lib/tray.ts src/main/index.ts
+git commit -m "feat: add basic system tray infrastructure"
+```
+
+---
+
+## Task 2: Add Tray Icon Assets
+
+**Files:**
+- Create: `resources/icons/tray-icon.png`
+- Create: `resources/icons/tray-iconTemplate.png`
+- Create: `resources/icons/[email protected]`
+- Create: `resources/icons/tray-iconTemplate@2x.png`
+
+**Step 1: Create placeholder tray icons**
+
+Since you have an existing icon system, we'll use a simple approach:
+
+For macOS template icon (auto dark mode support):
+- `tray-iconTemplate.png` - 16x16px, monochrome, black on transparent
+- `tray-iconTemplate@2x.png` - 32x32px, monochrome, black on transparent
+
+For Windows/Linux:
+- `tray-icon.png` - 16x16px, colored
+- `[email protected]` - 32x32px, colored
+
+**Note:** You can use your existing app icon scaled down and converted to monochrome for the template icons. The "Template" suffix is required for macOS dark mode support.
+
+**Step 2: Test icons appear correctly**
+
+Run: `pnpm dev`
+Expected: Tray icon visible and sized correctly on current platform
+
+**Step 3: Commit**
+
+```bash
+git add resources/icons/tray-*.png
+git commit -m "feat: add tray icon assets for all platforms"
+```
+
+---
+
+## Task 3: Prevent App Quit on Window Close
+
+**Files:**
+- Modify: `src/main/windows/main.ts`
+
+**Step 1: View current window close handling**
+
+View `src/main/windows/main.ts` to understand current window lifecycle.
+
+**Step 2: Modify window close behavior to hide instead of quit**
+
+In `src/main/windows/main.ts`, find where the window is created (in `createMainWindow` or similar). Add a `close` event handler to prevent app quit:
+
+```typescript
+// Prevent app quit when window closes - hide to tray instead
+mainWindow.on("close", (event) => {
+ if (!app.isQuitting) {
+ event.preventDefault()
+ mainWindow.hide()
+ console.log("[Window] Hidden to tray (prevented quit)")
+ }
+})
+```
+
+**Step 3: Add app quit flag**
+
+In `src/main/index.ts`, add flag to track intentional quits. Add near the top after imports:
+
+```typescript
+// Track when app is intentionally quitting (vs just closing windows)
+let isQuitting = false
+
+// Make it accessible globally
+;(global as any).__isAppQuitting = () => isQuitting
+```
+
+**Step 4: Set quit flag in before-quit handler**
+
+Modify the `before-quit` handler in `src/main/index.ts`:
+
+```typescript
+app.on("before-quit", () => {
+ console.log("[App] before-quit event")
+ isQuitting = true
+ ;(global as any).__isAppQuitting = () => true
+ destroyTray()
+ // ... existing cleanup
+})
+```
+
+**Step 5: Update window close handler to use global flag**
+
+In `src/main/windows/main.ts`, modify the close handler:
+
+```typescript
+mainWindow.on("close", (event) => {
+ const isQuitting = (global as any).__isAppQuitting?.() || false
+ if (!isQuitting) {
+ event.preventDefault()
+ mainWindow.hide()
+ console.log("[Window] Hidden to tray (prevented quit)")
+ }
+})
+```
+
+**Step 6: Test window close behavior**
+
+Run: `pnpm dev`
+Actions:
+1. Close main window (Cmd+W or close button)
+2. Verify app still running (tray icon visible)
+3. Right-click tray → Quit
+4. Verify app quits completely
+
+**Step 7: Commit**
+
+```bash
+git add src/main/windows/main.ts src/main/index.ts
+git commit -m "feat: prevent app quit on window close, hide to tray"
+```
+
+---
+
+## Task 4: Add Tray Click to Restore Window
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+
+**Step 1: Add window restore logic on tray click**
+
+In `src/main/lib/tray.ts`, add import for window management:
+
+```typescript
+import { getWindow, getAllWindows, createMainWindow } from "../windows/main"
+```
+
+**Step 2: Add click handler in initTray**
+
+In the `initTray()` function, after creating the tray, add:
+
+```typescript
+ // Left-click: restore or create window
+ tray.on("click", () => {
+ console.log("[Tray] Click event - restoring window")
+
+ const windows = getAllWindows()
+ if (windows.length > 0) {
+ // Show and focus first window
+ const window = windows[0]!
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+ } else {
+ // No windows exist, create new one
+ createMainWindow()
+ }
+ })
+```
+
+**Step 3: Handle double-click on Windows**
+
+Add after the click handler:
+
+```typescript
+ // Double-click on Windows also shows window
+ tray.on("double-click", () => {
+ console.log("[Tray] Double-click event - restoring window")
+
+ const windows = getAllWindows()
+ if (windows.length > 0) {
+ const window = windows[0]!
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+ } else {
+ createMainWindow()
+ }
+ })
+```
+
+**Step 4: Test tray click behavior**
+
+Run: `pnpm dev`
+Actions:
+1. Close window (hides to tray)
+2. Click tray icon
+3. Verify window reappears and is focused
+
+**Step 5: Commit**
+
+```bash
+git add src/main/lib/tray.ts
+git commit -m "feat: restore window on tray icon click"
+```
+
+---
+
+## Task 5: Add IPC Handler for Profile Data
+
+**Files:**
+- Create: `src/main/lib/ipc/tray-handlers.ts`
+- Modify: `src/main/index.ts`
+
+**Step 1: Create IPC handlers for tray menu data**
+
+Create `src/main/lib/ipc/tray-handlers.ts`:
+
+```typescript
+import { ipcMain } from "electron"
+import { getDatabase } from "../db"
+import { modelProfiles } from "../db/schema"
+import { projects } from "../db/schema"
+
+/**
+ * Register IPC handlers for tray menu data fetching
+ */
+export function registerTrayHandlers(): void {
+ // Get all profiles for tray menu
+ ipcMain.handle("tray:get-profiles", async () => {
+ try {
+ const db = getDatabase()
+ const allProfiles = db.select().from(modelProfiles).all()
+
+ // Also get active profile ID from localStorage
+ // Note: We'll need to get this from renderer via another call
+ return allProfiles
+ } catch (error) {
+ console.error("[Tray] Failed to get profiles:", error)
+ return []
+ }
+ })
+
+ // Get all projects/workspaces for tray menu
+ ipcMain.handle("tray:get-projects", async () => {
+ try {
+ const db = getDatabase()
+ const allProjects = db.select().from(projects).all()
+ return allProjects
+ } catch (error) {
+ console.error("[Tray] Failed to get projects:", error)
+ return []
+ }
+ })
+
+ // Get active profile ID (from renderer localStorage)
+ ipcMain.handle("tray:get-active-profile-id", async (event) => {
+ try {
+ // Execute JS in renderer to get localStorage value
+ const result = await event.sender.executeJavaScript(
+ `localStorage.getItem('agents:active-profile-id')`
+ )
+ return result ? JSON.parse(result) : null
+ } catch (error) {
+ console.error("[Tray] Failed to get active profile:", error)
+ return null
+ }
+ })
+
+ console.log("[Tray] IPC handlers registered")
+}
+```
+
+**Step 2: Register handlers in main process**
+
+In `src/main/index.ts`, add import:
+
+```typescript
+import { registerTrayHandlers } from "./lib/ipc/tray-handlers"
+```
+
+**Step 3: Call registration in app ready**
+
+In the `app.whenReady()` section, after database initialization:
+
+```typescript
+ // Register tray IPC handlers
+ registerTrayHandlers()
+```
+
+**Step 4: Test IPC handlers (manual console test)**
+
+In renderer devtools console:
+```javascript
+await window.electronAPI.invoke('tray:get-profiles')
+await window.electronAPI.invoke('tray:get-projects')
+```
+
+Expected: Returns arrays of profiles and projects
+
+**Step 5: Commit**
+
+```bash
+git add src/main/lib/ipc/tray-handlers.ts src/main/index.ts
+git commit -m "feat: add IPC handlers for tray menu data"
+```
+
+---
+
+## Task 6: Build Dynamic Tray Menu with Profiles
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+- Modify: `src/main/lib/ipc/tray-handlers.ts`
+
+**Step 1: Add async menu building with profile data**
+
+In `src/main/lib/tray.ts`, modify `buildTrayMenu` to be async and fetch data:
+
+```typescript
+/**
+ * Build and set the tray context menu
+ * Fetches current profiles and projects from database
+ */
+export async function buildTrayMenu(): Promise {
+ if (!tray) {
+ console.warn("[Tray] Cannot build menu - tray not initialized")
+ return
+ }
+
+ try {
+ // Fetch profiles and active profile ID
+ const db = getDatabase()
+ const allProfiles = db.select().from(modelProfiles).all()
+
+ // Get active profile from first window's localStorage
+ let activeProfileId: string | null = null
+ const windows = getAllWindows()
+ if (windows.length > 0) {
+ try {
+ const result = await windows[0]!.webContents.executeJavaScript(
+ `localStorage.getItem('agents:active-profile-id')`
+ )
+ activeProfileId = result ? JSON.parse(result) : null
+ } catch (e) {
+ console.warn("[Tray] Could not get active profile:", e)
+ }
+ }
+
+ // Build profile menu items
+ const profileMenuItems: Electron.MenuItemConstructorOptions[] = allProfiles.map(profile => ({
+ label: profile.name,
+ type: "checkbox",
+ checked: profile.id === activeProfileId,
+ click: () => {
+ handleProfileSwitch(profile.id)
+ },
+ }))
+
+ const template: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: "Profiles",
+ submenu: profileMenuItems.length > 0
+ ? profileMenuItems
+ : [{ label: "No profiles", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "Quit",
+ click: () => {
+ app.quit()
+ },
+ },
+ ]
+
+ const contextMenu = Menu.buildFromTemplate(template)
+ tray.setContextMenu(contextMenu)
+ } catch (error) {
+ console.error("[Tray] Failed to build menu:", error)
+ }
+}
+```
+
+**Step 2: Add imports for database access**
+
+At the top of `src/main/lib/tray.ts`:
+
+```typescript
+import { getDatabase } from "../db"
+import { modelProfiles, projects } from "../db/schema"
+import { getAllWindows } from "../windows/main"
+```
+
+**Step 3: Add profile switch handler**
+
+In `src/main/lib/tray.ts`, add the handler function:
+
+```typescript
+/**
+ * Handle profile switch from tray menu
+ * Updates localStorage in all windows and reloads them
+ */
+async function handleProfileSwitch(profileId: string): Promise {
+ console.log("[Tray] Switching to profile:", profileId)
+
+ const windows = getAllWindows()
+ for (const window of windows) {
+ try {
+ if (window.isDestroyed()) continue
+
+ // Update localStorage
+ await window.webContents.executeJavaScript(
+ `localStorage.setItem('agents:active-profile-id', '${JSON.stringify(profileId)}')`
+ )
+
+ // Reload window to apply new profile
+ window.reload()
+ } catch (error) {
+ console.error("[Tray] Failed to switch profile for window:", error)
+ }
+ }
+
+ // Rebuild menu to update checkmarks
+ await buildTrayMenu()
+}
+```
+
+**Step 4: Update initTray to use async buildTrayMenu**
+
+Change the call in `initTray()`:
+
+```typescript
+ // Build initial menu (async, don't await)
+ buildTrayMenu().catch(err => {
+ console.error("[Tray] Failed to build initial menu:", err)
+ })
+```
+
+**Step 5: Test profile switching from tray**
+
+Run: `pnpm dev`
+Actions:
+1. Right-click tray icon
+2. Click on Profiles submenu
+3. Select different profile
+4. Verify window reloads with new profile active
+
+**Step 6: Commit**
+
+```bash
+git add src/main/lib/tray.ts
+git commit -m "feat: add dynamic profile menu to tray"
+```
+
+---
+
+## Task 7: Add Workspaces Menu to Tray
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+
+**Step 1: Add workspace menu items to buildTrayMenu**
+
+In `buildTrayMenu()`, after fetching profiles, add project fetching:
+
+```typescript
+ // Fetch projects/workspaces
+ const allProjects = db.select().from(projects).all()
+
+ // Build workspace menu items
+ const workspaceMenuItems: Electron.MenuItemConstructorOptions[] = allProjects.map(project => ({
+ label: project.displayName || project.path,
+ click: () => {
+ handleWorkspaceClick(project.id)
+ },
+ }))
+```
+
+**Step 2: Add workspaces to menu template**
+
+Update the template array:
+
+```typescript
+ const template: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: "Profiles",
+ submenu: profileMenuItems.length > 0
+ ? profileMenuItems
+ : [{ label: "No profiles", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "Workspaces",
+ submenu: workspaceMenuItems.length > 0
+ ? workspaceMenuItems
+ : [{ label: "No workspaces", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "Quit",
+ click: () => {
+ app.quit()
+ },
+ },
+ ]
+```
+
+**Step 3: Add workspace click handler**
+
+```typescript
+/**
+ * Handle workspace click from tray menu
+ * Sets active workspace and opens new chat form
+ */
+async function handleWorkspaceClick(projectId: string): Promise {
+ console.log("[Tray] Opening workspace:", projectId)
+
+ // Get or create main window
+ let window = getWindow()
+ if (!window) {
+ window = await createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Set selected project in localStorage and trigger new chat
+ try {
+ await window.webContents.executeJavaScript(`
+ (async () => {
+ const project = ${JSON.stringify(projectId)};
+ // Get project data
+ const projectData = await window.trpcClient.projects.list.query();
+ const selectedProject = projectData.find(p => p.id === project);
+ if (selectedProject) {
+ localStorage.setItem('agents:selected-project', JSON.stringify(selectedProject));
+ // Trigger new chat form opening (send IPC to renderer)
+ window.dispatchEvent(new CustomEvent('tray:open-workspace', {
+ detail: { projectId: project }
+ }));
+ }
+ })()
+ `)
+ } catch (error) {
+ console.error("[Tray] Failed to open workspace:", error)
+ }
+}
+```
+
+**Step 4: Add renderer listener for workspace opening**
+
+This will need renderer-side handling which we'll add in next task.
+
+**Step 5: Test workspace menu**
+
+Run: `pnpm dev`
+Expected: Workspaces submenu shows all projects from database
+
+**Step 6: Commit**
+
+```bash
+git add src/main/lib/tray.ts
+git commit -m "feat: add workspaces menu to tray"
+```
+
+---
+
+## Task 8: Add "New Workspace" and "Settings" Menu Items
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+
+**Step 1: Add menu items to template**
+
+In `buildTrayMenu()`, update the template to include new items:
+
+```typescript
+ const template: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: "Profiles",
+ submenu: profileMenuItems.length > 0
+ ? profileMenuItems
+ : [{ label: "No profiles", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "Workspaces",
+ submenu: workspaceMenuItems.length > 0
+ ? workspaceMenuItems
+ : [{ label: "No workspaces", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "New Workspace...",
+ click: () => {
+ handleNewWorkspace()
+ },
+ },
+ {
+ label: "Settings...",
+ click: () => {
+ handleOpenSettings()
+ },
+ },
+ { type: "separator" },
+ {
+ label: "Quit",
+ click: () => {
+ app.quit()
+ },
+ },
+ ]
+```
+
+**Step 2: Add New Workspace handler**
+
+```typescript
+/**
+ * Handle "New Workspace" click
+ * Opens select-repo-page
+ */
+async function handleNewWorkspace(): Promise {
+ console.log("[Tray] Opening new workspace dialog")
+
+ // Get or create main window
+ let window = getWindow()
+ if (!window) {
+ window = await createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Navigate to select-repo page by clearing selected project
+ try {
+ await window.webContents.executeJavaScript(`
+ localStorage.removeItem('agents:selected-project');
+ window.location.reload();
+ `)
+ } catch (error) {
+ console.error("[Tray] Failed to open new workspace:", error)
+ }
+}
+```
+
+**Step 3: Add Settings handler**
+
+```typescript
+/**
+ * Handle "Settings" click
+ * Opens settings page
+ */
+async function handleOpenSettings(): Promise {
+ console.log("[Tray] Opening settings")
+
+ // Get or create main window
+ let window = getWindow()
+ if (!window) {
+ window = await createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Send custom event to open settings
+ try {
+ await window.webContents.executeJavaScript(`
+ window.dispatchEvent(new CustomEvent('tray:open-settings'));
+ `)
+ } catch (error) {
+ console.error("[Tray] Failed to open settings:", error)
+ }
+}
+```
+
+**Step 4: Test new menu items**
+
+Run: `pnpm dev`
+Actions:
+1. Right-click tray → "New Workspace..." → should clear project and reload
+2. Right-click tray → "Settings..." → should dispatch settings event
+
+**Step 5: Commit**
+
+```bash
+git add src/main/lib/tray.ts
+git commit -m "feat: add New Workspace and Settings to tray menu"
+```
+
+---
+
+## Task 9: Add Renderer Event Listeners
+
+**Files:**
+- Create: `src/renderer/lib/tray-events.ts`
+- Modify: `src/renderer/App.tsx`
+
+**Step 1: Create tray event handler**
+
+Create `src/renderer/lib/tray-events.ts`:
+
+```typescript
+import { useEffect } from "react"
+import { useSetAtom } from "jotai"
+import { selectedProjectAtom } from "../features/agents/atoms"
+
+/**
+ * Hook to listen for tray events and handle them
+ */
+export function useTrayEvents() {
+ const setSelectedProject = useSetAtom(selectedProjectAtom)
+
+ useEffect(() => {
+ // Handle workspace selection from tray
+ const handleWorkspaceOpen = (event: CustomEvent) => {
+ const { projectId } = event.detail
+ console.log("[Tray Events] Workspace opened:", projectId)
+
+ // The project is already set in localStorage by the tray handler
+ // Just trigger a reload to show new chat form
+ window.location.reload()
+ }
+
+ // Handle settings open from tray
+ const handleSettingsOpen = () => {
+ console.log("[Tray Events] Settings opened")
+ // Navigate to settings page
+ // TODO: Implement settings navigation
+ // For now, just log
+ }
+
+ window.addEventListener("tray:open-workspace", handleWorkspaceOpen as EventListener)
+ window.addEventListener("tray:open-settings", handleSettingsOpen)
+
+ return () => {
+ window.removeEventListener("tray:open-workspace", handleWorkspaceOpen as EventListener)
+ window.removeEventListener("tray:open-settings", handleSettingsOpen)
+ }
+ }, [setSelectedProject])
+}
+```
+
+**Step 2: Use the hook in App component**
+
+In `src/renderer/App.tsx`, import and use the hook:
+
+```typescript
+import { useTrayEvents } from "./lib/tray-events"
+```
+
+In the `App` component function:
+
+```typescript
+export function App() {
+ // Listen for tray events
+ useTrayEvents()
+
+ // ... rest of App component
+}
+```
+
+**Step 3: Test tray workspace opening**
+
+Run: `pnpm dev`
+Actions:
+1. Close window
+2. Right-click tray → Workspaces → select a workspace
+3. Verify window opens with workspace selected
+
+**Step 4: Commit**
+
+```bash
+git add src/renderer/lib/tray-events.ts src/renderer/App.tsx
+git commit -m "feat: add renderer event listeners for tray actions"
+```
+
+---
+
+## Task 10: Add Dynamic Menu Refresh
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+- Modify: `src/main/lib/ipc/tray-handlers.ts`
+
+**Step 1: Export menu rebuild function for IPC**
+
+In `src/main/lib/tray.ts`, ensure `buildTrayMenu` is exported (it already is).
+
+**Step 2: Add IPC handler to trigger menu rebuild**
+
+In `src/main/lib/ipc/tray-handlers.ts`, add handler:
+
+```typescript
+import { buildTrayMenu } from "../tray"
+
+ // Rebuild tray menu (called when profiles/projects change)
+ ipcMain.handle("tray:rebuild-menu", async () => {
+ try {
+ await buildTrayMenu()
+ return { success: true }
+ } catch (error) {
+ console.error("[Tray] Failed to rebuild menu:", error)
+ return { success: false, error: String(error) }
+ }
+ })
+```
+
+**Step 3: Add menu refresh on data changes**
+
+In renderer, wherever profiles or projects are added/removed, call:
+
+```typescript
+// After profile/project changes:
+await window.electronAPI?.invoke("tray:rebuild-menu")
+```
+
+This can be added to profile management and project management components in a follow-up.
+
+**Step 4: Test menu updates**
+
+Manual test:
+1. Open app
+2. Add a new profile via UI
+3. Check tray menu updates (will need manual refresh for now)
+
+**Step 5: Commit**
+
+```bash
+git add src/main/lib/ipc/tray-handlers.ts
+git commit -m "feat: add IPC handler for tray menu refresh"
+```
+
+---
+
+## Task 11: Add Platform-Specific Polish
+
+**Files:**
+- Modify: `src/main/lib/tray.ts`
+
+**Step 1: Add macOS-specific menu improvements**
+
+In `buildTrayMenu()`, add macOS-specific polish:
+
+```typescript
+ // macOS: Add "Show 1Code" as first item
+ if (process.platform === "darwin") {
+ template.unshift({
+ label: "Show 1Code",
+ click: async () => {
+ const windows = getAllWindows()
+ if (windows.length > 0) {
+ const window = windows[0]!
+ if (window.isMinimized()) window.restore()
+ window.show()
+ window.focus()
+ } else {
+ await createMainWindow()
+ }
+ },
+ })
+ template.splice(1, 0, { type: "separator" })
+ }
+```
+
+**Step 2: Improve tray icon template for macOS**
+
+The icon with "Template" suffix should be monochrome. Ensure assets are properly formatted.
+
+**Step 3: Test on macOS**
+
+Run on macOS:
+- Verify template icon adapts to light/dark menu bar
+- Verify "Show 1Code" appears at top of menu
+- Verify menu follows macOS design guidelines
+
+**Step 4: Test on other platforms (if available)**
+
+Windows: Verify icon and menu work correctly
+Linux: Verify tray positioning and menu
+
+**Step 5: Commit**
+
+```bash
+git add src/main/lib/tray.ts
+git commit -m "feat: add platform-specific tray menu polish"
+```
+
+---
+
+## Task 12: Documentation and Testing
+
+**Files:**
+- Create: `docs/features/system-tray.md`
+
+**Step 1: Write feature documentation**
+
+Create `docs/features/system-tray.md`:
+
+```markdown
+# System Tray
+
+The app includes a persistent system tray icon that allows quick access to profiles and workspaces even when all windows are closed.
+
+## Platform Support
+
+- **macOS**: Menu bar icon (template image, adapts to light/dark mode)
+- **Windows**: System tray icon (bottom-right)
+- **Linux**: System tray icon (desktop environment dependent)
+
+## Features
+
+### Left Click
+- Restores or creates the main window
+- If windows are minimized, brings them to front
+- If no windows exist, creates a new one
+
+### Right Click Menu
+
+```
+├── Show 1Code (macOS only)
+├── ─────────
+├── Profiles ⊳
+│ ├── ✓ Active Profile
+│ └── Other Profiles
+├── ─────────
+├── Workspaces ⊳
+│ └── Project List
+├── ─────────
+├── New Workspace...
+├── Settings...
+├── ─────────
+└── Quit
+```
+
+### Profile Switching
+- Click any profile to switch immediately
+- All windows reload with new profile
+- Active profile shown with checkmark
+
+### Workspace Navigation
+- Click workspace to open new chat with that workspace selected
+- Creates window if needed
+- Sets workspace and shows new chat form
+
+### Background Mode
+- App continues running when all windows are closed
+- Only way to quit is via tray menu or Cmd+Q
+- Windows can be restored by clicking tray icon
+
+## Implementation Details
+
+### Files
+- `src/main/lib/tray.ts` - Tray initialization and menu building
+- `src/main/lib/ipc/tray-handlers.ts` - IPC handlers for tray data
+- `src/renderer/lib/tray-events.ts` - Renderer event listeners
+- `resources/icons/tray-*.png` - Tray icon assets
+
+### Menu Updates
+Menu rebuilds automatically when:
+- Profiles are added/removed/switched
+- Projects are added/removed
+- App starts
+
+To manually trigger rebuild:
+```typescript
+await window.electronAPI?.invoke("tray:rebuild-menu")
+```
+
+## Testing
+
+1. **Basic functionality**:
+ - Close all windows → verify tray remains
+ - Click tray → verify window restores
+ - Right-click → verify menu appears
+
+2. **Profile switching**:
+ - Right-click → Profiles → select profile
+ - Verify window reloads with new profile
+
+3. **Workspace navigation**:
+ - Right-click → Workspaces → select workspace
+ - Verify new chat form opens with workspace
+
+4. **Platform-specific**:
+ - macOS: Verify template icon in light/dark modes
+ - Windows: Verify tray icon positioning
+ - Linux: Test on various desktop environments
+```
+
+**Step 2: Add troubleshooting section**
+
+Append to the doc:
+
+```markdown
+## Troubleshooting
+
+### Tray icon not appearing (macOS)
+- Check System Settings → Control Center → Menu Bar Only
+- Ensure "Show in Menu Bar" is enabled for the app
+
+### Tray icon not appearing (Linux)
+- Ensure desktop environment supports system tray
+- Install system tray extensions if using GNOME
+- Check if `libappindicator` is installed
+
+### Menu not updating
+- Call `tray:rebuild-menu` IPC after data changes
+- Check console for errors
+
+### App won't quit
+- Use tray menu → Quit
+- Or press Cmd+Q from menu bar (macOS)
+- Force quit as last resort
+```
+
+**Step 3: Commit documentation**
+
+```bash
+git add docs/features/system-tray.md
+git commit -m "docs: add system tray feature documentation"
+```
+
+---
+
+## Verification Plan
+
+### Manual Testing
+1. **Tray Initialization**
+ - Start app → verify tray icon appears
+ - Right-click tray → verify menu matches design
+
+2. **Window Lifecycle**
+ - Close all windows → verify app stays running
+ - Click tray → verify window restores
+ - Quit from tray → verify clean shutdown
+
+3. **Profile Switching**
+ - Create multiple profiles
+ - Switch via tray menu → verify reload and active checkmark
+
+4. **Workspace Navigation**
+ - Add multiple projects
+ - Click workspace in tray → verify new chat with workspace
+
+5. **New Workspace / Settings**
+ - Click "New Workspace" → verify select-repo-page
+ - Click "Settings" → verify settings navigation
+
+### Platform Testing
+- Test on macOS (menu bar, template icons)
+- Test on Windows (system tray, standard icons)
+- Test on Linux if possible (varies by DE)
+
+### Edge Cases
+- No profiles: verify "No profiles" placeholder
+- No workspaces: verify "No workspaces" placeholder
+- All windows destroyed: verify tray click creates new window
+- Rapid clicking: verify no duplicate windows
+
+---
+
+## Notes
+
+- Tray icons should be simple, monochrome for macOS template
+- Menu rebuilding is async - errors are logged but don't crash
+- Window restoration prefers existing windows over creating new ones
+- Profile switching causes window reload (slight UX disruption, but ensures clean state)
diff --git a/docs/plans/2026-02-01-remote-access-design.md b/docs/plans/2026-02-01-remote-access-design.md
new file mode 100644
index 000000000..9f5fc3fb6
--- /dev/null
+++ b/docs/plans/2026-02-01-remote-access-design.md
@@ -0,0 +1,266 @@
+# Remote Access Feature Design
+
+## Overview
+
+Enable users to access their desktop 1Code app from any browser via Cloudflare Tunnel with PIN authentication.
+
+## Architecture
+
+```
+┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
+│ Web Browser │◄═══════►│ Cloudflare │◄═══════►│ Desktop App │
+│ (WebSocket) │ WSS │ Tunnel │ WS │ (WS server + │
+│ │ │ │ │ cloudflared) │
+└─────────────────┘ └──────────────────┘ └─────────────────┘
+```
+
+**Key Properties:**
+- Desktop initiates outbound connection (no firewall issues)
+- Random URL changes each session (private)
+- PIN prevents unauthorized access
+- Cloudflare handles SSL automatically
+- All communication via WebSocket (real-time)
+
+## User Flow
+
+1. User clicks "Enable Remote Access" in desktop
+2. Desktop starts WebSocket server on `localhost:PORT`
+3. Desktop runs `cloudflared tunnel --url http://localhost:PORT`
+4. Cloudflare generates random URL: `https://abc-xyz.trycloudflare.com`
+5. Desktop generates 6-digit PIN
+6. Desktop displays URL + PIN to user
+7. User opens URL in browser, enters PIN
+8. Web client loads (same renderer UI), all tRPC calls via WebSocket
+
+## Transport Adapter
+
+Same renderer UI, different transport based on environment:
+
+```
+Desktop App Browser
+┌──────────────┐ ┌──────────────┐
+│ Renderer UI │ │ Renderer UI │ ← Same build
+│ (Electron) │ │ (Browser) │
+├──────────────┤ ├──────────────┤
+│ desktopApi │ │ wsAdapter │ ← Different transport
+│ (IPC) │ │ (WebSocket) │
+└──────────────┘ └──────────────┘
+ ↓ ↓
+┌──────────────┐ ┌──────────────┐
+│ Main Process │◄═══════════════════│ WS Server │
+│ (tRPC) │ tunnel │ (cloudflared)│
+└──────────────┘ └──────────────┘
+```
+
+## WebSocket Protocol
+
+### Message Types
+
+```typescript
+// Client → Server
+interface WSRequest {
+ id: string // Request ID for matching response
+ type: "trpc" | "api" // trpc call or desktopApi call
+ method: string // e.g., "claude.sendMessage" or "getUser"
+ params: unknown
+}
+
+// Server → Client
+interface WSResponse {
+ id: string // Match request ID
+ type: "result" | "error" | "stream"
+ data: unknown
+}
+
+// Server → Client (Push/Subscription)
+interface WSPush {
+ id: null // No request ID
+ type: "subscription"
+ channel: string // e.g., "claude.onMessage", "terminal.onOutput"
+ data: unknown
+}
+```
+
+### Connection Flow
+
+```
+1. Client connect → WS handshake
+2. Server ← { type: "auth_required" }
+3. Client → { type: "auth", pin: "123456" }
+4. Server ← { type: "auth_success" } or { type: "auth_failed" }
+5. Client → { id: "1", type: "trpc", method: "projects.list", params: {} }
+6. Server ← { id: "1", type: "result", data: [...] }
+```
+
+### Subscriptions (Real-time)
+
+```
+// Client subscribe
+→ { id: "2", type: "trpc", method: "claude.onMessage", params: { subChatId: "xxx" } }
+
+// Server sends stream continuously
+← { id: null, type: "subscription", channel: "claude.onMessage:xxx", data: { ... } }
+← { id: null, type: "subscription", channel: "claude.onMessage:xxx", data: { ... } }
+
+// Client unsubscribe
+→ { id: "3", type: "unsubscribe", channel: "claude.onMessage:xxx" }
+```
+
+## Cloudflared Management
+
+### Binary Download
+
+Download on first use, store in userData:
+
+```typescript
+const CLOUDFLARED_URLS = {
+ darwin_arm64: "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-arm64.tgz",
+ darwin_x64: "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-amd64.tgz",
+ win32_x64: "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe",
+ linux_x64: "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
+}
+
+// Storage location:
+// ~/Library/Application Support/1Code/bin/cloudflared
+```
+
+### Lifecycle
+
+```
+1. Check cloudflared binary exists
+ - If missing → download + extract
+ - If exists → continue
+
+2. Start WebSocket server on localhost:random_port
+
+3. Spawn cloudflared process
+ cloudflared tunnel --url http://localhost:PORT
+
+4. Parse output for URL
+ "https://random-words.trycloudflare.com"
+
+5. Generate 6-digit PIN
+
+6. Display URL + PIN to user
+```
+
+### Cleanup
+
+```typescript
+async function stopRemoteAccess() {
+ cloudflaredProcess.kill()
+ wsServer.close()
+ pin = null
+}
+
+app.on('before-quit', stopRemoteAccess)
+```
+
+## File Structure
+
+### New Files (Main Process)
+
+```
+src/main/lib/remote-access/
+├── index.ts # Main export
+├── ws-server.ts # WebSocket server + tRPC adapter
+├── cloudflared.ts # Manage cloudflared process
+└── session.ts # PIN auth, connected clients
+```
+
+### New Files (Renderer)
+
+```
+src/renderer/lib/transport/
+├── index.ts # Auto-detect and export transport
+├── electron.ts # IPC transport (existing logic)
+└── websocket.ts # WebSocket transport (new)
+```
+
+## UI Components
+
+### Location
+
+Bottom sidebar icon bar, add Remote Access button:
+
+```
+┌─────────────────────┐
+│ ⚙️ ❓ 📅 │ ← Existing icon bar
+│ [🌐] │ ← Add Remote Access icon
+│ [Feedback] │
+└─────────────────────┘
+```
+
+### State Atom
+
+```typescript
+remoteAccessStateAtom:
+ | { status: "disabled" }
+ | { status: "starting" }
+ | { status: "active", url: string, pin: string, clients: number }
+ | { status: "error", message: string }
+```
+
+### Dialog (Active State)
+
+```
+┌────────────────────────────────────────────┐
+│ 🌐 Remote Access [X] │
+├────────────────────────────────────────────┤
+│ │
+│ Status: ● Active │
+│ │
+│ URL: │
+│ ┌──────────────────────────────────────┐ │
+│ │ https://abc-xyz.trycloudflare.com │ │
+│ │ [Copy] │ │
+│ └──────────────────────────────────────┘ │
+│ │
+│ PIN: │
+│ ┌──────────────────────────────────────┐ │
+│ │ 1 2 3 4 5 6 │ │
+│ └──────────────────────────────────────┘ │
+│ │
+│ Connected clients: 1 │
+│ │
+│ [Disable Remote Access] │
+│ │
+└────────────────────────────────────────────┘
+```
+
+### Dialog (Disabled State)
+
+```
+┌────────────────────────────────────────────┐
+│ 🌐 Remote Access [X] │
+├────────────────────────────────────────────┤
+│ │
+│ Access your desktop from any browser. │
+│ │
+│ [Enable Remote Access] │
+│ │
+└────────────────────────────────────────────┘
+```
+
+## Real-time Features
+
+All desktop real-time features must work in web client:
+
+| Feature | Implementation |
+|---------|----------------|
+| Claude streaming | Subscribe `claude.onMessage` → forward via WS |
+| Terminal output | Subscribe `terminal.onOutput` → forward via WS |
+| Git status changes | Subscribe `git.onStatusChange` → forward via WS |
+| File watcher | Subscribe `files.onFileChange` → forward via WS |
+| Thinking indicator | Real-time status updates |
+| Tool execution | Read, Write, Bash output streaming |
+| File change badges | +157 -180 badge updates |
+| Chat list updates | New chats appear instantly |
+
+## Security
+
+- PIN required for authentication
+- PIN expires with session
+- Session ends when tunnel closes or user disables
+- No persistent credentials stored
+- Random URL per session
diff --git a/docs/plans/2026-02-01-remote-access-implementation.md b/docs/plans/2026-02-01-remote-access-implementation.md
new file mode 100644
index 000000000..6c5034097
--- /dev/null
+++ b/docs/plans/2026-02-01-remote-access-implementation.md
@@ -0,0 +1,1342 @@
+# Remote Access Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Enable users to access their desktop 1Code app from any browser via Cloudflare Tunnel with PIN authentication.
+
+**Architecture:** Desktop starts a WebSocket server, runs cloudflared to create a public tunnel, generates a PIN. Browser loads the same renderer UI but uses WebSocket transport instead of IPC. All tRPC calls and desktopApi calls are bridged through WebSocket.
+
+**Tech Stack:** ws (WebSocket server), cloudflared binary, existing tRPC routers, existing renderer UI
+
+---
+
+## Task 1: Create Session Manager
+
+**Files:**
+- Create: `src/main/lib/remote-access/session.ts`
+
+**Step 1: Create session types and state**
+
+```typescript
+// src/main/lib/remote-access/session.ts
+
+import { randomInt } from "crypto"
+
+export interface RemoteSession {
+ pin: string
+ url: string | null
+ createdAt: Date
+ clients: Set // client IDs
+}
+
+let currentSession: RemoteSession | null = null
+
+/**
+ * Generate a 6-digit PIN
+ */
+export function generatePin(): string {
+ return randomInt(100000, 999999).toString()
+}
+
+/**
+ * Start a new remote session
+ */
+export function createSession(): RemoteSession {
+ currentSession = {
+ pin: generatePin(),
+ url: null,
+ createdAt: new Date(),
+ clients: new Set(),
+ }
+ return currentSession
+}
+
+/**
+ * Get current session
+ */
+export function getSession(): RemoteSession | null {
+ return currentSession
+}
+
+/**
+ * Set tunnel URL after cloudflared starts
+ */
+export function setSessionUrl(url: string): void {
+ if (currentSession) {
+ currentSession.url = url
+ }
+}
+
+/**
+ * Validate PIN
+ */
+export function validatePin(pin: string): boolean {
+ return currentSession?.pin === pin
+}
+
+/**
+ * Add a connected client
+ */
+export function addClient(clientId: string): void {
+ currentSession?.clients.add(clientId)
+}
+
+/**
+ * Remove a connected client
+ */
+export function removeClient(clientId: string): void {
+ currentSession?.clients.delete(clientId)
+}
+
+/**
+ * Get connected client count
+ */
+export function getClientCount(): number {
+ return currentSession?.clients.size ?? 0
+}
+
+/**
+ * End current session
+ */
+export function endSession(): void {
+ currentSession = null
+}
+
+/**
+ * Check if session is active
+ */
+export function isSessionActive(): boolean {
+ return currentSession !== null
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/session.ts
+git commit -m "feat(remote-access): add session manager with PIN generation"
+```
+
+---
+
+## Task 2: Create Cloudflared Manager
+
+**Files:**
+- Create: `src/main/lib/remote-access/cloudflared.ts`
+
+**Step 1: Create cloudflared download and process manager**
+
+```typescript
+// src/main/lib/remote-access/cloudflared.ts
+
+import { spawn, ChildProcess } from "child_process"
+import { createWriteStream, existsSync, chmodSync } from "fs"
+import { mkdir } from "fs/promises"
+import { join } from "path"
+import { app } from "electron"
+import { pipeline } from "stream/promises"
+import { createGunzip } from "zlib"
+import { extract } from "tar"
+
+const CLOUDFLARED_VERSION = "2024.1.5"
+
+const DOWNLOAD_URLS: Record = {
+ "darwin-arm64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-arm64.tgz`,
+ "darwin-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-amd64.tgz`,
+ "win32-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,
+ "linux-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,
+}
+
+let cloudflaredProcess: ChildProcess | null = null
+
+/**
+ * Get the path where cloudflared binary should be stored
+ */
+export function getCloudflaredPath(): string {
+ const binDir = join(app.getPath("userData"), "bin")
+ const ext = process.platform === "win32" ? ".exe" : ""
+ return join(binDir, `cloudflared${ext}`)
+}
+
+/**
+ * Check if cloudflared is already downloaded
+ */
+export function isCloudflaredInstalled(): boolean {
+ return existsSync(getCloudflaredPath())
+}
+
+/**
+ * Download cloudflared binary for current platform
+ */
+export async function downloadCloudflared(
+ onProgress?: (percent: number) => void
+): Promise {
+ const platform = process.platform
+ const arch = process.arch
+ const key = `${platform}-${arch}`
+
+ const url = DOWNLOAD_URLS[key]
+ if (!url) {
+ throw new Error(`Unsupported platform: ${key}`)
+ }
+
+ const binDir = join(app.getPath("userData"), "bin")
+ await mkdir(binDir, { recursive: true })
+
+ const cloudflaredPath = getCloudflaredPath()
+ const isTgz = url.endsWith(".tgz")
+
+ console.log(`[Cloudflared] Downloading from ${url}`)
+
+ const response = await fetch(url)
+ if (!response.ok) {
+ throw new Error(`Failed to download: ${response.statusText}`)
+ }
+
+ const contentLength = parseInt(response.headers.get("content-length") || "0")
+ let downloaded = 0
+
+ if (isTgz) {
+ // For .tgz files, extract directly
+ const tempPath = join(binDir, "cloudflared.tgz")
+ const fileStream = createWriteStream(tempPath)
+
+ const reader = response.body?.getReader()
+ if (!reader) throw new Error("No response body")
+
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ fileStream.write(value)
+ downloaded += value.length
+ if (contentLength && onProgress) {
+ onProgress(Math.round((downloaded / contentLength) * 100))
+ }
+ }
+ fileStream.end()
+
+ // Wait for file to be written
+ await new Promise((resolve) => fileStream.on("finish", resolve))
+
+ // Extract tar.gz
+ await extract({
+ file: tempPath,
+ cwd: binDir,
+ })
+
+ // Clean up temp file
+ const { unlink } = await import("fs/promises")
+ await unlink(tempPath)
+ } else {
+ // For direct executables (Windows, Linux)
+ const fileStream = createWriteStream(cloudflaredPath)
+ const reader = response.body?.getReader()
+ if (!reader) throw new Error("No response body")
+
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ fileStream.write(value)
+ downloaded += value.length
+ if (contentLength && onProgress) {
+ onProgress(Math.round((downloaded / contentLength) * 100))
+ }
+ }
+ fileStream.end()
+ await new Promise((resolve) => fileStream.on("finish", resolve))
+ }
+
+ // Make executable on Unix
+ if (process.platform !== "win32") {
+ chmodSync(cloudflaredPath, 0o755)
+ }
+
+ console.log(`[Cloudflared] Downloaded to ${cloudflaredPath}`)
+}
+
+/**
+ * Start cloudflared tunnel
+ * Returns the public URL when ready
+ */
+export function startTunnel(localPort: number): Promise {
+ return new Promise((resolve, reject) => {
+ const cloudflaredPath = getCloudflaredPath()
+
+ if (!existsSync(cloudflaredPath)) {
+ reject(new Error("Cloudflared not installed"))
+ return
+ }
+
+ console.log(`[Cloudflared] Starting tunnel to localhost:${localPort}`)
+
+ cloudflaredProcess = spawn(cloudflaredPath, [
+ "tunnel",
+ "--url",
+ `http://localhost:${localPort}`,
+ ])
+
+ let urlFound = false
+
+ cloudflaredProcess.stderr?.on("data", (data: Buffer) => {
+ const output = data.toString()
+ console.log(`[Cloudflared] ${output}`)
+
+ // Parse URL from output
+ // Example: "INF +-----------------------------------------------------------+"
+ // followed by "INF | https://random-words.trycloudflare.com |"
+ const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i)
+ if (urlMatch && !urlFound) {
+ urlFound = true
+ resolve(urlMatch[0])
+ }
+ })
+
+ cloudflaredProcess.on("error", (err) => {
+ console.error("[Cloudflared] Process error:", err)
+ reject(err)
+ })
+
+ cloudflaredProcess.on("exit", (code) => {
+ console.log(`[Cloudflared] Process exited with code ${code}`)
+ cloudflaredProcess = null
+ if (!urlFound) {
+ reject(new Error(`Cloudflared exited with code ${code}`))
+ }
+ })
+
+ // Timeout after 30 seconds
+ setTimeout(() => {
+ if (!urlFound) {
+ stopTunnel()
+ reject(new Error("Timeout waiting for tunnel URL"))
+ }
+ }, 30000)
+ })
+}
+
+/**
+ * Stop cloudflared tunnel
+ */
+export function stopTunnel(): void {
+ if (cloudflaredProcess) {
+ console.log("[Cloudflared] Stopping tunnel")
+ cloudflaredProcess.kill()
+ cloudflaredProcess = null
+ }
+}
+
+/**
+ * Check if tunnel is running
+ */
+export function isTunnelRunning(): boolean {
+ return cloudflaredProcess !== null
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/cloudflared.ts
+git commit -m "feat(remote-access): add cloudflared download and tunnel manager"
+```
+
+---
+
+## Task 3: Create WebSocket Server with tRPC Bridge
+
+**Files:**
+- Create: `src/main/lib/remote-access/ws-server.ts`
+
+**Step 1: Create WebSocket message types**
+
+```typescript
+// src/main/lib/remote-access/ws-server.ts
+
+import { WebSocketServer, WebSocket } from "ws"
+import { createServer, Server, IncomingMessage } from "http"
+import { parse } from "url"
+import { randomUUID } from "crypto"
+import { validatePin, addClient, removeClient } from "./session"
+import { createAppRouter } from "../trpc/routers"
+import { BrowserWindow } from "electron"
+import superjson from "superjson"
+
+// Message types
+interface WSRequest {
+ id: string
+ type: "auth" | "trpc" | "api" | "subscribe" | "unsubscribe"
+ method?: string
+ params?: unknown
+ pin?: string
+ channel?: string
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "result" | "error" | "subscription"
+ data?: unknown
+ error?: string
+ channel?: string
+}
+
+interface AuthenticatedClient {
+ ws: WebSocket
+ id: string
+ subscriptions: Set
+}
+
+let httpServer: Server | null = null
+let wss: WebSocketServer | null = null
+const authenticatedClients = new Map()
+
+// Subscription handlers - map of channel pattern to active subscriptions
+const subscriptionCleanups = new Map void>()
+
+/**
+ * Send message to a WebSocket client
+ */
+function send(ws: WebSocket, message: WSResponse): void {
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify(message))
+ }
+}
+
+/**
+ * Broadcast to all authenticated clients
+ */
+export function broadcast(channel: string, data: unknown): void {
+ const message: WSResponse = {
+ id: null,
+ type: "subscription",
+ channel,
+ data,
+ }
+
+ for (const [ws, client] of authenticatedClients) {
+ if (client.subscriptions.has(channel)) {
+ send(ws, message)
+ }
+ }
+}
+
+/**
+ * Handle incoming WebSocket message
+ */
+async function handleMessage(
+ ws: WebSocket,
+ client: AuthenticatedClient | undefined,
+ message: WSRequest,
+ router: ReturnType
+): Promise {
+ // Auth message
+ if (message.type === "auth") {
+ if (message.pin && validatePin(message.pin)) {
+ const clientId = randomUUID()
+ const newClient: AuthenticatedClient = {
+ ws,
+ id: clientId,
+ subscriptions: new Set(),
+ }
+ authenticatedClients.set(ws, newClient)
+ addClient(clientId)
+ send(ws, { id: message.id, type: "auth_success" })
+ console.log(`[WS] Client authenticated: ${clientId}`)
+ } else {
+ send(ws, { id: message.id, type: "auth_failed", error: "Invalid PIN" })
+ }
+ return
+ }
+
+ // All other messages require authentication
+ if (!client) {
+ send(ws, { id: message.id, type: "auth_required" })
+ return
+ }
+
+ // tRPC call
+ if (message.type === "trpc" && message.method) {
+ try {
+ const [routerName, procedureName] = message.method.split(".")
+
+ // Get the router and procedure
+ const routerObj = (router as any)[routerName]
+ if (!routerObj) {
+ throw new Error(`Router not found: ${routerName}`)
+ }
+
+ // Create a caller for the procedure
+ const caller = router.createCaller({
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ })
+
+ // Call the procedure
+ const routerCaller = (caller as any)[routerName]
+ if (!routerCaller || !routerCaller[procedureName]) {
+ throw new Error(`Procedure not found: ${message.method}`)
+ }
+
+ const result = await routerCaller[procedureName](message.params)
+
+ send(ws, {
+ id: message.id,
+ type: "result",
+ data: superjson.serialize(result),
+ })
+ } catch (error) {
+ send(ws, {
+ id: message.id,
+ type: "error",
+ error: error instanceof Error ? error.message : "Unknown error",
+ })
+ }
+ return
+ }
+
+ // Subscribe
+ if (message.type === "subscribe" && message.channel) {
+ client.subscriptions.add(message.channel)
+ send(ws, { id: message.id, type: "result", data: { subscribed: message.channel } })
+ return
+ }
+
+ // Unsubscribe
+ if (message.type === "unsubscribe" && message.channel) {
+ client.subscriptions.delete(message.channel)
+ send(ws, { id: message.id, type: "result", data: { unsubscribed: message.channel } })
+ return
+ }
+
+ // Unknown message type
+ send(ws, { id: message.id, type: "error", error: "Unknown message type" })
+}
+
+/**
+ * Start WebSocket server
+ * Returns the port number
+ */
+export function startWSServer(): Promise {
+ return new Promise((resolve, reject) => {
+ // Create HTTP server for WebSocket upgrade
+ httpServer = createServer((req, res) => {
+ // Serve static files for web client
+ // TODO: Implement static file serving in Task 6
+ res.writeHead(200, { "Content-Type": "text/plain" })
+ res.end("1Code Remote Access")
+ })
+
+ wss = new WebSocketServer({ server: httpServer })
+
+ // Create tRPC router
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+
+ wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
+ console.log("[WS] New connection from:", req.socket.remoteAddress)
+
+ // Send auth required message
+ send(ws, { id: null, type: "auth_required" })
+
+ ws.on("message", async (data: Buffer) => {
+ try {
+ const message = JSON.parse(data.toString()) as WSRequest
+ const client = authenticatedClients.get(ws)
+ await handleMessage(ws, client, message, router)
+ } catch (error) {
+ console.error("[WS] Message parse error:", error)
+ send(ws, { id: null, type: "error", error: "Invalid message format" })
+ }
+ })
+
+ ws.on("close", () => {
+ const client = authenticatedClients.get(ws)
+ if (client) {
+ removeClient(client.id)
+ authenticatedClients.delete(ws)
+ console.log(`[WS] Client disconnected: ${client.id}`)
+ }
+ })
+
+ ws.on("error", (error) => {
+ console.error("[WS] WebSocket error:", error)
+ })
+ })
+
+ // Listen on random available port
+ httpServer.listen(0, () => {
+ const address = httpServer?.address()
+ if (address && typeof address === "object") {
+ console.log(`[WS] Server listening on port ${address.port}`)
+ resolve(address.port)
+ } else {
+ reject(new Error("Failed to get server port"))
+ }
+ })
+
+ httpServer.on("error", reject)
+ })
+}
+
+/**
+ * Stop WebSocket server
+ */
+export function stopWSServer(): Promise {
+ return new Promise((resolve) => {
+ // Close all client connections
+ for (const [ws] of authenticatedClients) {
+ ws.close()
+ }
+ authenticatedClients.clear()
+
+ // Clean up subscriptions
+ for (const cleanup of subscriptionCleanups.values()) {
+ cleanup()
+ }
+ subscriptionCleanups.clear()
+
+ // Close servers
+ wss?.close(() => {
+ httpServer?.close(() => {
+ wss = null
+ httpServer = null
+ console.log("[WS] Server stopped")
+ resolve()
+ })
+ })
+ })
+}
+
+/**
+ * Check if server is running
+ */
+export function isWSServerRunning(): boolean {
+ return httpServer !== null
+}
+
+/**
+ * Get connected client count
+ */
+export function getConnectedClientCount(): number {
+ return authenticatedClients.size
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/ws-server.ts
+git commit -m "feat(remote-access): add WebSocket server with tRPC bridge"
+```
+
+---
+
+## Task 4: Create Remote Access Controller
+
+**Files:**
+- Create: `src/main/lib/remote-access/index.ts`
+
+**Step 1: Create main controller that orchestrates all components**
+
+```typescript
+// src/main/lib/remote-access/index.ts
+
+import {
+ createSession,
+ endSession,
+ getSession,
+ setSessionUrl,
+ isSessionActive,
+ getClientCount,
+} from "./session"
+import {
+ isCloudflaredInstalled,
+ downloadCloudflared,
+ startTunnel,
+ stopTunnel,
+ isTunnelRunning,
+} from "./cloudflared"
+import {
+ startWSServer,
+ stopWSServer,
+ isWSServerRunning,
+ getConnectedClientCount,
+ broadcast,
+} from "./ws-server"
+import { BrowserWindow } from "electron"
+
+export type RemoteAccessStatus =
+ | { status: "disabled" }
+ | { status: "downloading"; progress: number }
+ | { status: "starting" }
+ | { status: "active"; url: string; pin: string; clients: number }
+ | { status: "error"; message: string }
+
+let currentStatus: RemoteAccessStatus = { status: "disabled" }
+let statusListeners: ((status: RemoteAccessStatus) => void)[] = []
+
+/**
+ * Get current status
+ */
+export function getRemoteAccessStatus(): RemoteAccessStatus {
+ if (isSessionActive() && isTunnelRunning()) {
+ const session = getSession()
+ if (session?.url) {
+ return {
+ status: "active",
+ url: session.url,
+ pin: session.pin,
+ clients: getConnectedClientCount(),
+ }
+ }
+ }
+ return currentStatus
+}
+
+/**
+ * Set status and notify listeners
+ */
+function setStatus(status: RemoteAccessStatus): void {
+ currentStatus = status
+ for (const listener of statusListeners) {
+ listener(status)
+ }
+ // Also notify via IPC to all windows
+ for (const win of BrowserWindow.getAllWindows()) {
+ if (!win.isDestroyed()) {
+ win.webContents.send("remote-access:status", status)
+ }
+ }
+}
+
+/**
+ * Subscribe to status changes
+ */
+export function onStatusChange(
+ listener: (status: RemoteAccessStatus) => void
+): () => void {
+ statusListeners.push(listener)
+ return () => {
+ statusListeners = statusListeners.filter((l) => l !== listener)
+ }
+}
+
+/**
+ * Enable remote access
+ */
+export async function enableRemoteAccess(): Promise {
+ if (isSessionActive()) {
+ throw new Error("Remote access already enabled")
+ }
+
+ try {
+ // Check if cloudflared is installed
+ if (!isCloudflaredInstalled()) {
+ setStatus({ status: "downloading", progress: 0 })
+ await downloadCloudflared((progress) => {
+ setStatus({ status: "downloading", progress })
+ })
+ }
+
+ setStatus({ status: "starting" })
+
+ // Create session
+ const session = createSession()
+
+ // Start WebSocket server
+ const port = await startWSServer()
+
+ // Start cloudflared tunnel
+ const url = await startTunnel(port)
+ setSessionUrl(url)
+
+ setStatus({
+ status: "active",
+ url,
+ pin: session.pin,
+ clients: 0,
+ })
+
+ console.log(`[RemoteAccess] Enabled - URL: ${url}, PIN: ${session.pin}`)
+ } catch (error) {
+ await disableRemoteAccess()
+ const message = error instanceof Error ? error.message : "Unknown error"
+ setStatus({ status: "error", message })
+ throw error
+ }
+}
+
+/**
+ * Disable remote access
+ */
+export async function disableRemoteAccess(): Promise {
+ stopTunnel()
+ await stopWSServer()
+ endSession()
+ setStatus({ status: "disabled" })
+ console.log("[RemoteAccess] Disabled")
+}
+
+/**
+ * Refresh client count (called periodically or on connection change)
+ */
+export function refreshClientCount(): void {
+ if (currentStatus.status === "active") {
+ setStatus({
+ ...currentStatus,
+ clients: getConnectedClientCount(),
+ })
+ }
+}
+
+// Re-export broadcast for use by other modules
+export { broadcast }
+
+// Re-export for type checking
+export type { RemoteAccessStatus as RemoteAccessState }
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/lib/remote-access/index.ts
+git commit -m "feat(remote-access): add main controller"
+```
+
+---
+
+## Task 5: Add tRPC Router for Remote Access
+
+**Files:**
+- Create: `src/main/lib/trpc/routers/remote-access.ts`
+- Modify: `src/main/lib/trpc/routers/index.ts`
+
+**Step 1: Create remote access router**
+
+```typescript
+// src/main/lib/trpc/routers/remote-access.ts
+
+import { z } from "zod"
+import { router, publicProcedure } from "../index"
+import {
+ enableRemoteAccess,
+ disableRemoteAccess,
+ getRemoteAccessStatus,
+} from "../../remote-access"
+
+export const remoteAccessRouter = router({
+ /**
+ * Get current remote access status
+ */
+ getStatus: publicProcedure.query(() => {
+ return getRemoteAccessStatus()
+ }),
+
+ /**
+ * Enable remote access
+ */
+ enable: publicProcedure.mutation(async () => {
+ await enableRemoteAccess()
+ return getRemoteAccessStatus()
+ }),
+
+ /**
+ * Disable remote access
+ */
+ disable: publicProcedure.mutation(async () => {
+ await disableRemoteAccess()
+ return getRemoteAccessStatus()
+ }),
+})
+```
+
+**Step 2: Add router to app router**
+
+Edit `src/main/lib/trpc/routers/index.ts`:
+
+```typescript
+// Add import at top
+import { remoteAccessRouter } from "./remote-access"
+
+// Add to router object in createAppRouter function
+export function createAppRouter(getWindow: () => BrowserWindow | null) {
+ return router({
+ // ... existing routers ...
+ remoteAccess: remoteAccessRouter, // Add this line
+ })
+}
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/main/lib/trpc/routers/remote-access.ts src/main/lib/trpc/routers/index.ts
+git commit -m "feat(remote-access): add tRPC router for remote access control"
+```
+
+---
+
+## Task 6: Add IPC Handlers for Remote Access Events
+
+**Files:**
+- Modify: `src/main/windows/main.ts`
+- Modify: `src/preload/index.ts`
+
+**Step 1: Add IPC handlers in main.ts**
+
+Add to `src/main/windows/main.ts` in the IPC handlers section:
+
+```typescript
+// Remote access IPC handlers
+ipcMain.handle("remote-access:get-status", () => {
+ const { getRemoteAccessStatus } = require("../lib/remote-access")
+ return getRemoteAccessStatus()
+})
+
+ipcMain.handle("remote-access:enable", async () => {
+ const { enableRemoteAccess, getRemoteAccessStatus } = require("../lib/remote-access")
+ await enableRemoteAccess()
+ return getRemoteAccessStatus()
+})
+
+ipcMain.handle("remote-access:disable", async () => {
+ const { disableRemoteAccess, getRemoteAccessStatus } = require("../lib/remote-access")
+ await disableRemoteAccess()
+ return getRemoteAccessStatus()
+})
+```
+
+**Step 2: Add to preload**
+
+Add to `src/preload/index.ts` in the desktopApi object:
+
+```typescript
+// Remote access
+getRemoteAccessStatus: () => ipcRenderer.invoke("remote-access:get-status"),
+enableRemoteAccess: () => ipcRenderer.invoke("remote-access:enable"),
+disableRemoteAccess: () => ipcRenderer.invoke("remote-access:disable"),
+onRemoteAccessStatusChange: (callback: (status: any) => void) => {
+ const handler = (_event: unknown, status: any) => callback(status)
+ ipcRenderer.on("remote-access:status", handler)
+ return () => ipcRenderer.removeListener("remote-access:status", handler)
+},
+```
+
+**Step 3: Add types to DesktopApi interface**
+
+Add to the DesktopApi interface in `src/preload/index.ts`:
+
+```typescript
+// Remote access
+getRemoteAccessStatus: () => Promise<{
+ status: "disabled" | "downloading" | "starting" | "active" | "error"
+ progress?: number
+ url?: string
+ pin?: string
+ clients?: number
+ message?: string
+}>
+enableRemoteAccess: () => Promise
+disableRemoteAccess: () => Promise
+onRemoteAccessStatusChange: (callback: (status: any) => void) => () => void
+```
+
+**Step 4: Commit**
+
+```bash
+git add src/main/windows/main.ts src/preload/index.ts
+git commit -m "feat(remote-access): add IPC handlers and preload bridge"
+```
+
+---
+
+## Task 7: Create Remote Access UI Atom
+
+**Files:**
+- Create: `src/renderer/lib/atoms/remote-access.ts`
+- Modify: `src/renderer/lib/atoms/index.ts`
+
+**Step 1: Create remote access atoms**
+
+```typescript
+// src/renderer/lib/atoms/remote-access.ts
+
+import { atom } from "jotai"
+
+export type RemoteAccessStatus =
+ | { status: "disabled" }
+ | { status: "downloading"; progress: number }
+ | { status: "starting" }
+ | { status: "active"; url: string; pin: string; clients: number }
+ | { status: "error"; message: string }
+
+export const remoteAccessStatusAtom = atom({ status: "disabled" })
+
+export const remoteAccessDialogOpenAtom = atom(false)
+```
+
+**Step 2: Export from index**
+
+Add to `src/renderer/lib/atoms/index.ts`:
+
+```typescript
+export * from "./remote-access"
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/renderer/lib/atoms/remote-access.ts src/renderer/lib/atoms/index.ts
+git commit -m "feat(remote-access): add UI atoms for remote access state"
+```
+
+---
+
+## Task 8: Create Remote Access Dialog Component
+
+**Files:**
+- Create: `src/renderer/components/dialogs/remote-access-dialog.tsx`
+
+**Step 1: Create dialog component**
+
+```typescript
+// src/renderer/components/dialogs/remote-access-dialog.tsx
+
+import { useAtom } from "jotai"
+import { useEffect, useCallback } from "react"
+import { Globe, Copy, Check, Loader2, X } from "lucide-react"
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
+import { Button } from "../ui/button"
+import {
+ remoteAccessStatusAtom,
+ remoteAccessDialogOpenAtom,
+ type RemoteAccessStatus,
+} from "../../lib/atoms/remote-access"
+import { toast } from "sonner"
+
+export function RemoteAccessDialog() {
+ const [open, setOpen] = useAtom(remoteAccessDialogOpenAtom)
+ const [status, setStatus] = useAtom(remoteAccessStatusAtom)
+
+ // Fetch initial status and subscribe to changes
+ useEffect(() => {
+ if (!window.desktopApi) return
+
+ // Get initial status
+ window.desktopApi.getRemoteAccessStatus?.().then(setStatus)
+
+ // Subscribe to status changes
+ const unsubscribe = window.desktopApi.onRemoteAccessStatusChange?.(setStatus)
+ return unsubscribe
+ }, [setStatus])
+
+ const handleEnable = useCallback(async () => {
+ try {
+ await window.desktopApi?.enableRemoteAccess?.()
+ } catch (error) {
+ toast.error("Failed to enable remote access")
+ }
+ }, [])
+
+ const handleDisable = useCallback(async () => {
+ try {
+ await window.desktopApi?.disableRemoteAccess?.()
+ } catch (error) {
+ toast.error("Failed to disable remote access")
+ }
+ }, [])
+
+ const handleCopyUrl = useCallback(() => {
+ if (status.status === "active") {
+ navigator.clipboard.writeText(status.url)
+ toast.success("URL copied to clipboard")
+ }
+ }, [status])
+
+ const handleCopyPin = useCallback(() => {
+ if (status.status === "active") {
+ navigator.clipboard.writeText(status.pin)
+ toast.success("PIN copied to clipboard")
+ }
+ }, [status])
+
+ return (
+
+
+
+
+
+ Remote Access
+
+
+
+
+ {status.status === "disabled" && (
+ <>
+
+ Access your desktop from any browser.
+
+
+ Enable Remote Access
+
+ >
+ )}
+
+ {status.status === "downloading" && (
+
+
+
+ Downloading cloudflared...
+
+
+
+ )}
+
+ {status.status === "starting" && (
+
+
+ Starting tunnel...
+
+ )}
+
+ {status.status === "active" && (
+ <>
+
+
+
+
URL
+
+
+ {status.url}
+
+
+
+
+
+
+
+
+
PIN
+
+
+ {status.pin}
+
+
+
+
+
+
+
+
+ Connected clients: {status.clients}
+
+
+
+ Disable Remote Access
+
+ >
+ )}
+
+ {status.status === "error" && (
+ <>
+
+
+ {status.message}
+
+
+ Try Again
+
+ >
+ )}
+
+
+
+ )
+}
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/renderer/components/dialogs/remote-access-dialog.tsx
+git commit -m "feat(remote-access): add remote access dialog component"
+```
+
+---
+
+## Task 9: Add Remote Access Button to Sidebar
+
+**Files:**
+- Modify: `src/renderer/features/sidebar/agents-sidebar.tsx`
+
+**Step 1: Find the sidebar footer icons section**
+
+Search for the icon bar area (near settings, help icons) and add remote access button:
+
+```typescript
+// Import at top
+import { Globe } from "lucide-react"
+import { useSetAtom } from "jotai"
+import { remoteAccessDialogOpenAtom } from "../../lib/atoms/remote-access"
+
+// In the component, add:
+const setRemoteAccessDialogOpen = useSetAtom(remoteAccessDialogOpenAtom)
+
+// In the icon bar section (footer), add button:
+ setRemoteAccessDialogOpen(true)}
+ title="Remote Access"
+>
+
+
+```
+
+**Step 2: Import dialog in layout**
+
+Add to `src/renderer/features/layout/agents-layout.tsx`:
+
+```typescript
+// Import at top
+import { RemoteAccessDialog } from "../../components/dialogs/remote-access-dialog"
+
+// Add inside the return, after ClaudeLoginModal:
+
+```
+
+**Step 3: Commit**
+
+```bash
+git add src/renderer/features/sidebar/agents-sidebar.tsx src/renderer/features/layout/agents-layout.tsx
+git commit -m "feat(remote-access): add remote access button to sidebar"
+```
+
+---
+
+## Task 10: Add Cleanup on App Quit
+
+**Files:**
+- Modify: `src/main/index.ts`
+
+**Step 1: Add cleanup in before-quit handler**
+
+Find the `app.on("before-quit", ...)` section and add:
+
+```typescript
+// Import at top
+import { disableRemoteAccess } from "./lib/remote-access"
+
+// In before-quit handler, add before other cleanup:
+app.on("before-quit", async () => {
+ console.log("[App] Shutting down...")
+ isQuitting = true
+ ;(global as any).__isAppQuitting = () => true
+
+ // Disable remote access first
+ try {
+ await disableRemoteAccess()
+ } catch (error) {
+ console.warn("[App] Failed to disable remote access:", error)
+ }
+
+ destroyTray()
+ // ... rest of cleanup
+})
+```
+
+**Step 2: Commit**
+
+```bash
+git add src/main/index.ts
+git commit -m "feat(remote-access): add cleanup on app quit"
+```
+
+---
+
+## Task 11: Install ws Package
+
+**Files:**
+- Modify: `package.json`
+
+**Step 1: Add ws dependency**
+
+```bash
+bun add ws
+bun add -d @types/ws
+```
+
+**Step 2: Commit**
+
+```bash
+git add package.json bun.lockb
+git commit -m "feat(remote-access): add ws package for WebSocket server"
+```
+
+---
+
+## Task 12: Test Remote Access Feature
+
+**Step 1: Run the app in dev mode**
+
+```bash
+bun run dev
+```
+
+**Step 2: Test enable remote access**
+
+1. Click the Globe icon in sidebar footer
+2. Click "Enable Remote Access"
+3. Wait for cloudflared to download (first time only)
+4. Wait for tunnel to start
+5. Verify URL and PIN are displayed
+
+**Step 3: Test web client access**
+
+1. Open the URL in another browser
+2. Verify you see a page (for now just "1Code Remote Access" text)
+3. Test WebSocket connection (will implement full UI in future tasks)
+
+**Step 4: Test disable**
+
+1. Click "Disable Remote Access"
+2. Verify status returns to disabled
+3. Verify tunnel is stopped
+
+---
+
+## Summary
+
+| Task | Description | Files |
+|------|-------------|-------|
+| 1 | Session Manager | `session.ts` |
+| 2 | Cloudflared Manager | `cloudflared.ts` |
+| 3 | WebSocket Server | `ws-server.ts` |
+| 4 | Remote Access Controller | `index.ts` |
+| 5 | tRPC Router | `remote-access.ts`, `routers/index.ts` |
+| 6 | IPC Handlers | `main.ts`, `preload/index.ts` |
+| 7 | UI Atoms | `atoms/remote-access.ts` |
+| 8 | Dialog Component | `remote-access-dialog.tsx` |
+| 9 | Sidebar Button | `agents-sidebar.tsx`, `agents-layout.tsx` |
+| 10 | App Cleanup | `index.ts` |
+| 11 | Dependencies | `package.json` |
+| 12 | Testing | Manual test |
+
+---
+
+## Future Tasks (Not in This Plan)
+
+1. **Web Client Transport** - Create WebSocket transport adapter for renderer
+2. **Static File Serving** - Serve built renderer UI through WS server
+3. **Real-time Subscriptions** - Bridge Claude streaming, terminal, git status
+4. **PIN Auth UI in Browser** - Create PIN entry page for web client
+5. **Session Timeout** - Auto-disable after inactivity
diff --git a/drizzle.config.ts b/drizzle.config.ts
index 08fab6e69..efa1a903b 100644
--- a/drizzle.config.ts
+++ b/drizzle.config.ts
@@ -1,7 +1,15 @@
import { defineConfig } from "drizzle-kit"
+import { homedir } from "os"
+import { join } from "path"
+
+// Dev database path (matches Electron's userData for dev mode)
+const devDbPath = join(homedir(), "Library/Application Support/Agents Dev/data/agents.db")
export default defineConfig({
schema: "./src/main/lib/db/schema/index.ts",
out: "./drizzle",
dialect: "sqlite",
+ dbCredentials: {
+ url: devDbPath,
+ },
})
diff --git a/drizzle/0008_add_model_profile_id.sql b/drizzle/0008_add_model_profile_id.sql
new file mode 100644
index 000000000..4dba051fd
--- /dev/null
+++ b/drizzle/0008_add_model_profile_id.sql
@@ -0,0 +1,2 @@
+-- Add model_profile_id column to chats table
+ALTER TABLE `chats` ADD `model_profile_id` text;
diff --git a/drizzle/0009_add_selected_model_id.sql b/drizzle/0009_add_selected_model_id.sql
new file mode 100644
index 000000000..491cd1fbe
--- /dev/null
+++ b/drizzle/0009_add_selected_model_id.sql
@@ -0,0 +1,2 @@
+-- Add selected_model_id column to chats table
+ALTER TABLE `chats` ADD `selected_model_id` text;
diff --git a/drizzle/0010_cold_masked_marvel.sql b/drizzle/0010_cold_masked_marvel.sql
new file mode 100644
index 000000000..43e0e4faf
--- /dev/null
+++ b/drizzle/0010_cold_masked_marvel.sql
@@ -0,0 +1,18 @@
+CREATE TABLE `model_profile_settings` (
+ `id` text PRIMARY KEY DEFAULT 'singleton' NOT NULL,
+ `last_used_profile_id` text,
+ `updated_at` integer
+);
+--> statement-breakpoint
+CREATE TABLE `model_profiles` (
+ `id` text PRIMARY KEY NOT NULL,
+ `name` text NOT NULL,
+ `config` text NOT NULL,
+ `models` text DEFAULT '[]' NOT NULL,
+ `is_offline` integer DEFAULT false NOT NULL,
+ `created_at` integer,
+ `updated_at` integer
+);
+--> statement-breakpoint
+ALTER TABLE `chats` ADD `model_profile_id` text;--> statement-breakpoint
+ALTER TABLE `chats` ADD `selected_model_id` text;
\ No newline at end of file
diff --git a/drizzle/meta/0010_snapshot.json b/drizzle/meta/0010_snapshot.json
new file mode 100644
index 000000000..c87c66a2f
--- /dev/null
+++ b/drizzle/meta/0010_snapshot.json
@@ -0,0 +1,541 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "8fe29cf3-6ce4-4de5-b897-767c62effb23",
+ "prevId": "b2d2d602-5de1-43b1-ada8-c9ed3edde22d",
+ "tables": {
+ "anthropic_accounts": {
+ "name": "anthropic_accounts",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "oauth_token": {
+ "name": "oauth_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "desktop_user_id": {
+ "name": "desktop_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "anthropic_settings": {
+ "name": "anthropic_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'singleton'"
+ },
+ "active_account_id": {
+ "name": "active_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "chats": {
+ "name": "chats",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "worktree_path": {
+ "name": "worktree_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "base_branch": {
+ "name": "base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "model_profile_id": {
+ "name": "model_profile_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "selected_model_id": {
+ "name": "selected_model_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "chats_worktree_path_idx": {
+ "name": "chats_worktree_path_idx",
+ "columns": [
+ "worktree_path"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "chats_project_id_projects_id_fk": {
+ "name": "chats_project_id_projects_id_fk",
+ "tableFrom": "chats",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "claude_code_credentials": {
+ "name": "claude_code_credentials",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'default'"
+ },
+ "oauth_token": {
+ "name": "oauth_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "model_profile_settings": {
+ "name": "model_profile_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'singleton'"
+ },
+ "last_used_profile_id": {
+ "name": "last_used_profile_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "model_profiles": {
+ "name": "model_profiles",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "models": {
+ "name": "models",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "is_offline": {
+ "name": "is_offline",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "projects": {
+ "name": "projects",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "git_remote_url": {
+ "name": "git_remote_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "git_provider": {
+ "name": "git_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "git_owner": {
+ "name": "git_owner",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "git_repo": {
+ "name": "git_repo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_path": {
+ "name": "icon_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "projects_path_unique": {
+ "name": "projects_path_unique",
+ "columns": [
+ "path"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sub_chats": {
+ "name": "sub_chats",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "chat_id": {
+ "name": "chat_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "stream_id": {
+ "name": "stream_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'agent'"
+ },
+ "messages": {
+ "name": "messages",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sub_chats_chat_id_chats_id_fk": {
+ "name": "sub_chats_chat_id_chats_id_fk",
+ "tableFrom": "sub_chats",
+ "tableTo": "chats",
+ "columnsFrom": [
+ "chat_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index 88a3e0a60..861180bf1 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -57,6 +57,27 @@
"when": 1769810815497,
"tag": "0007_clammy_grim_reaper",
"breakpoints": true
+ },
+ {
+ "idx": 8,
+ "version": "6",
+ "when": 1769999999999,
+ "tag": "0008_add_model_profile_id",
+ "breakpoints": true
+ },
+ {
+ "idx": 9,
+ "version": "6",
+ "when": 1769999999999,
+ "tag": "0009_add_selected_model_id",
+ "breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "6",
+ "when": 1769994872133,
+ "tag": "0010_cold_masked_marvel",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 48e649c67..4c561c561 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,7 @@
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
+ "@radix-ui/react-visually-hidden": "^1.2.4",
"@sentry/electron": "^7.5.0",
"@tailwindcss/typography": "^0.5.19",
"@tanstack/react-query": "^5.90.10",
@@ -108,6 +109,7 @@
"tailwindcss-animate": "^1.0.7",
"trpc-electron": "^0.1.2",
"unique-names-generator": "^4.7.1",
+ "ws": "^8.19.0",
"xterm": "^5.3.0",
"zod": "^3.24.1",
"zustand": "^5.0.3"
@@ -115,18 +117,19 @@
"devDependencies": {
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^4.0.0",
+ "@electron/rebuild": "^4.0.3",
"@types/better-sqlite3": "^7.6.13",
"@types/diff": "^8.0.0",
"@types/node": "^20.17.50",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
+ "@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.3.4",
"@welldone-software/why-did-you-render": "^10.0.1",
"autoprefixer": "^10.4.20",
"drizzle-kit": "^0.31.8",
"electron": "~39.4.0",
"electron-builder": "^25.1.8",
- "@electron/rebuild": "^4.0.3",
"electron-vite": "^3.0.0",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17",
@@ -167,6 +170,10 @@
{
"from": "resources/bin/VERSION",
"to": "bin/VERSION"
+ },
+ {
+ "from": "build/trayTemplate.png",
+ "to": "trayTemplate.png"
}
],
"asar": true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 000000000..13835d6e1
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,11295 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@ai-sdk/react':
+ specifier: ^3.0.14
+ version: 3.0.51(react@19.2.1)(zod@3.25.76)
+ '@anthropic-ai/claude-agent-sdk':
+ specifier: 0.2.25
+ version: 0.2.25(zod@3.25.76)
+ '@git-diff-view/react':
+ specifier: ^0.0.35
+ version: 0.0.35(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@git-diff-view/shiki':
+ specifier: ^0.0.36
+ version: 0.0.36
+ '@modelcontextprotocol/sdk':
+ specifier: ^1.25.3
+ version: 1.25.3(hono@4.11.5)(zod@3.25.76)
+ '@monaco-editor/react':
+ specifier: ^4.7.0
+ version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@pierre/diffs':
+ specifier: ^1.0.10
+ version: 1.0.10(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-accordion':
+ specifier: ^1.2.12
+ version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-alert-dialog':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-checkbox':
+ specifier: ^1.3.3
+ version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-collapsible':
+ specifier: ^1.1.12
+ version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-context-menu':
+ specifier: ^2.2.16
+ version: 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-dropdown-menu':
+ specifier: ^2.1.16
+ version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-hover-card':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-icons':
+ specifier: ^1.3.2
+ version: 1.3.2(react@19.2.1)
+ '@radix-ui/react-label':
+ specifier: ^2.1.8
+ version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-popover':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-progress':
+ specifier: ^1.1.8
+ version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-select':
+ specifier: ^2.2.6
+ version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot':
+ specifier: ^1.2.4
+ version: 1.2.4(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-switch':
+ specifier: ^1.2.6
+ version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-tabs':
+ specifier: ^1.1.13
+ version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.2.8
+ version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-visually-hidden':
+ specifier: ^1.2.4
+ version: 1.2.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@sentry/electron':
+ specifier: ^7.5.0
+ version: 7.6.0
+ '@tailwindcss/typography':
+ specifier: ^0.5.19
+ version: 0.5.19(tailwindcss@3.4.19)
+ '@tanstack/react-query':
+ specifier: ^5.90.10
+ version: 5.90.20(react@19.2.1)
+ '@tanstack/react-virtual':
+ specifier: ^3.13.18
+ version: 3.13.18(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@trpc/client':
+ specifier: ^11.7.1
+ version: 11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)
+ '@trpc/react-query':
+ specifier: ^11.7.1
+ version: 11.8.1(@tanstack/react-query@5.90.20(react@19.2.1))(@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.8.1(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3)
+ '@trpc/server':
+ specifier: ^11.7.1
+ version: 11.8.1(typescript@5.9.3)
+ '@xterm/addon-canvas':
+ specifier: ^0.7.0
+ version: 0.7.0(@xterm/xterm@5.5.0)
+ '@xterm/addon-fit':
+ specifier: ^0.11.0
+ version: 0.11.0
+ '@xterm/addon-search':
+ specifier: ^0.16.0
+ version: 0.16.0
+ '@xterm/addon-serialize':
+ specifier: ^0.14.0
+ version: 0.14.0
+ '@xterm/addon-web-links':
+ specifier: ^0.12.0
+ version: 0.12.0
+ '@xterm/addon-webgl':
+ specifier: ^0.19.0
+ version: 0.19.0
+ ai:
+ specifier: ^6.0.14
+ version: 6.0.49(zod@3.25.76)
+ async-mutex:
+ specifier: ^0.5.0
+ version: 0.5.0
+ better-sqlite3:
+ specifier: ^11.8.1
+ version: 11.10.0
+ chokidar:
+ specifier: ^5.0.0
+ version: 5.0.0
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ date-fns:
+ specifier: ^3.6.0
+ version: 3.6.0
+ drizzle-orm:
+ specifier: ^0.45.1
+ version: 0.45.1(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@11.10.0)
+ electron-log:
+ specifier: ^5.4.3
+ version: 5.4.3
+ electron-updater:
+ specifier: ^6.7.3
+ version: 6.7.3
+ gray-matter:
+ specifier: ^4.0.3
+ version: 4.0.3
+ jotai:
+ specifier: ^2.11.1
+ version: 2.16.2(@babel/core@7.28.6)(@babel/template@7.28.6)(@types/react@19.2.9)(react@19.2.1)
+ jsonc-parser:
+ specifier: ^3.3.1
+ version: 3.3.1
+ lucide-react:
+ specifier: ^0.468.0
+ version: 0.468.0(react@19.2.1)
+ mermaid:
+ specifier: ^11.12.2
+ version: 11.12.2
+ monaco-editor:
+ specifier: ^0.55.1
+ version: 0.55.1
+ motion:
+ specifier: ^11.15.0
+ version: 11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ next-themes:
+ specifier: ^0.4.4
+ version: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ node-pty:
+ specifier: ^1.1.0
+ version: 1.1.0
+ pidtree:
+ specifier: ^0.6.0
+ version: 0.6.0
+ posthog-js:
+ specifier: ^1.239.1
+ version: 1.335.2
+ posthog-node:
+ specifier: ^5.20.0
+ version: 5.24.2
+ react:
+ specifier: 19.2.1
+ version: 19.2.1
+ react-dom:
+ specifier: 19.2.1
+ version: 19.2.1(react@19.2.1)
+ react-hotkeys-hook:
+ specifier: ^4.6.1
+ version: 4.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react-icons:
+ specifier: ^5.5.0
+ version: 5.5.0(react@19.2.1)
+ react-zoom-pan-pinch:
+ specifier: ^3.7.0
+ version: 3.7.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ remark-breaks:
+ specifier: ^4.0.0
+ version: 4.0.0
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ shiki:
+ specifier: ^1.24.4
+ version: 1.29.2
+ simple-git:
+ specifier: ^3.28.0
+ version: 3.30.0
+ sonner:
+ specifier: ^1.7.1
+ version: 1.7.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ streamdown:
+ specifier: ^2.0.1
+ version: 2.1.0(react@19.2.1)
+ superjson:
+ specifier: ^2.2.2
+ version: 2.2.6
+ tailwind-merge:
+ specifier: ^2.6.0
+ version: 2.6.0
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.19)
+ trpc-electron:
+ specifier: ^0.1.2
+ version: 0.1.2(@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.8.1(typescript@5.9.3))(electron@33.4.5)
+ unique-names-generator:
+ specifier: ^4.7.1
+ version: 4.7.1
+ xterm:
+ specifier: ^5.3.0
+ version: 5.3.0
+ zod:
+ specifier: ^3.24.1
+ version: 3.25.76
+ zustand:
+ specifier: ^5.0.3
+ version: 5.0.10(@types/react@19.2.9)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1))
+ devDependencies:
+ '@electron-toolkit/preload':
+ specifier: ^3.0.1
+ version: 3.0.2(electron@33.4.5)
+ '@electron-toolkit/utils':
+ specifier: ^4.0.0
+ version: 4.0.0(electron@33.4.5)
+ '@types/better-sqlite3':
+ specifier: ^7.6.13
+ version: 7.6.13
+ '@types/node':
+ specifier: ^20.17.50
+ version: 20.19.30
+ '@types/react':
+ specifier: ^19.0.7
+ version: 19.2.9
+ '@types/react-dom':
+ specifier: ^19.0.3
+ version: 19.2.3(@types/react@19.2.9)
+ '@vitejs/plugin-react':
+ specifier: ^4.3.4
+ version: 4.7.0(vite@6.4.1(@types/node@20.19.30)(jiti@1.21.7))
+ '@welldone-software/why-did-you-render':
+ specifier: ^10.0.1
+ version: 10.0.1(react@19.2.1)
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.23(postcss@8.5.6)
+ drizzle-kit:
+ specifier: ^0.31.8
+ version: 0.31.8
+ electron:
+ specifier: 33.4.5
+ version: 33.4.5
+ electron-builder:
+ specifier: ^25.1.8
+ version: 25.1.8(electron-builder-squirrel-windows@25.1.8)
+ electron-rebuild:
+ specifier: ^3.2.9
+ version: 3.2.9
+ electron-vite:
+ specifier: ^3.0.0
+ version: 3.1.0(vite@6.4.1(@types/node@20.19.30)(jiti@1.21.7))
+ postcss:
+ specifier: ^8.5.1
+ version: 8.5.6
+ tailwindcss:
+ specifier: ^3.4.17
+ version: 3.4.19
+ typescript:
+ specifier: ^5.4.5
+ version: 5.9.3
+ vite:
+ specifier: ^6.3.4
+ version: 6.4.1(@types/node@20.19.30)(jiti@1.21.7)
+
+packages:
+
+ 7zip-bin@5.2.0:
+ resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==}
+
+ '@ai-sdk/gateway@3.0.22':
+ resolution: {integrity: sha512-NgnlY73JNuooACHqUIz5uMOEWvqR1MMVbb2soGLMozLY1fgwEIF5iJFDAGa5/YArlzw2ATVU7zQu7HkR/FUjgA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider-utils@4.0.9':
+ resolution: {integrity: sha512-bB4r6nfhBOpmoS9mePxjRoCy+LnzP3AfhyMGCkGL4Mn9clVNlqEeKj26zEKEtB6yoSVcT1IQ0Zh9fytwMCDnow==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider@3.0.5':
+ resolution: {integrity: sha512-2Xmoq6DBJqmSl80U6V9z5jJSJP7ehaJJQMy2iFUqTay06wdCqTnPVBBQbtEL8RCChenL+q5DC5H5WzU3vV3v8w==}
+ engines: {node: '>=18'}
+
+ '@ai-sdk/react@3.0.51':
+ resolution: {integrity: sha512-7nmCwEJM52NQZB4/ED8qJ4wbDg7EEWh94qJ7K9GSJxD6sWF3GOKrRZ5ivm4qNmKhY+JfCxCAxfghGY5mTKOsxw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@antfu/install-pkg@1.1.0':
+ resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
+ '@anthropic-ai/claude-agent-sdk@0.2.25':
+ resolution: {integrity: sha512-YIP3I40+XSkC3zE1Z8KRQY02VA7UfofFamF1cFrLe7FbtCnjpslyDl9coGBh2DAi9xj2yQcKZZf751jEWpB+dQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ zod: ^4.0.0
+
+ '@apm-js-collab/code-transformer@0.8.2':
+ resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==}
+
+ '@apm-js-collab/tracing-hooks@0.3.1':
+ resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==}
+
+ '@babel/code-frame@7.28.6':
+ resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.6':
+ resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.6':
+ resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.6':
+ resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.6':
+ resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-arrow-functions@7.27.1':
+ resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.6':
+ resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.6':
+ resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==}
+ engines: {node: '>=6.9.0'}
+
+ '@braintree/sanitize-url@7.1.1':
+ resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==}
+
+ '@chevrotain/cst-dts-gen@11.0.3':
+ resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==}
+
+ '@chevrotain/gast@11.0.3':
+ resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==}
+
+ '@chevrotain/regexp-to-ast@11.0.3':
+ resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==}
+
+ '@chevrotain/types@11.0.3':
+ resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==}
+
+ '@chevrotain/utils@11.0.3':
+ resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==}
+
+ '@develar/schema-utils@2.6.5':
+ resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==}
+ engines: {node: '>= 8.9.0'}
+
+ '@drizzle-team/brocli@0.10.2':
+ resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
+
+ '@electron-toolkit/preload@3.0.2':
+ resolution: {integrity: sha512-TWWPToXd8qPRfSXwzf5KVhpXMfONaUuRAZJHsKthKgZR/+LqX1dZVSSClQ8OTAEduvLGdecljCsoT2jSshfoUg==}
+ peerDependencies:
+ electron: '>=13.0.0'
+
+ '@electron-toolkit/utils@4.0.0':
+ resolution: {integrity: sha512-qXSntwEzluSzKl4z5yFNBknmPGjPa3zFhE4mp9+h0cgokY5ornAeP+CJQDBhKsL1S58aOQfcwkD3NwLZCl+64g==}
+ peerDependencies:
+ electron: '>=13.0.0'
+
+ '@electron/asar@3.4.1':
+ resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==}
+ engines: {node: '>=10.12.0'}
+ hasBin: true
+
+ '@electron/get@2.0.3':
+ resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
+ engines: {node: '>=12'}
+
+ '@electron/notarize@2.5.0':
+ resolution: {integrity: sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==}
+ engines: {node: '>= 10.0.0'}
+
+ '@electron/osx-sign@1.3.1':
+ resolution: {integrity: sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+
+ '@electron/rebuild@3.6.1':
+ resolution: {integrity: sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+
+ '@electron/universal@2.0.1':
+ resolution: {integrity: sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==}
+ engines: {node: '>=16.4'}
+
+ '@esbuild-kit/core-utils@3.3.2':
+ resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
+ deprecated: 'Merged into tsx: https://tsx.is'
+
+ '@esbuild-kit/esm-loader@2.6.5':
+ resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
+ deprecated: 'Merged into tsx: https://tsx.is'
+
+ '@esbuild/aix-ppc64@0.25.12':
+ resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.18.20':
+ resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.25.12':
+ resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.18.20':
+ resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.12':
+ resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.18.20':
+ resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.12':
+ resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.18.20':
+ resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.25.12':
+ resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.18.20':
+ resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.12':
+ resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.18.20':
+ resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.18.20':
+ resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.12':
+ resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.18.20':
+ resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.25.12':
+ resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.18.20':
+ resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.12':
+ resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.18.20':
+ resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.12':
+ resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.18.20':
+ resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.12':
+ resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.18.20':
+ resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.12':
+ resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.18.20':
+ resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.12':
+ resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.18.20':
+ resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.12':
+ resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.18.20':
+ resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.12':
+ resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.18.20':
+ resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.12':
+ resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.18.20':
+ resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.12':
+ resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.18.20':
+ resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.12':
+ resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.18.20':
+ resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.25.12':
+ resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.18.20':
+ resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.25.12':
+ resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.18.20':
+ resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.12':
+ resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.18.20':
+ resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.12':
+ resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@floating-ui/core@1.7.3':
+ resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
+
+ '@floating-ui/dom@1.7.4':
+ resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
+
+ '@floating-ui/react-dom@2.1.6':
+ resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+ '@gar/promisify@1.1.3':
+ resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
+
+ '@git-diff-view/core@0.0.35':
+ resolution: {integrity: sha512-cdH3BopR6AWUW+6hP78zGyryKxR9JkPgryd1JN78i5k+F9Eo4x/4S23ZF1VZnrpPlGLrSuYfiAZ0ho5m+pTuKg==}
+
+ '@git-diff-view/lowlight@0.0.35':
+ resolution: {integrity: sha512-MVpOxrNn1oHVOTOWUjxLbbf1W4OtVHjj6CHxwJbBRg9ZWZdShBINjuEgHVMSGB6vZuHKfwruRfXw8XxV3aF8zw==}
+
+ '@git-diff-view/react@0.0.35':
+ resolution: {integrity: sha512-jIzESQ/oB2FZgEGK1urvAqju89cThv5+enEC4r2atDX15W5xMDNsDBe8FT2U74zBj8JkgROaiGmbF93KIJrWpg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@git-diff-view/shiki@0.0.36':
+ resolution: {integrity: sha512-t6LGKISEvE0q7u2AR1rq2RLgCcccRttuKE617D1MiHecO0Xd/xvsMhqxW4PgDMkMadhbzWnt6IhBbrdgqBKPwQ==}
+
+ '@hono/node-server@1.19.9':
+ resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==}
+ engines: {node: '>=18.14.1'}
+ peerDependencies:
+ hono: ^4
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@3.1.0':
+ resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.33.5':
+ resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.33.5':
+ resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.33.5':
+ resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.33.5':
+ resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-win32-x64@0.33.5':
+ resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@isaacs/balanced-match@4.0.1':
+ resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
+ engines: {node: 20 || >=22}
+
+ '@isaacs/brace-expansion@5.0.0':
+ resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
+ engines: {node: 20 || >=22}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@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==}
+
+ '@kwsites/file-exists@1.1.1':
+ resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==}
+
+ '@kwsites/promise-deferred@1.1.1':
+ resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
+
+ '@malept/cross-spawn-promise@2.0.0':
+ resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==}
+ engines: {node: '>= 12.13.0'}
+
+ '@malept/flatpak-bundler@0.4.0':
+ resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
+ engines: {node: '>= 10.0.0'}
+
+ '@mermaid-js/parser@0.6.3':
+ resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==}
+
+ '@modelcontextprotocol/sdk@1.25.3':
+ resolution: {integrity: sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@cfworker/json-schema': ^4.1.1
+ zod: ^3.25 || ^4.0
+ peerDependenciesMeta:
+ '@cfworker/json-schema':
+ optional: true
+
+ '@monaco-editor/loader@1.7.0':
+ resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==}
+
+ '@monaco-editor/react@4.7.0':
+ resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==}
+ peerDependencies:
+ monaco-editor: '>= 0.25.0 < 1'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@npmcli/fs@2.1.2':
+ resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ '@npmcli/move-file@2.0.1':
+ resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ deprecated: This functionality has been moved to @npmcli/fs
+
+ '@opentelemetry/api-logs@0.208.0':
+ resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==}
+ engines: {node: '>=8.0.0'}
+
+ '@opentelemetry/api@1.9.0':
+ resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
+ engines: {node: '>=8.0.0'}
+
+ '@opentelemetry/context-async-hooks@2.5.0':
+ resolution: {integrity: sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/core@2.2.0':
+ resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/core@2.5.0':
+ resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/exporter-logs-otlp-http@0.208.0':
+ resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-amqplib@0.55.0':
+ resolution: {integrity: sha512-5ULoU8p+tWcQw5PDYZn8rySptGSLZHNX/7srqo2TioPnAAcvTy6sQFQXsNPrAnyRRtYGMetXVyZUy5OaX1+IfA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-connect@0.52.0':
+ resolution: {integrity: sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-dataloader@0.26.0':
+ resolution: {integrity: sha512-P2BgnFfTOarZ5OKPmYfbXfDFjQ4P9WkQ1Jji7yH5/WwB6Wm/knynAoA1rxbjWcDlYupFkyT0M1j6XLzDzy0aCA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-express@0.57.0':
+ resolution: {integrity: sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-fs@0.28.0':
+ resolution: {integrity: sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-generic-pool@0.52.0':
+ resolution: {integrity: sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-graphql@0.56.0':
+ resolution: {integrity: sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-hapi@0.55.0':
+ resolution: {integrity: sha512-prqAkRf9e4eEpy4G3UcR32prKE8NLNlA90TdEU1UsghOTg0jUvs40Jz8LQWFEs5NbLbXHYGzB4CYVkCI8eWEVQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-http@0.208.0':
+ resolution: {integrity: sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-ioredis@0.56.0':
+ resolution: {integrity: sha512-XSWeqsd3rKSsT3WBz/JKJDcZD4QYElZEa0xVdX8f9dh4h4QgXhKRLorVsVkK3uXFbC2sZKAS2Ds+YolGwD83Dg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-kafkajs@0.18.0':
+ resolution: {integrity: sha512-KCL/1HnZN5zkUMgPyOxfGjLjbXjpd4odDToy+7c+UsthIzVLFf99LnfIBE8YSSrYE4+uS7OwJMhvhg3tWjqMBg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-knex@0.53.0':
+ resolution: {integrity: sha512-xngn5cH2mVXFmiT1XfQ1aHqq1m4xb5wvU6j9lSgLlihJ1bXzsO543cpDwjrZm2nMrlpddBf55w8+bfS4qDh60g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-koa@0.57.0':
+ resolution: {integrity: sha512-3JS8PU/D5E3q295mwloU2v7c7/m+DyCqdu62BIzWt+3u9utjxC9QS7v6WmUNuoDN3RM+Q+D1Gpj13ERo+m7CGg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.9.0
+
+ '@opentelemetry/instrumentation-lru-memoizer@0.53.0':
+ resolution: {integrity: sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongodb@0.61.0':
+ resolution: {integrity: sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongoose@0.55.0':
+ resolution: {integrity: sha512-5afj0HfF6aM6Nlqgu6/PPHFk8QBfIe3+zF9FGpX76jWPS0/dujoEYn82/XcLSaW5LPUDW8sni+YeK0vTBNri+w==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql2@0.55.0':
+ resolution: {integrity: sha512-0cs8whQG55aIi20gnK8B7cco6OK6N+enNhW0p5284MvqJ5EPi+I1YlWsWXgzv/V2HFirEejkvKiI4Iw21OqDWg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql@0.54.0':
+ resolution: {integrity: sha512-bqC1YhnwAeWmRzy1/Xf9cDqxNG2d/JDkaxnqF5N6iJKN1eVWI+vg7NfDkf52/Nggp3tl1jcC++ptC61BD6738A==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-pg@0.61.0':
+ resolution: {integrity: sha512-UeV7KeTnRSM7ECHa3YscoklhUtTQPs6V6qYpG283AB7xpnPGCUCUfECFT9jFg6/iZOQTt3FHkB1wGTJCNZEvPw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-redis@0.57.0':
+ resolution: {integrity: sha512-bCxTHQFXzrU3eU1LZnOZQ3s5LURxQPDlU3/upBzlWY77qOI1GZuGofazj3jtzjctMJeBEJhNwIFEgRPBX1kp/Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-tedious@0.27.0':
+ resolution: {integrity: sha512-jRtyUJNZppPBjPae4ZjIQ2eqJbcRaRfJkr0lQLHFmOU/no5A6e9s1OHLd5XZyZoBJ/ymngZitanyRRA5cniseA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-undici@0.19.0':
+ resolution: {integrity: sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.7.0
+
+ '@opentelemetry/instrumentation@0.208.0':
+ resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/otlp-exporter-base@0.208.0':
+ resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/otlp-transformer@0.208.0':
+ resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/redis-common@0.38.2':
+ resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+
+ '@opentelemetry/resources@2.2.0':
+ resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/resources@2.5.0':
+ resolution: {integrity: sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/sdk-logs@0.208.0':
+ resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.4.0 <1.10.0'
+
+ '@opentelemetry/sdk-metrics@2.2.0':
+ resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.9.0 <1.10.0'
+
+ '@opentelemetry/sdk-trace-base@2.2.0':
+ resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/sdk-trace-base@2.5.0':
+ resolution: {integrity: sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/semantic-conventions@1.39.0':
+ resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==}
+ engines: {node: '>=14'}
+
+ '@opentelemetry/sql-common@0.41.2':
+ resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+
+ '@pierre/diffs@1.0.10':
+ resolution: {integrity: sha512-ahkpfS30NfaB+PBxnf0/Mc20ySBRTQmM28a7Ojpd0UZixmTyhGhJfBFjvmhX8dSzR22lB3h3OIMMxpB4yYTIOQ==}
+ peerDependencies:
+ react: ^18.3.1 || ^19.0.0
+ react-dom: ^18.3.1 || ^19.0.0
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@posthog/core@1.14.0':
+ resolution: {integrity: sha512-havjGYHwL8Gy6LXIR911h+M/sYlJLQbepxP/cc1M7Cp3v8F92bzpqkbuvUIUyb7/izkxfGwc9wMqKAo0QxMTrg==}
+
+ '@posthog/types@1.335.2':
+ resolution: {integrity: sha512-cyl6eFrt0nR7lxb8+oGXyS16wDxQJz6awMWPyDB423lI+MiM64vz0VV5LNABahEc4BuytJzfEOyvyA3LPJ4hOQ==}
+
+ '@prisma/instrumentation@6.19.0':
+ resolution: {integrity: sha512-QcuYy25pkXM8BJ37wVFBO7Zh34nyRV1GOb2n3lPkkbRYfl4hWl3PTcImP41P0KrzVXfa/45p6eVCos27x3exIg==}
+ peerDependencies:
+ '@opentelemetry/api': ^1.8
+
+ '@protobufjs/aspromise@1.1.2':
+ resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
+
+ '@protobufjs/base64@1.1.2':
+ resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
+
+ '@protobufjs/codegen@2.0.4':
+ resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
+
+ '@protobufjs/eventemitter@1.1.0':
+ resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
+
+ '@protobufjs/fetch@1.1.0':
+ resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
+
+ '@protobufjs/float@1.0.2':
+ resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
+
+ '@protobufjs/inquire@1.1.0':
+ resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
+
+ '@protobufjs/path@1.1.2':
+ resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
+
+ '@protobufjs/pool@1.1.0':
+ resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
+
+ '@protobufjs/utf8@1.1.0':
+ resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
+
+ '@radix-ui/number@1.1.1':
+ resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
+
+ '@radix-ui/primitive@1.1.3':
+ resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+
+ '@radix-ui/react-accordion@1.2.12':
+ resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==}
+ 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.15':
+ resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==}
+ 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.7':
+ resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
+ 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.3':
+ resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==}
+ 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.12':
+ resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==}
+ 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.7':
+ resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
+ 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.2':
+ resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
+ 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.2.16':
+ resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==}
+ 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.2':
+ resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
+ 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@1.1.3':
+ resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==}
+ 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.15':
+ resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
+ 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.1':
+ resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
+ 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.11':
+ resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
+ 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.16':
+ resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==}
+ 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.3':
+ resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
+ 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.7':
+ resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
+ 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.15':
+ resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==}
+ 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-icons@1.3.2':
+ resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==}
+ peerDependencies:
+ react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc
+
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
+ 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.8':
+ resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==}
+ 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.16':
+ resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==}
+ 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.15':
+ resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==}
+ 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.2.8':
+ resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
+ 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.9':
+ resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
+ 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.5':
+ resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
+ 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.3':
+ resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
+ 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.4':
+ resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
+ 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.8':
+ resolution: {integrity: sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==}
+ 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.11':
+ resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
+ 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.2.6':
+ resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
+ 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.3':
+ resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
+ 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.2.4':
+ resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
+ 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.2.6':
+ resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
+ 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.13':
+ resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
+ 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.8':
+ resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
+ 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.1':
+ resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
+ 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.2':
+ resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
+ 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.2':
+ resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
+ 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.1':
+ resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
+ 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.1':
+ resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
+ 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.1':
+ resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
+ 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.1':
+ resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
+ 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.1':
+ resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
+ 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.3':
+ resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
+ 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.4':
+ resolution: {integrity: sha512-kaeiyGCe844dkb9AVF+rb4yTyb1LiLN/e3es3nLiRyN4dC8AduBYPMnnNlDjX2VDOcvDEiPnRNMJeWCfsX0txg==}
+ 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.1':
+ resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
+
+ '@rolldown/pluginutils@1.0.0-beta.27':
+ resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
+ '@rollup/rollup-android-arm-eabi@4.56.0':
+ resolution: {integrity: sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.56.0':
+ resolution: {integrity: sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.56.0':
+ resolution: {integrity: sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.56.0':
+ resolution: {integrity: sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.56.0':
+ resolution: {integrity: sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.56.0':
+ resolution: {integrity: sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.56.0':
+ resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.56.0':
+ resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.56.0':
+ resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.56.0':
+ resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.56.0':
+ resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.56.0':
+ resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.56.0':
+ resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.56.0':
+ resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.56.0':
+ resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.56.0':
+ resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.56.0':
+ resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.56.0':
+ resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.56.0':
+ resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.56.0':
+ resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.56.0':
+ resolution: {integrity: sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.56.0':
+ resolution: {integrity: sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.56.0':
+ resolution: {integrity: sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.56.0':
+ resolution: {integrity: sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.56.0':
+ resolution: {integrity: sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sentry-internal/browser-utils@10.34.0':
+ resolution: {integrity: sha512-0YNr60rGHyedmwkO0lbDBjNx2KAmT3kWamjaqu7Aw+jsESoPLgt+fzaTVvUBvkftBDui2PeTSzXm/nqzssctYg==}
+ engines: {node: '>=18'}
+
+ '@sentry-internal/feedback@10.34.0':
+ resolution: {integrity: sha512-wgGnq+iNxsFSOe9WX/FOvtoItSTjgLJJ4dQkVYtcVM6WGBVIg4wgNYfECCnRNztUTPzpZHLjC9r+4Pym451DDQ==}
+ engines: {node: '>=18'}
+
+ '@sentry-internal/replay-canvas@10.34.0':
+ resolution: {integrity: sha512-XWH/9njtgMD+LLWjc4KKgBpb+dTCkoUEIFDxcvzG/87d+jirmzf0+r8EfpLwKG+GrqNiiGRV39zIqu0SfPl+cw==}
+ engines: {node: '>=18'}
+
+ '@sentry-internal/replay@10.34.0':
+ resolution: {integrity: sha512-Vmea0GcOg57z/S1bVSj3saFcRvDqdLzdy4wd9fQMpMgy5OCbTlo7lxVUndKzbcZnanma6zF6VxwnWER1WuN9RA==}
+ engines: {node: '>=18'}
+
+ '@sentry/browser@10.34.0':
+ resolution: {integrity: sha512-8WCsAXli5Z+eIN8dMY8KGQjrS3XgUp1np/pjdeWNrVPVR8q8XpS34qc+f+y/LFrYQC9bs2Of5aIBwRtDCIvRsg==}
+ engines: {node: '>=18'}
+
+ '@sentry/core@10.34.0':
+ resolution: {integrity: sha512-4FFpYBMf0VFdPcsr4grDYDOR87mRu6oCfb51oQjU/Pndmty7UgYo0Bst3LEC/8v0SpytBtzXq+Wx/fkwulBesg==}
+ engines: {node: '>=18'}
+
+ '@sentry/electron@7.6.0':
+ resolution: {integrity: sha512-ueW3Coa0BtOQFPaf+QaI3mBHMi/t7CkZnuzZ6PNoVpHe6CgYfCtNdE7H1BpMsCpG1FhEAgCLBJtpaMKyQBFdzQ==}
+ peerDependencies:
+ '@sentry/node-native': 10.34.0
+ peerDependenciesMeta:
+ '@sentry/node-native':
+ optional: true
+
+ '@sentry/node-core@10.34.0':
+ resolution: {integrity: sha512-FrGfC8GzD1cnZDO3zwQ4cjyoY1ZwNHvZbXSvXRYxpjhXidZhvaPurjgLRSB0xGaFgoemmOp1ufsx/w6fQOGA6Q==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.9.0
+ '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/instrumentation': '>=0.57.1 <1'
+ '@opentelemetry/resources': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/semantic-conventions': ^1.37.0
+
+ '@sentry/node@10.34.0':
+ resolution: {integrity: sha512-bEOyH97HuVtWZYAZ5mp0NhYNc+n6QCfiKuLee2P75n2kt4cIPTGvLOSdUwwjllf795uOdKZJuM1IUN0W+YMcVg==}
+ engines: {node: '>=18'}
+
+ '@sentry/opentelemetry@10.34.0':
+ resolution: {integrity: sha512-uKuULBOmdVu3bYdD8doMLqKgN0PP3WWtI7Shu11P9PVrhSNT4U9yM9Z6v1aFlQcbrgyg3LynZuXs8lyjt90UbA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.9.0
+ '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/semantic-conventions': ^1.37.0
+
+ '@shikijs/core@1.29.2':
+ resolution: {integrity: sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==}
+
+ '@shikijs/core@3.21.0':
+ resolution: {integrity: sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==}
+
+ '@shikijs/core@3.22.0':
+ resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==}
+
+ '@shikijs/engine-javascript@1.29.2':
+ resolution: {integrity: sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==}
+
+ '@shikijs/engine-javascript@3.21.0':
+ resolution: {integrity: sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ==}
+
+ '@shikijs/engine-oniguruma@1.29.2':
+ resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==}
+
+ '@shikijs/engine-oniguruma@3.21.0':
+ resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==}
+
+ '@shikijs/langs@1.29.2':
+ resolution: {integrity: sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==}
+
+ '@shikijs/langs@3.21.0':
+ resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==}
+
+ '@shikijs/themes@1.29.2':
+ resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==}
+
+ '@shikijs/themes@3.21.0':
+ resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==}
+
+ '@shikijs/transformers@3.22.0':
+ resolution: {integrity: sha512-E7eRV7mwDBjueLF6852n2oYeJYxBq3NSsDk+uyruYAXONv4U8holGmIrT+mPRJQ1J1SNOH6L8G19KRzmBawrFw==}
+
+ '@shikijs/types@1.29.2':
+ resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==}
+
+ '@shikijs/types@3.21.0':
+ resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==}
+
+ '@shikijs/types@3.22.0':
+ resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
+ '@sindresorhus/is@4.6.0':
+ resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
+ engines: {node: '>=10'}
+
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@szmarczak/http-timer@4.0.6':
+ resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
+ engines: {node: '>=10'}
+
+ '@tailwindcss/typography@0.5.19':
+ resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
+
+ '@tanstack/query-core@5.90.20':
+ resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==}
+
+ '@tanstack/react-query@5.90.20':
+ resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==}
+ peerDependencies:
+ react: ^18 || ^19
+
+ '@tanstack/react-virtual@3.13.18':
+ resolution: {integrity: sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@tanstack/virtual-core@3.13.18':
+ resolution: {integrity: sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg==}
+
+ '@tootallnate/once@2.0.0':
+ resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
+ engines: {node: '>= 10'}
+
+ '@trpc/client@11.8.1':
+ resolution: {integrity: sha512-L/SJFGanr9xGABmuDoeXR4xAdHJmsXsiF9OuH+apecJ+8sUITzVT1EPeqp0ebqA6lBhEl5pPfg3rngVhi/h60Q==}
+ peerDependencies:
+ '@trpc/server': 11.8.1
+ typescript: '>=5.7.2'
+
+ '@trpc/react-query@11.8.1':
+ resolution: {integrity: sha512-0Vu55ld/oINb4U6nIPPi7eZMhxUop6K+4QUK90RVsfSD5r+957sM80M4c8bjh/JBZUxMFv9JOhxxlWcrgHxHow==}
+ peerDependencies:
+ '@tanstack/react-query': ^5.80.3
+ '@trpc/client': 11.8.1
+ '@trpc/server': 11.8.1
+ react: '>=18.2.0'
+ react-dom: '>=18.2.0'
+ typescript: '>=5.7.2'
+
+ '@trpc/server@11.8.1':
+ resolution: {integrity: sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA==}
+ peerDependencies:
+ typescript: '>=5.7.2'
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/better-sqlite3@7.6.13':
+ resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
+
+ '@types/cacheable-request@6.0.3':
+ resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+
+ '@types/connect@3.4.38':
+ resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-axis@3.0.6':
+ resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
+
+ '@types/d3-brush@3.0.6':
+ resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
+
+ '@types/d3-chord@3.0.6':
+ resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-contour@3.0.6':
+ resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-dispatch@3.0.7':
+ resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
+ '@types/d3-drag@3.0.7':
+ resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+
+ '@types/d3-dsv@3.0.7':
+ resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-fetch@3.0.7':
+ resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+ '@types/d3-force@3.0.10':
+ resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+ '@types/d3-format@3.0.4':
+ resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+ '@types/d3-geo@3.1.0':
+ resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
+
+ '@types/d3-hierarchy@3.1.7':
+ resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-polygon@3.0.2':
+ resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
+
+ '@types/d3-quadtree@3.0.6':
+ resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+ '@types/d3-random@3.0.3':
+ resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
+
+ '@types/d3-scale-chromatic@3.1.0':
+ resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-selection@3.0.11':
+ resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+
+ '@types/d3-shape@3.1.8':
+ resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
+
+ '@types/d3-time-format@4.0.3':
+ resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@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/d3@7.4.3':
+ resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
+
+ '@types/debug@4.1.12':
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+ '@types/estree-jsx@1.0.5':
+ resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/fs-extra@9.0.13':
+ resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
+
+ '@types/geojson@7946.0.16':
+ resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+ '@types/http-cache-semantics@4.0.4':
+ resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+
+ '@types/keyv@3.1.4':
+ resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/mysql@2.15.27':
+ resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==}
+
+ '@types/node@20.19.30':
+ resolution: {integrity: sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==}
+
+ '@types/pg-pool@2.0.6':
+ resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==}
+
+ '@types/pg@8.15.6':
+ resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==}
+
+ '@types/plist@3.0.5':
+ resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==}
+
+ '@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.9':
+ resolution: {integrity: sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==}
+
+ '@types/responselike@1.0.3':
+ resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
+
+ '@types/tedious@4.0.14':
+ resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
+
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
+ '@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/verror@1.10.11':
+ resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==}
+
+ '@types/yauzl@2.10.3':
+ resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@vercel/oidc@3.1.0':
+ resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==}
+ engines: {node: '>= 20'}
+
+ '@vitejs/plugin-react@4.7.0':
+ resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ '@vue/reactivity@3.5.27':
+ resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==}
+
+ '@vue/shared@3.5.27':
+ resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==}
+
+ '@welldone-software/why-did-you-render@10.0.1':
+ resolution: {integrity: sha512-tMgGkt30iVYeLMUKExNmtm019QgyjLtA7lwB0QAizYNEuihlCG2eoAWBBaz/bDeI7LeqAJ9msC6hY3vX+JB97g==}
+ peerDependencies:
+ react: ^19
+
+ '@xmldom/xmldom@0.8.11':
+ resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
+ engines: {node: '>=10.0.0'}
+
+ '@xterm/addon-canvas@0.7.0':
+ resolution: {integrity: sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==}
+ peerDependencies:
+ '@xterm/xterm': ^5.0.0
+
+ '@xterm/addon-fit@0.11.0':
+ resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==}
+
+ '@xterm/addon-search@0.16.0':
+ resolution: {integrity: sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==}
+
+ '@xterm/addon-serialize@0.14.0':
+ resolution: {integrity: sha512-uteyTU1EkrQa2Ux6P/uFl2fzmXI46jy5uoQMKEOM0fKTyiW7cSn0WrFenHm5vO5uEXX/GpwW/FgILvv3r0WbkA==}
+
+ '@xterm/addon-web-links@0.12.0':
+ resolution: {integrity: sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==}
+
+ '@xterm/addon-webgl@0.19.0':
+ resolution: {integrity: sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A==}
+
+ '@xterm/xterm@5.5.0':
+ resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
+
+ abbrev@1.1.1:
+ resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+
+ accepts@2.0.0:
+ resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
+ engines: {node: '>= 0.6'}
+
+ acorn-import-attributes@1.9.5:
+ resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
+ peerDependencies:
+ acorn: ^8
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@6.0.2:
+ resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+ engines: {node: '>= 6.0.0'}
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ agentkeepalive@4.6.0:
+ resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
+ engines: {node: '>= 8.0.0'}
+
+ aggregate-error@3.1.0:
+ resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+ engines: {node: '>=8'}
+
+ ai@6.0.49:
+ resolution: {integrity: sha512-LABniBX/0R6Tv+iUK5keUZhZLaZUe4YjP5M2rZ4wAdZ8iKV3EfTAoJxuL1aaWTSJKIilKa9QUEkCgnp89/32bw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ ajv-formats@3.0.1:
+ resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
+ ajv-keywords@3.5.2:
+ resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+ peerDependencies:
+ ajv: ^6.9.1
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ajv@8.17.1:
+ resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ app-builder-bin@5.0.0-alpha.10:
+ resolution: {integrity: sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==}
+
+ app-builder-lib@25.1.8:
+ resolution: {integrity: sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ dmg-builder: 25.1.8
+ electron-builder-squirrel-windows: 25.1.8
+
+ aproba@2.1.0:
+ resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==}
+
+ archiver-utils@2.1.0:
+ resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
+ engines: {node: '>= 6'}
+
+ archiver-utils@3.0.4:
+ resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==}
+ engines: {node: '>= 10'}
+
+ archiver@5.3.2:
+ resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==}
+ engines: {node: '>= 10'}
+
+ are-we-there-yet@3.0.1:
+ resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ deprecated: This package is no longer supported.
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ assert-plus@1.0.0:
+ resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
+ engines: {node: '>=0.8'}
+
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
+ async-exit-hook@2.0.1:
+ resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==}
+ engines: {node: '>=0.12.0'}
+
+ async-mutex@0.5.0:
+ resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
+
+ async@3.2.6:
+ resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ at-least-node@1.0.0:
+ resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+ engines: {node: '>= 4.0.0'}
+
+ autoprefixer@10.4.23:
+ resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ baseline-browser-mapping@2.9.18:
+ resolution: {integrity: sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==}
+ hasBin: true
+
+ better-sqlite3@11.10.0:
+ resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ bindings@1.5.0:
+ resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+
+ bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+ bluebird-lst@1.0.9:
+ resolution: {integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==}
+
+ bluebird@3.7.2:
+ resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+
+ body-parser@2.2.2:
+ resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
+ engines: {node: '>=18'}
+
+ boolean@3.2.0:
+ resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
+ deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ builder-util-runtime@9.2.10:
+ resolution: {integrity: sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==}
+ engines: {node: '>=12.0.0'}
+
+ builder-util-runtime@9.5.1:
+ resolution: {integrity: sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==}
+ engines: {node: '>=12.0.0'}
+
+ builder-util@25.1.7:
+ resolution: {integrity: sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ cacache@16.1.3:
+ resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ cacheable-lookup@5.0.4:
+ resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
+ engines: {node: '>=10.6.0'}
+
+ cacheable-request@7.0.4:
+ resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
+ engines: {node: '>=8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ caniuse-lite@1.0.30001766:
+ resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ 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==}
+
+ chevrotain-allstar@0.3.1:
+ resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
+ peerDependencies:
+ chevrotain: ^11.0.0
+
+ chevrotain@11.0.3:
+ resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chokidar@5.0.0:
+ resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
+ engines: {node: '>= 20.19.0'}
+
+ chownr@1.1.4:
+ resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
+ chromium-pickle-js@0.2.0:
+ resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ cjs-module-lexer@2.2.0:
+ resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ clean-stack@2.2.0:
+ resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+ engines: {node: '>=6'}
+
+ cli-cursor@3.1.0:
+ resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+ engines: {node: '>=8'}
+
+ cli-spinners@2.9.2:
+ resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
+ engines: {node: '>=6'}
+
+ cli-truncate@2.1.0:
+ resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
+ engines: {node: '>=8'}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ clone-response@1.0.3:
+ resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
+
+ clone@1.0.4:
+ resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
+ engines: {node: '>=0.8'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-support@1.1.3:
+ resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
+ hasBin: true
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ commander@5.1.0:
+ resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
+ engines: {node: '>= 6'}
+
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
+ compare-version@0.1.2:
+ resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==}
+ engines: {node: '>=0.10.0'}
+
+ compress-commons@4.1.2:
+ resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
+ engines: {node: '>= 10'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ confbox@0.1.8:
+ resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+ config-file-ts@0.2.8-rc1:
+ resolution: {integrity: sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==}
+
+ console-control-strings@1.1.0:
+ resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+
+ content-disposition@1.0.1:
+ resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
+ engines: {node: '>=18'}
+
+ content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cookie-signature@1.2.2:
+ resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
+ engines: {node: '>=6.6.0'}
+
+ cookie@0.7.2:
+ resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
+ engines: {node: '>= 0.6'}
+
+ copy-anything@4.0.5:
+ resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
+ engines: {node: '>=18'}
+
+ core-js@3.48.0:
+ resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==}
+
+ core-util-is@1.0.2:
+ resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
+
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+ cors@2.8.6:
+ resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
+ engines: {node: '>= 0.10'}
+
+ cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+
+ cose-base@2.2.0:
+ resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+
+ crc-32@1.2.2:
+ resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+ engines: {node: '>=0.8'}
+ hasBin: true
+
+ crc32-stream@4.0.3:
+ resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==}
+ engines: {node: '>= 10'}
+
+ crc@3.8.0:
+ resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ cytoscape-cose-bilkent@4.1.0:
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape-fcose@2.2.0:
+ resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape@3.33.1:
+ resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==}
+ engines: {node: '>=0.10'}
+
+ d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+
+ d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+
+ d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ 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-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+
+ d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.2:
+ resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
+ engines: {node: '>=12'}
+
+ d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+
+ d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+
+ d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+
+ d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+
+ d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+
+ d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ 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'}
+
+ d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+
+ dagre-d3-es@7.0.13:
+ resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==}
+
+ date-fns@3.6.0:
+ resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
+
+ dayjs@1.11.19:
+ resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
+
+ 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==}
+
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ defaults@1.0.4:
+ resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+
+ defer-to-connect@2.0.1:
+ resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+ engines: {node: '>=10'}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ delaunator@5.0.1:
+ resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ delegates@1.0.0:
+ resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ 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==}
+
+ detect-node@2.1.0:
+ resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ diff@8.0.3:
+ resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==}
+ engines: {node: '>=0.3.1'}
+
+ dir-compare@4.2.0:
+ resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==}
+
+ dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ dmg-builder@25.1.8:
+ resolution: {integrity: sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==}
+
+ dmg-license@1.0.11:
+ resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==}
+ engines: {node: '>=8'}
+ os: [darwin]
+ hasBin: true
+
+ dompurify@3.2.7:
+ resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
+
+ dompurify@3.3.1:
+ resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
+
+ dotenv-expand@11.0.7:
+ resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
+ engines: {node: '>=12'}
+
+ dotenv@16.6.1:
+ resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
+ engines: {node: '>=12'}
+
+ drizzle-kit@0.31.8:
+ resolution: {integrity: sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==}
+ hasBin: true
+
+ drizzle-orm@0.45.1:
+ resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==}
+ peerDependencies:
+ '@aws-sdk/client-rds-data': '>=3'
+ '@cloudflare/workers-types': '>=4'
+ '@electric-sql/pglite': '>=0.2.0'
+ '@libsql/client': '>=0.10.0'
+ '@libsql/client-wasm': '>=0.10.0'
+ '@neondatabase/serverless': '>=0.10.0'
+ '@op-engineering/op-sqlite': '>=2'
+ '@opentelemetry/api': ^1.4.1
+ '@planetscale/database': '>=1.13'
+ '@prisma/client': '*'
+ '@tidbcloud/serverless': '*'
+ '@types/better-sqlite3': '*'
+ '@types/pg': '*'
+ '@types/sql.js': '*'
+ '@upstash/redis': '>=1.34.7'
+ '@vercel/postgres': '>=0.8.0'
+ '@xata.io/client': '*'
+ better-sqlite3: '>=7'
+ bun-types: '*'
+ expo-sqlite: '>=14.0.0'
+ gel: '>=2'
+ knex: '*'
+ kysely: '*'
+ mysql2: '>=2'
+ pg: '>=8'
+ postgres: '>=3'
+ prisma: '*'
+ sql.js: '>=1'
+ sqlite3: '>=5'
+ peerDependenciesMeta:
+ '@aws-sdk/client-rds-data':
+ optional: true
+ '@cloudflare/workers-types':
+ optional: true
+ '@electric-sql/pglite':
+ optional: true
+ '@libsql/client':
+ optional: true
+ '@libsql/client-wasm':
+ optional: true
+ '@neondatabase/serverless':
+ optional: true
+ '@op-engineering/op-sqlite':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@planetscale/database':
+ optional: true
+ '@prisma/client':
+ optional: true
+ '@tidbcloud/serverless':
+ optional: true
+ '@types/better-sqlite3':
+ optional: true
+ '@types/pg':
+ optional: true
+ '@types/sql.js':
+ optional: true
+ '@upstash/redis':
+ optional: true
+ '@vercel/postgres':
+ optional: true
+ '@xata.io/client':
+ optional: true
+ better-sqlite3:
+ optional: true
+ bun-types:
+ optional: true
+ expo-sqlite:
+ optional: true
+ gel:
+ optional: true
+ knex:
+ optional: true
+ kysely:
+ optional: true
+ mysql2:
+ optional: true
+ pg:
+ optional: true
+ postgres:
+ optional: true
+ prisma:
+ optional: true
+ sql.js:
+ optional: true
+ sqlite3:
+ optional: true
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+ ejs@3.1.10:
+ resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+
+ electron-builder-squirrel-windows@25.1.8:
+ resolution: {integrity: sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==}
+
+ electron-builder@25.1.8:
+ resolution: {integrity: sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ electron-log@5.4.3:
+ resolution: {integrity: sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ==}
+ engines: {node: '>= 14'}
+
+ electron-publish@25.1.7:
+ resolution: {integrity: sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==}
+
+ electron-rebuild@3.2.9:
+ resolution: {integrity: sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==}
+ engines: {node: '>=12.13.0'}
+ deprecated: Please use @electron/rebuild moving forward. There is no API change, just a package name change
+ hasBin: true
+
+ electron-to-chromium@1.5.278:
+ resolution: {integrity: sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw==}
+
+ electron-updater@6.7.3:
+ resolution: {integrity: sha512-EgkT8Z9noqXKbwc3u5FkJA+r48jwZ5DTUiOkJMOTEEH//n5Am6wfQGz7nvSFEA2oIAMv9jRzn5JKTyWeSKOPgg==}
+
+ electron-vite@3.1.0:
+ resolution: {integrity: sha512-M7aAzaRvSl5VO+6KN4neJCYLHLpF/iWo5ztchI/+wMxIieDZQqpbCYfaEHHHPH6eupEzfvZdLYdPdmvGqoVe0Q==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': ^1.0.0
+ vite: ^4.0.0 || ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+
+ electron@33.4.5:
+ resolution: {integrity: sha512-rbDc4QOqfMT1uopUG+KcaMKzKgFAXAzN3wNIdgErnB1tUnpgTxwFv1BDN/exCl1vaVWBeM9YtbO5PgbGZeq7xw==}
+ engines: {node: '>= 12.20.55'}
+ hasBin: true
+
+ emoji-regex-xs@1.0.0:
+ resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ encodeurl@2.0.0:
+ resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+ engines: {node: '>= 0.8'}
+
+ encoding@0.1.13:
+ resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
+
+ end-of-stream@1.4.5:
+ resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ err-code@2.0.3:
+ resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es6-error@4.1.1:
+ resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
+
+ esbuild-register@3.6.0:
+ resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
+ peerDependencies:
+ esbuild: '>=0.12 <1'
+
+ esbuild@0.18.20:
+ resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.25.12:
+ resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ 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'}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ estree-util-is-identifier-name@3.0.0:
+ resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
+
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
+ eventsource-parser@3.0.6:
+ resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
+ engines: {node: '>=18.0.0'}
+
+ eventsource@3.0.7:
+ resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
+ engines: {node: '>=18.0.0'}
+
+ expand-template@2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+
+ exponential-backoff@3.1.3:
+ resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
+
+ express-rate-limit@7.5.1:
+ resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==}
+ engines: {node: '>= 16'}
+ peerDependencies:
+ express: '>= 4.11'
+
+ express@5.2.1:
+ resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
+ engines: {node: '>= 18'}
+
+ extend-shallow@2.0.1:
+ resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
+ engines: {node: '>=0.10.0'}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ extract-zip@2.0.1:
+ resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
+ engines: {node: '>= 10.17.0'}
+ hasBin: true
+
+ extsprintf@1.4.1:
+ resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==}
+ engines: {'0': node >=0.6.0}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+ fd-slicer@1.1.0:
+ resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fflate@0.4.8:
+ resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+
+ file-uri-to-path@1.0.0:
+ resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+
+ filelist@1.0.4:
+ resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ finalhandler@2.1.1:
+ resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
+ engines: {node: '>= 18.0.0'}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ form-data@4.0.5:
+ resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
+ engines: {node: '>= 6'}
+
+ forwarded-parse@2.1.2:
+ resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
+
+ forwarded@0.2.0:
+ resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+ engines: {node: '>= 0.6'}
+
+ fraction.js@5.3.4:
+ resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
+
+ framer-motion@11.18.2:
+ resolution: {integrity: sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
+ fresh@2.0.0:
+ resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
+ engines: {node: '>= 0.8'}
+
+ fs-constants@1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
+ fs-extra@10.1.0:
+ resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+ engines: {node: '>=12'}
+
+ fs-extra@11.3.3:
+ resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==}
+ engines: {node: '>=14.14'}
+
+ fs-extra@8.1.0:
+ resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@9.1.0:
+ resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+ engines: {node: '>=10'}
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ gauge@4.0.4:
+ resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ deprecated: This package is no longer supported.
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-stream@5.2.0:
+ resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+ engines: {node: '>=8'}
+
+ get-tsconfig@4.13.0:
+ resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
+
+ github-from-package@0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.5.0:
+ resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ glob@8.1.0:
+ resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+ engines: {node: '>=12'}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-agent@3.0.0:
+ resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
+ engines: {node: '>=10.0'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ got@11.8.6:
+ resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
+ engines: {node: '>=10.19.0'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ gray-matter@4.0.3:
+ resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
+ engines: {node: '>=6.0'}
+
+ hachure-fill@0.5.2:
+ resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ has-unicode@2.0.1:
+ resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hast-util-from-parse5@8.0.3:
+ resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
+
+ hast-util-parse-selector@4.0.0:
+ resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
+
+ hast-util-raw@9.1.0:
+ resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
+
+ hast-util-sanitize@5.0.2:
+ resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==}
+
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
+ hast-util-to-jsx-runtime@2.3.6:
+ resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
+
+ hast-util-to-parse5@8.0.1:
+ resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ hastscript@9.0.1:
+ resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
+
+ highlight.js@11.11.1:
+ resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
+ engines: {node: '>=12.0.0'}
+
+ hono@4.11.5:
+ resolution: {integrity: sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==}
+ engines: {node: '>=16.9.0'}
+
+ hosted-git-info@4.1.0:
+ resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
+ engines: {node: '>=10'}
+
+ html-url-attributes@3.0.1:
+ resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+
+ html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+
+ http-cache-semantics@4.2.0:
+ resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
+
+ http-errors@2.0.1:
+ resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
+ engines: {node: '>= 0.8'}
+
+ http-proxy-agent@5.0.0:
+ resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
+ engines: {node: '>= 6'}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ http2-wrapper@1.0.3:
+ resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
+ engines: {node: '>=10.19.0'}
+
+ https-proxy-agent@5.0.1:
+ resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+ engines: {node: '>= 6'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ humanize-ms@1.2.1:
+ resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
+
+ iconv-corefoundation@1.1.7:
+ resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==}
+ engines: {node: ^8.11.2 || >=10}
+ os: [darwin]
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ iconv-lite@0.7.2:
+ resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
+ engines: {node: '>=0.10.0'}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ import-in-the-middle@2.0.5:
+ resolution: {integrity: sha512-0InH9/4oDCBRzWXhpOqusspLBrVfK1vPvbn9Wxl8DAQ8yyx5fWJRETICSwkiAMaYntjJAMBP1R4B6cQnEUYVEA==}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ infer-owner@1.0.4:
+ resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ inline-style-parser@0.2.7:
+ resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
+
+ internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
+ ip-address@10.1.0:
+ resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
+ engines: {node: '>= 12'}
+
+ ipaddr.js@1.9.1:
+ resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+ engines: {node: '>= 0.10'}
+
+ 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-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-ci@3.0.1:
+ resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
+ hasBin: true
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-decimal@2.0.1:
+ resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+
+ is-extendable@0.1.1:
+ resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
+ engines: {node: '>=0.10.0'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ 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-interactive@1.0.0:
+ resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
+ engines: {node: '>=8'}
+
+ is-lambda@1.0.1:
+ resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ is-promise@4.0.0:
+ resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+
+ is-unicode-supported@0.1.0:
+ resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+ engines: {node: '>=10'}
+
+ is-what@5.5.0:
+ resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
+ engines: {node: '>=18'}
+
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+ isbinaryfile@4.0.10:
+ resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
+ engines: {node: '>= 8.0.0'}
+
+ isbinaryfile@5.0.7:
+ resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==}
+ engines: {node: '>= 18.0.0'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jake@10.9.4:
+ resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ jiti@1.21.7:
+ resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+ hasBin: true
+
+ jose@6.1.3:
+ resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==}
+
+ jotai@2.16.2:
+ resolution: {integrity: sha512-DH0lBiTXvewsxtqqwjDW6Hg9JPTDnq9LcOsXSFWCAUEt+qj5ohl9iRVX9zQXPPHKLXCdH+5mGvM28fsXMl17/g==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@babel/core': '>=7.0.0'
+ '@babel/template': '>=7.0.0'
+ '@types/react': '>=17.0.0'
+ react: '>=17.0.0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ '@babel/template':
+ optional: true
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.2:
+ resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
+ hasBin: true
+
+ js-yaml@4.1.1:
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
+ hasBin: true
+
+ 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-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-schema-typed@8.0.2:
+ resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==}
+
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+ json-stringify-safe@5.0.1:
+ resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonc-parser@3.3.1:
+ resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+ jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
+ katex@0.16.28:
+ resolution: {integrity: sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==}
+ hasBin: true
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+
+ kind-of@6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+
+ langium@3.3.1:
+ resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==}
+ engines: {node: '>=16.0.0'}
+
+ layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+
+ layout-base@2.0.1:
+ resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==}
+
+ lazy-val@1.0.5:
+ resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==}
+
+ lazystream@1.0.1:
+ resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
+ engines: {node: '>= 0.6.3'}
+
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+ lodash-es@4.17.23:
+ resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
+
+ lodash.defaults@4.2.0:
+ resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+
+ lodash.difference@4.5.0:
+ resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
+
+ lodash.escaperegexp@4.1.2:
+ resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}
+
+ lodash.flatten@4.4.0:
+ resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
+
+ lodash.isequal@4.5.0:
+ resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+ deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+ lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
+ lodash.union@4.6.0:
+ resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
+
+ lodash@4.17.23:
+ resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
+
+ log-symbols@4.1.0:
+ resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+ engines: {node: '>=10'}
+
+ long@5.3.2:
+ resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lowercase-keys@2.0.0:
+ resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
+ engines: {node: '>=8'}
+
+ lowlight@3.3.0:
+ resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lru-cache@6.0.0:
+ resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+ engines: {node: '>=10'}
+
+ lru-cache@7.18.3:
+ resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
+ engines: {node: '>=12'}
+
+ lru_map@0.4.1:
+ resolution: {integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==}
+
+ lucide-react@0.468.0:
+ resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
+
+ lzma-native@8.0.6:
+ resolution: {integrity: sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ make-fetch-happen@10.2.1:
+ resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ marked@14.0.0:
+ resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
+ engines: {node: '>= 18'}
+ hasBin: true
+
+ marked@16.4.2:
+ resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ marked@17.0.1:
+ resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ matcher@3.0.0:
+ resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
+ engines: {node: '>=10'}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.2:
+ resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
+
+ 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-newline-to-break@2.0.0:
+ resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==}
+
+ 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==}
+
+ media-typer@1.1.0:
+ resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
+ engines: {node: '>= 0.8'}
+
+ merge-descriptors@2.0.0:
+ resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
+ engines: {node: '>=18'}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ mermaid@11.12.2:
+ resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==}
+
+ 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==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-db@1.54.0:
+ resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@3.0.2:
+ resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
+ engines: {node: '>=18'}
+
+ mime@2.6.0:
+ resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
+ engines: {node: '>=4.0.0'}
+ hasBin: true
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ mimic-response@1.0.1:
+ resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
+ engines: {node: '>=4'}
+
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
+ minimatch@10.1.1:
+ resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
+ engines: {node: 20 || >=22}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass-collect@1.0.2:
+ resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
+ engines: {node: '>= 8'}
+
+ minipass-fetch@2.1.2:
+ resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ minipass-flush@1.0.5:
+ resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
+ engines: {node: '>= 8'}
+
+ minipass-pipeline@1.2.4:
+ resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
+ engines: {node: '>=8'}
+
+ minipass-sized@1.0.3:
+ resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
+ engines: {node: '>=8'}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp-classic@0.5.3:
+ resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mlly@1.8.0:
+ resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
+
+ module-details-from-path@1.0.4:
+ resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
+
+ monaco-editor@0.55.1:
+ resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
+
+ motion-dom@11.18.1:
+ resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==}
+
+ motion-utils@11.18.1:
+ resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==}
+
+ motion@11.18.2:
+ resolution: {integrity: sha512-JLjvFDuFr42NFtcVoMAyC2sEjnpA8xpy6qWPyzQvCloznAyQ8FIXioxWfHiLtgYhoVpfUqSWpn1h9++skj9+Wg==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-build-utils@2.0.0:
+ resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
+
+ negotiator@0.6.4:
+ resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
+ engines: {node: '>= 0.6'}
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ next-themes@0.4.6:
+ resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ node-abi@3.87.0:
+ resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==}
+ engines: {node: '>=10'}
+
+ node-addon-api@1.7.2:
+ resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
+
+ node-addon-api@3.2.1:
+ resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-api-version@0.1.4:
+ resolution: {integrity: sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==}
+
+ node-api-version@0.2.1:
+ resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==}
+
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
+ node-gyp@9.4.1:
+ resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==}
+ engines: {node: ^12.13 || ^14.13 || >=16}
+ hasBin: true
+
+ node-pty@1.1.0:
+ resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==}
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ nopt@6.0.0:
+ resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ hasBin: true
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-url@6.1.0:
+ resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
+ engines: {node: '>=10'}
+
+ npmlog@6.0.2:
+ resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+ deprecated: This package is no longer supported.
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ oniguruma-parser@0.12.1:
+ resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
+
+ oniguruma-to-es@2.3.0:
+ resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==}
+
+ oniguruma-to-es@4.3.4:
+ resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
+
+ ora@5.4.1:
+ resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
+ engines: {node: '>=10'}
+
+ p-cancelable@2.1.1:
+ resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
+ engines: {node: '>=8'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-map@4.0.0:
+ resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+ engines: {node: '>=10'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ package-manager-detector@1.6.0:
+ resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
+
+ parse-entities@4.0.2:
+ resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
+ path-data-parser@0.1.0:
+ resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ path-to-regexp@8.3.0:
+ resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ pe-library@0.4.1:
+ resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==}
+ engines: {node: '>=12', npm: '>=6'}
+
+ pend@1.2.0:
+ resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
+ pg-int8@1.0.1:
+ resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
+ engines: {node: '>=4.0.0'}
+
+ pg-protocol@1.11.0:
+ resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==}
+
+ pg-types@2.2.0:
+ resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
+ engines: {node: '>=4'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ pkce-challenge@5.0.1:
+ resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
+ engines: {node: '>=16.20.0'}
+
+ pkg-types@1.3.1:
+ resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+ plist@3.1.0:
+ resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
+ engines: {node: '>=10.4.0'}
+
+ points-on-curve@0.2.0:
+ resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
+
+ points-on-path@0.2.1:
+ resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+
+ postcss-import@15.1.0:
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-js@4.1.0:
+ resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ postcss-load-config@6.0.1:
+ resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ jiti: '>=1.21.0'
+ postcss: '>=8.0.9'
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+ postcss:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ postcss-nested@6.2.0:
+ resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+
+ postcss-selector-parser@6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postgres-array@2.0.0:
+ resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
+ engines: {node: '>=4'}
+
+ postgres-bytea@1.0.1:
+ resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==}
+ engines: {node: '>=0.10.0'}
+
+ postgres-date@1.0.7:
+ resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
+ engines: {node: '>=0.10.0'}
+
+ postgres-interval@1.2.0:
+ resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
+ engines: {node: '>=0.10.0'}
+
+ posthog-js@1.335.2:
+ resolution: {integrity: sha512-xiPh9eXqNiNiFZjVe+HMcuEeqhbMJuL+bOVUM6ywGAfxUe71av71q6hK/zlzIiPNsPxhV6PL08LC6yPooStQxA==}
+
+ posthog-node@5.24.2:
+ resolution: {integrity: sha512-cywIUYtSIC9BilgLlZd1R2xNk6omKL6tywG/SCPmUJKeG2jhjvJHSrHXYx4x3uQsUjn8aB9UVI8km+W326Zm8g==}
+ engines: {node: ^20.20.0 || >=22.22.0}
+
+ preact@10.28.2:
+ resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==}
+
+ prebuild-install@7.1.3:
+ resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+ progress@2.0.3:
+ resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+ engines: {node: '>=0.4.0'}
+
+ promise-inflight@1.0.1:
+ resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
+ peerDependencies:
+ bluebird: '*'
+ peerDependenciesMeta:
+ bluebird:
+ optional: true
+
+ promise-retry@2.0.1:
+ resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
+ engines: {node: '>=10'}
+
+ property-information@7.1.0:
+ resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
+
+ protobufjs@7.5.4:
+ resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
+ engines: {node: '>=12.0.0'}
+
+ proxy-addr@2.0.7:
+ resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+ engines: {node: '>= 0.10'}
+
+ pump@3.0.3:
+ resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qs@6.14.1:
+ resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
+ engines: {node: '>=0.6'}
+
+ query-selector-shadow-dom@1.0.1:
+ resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ quick-lru@5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+
+ range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+
+ raw-body@3.0.2:
+ resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
+ engines: {node: '>= 0.10'}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
+ react-dom@19.2.1:
+ resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==}
+ peerDependencies:
+ react: ^19.2.1
+
+ react-hotkeys-hook@4.6.2:
+ resolution: {integrity: sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==}
+ peerDependencies:
+ react: '>=16.8.1'
+ react-dom: '>=16.8.1'
+
+ react-icons@5.5.0:
+ resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
+ peerDependencies:
+ react: '*'
+
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
+ 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-zoom-pan-pinch@3.7.0:
+ resolution: {integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==}
+ engines: {node: '>=8', npm: '>=5'}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+
+ react@19.2.1:
+ resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==}
+ engines: {node: '>=0.10.0'}
+
+ reactivity-store@0.3.12:
+ resolution: {integrity: sha512-Idz9EL4dFUtQbHySZQzckWOTUfqjdYpUtNW0iOysC32mG7IjiUGB77QrsyR5eAWBkRiS9JscF6A3fuQAIy+LrQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ read-binary-file-arch@1.0.6:
+ resolution: {integrity: sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==}
+ hasBin: true
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdir-glob@1.1.3:
+ resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ readdirp@5.0.0:
+ resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
+ engines: {node: '>= 20.19.0'}
+
+ regex-recursion@5.1.1:
+ resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==}
+
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@5.1.1:
+ resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
+
+ regex@6.1.0:
+ resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
+
+ rehype-harden@1.1.7:
+ resolution: {integrity: sha512-j5DY0YSK2YavvNGV+qBHma15J9m0WZmRe8posT5AtKDS6TNWtMVTo6RiqF8SidfcASYz8f3k2J/1RWmq5zTXUw==}
+
+ rehype-raw@7.0.0:
+ resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
+
+ rehype-sanitize@6.0.0:
+ resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
+
+ remark-breaks@4.0.0:
+ resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
+
+ 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==}
+
+ remend@1.1.0:
+ resolution: {integrity: sha512-JENGyuIhTwzUfCarW43X4r9cehoqTo9QyYxfNDZSud2AmqeuWjZ5pfybasTa4q0dxTJAj5m8NB+wR+YueAFpxQ==}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ require-in-the-middle@8.0.1:
+ resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
+ engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
+
+ resedit@1.7.2:
+ resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==}
+ engines: {node: '>=12', npm: '>=6'}
+
+ resolve-alpn@1.2.1:
+ resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ responselike@2.0.1:
+ resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+
+ restore-cursor@3.1.0:
+ resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+ engines: {node: '>=8'}
+
+ retry@0.12.0:
+ resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
+ engines: {node: '>= 4'}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ roarr@2.15.4:
+ resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
+ engines: {node: '>=8.0'}
+
+ robust-predicates@3.0.2:
+ resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
+
+ rollup@4.56.0:
+ resolution: {integrity: sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ roughjs@4.6.6:
+ resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
+
+ router@2.2.0:
+ resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
+ engines: {node: '>= 18'}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sanitize-filename@1.6.3:
+ resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==}
+
+ sax@1.4.4:
+ resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==}
+ engines: {node: '>=11.0.0'}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ section-matter@1.0.0:
+ resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
+ engines: {node: '>=4'}
+
+ semver-compare@1.0.0:
+ resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ send@1.2.1:
+ resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
+ engines: {node: '>= 18'}
+
+ serialize-error@7.0.1:
+ resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
+ engines: {node: '>=10'}
+
+ serve-static@2.2.1:
+ resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
+ engines: {node: '>= 18'}
+
+ set-blocking@2.0.0:
+ resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ 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'}
+
+ shiki@1.29.2:
+ resolution: {integrity: sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==}
+
+ shiki@3.21.0:
+ resolution: {integrity: sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ simple-concat@1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+
+ simple-get@4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+
+ simple-git@3.30.0:
+ resolution: {integrity: sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==}
+
+ simple-update-notifier@2.0.0:
+ resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
+ engines: {node: '>=10'}
+
+ slice-ansi@3.0.0:
+ resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
+ engines: {node: '>=8'}
+
+ smart-buffer@4.2.0:
+ resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+ engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
+ socks-proxy-agent@7.0.0:
+ resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
+ engines: {node: '>= 10'}
+
+ socks@2.8.7:
+ resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
+ engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
+
+ sonner@1.7.4:
+ resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==}
+ 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'}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ sprintf-js@1.1.3:
+ resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
+
+ ssri@9.0.1:
+ resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ stat-mode@1.0.0:
+ resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==}
+ engines: {node: '>= 6'}
+
+ state-local@1.0.7:
+ resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
+
+ statuses@2.0.2:
+ resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
+ engines: {node: '>= 0.8'}
+
+ streamdown@2.1.0:
+ resolution: {integrity: sha512-u9gWd0AmjKg1d+74P44XaPlGrMeC21oDOSIhjGNEYMAttDMzCzlJO6lpTyJ9JkSinQQF65YcK4eOd3q9iTvULw==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
+ 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==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.2:
+ resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
+ engines: {node: '>=12'}
+
+ strip-bom-string@1.0.0:
+ resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
+ engines: {node: '>=0.10.0'}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
+ style-to-js@1.1.21:
+ resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
+
+ style-to-object@1.0.14:
+ resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
+
+ stylis@4.3.6:
+ resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
+
+ sucrase@3.35.1:
+ resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ sumchecker@3.0.1:
+ resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==}
+ engines: {node: '>= 8.0'}
+
+ superjson@2.2.6:
+ resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==}
+ engines: {node: '>=16'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ swr@2.3.8:
+ resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==}
+ peerDependencies:
+ react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ tailwind-merge@2.6.0:
+ resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
+
+ tailwind-merge@3.4.0:
+ resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
+
+ tailwindcss-animate@1.0.7:
+ resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+
+ tailwindcss@3.4.19:
+ resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ tar-fs@2.1.4:
+ resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
+
+ tar-stream@2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
+
+ temp-file@3.4.0:
+ resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==}
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ throttleit@2.1.0:
+ resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
+ engines: {node: '>=18'}
+
+ tiny-typed-emitter@2.1.0:
+ resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==}
+
+ tinyexec@1.0.2:
+ resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tmp-promise@3.0.3:
+ resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
+
+ tmp@0.2.5:
+ resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
+ engines: {node: '>=14.14'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
+ trpc-electron@0.1.2:
+ resolution: {integrity: sha512-sQpWBwQWzsgrERugjzUpPqY/+/n8NxkUq6YssQ5+5rALkvGCWq45T5Dreiwm2kh91dZMFlALTyMd8PhB0vgbIg==}
+ peerDependencies:
+ '@trpc/client': '>=11.0.0'
+ '@trpc/server': '>=11.0.0'
+ electron: '>19.0.0'
+
+ truncate-utf8-bytes@1.0.2:
+ resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
+
+ ts-dedent@2.2.0:
+ resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
+ engines: {node: '>=6.10'}
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
+ type-fest@0.13.1:
+ resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
+ engines: {node: '>=10'}
+
+ type-is@2.0.1:
+ resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
+ engines: {node: '>= 0.6'}
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ufo@1.6.3:
+ resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ unified@11.0.5:
+ resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
+ unique-filename@2.0.1:
+ resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ unique-names-generator@4.7.1:
+ resolution: {integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==}
+ engines: {node: '>=8'}
+
+ unique-slug@3.0.0:
+ resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ 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==}
+
+ universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ 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
+
+ utf8-byte-length@1.0.5:
+ resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ uuid@11.1.0:
+ resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
+ hasBin: true
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
+ verror@1.10.1:
+ resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==}
+ engines: {node: '>=0.6.0'}
+
+ vfile-location@5.0.3:
+ resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@6.4.1:
+ resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vscode-jsonrpc@8.2.0:
+ resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
+ engines: {node: '>=14.0.0'}
+
+ vscode-languageserver-protocol@3.17.5:
+ resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+
+ vscode-languageserver-textdocument@1.0.12:
+ resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
+
+ vscode-languageserver-types@3.17.5:
+ resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
+
+ vscode-languageserver@9.0.1:
+ resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
+ hasBin: true
+
+ vscode-uri@3.0.8:
+ resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
+
+ wcwidth@1.0.1:
+ resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+
+ web-namespaces@2.0.1:
+ resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+
+ web-vitals@5.1.0:
+ resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ wide-align@1.1.5:
+ resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ xmlbuilder@15.1.1:
+ resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
+ engines: {node: '>=8.0'}
+
+ xtend@4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+
+ xterm@5.3.0:
+ resolution: {integrity: sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==}
+ deprecated: This package is now deprecated. Move to @xterm/xterm instead.
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yauzl@2.10.0:
+ resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zip-stream@4.1.1:
+ resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
+ engines: {node: '>= 10'}
+
+ zod-to-json-schema@3.25.1:
+ resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==}
+ peerDependencies:
+ zod: ^3.25 || ^4
+
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
+ zustand@5.0.10:
+ resolution: {integrity: sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ 7zip-bin@5.2.0: {}
+
+ '@ai-sdk/gateway@3.0.22(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.5
+ '@ai-sdk/provider-utils': 4.0.9(zod@3.25.76)
+ '@vercel/oidc': 3.1.0
+ zod: 3.25.76
+
+ '@ai-sdk/provider-utils@4.0.9(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.5
+ '@standard-schema/spec': 1.1.0
+ eventsource-parser: 3.0.6
+ zod: 3.25.76
+
+ '@ai-sdk/provider@3.0.5':
+ dependencies:
+ json-schema: 0.4.0
+
+ '@ai-sdk/react@3.0.51(react@19.2.1)(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider-utils': 4.0.9(zod@3.25.76)
+ ai: 6.0.49(zod@3.25.76)
+ react: 19.2.1
+ swr: 2.3.8(react@19.2.1)
+ throttleit: 2.1.0
+ transitivePeerDependencies:
+ - zod
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@antfu/install-pkg@1.1.0':
+ dependencies:
+ package-manager-detector: 1.6.0
+ tinyexec: 1.0.2
+
+ '@anthropic-ai/claude-agent-sdk@0.2.25(zod@3.25.76)':
+ dependencies:
+ zod: 3.25.76
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.33.5
+ '@img/sharp-darwin-x64': 0.33.5
+ '@img/sharp-linux-arm': 0.33.5
+ '@img/sharp-linux-arm64': 0.33.5
+ '@img/sharp-linux-x64': 0.33.5
+ '@img/sharp-linuxmusl-arm64': 0.33.5
+ '@img/sharp-linuxmusl-x64': 0.33.5
+ '@img/sharp-win32-x64': 0.33.5
+
+ '@apm-js-collab/code-transformer@0.8.2': {}
+
+ '@apm-js-collab/tracing-hooks@0.3.1':
+ dependencies:
+ '@apm-js-collab/code-transformer': 0.8.2
+ debug: 4.4.3
+ module-details-from-path: 1.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/code-frame@7.28.6':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.6': {}
+
+ '@babel/core@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/generator': 7.28.6
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6)
+ '@babel/helpers': 7.28.6
+ '@babel/parser': 7.28.6
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.28.6
+ '@babel/types': 7.28.6
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.6':
+ dependencies:
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.28.6
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.28.6
+ '@babel/types': 7.28.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.28.6': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.6':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.28.6
+
+ '@babel/parser@7.28.6':
+ dependencies:
+ '@babel/types': 7.28.6
+
+ '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+
+ '@babel/traverse@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/generator': 7.28.6
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.6
+ '@babel/template': 7.28.6
+ '@babel/types': 7.28.6
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@braintree/sanitize-url@7.1.1': {}
+
+ '@chevrotain/cst-dts-gen@11.0.3':
+ dependencies:
+ '@chevrotain/gast': 11.0.3
+ '@chevrotain/types': 11.0.3
+ lodash-es: 4.17.21
+
+ '@chevrotain/gast@11.0.3':
+ dependencies:
+ '@chevrotain/types': 11.0.3
+ lodash-es: 4.17.21
+
+ '@chevrotain/regexp-to-ast@11.0.3': {}
+
+ '@chevrotain/types@11.0.3': {}
+
+ '@chevrotain/utils@11.0.3': {}
+
+ '@develar/schema-utils@2.6.5':
+ dependencies:
+ ajv: 6.12.6
+ ajv-keywords: 3.5.2(ajv@6.12.6)
+
+ '@drizzle-team/brocli@0.10.2': {}
+
+ '@electron-toolkit/preload@3.0.2(electron@33.4.5)':
+ dependencies:
+ electron: 33.4.5
+
+ '@electron-toolkit/utils@4.0.0(electron@33.4.5)':
+ dependencies:
+ electron: 33.4.5
+
+ '@electron/asar@3.4.1':
+ dependencies:
+ commander: 5.1.0
+ glob: 7.2.3
+ minimatch: 3.1.2
+
+ '@electron/get@2.0.3':
+ dependencies:
+ debug: 4.4.3
+ env-paths: 2.2.1
+ fs-extra: 8.1.0
+ got: 11.8.6
+ progress: 2.0.3
+ semver: 6.3.1
+ sumchecker: 3.0.1
+ optionalDependencies:
+ global-agent: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@electron/notarize@2.5.0':
+ dependencies:
+ debug: 4.4.3
+ fs-extra: 9.1.0
+ promise-retry: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@electron/osx-sign@1.3.1':
+ dependencies:
+ compare-version: 0.1.2
+ debug: 4.4.3
+ fs-extra: 10.1.0
+ isbinaryfile: 4.0.10
+ minimist: 1.2.8
+ plist: 3.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@electron/rebuild@3.6.1':
+ dependencies:
+ '@malept/cross-spawn-promise': 2.0.0
+ chalk: 4.1.2
+ debug: 4.4.3
+ detect-libc: 2.1.2
+ fs-extra: 10.1.0
+ got: 11.8.6
+ node-abi: 3.87.0
+ node-api-version: 0.2.1
+ node-gyp: 9.4.1
+ ora: 5.4.1
+ read-binary-file-arch: 1.0.6
+ semver: 7.7.3
+ tar: 6.2.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+
+ '@electron/universal@2.0.1':
+ dependencies:
+ '@electron/asar': 3.4.1
+ '@malept/cross-spawn-promise': 2.0.0
+ debug: 4.4.3
+ dir-compare: 4.2.0
+ fs-extra: 11.3.3
+ minimatch: 9.0.5
+ plist: 3.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@esbuild-kit/core-utils@3.3.2':
+ dependencies:
+ esbuild: 0.18.20
+ source-map-support: 0.5.21
+
+ '@esbuild-kit/esm-loader@2.6.5':
+ dependencies:
+ '@esbuild-kit/core-utils': 3.3.2
+ get-tsconfig: 4.13.0
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm@0.18.20':
+ optional: true
+
+ '@esbuild/android-arm@0.25.12':
+ optional: true
+
+ '@esbuild/android-x64@0.18.20':
+ optional: true
+
+ '@esbuild/android-x64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-x64@0.18.20':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.18.20':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm@0.18.20':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ia32@0.18.20':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/linux-loong64@0.18.20':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.18.20':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.18.20':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.18.20':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-s390x@0.18.20':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.12':
+ optional: true
+
+ '@esbuild/linux-x64@0.18.20':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.18.20':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.18.20':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/sunos-x64@0.18.20':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-ia32@0.18.20':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/win32-x64@0.18.20':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.12':
+ optional: true
+
+ '@floating-ui/core@1.7.3':
+ dependencies:
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/dom@1.7.4':
+ dependencies:
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/react-dom@2.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@gar/promisify@1.1.3': {}
+
+ '@git-diff-view/core@0.0.35':
+ dependencies:
+ '@git-diff-view/lowlight': 0.0.35
+ fast-diff: 1.3.0
+ highlight.js: 11.11.1
+ lowlight: 3.3.0
+
+ '@git-diff-view/lowlight@0.0.35':
+ dependencies:
+ '@types/hast': 3.0.4
+ highlight.js: 11.11.1
+ lowlight: 3.3.0
+
+ '@git-diff-view/react@0.0.35(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@git-diff-view/core': 0.0.35
+ '@types/hast': 3.0.4
+ fast-diff: 1.3.0
+ highlight.js: 11.11.1
+ lowlight: 3.3.0
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ reactivity-store: 0.3.12(react@19.2.1)
+ use-sync-external-store: 1.6.0(react@19.2.1)
+
+ '@git-diff-view/shiki@0.0.36':
+ dependencies:
+ '@types/hast': 3.0.4
+ shiki: 3.21.0
+
+ '@hono/node-server@1.19.9(hono@4.11.5)':
+ dependencies:
+ hono: 4.11.5
+
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@3.1.0':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@iconify/types': 2.0.0
+ mlly: 1.8.0
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ optional: true
+
+ '@img/sharp-linux-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-win32-x64@0.33.5':
+ optional: true
+
+ '@isaacs/balanced-match@4.0.1': {}
+
+ '@isaacs/brace-expansion@5.0.0':
+ dependencies:
+ '@isaacs/balanced-match': 4.0.1
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.2
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@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
+
+ '@kwsites/file-exists@1.1.1':
+ dependencies:
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@kwsites/promise-deferred@1.1.1': {}
+
+ '@malept/cross-spawn-promise@2.0.0':
+ dependencies:
+ cross-spawn: 7.0.6
+
+ '@malept/flatpak-bundler@0.4.0':
+ dependencies:
+ debug: 4.4.3
+ fs-extra: 9.1.0
+ lodash: 4.17.23
+ tmp-promise: 3.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@mermaid-js/parser@0.6.3':
+ dependencies:
+ langium: 3.3.1
+
+ '@modelcontextprotocol/sdk@1.25.3(hono@4.11.5)(zod@3.25.76)':
+ dependencies:
+ '@hono/node-server': 1.19.9(hono@4.11.5)
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ content-type: 1.0.5
+ cors: 2.8.6
+ cross-spawn: 7.0.6
+ eventsource: 3.0.7
+ eventsource-parser: 3.0.6
+ express: 5.2.1
+ express-rate-limit: 7.5.1(express@5.2.1)
+ jose: 6.1.3
+ json-schema-typed: 8.0.2
+ pkce-challenge: 5.0.1
+ raw-body: 3.0.2
+ zod: 3.25.76
+ zod-to-json-schema: 3.25.1(zod@3.25.76)
+ transitivePeerDependencies:
+ - hono
+ - supports-color
+
+ '@monaco-editor/loader@1.7.0':
+ dependencies:
+ state-local: 1.0.7
+
+ '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@monaco-editor/loader': 1.7.0
+ monaco-editor: 0.55.1
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@npmcli/fs@2.1.2':
+ dependencies:
+ '@gar/promisify': 1.1.3
+ semver: 7.7.3
+
+ '@npmcli/move-file@2.0.1':
+ dependencies:
+ mkdirp: 1.0.4
+ rimraf: 3.0.2
+
+ '@opentelemetry/api-logs@0.208.0':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/api@1.9.0': {}
+
+ '@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.208.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/instrumentation-amqplib@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-connect@0.52.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/connect': 3.4.38
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-dataloader@0.26.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-express@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-fs@0.28.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-generic-pool@0.52.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-graphql@0.56.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-hapi@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-http@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ forwarded-parse: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-ioredis@0.56.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/redis-common': 0.38.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-kafkajs@0.18.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-knex@0.53.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-koa@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-lru-memoizer@0.53.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongodb@0.61.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongoose@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql2@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql@0.54.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@types/mysql': 2.15.27
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-pg@0.61.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
+ '@types/pg': 8.15.6
+ '@types/pg-pool': 2.0.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-redis@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/redis-common': 0.38.2
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-tedious@0.27.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@types/tedious': 4.0.14
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-undici@0.19.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.208.0
+ import-in-the-middle: 2.0.5
+ require-in-the-middle: 8.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.208.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
+ protobufjs: 7.5.4
+
+ '@opentelemetry/redis-common@0.38.2': {}
+
+ '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.208.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/semantic-conventions@1.39.0': {}
+
+ '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@pierre/diffs@1.0.10(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@shikijs/core': 3.21.0
+ '@shikijs/engine-javascript': 3.21.0
+ '@shikijs/transformers': 3.22.0
+ diff: 8.0.3
+ hast-util-to-html: 9.0.5
+ lru_map: 0.4.1
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ shiki: 3.21.0
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@posthog/core@1.14.0':
+ dependencies:
+ cross-spawn: 7.0.6
+
+ '@posthog/types@1.335.2': {}
+
+ '@prisma/instrumentation@6.19.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@protobufjs/aspromise@1.1.2': {}
+
+ '@protobufjs/base64@1.1.2': {}
+
+ '@protobufjs/codegen@2.0.4': {}
+
+ '@protobufjs/eventemitter@1.1.0': {}
+
+ '@protobufjs/fetch@1.1.0':
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/inquire': 1.1.0
+
+ '@protobufjs/float@1.0.2': {}
+
+ '@protobufjs/inquire@1.1.0': {}
+
+ '@protobufjs/path@1.1.2': {}
+
+ '@protobufjs/pool@1.1.0': {}
+
+ '@protobufjs/utf8@1.1.0': {}
+
+ '@radix-ui/number@1.1.1': {}
+
+ '@radix-ui/primitive@1.1.3': {}
+
+ '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-context@1.1.2(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-context@1.1.3(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ aria-hidden: 1.2.6
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ react-remove-scroll: 2.7.2(@types/react@19.2.9)(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-direction@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-icons@1.3.2(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+
+ '@radix-ui/react-id@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ aria-hidden: 1.2.6
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ react-remove-scroll: 2.7.2(@types/react@19.2.9)(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ aria-hidden: 1.2.6
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ react-remove-scroll: 2.7.2(@types/react@19.2.9)(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/rect': 1.1.1
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.4(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-progress@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ aria-hidden: 1.2.6
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ react-remove-scroll: 2.7.2(@types/react@19.2.9)(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-slot@1.2.3(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-slot@1.2.4(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.9)(react@19.2.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/rect': 1.1.1
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-use-size@1.1.1(@types/react@19.2.9)(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.9)(react@19.2.1)
+ react: 19.2.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/react-visually-hidden@1.2.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+ '@types/react-dom': 19.2.3(@types/react@19.2.9)
+
+ '@radix-ui/rect@1.1.1': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.27': {}
+
+ '@rollup/rollup-android-arm-eabi@4.56.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.56.0':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.56.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.56.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.56.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.56.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.56.0':
+ optional: true
+
+ '@sentry-internal/browser-utils@10.34.0':
+ dependencies:
+ '@sentry/core': 10.34.0
+
+ '@sentry-internal/feedback@10.34.0':
+ dependencies:
+ '@sentry/core': 10.34.0
+
+ '@sentry-internal/replay-canvas@10.34.0':
+ dependencies:
+ '@sentry-internal/replay': 10.34.0
+ '@sentry/core': 10.34.0
+
+ '@sentry-internal/replay@10.34.0':
+ dependencies:
+ '@sentry-internal/browser-utils': 10.34.0
+ '@sentry/core': 10.34.0
+
+ '@sentry/browser@10.34.0':
+ dependencies:
+ '@sentry-internal/browser-utils': 10.34.0
+ '@sentry-internal/feedback': 10.34.0
+ '@sentry-internal/replay': 10.34.0
+ '@sentry-internal/replay-canvas': 10.34.0
+ '@sentry/core': 10.34.0
+
+ '@sentry/core@10.34.0': {}
+
+ '@sentry/electron@7.6.0':
+ dependencies:
+ '@sentry/browser': 10.34.0
+ '@sentry/core': 10.34.0
+ '@sentry/node': 10.34.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/node-core@10.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)':
+ dependencies:
+ '@apm-js-collab/tracing-hooks': 0.3.1
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@sentry/core': 10.34.0
+ '@sentry/opentelemetry': 10.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ import-in-the-middle: 2.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/node@10.34.0':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-amqplib': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-connect': 0.52.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-dataloader': 0.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-express': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-fs': 0.28.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-generic-pool': 0.52.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-graphql': 0.56.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-hapi': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-http': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-ioredis': 0.56.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-kafkajs': 0.18.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-knex': 0.53.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-koa': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-lru-memoizer': 0.53.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongodb': 0.61.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongoose': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql2': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-pg': 0.61.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-redis': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-tedious': 0.27.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-undici': 0.19.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0)
+ '@sentry/core': 10.34.0
+ '@sentry/node-core': 10.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ '@sentry/opentelemetry': 10.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ import-in-the-middle: 2.0.5
+ minimatch: 9.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/opentelemetry@10.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@sentry/core': 10.34.0
+
+ '@shikijs/core@1.29.2':
+ dependencies:
+ '@shikijs/engine-javascript': 1.29.2
+ '@shikijs/engine-oniguruma': 1.29.2
+ '@shikijs/types': 1.29.2
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/core@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/core@3.22.0':
+ dependencies:
+ '@shikijs/types': 3.22.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/engine-javascript@1.29.2':
+ dependencies:
+ '@shikijs/types': 1.29.2
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 2.3.0
+
+ '@shikijs/engine-javascript@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 4.3.4
+
+ '@shikijs/engine-oniguruma@1.29.2':
+ dependencies:
+ '@shikijs/types': 1.29.2
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/engine-oniguruma@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@1.29.2':
+ dependencies:
+ '@shikijs/types': 1.29.2
+
+ '@shikijs/langs@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+
+ '@shikijs/themes@1.29.2':
+ dependencies:
+ '@shikijs/types': 1.29.2
+
+ '@shikijs/themes@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+
+ '@shikijs/transformers@3.22.0':
+ dependencies:
+ '@shikijs/core': 3.22.0
+ '@shikijs/types': 3.22.0
+
+ '@shikijs/types@1.29.2':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/types@3.21.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/types@3.22.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
+ '@sindresorhus/is@4.6.0': {}
+
+ '@standard-schema/spec@1.1.0': {}
+
+ '@szmarczak/http-timer@4.0.6':
+ dependencies:
+ defer-to-connect: 2.0.1
+
+ '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19)':
+ dependencies:
+ postcss-selector-parser: 6.0.10
+ tailwindcss: 3.4.19
+
+ '@tanstack/query-core@5.90.20': {}
+
+ '@tanstack/react-query@5.90.20(react@19.2.1)':
+ dependencies:
+ '@tanstack/query-core': 5.90.20
+ react: 19.2.1
+
+ '@tanstack/react-virtual@3.13.18(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@tanstack/virtual-core': 3.13.18
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ '@tanstack/virtual-core@3.13.18': {}
+
+ '@tootallnate/once@2.0.0': {}
+
+ '@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)':
+ dependencies:
+ '@trpc/server': 11.8.1(typescript@5.9.3)
+ typescript: 5.9.3
+
+ '@trpc/react-query@11.8.1(@tanstack/react-query@5.90.20(react@19.2.1))(@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.8.1(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3)':
+ dependencies:
+ '@tanstack/react-query': 5.90.20(react@19.2.1)
+ '@trpc/client': 11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)
+ '@trpc/server': 11.8.1(typescript@5.9.3)
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+ typescript: 5.9.3
+
+ '@trpc/server@11.8.1(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.28.6
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.6
+
+ '@types/better-sqlite3@7.6.13':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/cacheable-request@6.0.3':
+ dependencies:
+ '@types/http-cache-semantics': 4.0.4
+ '@types/keyv': 3.1.4
+ '@types/node': 20.19.30
+ '@types/responselike': 1.0.3
+
+ '@types/connect@3.4.38':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-axis@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-brush@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-chord@3.0.6': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-contour@3.0.6':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-dispatch@3.0.7': {}
+
+ '@types/d3-drag@3.0.7':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-dsv@3.0.7': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-fetch@3.0.7':
+ dependencies:
+ '@types/d3-dsv': 3.0.7
+
+ '@types/d3-force@3.0.10': {}
+
+ '@types/d3-format@3.0.4': {}
+
+ '@types/d3-geo@3.1.0':
+ dependencies:
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-hierarchy@3.1.7': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-polygon@3.0.2': {}
+
+ '@types/d3-quadtree@3.0.6': {}
+
+ '@types/d3-random@3.0.3': {}
+
+ '@types/d3-scale-chromatic@3.1.0': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-selection@3.0.11': {}
+
+ '@types/d3-shape@3.1.8':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time-format@4.0.3': {}
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@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/d3@7.4.3':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-axis': 3.0.6
+ '@types/d3-brush': 3.0.6
+ '@types/d3-chord': 3.0.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-contour': 3.0.6
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-dispatch': 3.0.7
+ '@types/d3-drag': 3.0.7
+ '@types/d3-dsv': 3.0.7
+ '@types/d3-ease': 3.0.2
+ '@types/d3-fetch': 3.0.7
+ '@types/d3-force': 3.0.10
+ '@types/d3-format': 3.0.4
+ '@types/d3-geo': 3.1.0
+ '@types/d3-hierarchy': 3.1.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-path': 3.1.1
+ '@types/d3-polygon': 3.0.2
+ '@types/d3-quadtree': 3.0.6
+ '@types/d3-random': 3.0.3
+ '@types/d3-scale': 4.0.9
+ '@types/d3-scale-chromatic': 3.1.0
+ '@types/d3-selection': 3.0.11
+ '@types/d3-shape': 3.1.8
+ '@types/d3-time': 3.0.4
+ '@types/d3-time-format': 4.0.3
+ '@types/d3-timer': 3.0.2
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+
+ '@types/debug@4.1.12':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/estree-jsx@1.0.5':
+ dependencies:
+ '@types/estree': 1.0.8
+
+ '@types/estree@1.0.8': {}
+
+ '@types/fs-extra@9.0.13':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/geojson@7946.0.16': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/http-cache-semantics@4.0.4': {}
+
+ '@types/keyv@3.1.4':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
+ '@types/mysql@2.15.27':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/node@20.19.30':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/pg-pool@2.0.6':
+ dependencies:
+ '@types/pg': 8.15.6
+
+ '@types/pg@8.15.6':
+ dependencies:
+ '@types/node': 20.19.30
+ pg-protocol: 1.11.0
+ pg-types: 2.2.0
+
+ '@types/plist@3.0.5':
+ dependencies:
+ '@types/node': 20.19.30
+ xmlbuilder: 15.1.1
+ optional: true
+
+ '@types/react-dom@19.2.3(@types/react@19.2.9)':
+ dependencies:
+ '@types/react': 19.2.9
+
+ '@types/react@19.2.9':
+ dependencies:
+ csstype: 3.2.3
+
+ '@types/responselike@1.0.3':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/tedious@4.0.14':
+ dependencies:
+ '@types/node': 20.19.30
+
+ '@types/trusted-types@2.0.7':
+ optional: true
+
+ '@types/unist@2.0.11': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/verror@1.10.11':
+ optional: true
+
+ '@types/yauzl@2.10.3':
+ dependencies:
+ '@types/node': 20.19.30
+ optional: true
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@vercel/oidc@3.1.0': {}
+
+ '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@20.19.30)(jiti@1.21.7))':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.6)
+ '@rolldown/pluginutils': 1.0.0-beta.27
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 6.4.1(@types/node@20.19.30)(jiti@1.21.7)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/reactivity@3.5.27':
+ dependencies:
+ '@vue/shared': 3.5.27
+
+ '@vue/shared@3.5.27': {}
+
+ '@welldone-software/why-did-you-render@10.0.1(react@19.2.1)':
+ dependencies:
+ lodash: 4.17.23
+ react: 19.2.1
+
+ '@xmldom/xmldom@0.8.11': {}
+
+ '@xterm/addon-canvas@0.7.0(@xterm/xterm@5.5.0)':
+ dependencies:
+ '@xterm/xterm': 5.5.0
+
+ '@xterm/addon-fit@0.11.0': {}
+
+ '@xterm/addon-search@0.16.0': {}
+
+ '@xterm/addon-serialize@0.14.0': {}
+
+ '@xterm/addon-web-links@0.12.0': {}
+
+ '@xterm/addon-webgl@0.19.0': {}
+
+ '@xterm/xterm@5.5.0': {}
+
+ abbrev@1.1.1: {}
+
+ accepts@2.0.0:
+ dependencies:
+ mime-types: 3.0.2
+ negotiator: 1.0.0
+
+ acorn-import-attributes@1.9.5(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@6.0.2:
+ dependencies:
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ agent-base@7.1.4: {}
+
+ agentkeepalive@4.6.0:
+ dependencies:
+ humanize-ms: 1.2.1
+
+ aggregate-error@3.1.0:
+ dependencies:
+ clean-stack: 2.2.0
+ indent-string: 4.0.0
+
+ ai@6.0.49(zod@3.25.76):
+ dependencies:
+ '@ai-sdk/gateway': 3.0.22(zod@3.25.76)
+ '@ai-sdk/provider': 3.0.5
+ '@ai-sdk/provider-utils': 4.0.9(zod@3.25.76)
+ '@opentelemetry/api': 1.9.0
+ zod: 3.25.76
+
+ ajv-formats@3.0.1(ajv@8.17.1):
+ optionalDependencies:
+ ajv: 8.17.1
+
+ ajv-keywords@3.5.2(ajv@6.12.6):
+ dependencies:
+ ajv: 6.12.6
+
+ ajv@6.12.6:
+ 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
+
+ ajv@8.17.1:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ app-builder-bin@5.0.0-alpha.10: {}
+
+ app-builder-lib@25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8):
+ dependencies:
+ '@develar/schema-utils': 2.6.5
+ '@electron/notarize': 2.5.0
+ '@electron/osx-sign': 1.3.1
+ '@electron/rebuild': 3.6.1
+ '@electron/universal': 2.0.1
+ '@malept/flatpak-bundler': 0.4.0
+ '@types/fs-extra': 9.0.13
+ async-exit-hook: 2.0.1
+ bluebird-lst: 1.0.9
+ builder-util: 25.1.7
+ builder-util-runtime: 9.2.10
+ chromium-pickle-js: 0.2.0
+ config-file-ts: 0.2.8-rc1
+ debug: 4.4.3
+ dmg-builder: 25.1.8(electron-builder-squirrel-windows@25.1.8)
+ dotenv: 16.6.1
+ dotenv-expand: 11.0.7
+ ejs: 3.1.10
+ electron-builder-squirrel-windows: 25.1.8(dmg-builder@25.1.8)
+ electron-publish: 25.1.7
+ form-data: 4.0.5
+ fs-extra: 10.1.0
+ hosted-git-info: 4.1.0
+ is-ci: 3.0.1
+ isbinaryfile: 5.0.7
+ js-yaml: 4.1.1
+ json5: 2.2.3
+ lazy-val: 1.0.5
+ minimatch: 10.1.1
+ resedit: 1.7.2
+ sanitize-filename: 1.6.3
+ semver: 7.7.3
+ tar: 6.2.1
+ temp-file: 3.4.0
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+
+ aproba@2.1.0: {}
+
+ archiver-utils@2.1.0:
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 2.3.8
+
+ archiver-utils@3.0.4:
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
+
+ archiver@5.3.2:
+ dependencies:
+ archiver-utils: 2.1.0
+ async: 3.2.6
+ buffer-crc32: 0.2.13
+ readable-stream: 3.6.2
+ readdir-glob: 1.1.3
+ tar-stream: 2.2.0
+ zip-stream: 4.1.1
+
+ are-we-there-yet@3.0.1:
+ dependencies:
+ delegates: 1.0.0
+ readable-stream: 3.6.2
+
+ arg@5.0.2: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ assert-plus@1.0.0:
+ optional: true
+
+ astral-regex@2.0.0:
+ optional: true
+
+ async-exit-hook@2.0.1: {}
+
+ async-mutex@0.5.0:
+ dependencies:
+ tslib: 2.8.1
+
+ async@3.2.6: {}
+
+ asynckit@0.4.0: {}
+
+ at-least-node@1.0.0: {}
+
+ autoprefixer@10.4.23(postcss@8.5.6):
+ dependencies:
+ browserslist: 4.28.1
+ caniuse-lite: 1.0.30001766
+ fraction.js: 5.3.4
+ picocolors: 1.1.1
+ postcss: 8.5.6
+ postcss-value-parser: 4.2.0
+
+ bail@2.0.2: {}
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ baseline-browser-mapping@2.9.18: {}
+
+ better-sqlite3@11.10.0:
+ dependencies:
+ bindings: 1.5.0
+ prebuild-install: 7.1.3
+
+ binary-extensions@2.3.0: {}
+
+ bindings@1.5.0:
+ dependencies:
+ file-uri-to-path: 1.0.0
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ bluebird-lst@1.0.9:
+ dependencies:
+ bluebird: 3.7.2
+
+ bluebird@3.7.2: {}
+
+ body-parser@2.2.2:
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 4.4.3
+ http-errors: 2.0.1
+ iconv-lite: 0.7.2
+ on-finished: 2.4.1
+ qs: 6.14.1
+ raw-body: 3.0.2
+ type-is: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ boolean@3.2.0:
+ optional: true
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.18
+ caniuse-lite: 1.0.30001766
+ electron-to-chromium: 1.5.278
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
+ buffer-crc32@0.2.13: {}
+
+ buffer-from@1.1.2: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ builder-util-runtime@9.2.10:
+ dependencies:
+ debug: 4.4.3
+ sax: 1.4.4
+ transitivePeerDependencies:
+ - supports-color
+
+ builder-util-runtime@9.5.1:
+ dependencies:
+ debug: 4.4.3
+ sax: 1.4.4
+ transitivePeerDependencies:
+ - supports-color
+
+ builder-util@25.1.7:
+ dependencies:
+ 7zip-bin: 5.2.0
+ '@types/debug': 4.1.12
+ app-builder-bin: 5.0.0-alpha.10
+ bluebird-lst: 1.0.9
+ builder-util-runtime: 9.2.10
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ fs-extra: 10.1.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-ci: 3.0.1
+ js-yaml: 4.1.1
+ source-map-support: 0.5.21
+ stat-mode: 1.0.0
+ temp-file: 3.4.0
+ transitivePeerDependencies:
+ - supports-color
+
+ bytes@3.1.2: {}
+
+ cac@6.7.14: {}
+
+ cacache@16.1.3:
+ dependencies:
+ '@npmcli/fs': 2.1.2
+ '@npmcli/move-file': 2.0.1
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ glob: 8.1.0
+ infer-owner: 1.0.4
+ lru-cache: 7.18.3
+ minipass: 3.3.6
+ minipass-collect: 1.0.2
+ minipass-flush: 1.0.5
+ minipass-pipeline: 1.2.4
+ mkdirp: 1.0.4
+ p-map: 4.0.0
+ promise-inflight: 1.0.1
+ rimraf: 3.0.2
+ ssri: 9.0.1
+ tar: 6.2.1
+ unique-filename: 2.0.1
+ transitivePeerDependencies:
+ - bluebird
+
+ cacheable-lookup@5.0.4: {}
+
+ cacheable-request@7.0.4:
+ dependencies:
+ clone-response: 1.0.3
+ get-stream: 5.2.0
+ http-cache-semantics: 4.2.0
+ keyv: 4.5.4
+ lowercase-keys: 2.0.0
+ normalize-url: 6.1.0
+ responselike: 2.0.1
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ camelcase-css@2.0.1: {}
+
+ caniuse-lite@1.0.30001766: {}
+
+ ccount@2.0.1: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ character-entities@2.0.2: {}
+
+ character-reference-invalid@2.0.1: {}
+
+ chevrotain-allstar@0.3.1(chevrotain@11.0.3):
+ dependencies:
+ chevrotain: 11.0.3
+ lodash-es: 4.17.23
+
+ chevrotain@11.0.3:
+ dependencies:
+ '@chevrotain/cst-dts-gen': 11.0.3
+ '@chevrotain/gast': 11.0.3
+ '@chevrotain/regexp-to-ast': 11.0.3
+ '@chevrotain/types': 11.0.3
+ '@chevrotain/utils': 11.0.3
+ lodash-es: 4.17.21
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@5.0.0:
+ dependencies:
+ readdirp: 5.0.0
+
+ chownr@1.1.4: {}
+
+ chownr@2.0.0: {}
+
+ chromium-pickle-js@0.2.0: {}
+
+ ci-info@3.9.0: {}
+
+ cjs-module-lexer@2.2.0: {}
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ clean-stack@2.2.0: {}
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
+ cli-spinners@2.9.2: {}
+
+ cli-truncate@2.1.0:
+ dependencies:
+ slice-ansi: 3.0.0
+ string-width: 4.2.3
+ optional: true
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clone-response@1.0.3:
+ dependencies:
+ mimic-response: 1.0.1
+
+ clone@1.0.4: {}
+
+ clsx@2.1.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ color-support@1.1.3: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ comma-separated-tokens@2.0.3: {}
+
+ commander@4.1.1: {}
+
+ commander@5.1.0: {}
+
+ commander@7.2.0: {}
+
+ commander@8.3.0: {}
+
+ compare-version@0.1.2: {}
+
+ compress-commons@4.1.2:
+ dependencies:
+ buffer-crc32: 0.2.13
+ crc32-stream: 4.0.3
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
+
+ concat-map@0.0.1: {}
+
+ confbox@0.1.8: {}
+
+ config-file-ts@0.2.8-rc1:
+ dependencies:
+ glob: 10.5.0
+ typescript: 5.9.3
+
+ console-control-strings@1.1.0: {}
+
+ content-disposition@1.0.1: {}
+
+ content-type@1.0.5: {}
+
+ convert-source-map@2.0.0: {}
+
+ cookie-signature@1.2.2: {}
+
+ cookie@0.7.2: {}
+
+ copy-anything@4.0.5:
+ dependencies:
+ is-what: 5.5.0
+
+ core-js@3.48.0: {}
+
+ core-util-is@1.0.2:
+ optional: true
+
+ core-util-is@1.0.3: {}
+
+ cors@2.8.6:
+ dependencies:
+ object-assign: 4.1.1
+ vary: 1.1.2
+
+ cose-base@1.0.3:
+ dependencies:
+ layout-base: 1.0.2
+
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
+ crc-32@1.2.2: {}
+
+ crc32-stream@4.0.3:
+ dependencies:
+ crc-32: 1.2.2
+ readable-stream: 3.6.2
+
+ crc@3.8.0:
+ dependencies:
+ buffer: 5.7.1
+ optional: true
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cssesc@3.0.0: {}
+
+ csstype@3.2.3: {}
+
+ cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1):
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.33.1
+
+ cytoscape-fcose@2.2.0(cytoscape@3.33.1):
+ dependencies:
+ cose-base: 2.2.0
+ cytoscape: 3.33.1
+
+ cytoscape@3.33.1: {}
+
+ d3-array@2.12.1:
+ dependencies:
+ internmap: 1.0.1
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-axis@3.0.0: {}
+
+ d3-brush@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)
+
+ d3-chord@3.0.1:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-color@3.1.0: {}
+
+ d3-contour@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.0.1
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-dsv@3.0.1:
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+
+ d3-ease@3.0.1: {}
+
+ d3-fetch@3.0.1:
+ dependencies:
+ d3-dsv: 3.0.1
+
+ d3-force@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+
+ d3-format@3.1.2: {}
+
+ d3-geo@3.1.1:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-hierarchy@3.1.2: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@1.0.9: {}
+
+ d3-path@3.1.0: {}
+
+ d3-polygon@3.0.1: {}
+
+ d3-quadtree@3.0.1: {}
+
+ d3-random@3.0.1: {}
+
+ d3-sankey@0.12.3:
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+
+ d3-scale-chromatic@3.1.0:
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-shape@1.3.7:
+ dependencies:
+ d3-path: 1.0.9
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ 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)
+
+ d3@7.9.0:
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.2
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+
+ dagre-d3-es@7.0.13:
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.17.23
+
+ date-fns@3.6.0: {}
+
+ dayjs@1.11.19: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ decompress-response@6.0.0:
+ dependencies:
+ mimic-response: 3.1.0
+
+ deep-extend@0.6.0: {}
+
+ defaults@1.0.4:
+ dependencies:
+ clone: 1.0.4
+
+ defer-to-connect@2.0.1: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+ optional: true
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+ optional: true
+
+ delaunator@5.0.1:
+ dependencies:
+ robust-predicates: 3.0.2
+
+ delayed-stream@1.0.0: {}
+
+ delegates@1.0.0: {}
+
+ depd@2.0.0: {}
+
+ dequal@2.0.3: {}
+
+ detect-libc@2.1.2: {}
+
+ detect-node-es@1.1.0: {}
+
+ detect-node@2.1.0:
+ optional: true
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ didyoumean@1.2.2: {}
+
+ diff@8.0.3: {}
+
+ dir-compare@4.2.0:
+ dependencies:
+ minimatch: 3.1.2
+ p-limit: 3.1.0
+
+ dlv@1.1.3: {}
+
+ dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8):
+ dependencies:
+ app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
+ builder-util: 25.1.7
+ builder-util-runtime: 9.2.10
+ fs-extra: 10.1.0
+ iconv-lite: 0.6.3
+ js-yaml: 4.1.1
+ optionalDependencies:
+ dmg-license: 1.0.11
+ transitivePeerDependencies:
+ - bluebird
+ - electron-builder-squirrel-windows
+ - supports-color
+
+ dmg-license@1.0.11:
+ dependencies:
+ '@types/plist': 3.0.5
+ '@types/verror': 1.10.11
+ ajv: 6.12.6
+ crc: 3.8.0
+ iconv-corefoundation: 1.1.7
+ plist: 3.1.0
+ smart-buffer: 4.2.0
+ verror: 1.10.1
+ optional: true
+
+ dompurify@3.2.7:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
+ dompurify@3.3.1:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
+ dotenv-expand@11.0.7:
+ dependencies:
+ dotenv: 16.6.1
+
+ dotenv@16.6.1: {}
+
+ drizzle-kit@0.31.8:
+ dependencies:
+ '@drizzle-team/brocli': 0.10.2
+ '@esbuild-kit/esm-loader': 2.6.5
+ esbuild: 0.25.12
+ esbuild-register: 3.6.0(esbuild@0.25.12)
+ transitivePeerDependencies:
+ - supports-color
+
+ drizzle-orm@0.45.1(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@11.10.0):
+ optionalDependencies:
+ '@opentelemetry/api': 1.9.0
+ '@types/better-sqlite3': 7.6.13
+ '@types/pg': 8.15.6
+ better-sqlite3: 11.10.0
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ eastasianwidth@0.2.0: {}
+
+ ee-first@1.1.1: {}
+
+ ejs@3.1.10:
+ dependencies:
+ jake: 10.9.4
+
+ electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8):
+ dependencies:
+ app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
+ archiver: 5.3.2
+ builder-util: 25.1.7
+ fs-extra: 10.1.0
+ transitivePeerDependencies:
+ - bluebird
+ - dmg-builder
+ - supports-color
+
+ electron-builder@25.1.8(electron-builder-squirrel-windows@25.1.8):
+ dependencies:
+ app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
+ builder-util: 25.1.7
+ builder-util-runtime: 9.2.10
+ chalk: 4.1.2
+ dmg-builder: 25.1.8(electron-builder-squirrel-windows@25.1.8)
+ fs-extra: 10.1.0
+ is-ci: 3.0.1
+ lazy-val: 1.0.5
+ simple-update-notifier: 2.0.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - bluebird
+ - electron-builder-squirrel-windows
+ - supports-color
+
+ electron-log@5.4.3: {}
+
+ electron-publish@25.1.7:
+ dependencies:
+ '@types/fs-extra': 9.0.13
+ builder-util: 25.1.7
+ builder-util-runtime: 9.2.10
+ chalk: 4.1.2
+ fs-extra: 10.1.0
+ lazy-val: 1.0.5
+ mime: 2.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ electron-rebuild@3.2.9:
+ dependencies:
+ '@malept/cross-spawn-promise': 2.0.0
+ chalk: 4.1.2
+ debug: 4.4.3
+ detect-libc: 2.1.2
+ fs-extra: 10.1.0
+ got: 11.8.6
+ lzma-native: 8.0.6
+ node-abi: 3.87.0
+ node-api-version: 0.1.4
+ node-gyp: 9.4.1
+ ora: 5.4.1
+ semver: 7.7.3
+ tar: 6.2.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+
+ electron-to-chromium@1.5.278: {}
+
+ electron-updater@6.7.3:
+ dependencies:
+ builder-util-runtime: 9.5.1
+ fs-extra: 10.1.0
+ js-yaml: 4.1.1
+ lazy-val: 1.0.5
+ lodash.escaperegexp: 4.1.2
+ lodash.isequal: 4.5.0
+ semver: 7.7.3
+ tiny-typed-emitter: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ electron-vite@3.1.0(vite@6.4.1(@types/node@20.19.30)(jiti@1.21.7)):
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.6)
+ cac: 6.7.14
+ esbuild: 0.25.12
+ magic-string: 0.30.21
+ picocolors: 1.1.1
+ vite: 6.4.1(@types/node@20.19.30)(jiti@1.21.7)
+ transitivePeerDependencies:
+ - supports-color
+
+ electron@33.4.5:
+ dependencies:
+ '@electron/get': 2.0.3
+ '@types/node': 20.19.30
+ extract-zip: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ emoji-regex-xs@1.0.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ encodeurl@2.0.0: {}
+
+ encoding@0.1.13:
+ dependencies:
+ iconv-lite: 0.6.3
+ optional: true
+
+ end-of-stream@1.4.5:
+ dependencies:
+ once: 1.4.0
+
+ entities@6.0.1: {}
+
+ env-paths@2.2.1: {}
+
+ err-code@2.0.3: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es6-error@4.1.1:
+ optional: true
+
+ esbuild-register@3.6.0(esbuild@0.25.12):
+ dependencies:
+ debug: 4.4.3
+ esbuild: 0.25.12
+ transitivePeerDependencies:
+ - supports-color
+
+ esbuild@0.18.20:
+ optionalDependencies:
+ '@esbuild/android-arm': 0.18.20
+ '@esbuild/android-arm64': 0.18.20
+ '@esbuild/android-x64': 0.18.20
+ '@esbuild/darwin-arm64': 0.18.20
+ '@esbuild/darwin-x64': 0.18.20
+ '@esbuild/freebsd-arm64': 0.18.20
+ '@esbuild/freebsd-x64': 0.18.20
+ '@esbuild/linux-arm': 0.18.20
+ '@esbuild/linux-arm64': 0.18.20
+ '@esbuild/linux-ia32': 0.18.20
+ '@esbuild/linux-loong64': 0.18.20
+ '@esbuild/linux-mips64el': 0.18.20
+ '@esbuild/linux-ppc64': 0.18.20
+ '@esbuild/linux-riscv64': 0.18.20
+ '@esbuild/linux-s390x': 0.18.20
+ '@esbuild/linux-x64': 0.18.20
+ '@esbuild/netbsd-x64': 0.18.20
+ '@esbuild/openbsd-x64': 0.18.20
+ '@esbuild/sunos-x64': 0.18.20
+ '@esbuild/win32-arm64': 0.18.20
+ '@esbuild/win32-ia32': 0.18.20
+ '@esbuild/win32-x64': 0.18.20
+
+ esbuild@0.25.12:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.12
+ '@esbuild/android-arm': 0.25.12
+ '@esbuild/android-arm64': 0.25.12
+ '@esbuild/android-x64': 0.25.12
+ '@esbuild/darwin-arm64': 0.25.12
+ '@esbuild/darwin-x64': 0.25.12
+ '@esbuild/freebsd-arm64': 0.25.12
+ '@esbuild/freebsd-x64': 0.25.12
+ '@esbuild/linux-arm': 0.25.12
+ '@esbuild/linux-arm64': 0.25.12
+ '@esbuild/linux-ia32': 0.25.12
+ '@esbuild/linux-loong64': 0.25.12
+ '@esbuild/linux-mips64el': 0.25.12
+ '@esbuild/linux-ppc64': 0.25.12
+ '@esbuild/linux-riscv64': 0.25.12
+ '@esbuild/linux-s390x': 0.25.12
+ '@esbuild/linux-x64': 0.25.12
+ '@esbuild/netbsd-arm64': 0.25.12
+ '@esbuild/netbsd-x64': 0.25.12
+ '@esbuild/openbsd-arm64': 0.25.12
+ '@esbuild/openbsd-x64': 0.25.12
+ '@esbuild/openharmony-arm64': 0.25.12
+ '@esbuild/sunos-x64': 0.25.12
+ '@esbuild/win32-arm64': 0.25.12
+ '@esbuild/win32-ia32': 0.25.12
+ '@esbuild/win32-x64': 0.25.12
+
+ escalade@3.2.0: {}
+
+ escape-html@1.0.3: {}
+
+ escape-string-regexp@4.0.0:
+ optional: true
+
+ escape-string-regexp@5.0.0: {}
+
+ esprima@4.0.1: {}
+
+ estree-util-is-identifier-name@3.0.0: {}
+
+ etag@1.8.1: {}
+
+ eventsource-parser@3.0.6: {}
+
+ eventsource@3.0.7:
+ dependencies:
+ eventsource-parser: 3.0.6
+
+ expand-template@2.0.3: {}
+
+ exponential-backoff@3.1.3: {}
+
+ express-rate-limit@7.5.1(express@5.2.1):
+ dependencies:
+ express: 5.2.1
+
+ express@5.2.1:
+ dependencies:
+ accepts: 2.0.0
+ body-parser: 2.2.2
+ content-disposition: 1.0.1
+ content-type: 1.0.5
+ cookie: 0.7.2
+ cookie-signature: 1.2.2
+ debug: 4.4.3
+ depd: 2.0.0
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ finalhandler: 2.1.1
+ fresh: 2.0.0
+ http-errors: 2.0.1
+ merge-descriptors: 2.0.0
+ mime-types: 3.0.2
+ on-finished: 2.4.1
+ once: 1.4.0
+ parseurl: 1.3.3
+ proxy-addr: 2.0.7
+ qs: 6.14.1
+ range-parser: 1.2.1
+ router: 2.2.0
+ send: 1.2.1
+ serve-static: 2.2.1
+ statuses: 2.0.2
+ type-is: 2.0.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ extend-shallow@2.0.1:
+ dependencies:
+ is-extendable: 0.1.1
+
+ extend@3.0.2: {}
+
+ extract-zip@2.0.1:
+ dependencies:
+ debug: 4.4.3
+ get-stream: 5.2.0
+ yauzl: 2.10.0
+ optionalDependencies:
+ '@types/yauzl': 2.10.3
+ transitivePeerDependencies:
+ - supports-color
+
+ extsprintf@1.4.1:
+ optional: true
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-diff@1.3.0: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-uri@3.1.0: {}
+
+ fastq@1.20.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fd-slicer@1.1.0:
+ dependencies:
+ pend: 1.2.0
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fflate@0.4.8: {}
+
+ file-uri-to-path@1.0.0: {}
+
+ filelist@1.0.4:
+ dependencies:
+ minimatch: 5.1.6
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ finalhandler@2.1.1:
+ dependencies:
+ debug: 4.4.3
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ form-data@4.0.5:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ forwarded-parse@2.1.2: {}
+
+ forwarded@0.2.0: {}
+
+ fraction.js@5.3.4: {}
+
+ framer-motion@11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ motion-dom: 11.18.1
+ motion-utils: 11.18.1
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ fresh@2.0.0: {}
+
+ fs-constants@1.0.0: {}
+
+ fs-extra@10.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-extra@11.3.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-extra@8.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@9.1.0:
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ gauge@4.0.4:
+ dependencies:
+ aproba: 2.1.0
+ color-support: 1.1.3
+ console-control-strings: 1.1.0
+ has-unicode: 2.0.1
+ signal-exit: 3.0.7
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wide-align: 1.1.5
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-nonce@1.0.1: {}
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@5.2.0:
+ dependencies:
+ pump: 3.0.3
+
+ get-tsconfig@4.13.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ github-from-package@0.0.0: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.5.0:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ glob@8.1.0:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 5.1.6
+ once: 1.4.0
+
+ global-agent@3.0.0:
+ dependencies:
+ boolean: 3.2.0
+ es6-error: 4.1.1
+ matcher: 3.0.0
+ roarr: 2.15.4
+ semver: 7.7.3
+ serialize-error: 7.0.1
+ optional: true
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.2.0
+ optional: true
+
+ gopd@1.2.0: {}
+
+ got@11.8.6:
+ dependencies:
+ '@sindresorhus/is': 4.6.0
+ '@szmarczak/http-timer': 4.0.6
+ '@types/cacheable-request': 6.0.3
+ '@types/responselike': 1.0.3
+ cacheable-lookup: 5.0.4
+ cacheable-request: 7.0.4
+ decompress-response: 6.0.0
+ http2-wrapper: 1.0.3
+ lowercase-keys: 2.0.0
+ p-cancelable: 2.1.1
+ responselike: 2.0.1
+
+ graceful-fs@4.2.11: {}
+
+ gray-matter@4.0.3:
+ dependencies:
+ js-yaml: 3.14.2
+ kind-of: 6.0.3
+ section-matter: 1.0.0
+ strip-bom-string: 1.0.0
+
+ hachure-fill@0.5.2: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+ optional: true
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ has-unicode@2.0.1: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hast-util-from-parse5@8.0.3:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ devlop: 1.1.0
+ hastscript: 9.0.1
+ property-information: 7.1.0
+ vfile: 6.0.3
+ vfile-location: 5.0.3
+ web-namespaces: 2.0.1
+
+ hast-util-parse-selector@4.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ hast-util-raw@9.1.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ '@ungap/structured-clone': 1.3.0
+ hast-util-from-parse5: 8.0.3
+ hast-util-to-parse5: 8.0.1
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ parse5: 7.3.0
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+
+ hast-util-sanitize@5.0.2:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@ungap/structured-clone': 1.3.0
+ unist-util-position: 5.0.0
+
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-to-jsx-runtime@2.3.6:
+ dependencies:
+ '@types/estree': 1.0.8
+ '@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.1.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-to-parse5@8.0.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ hastscript@9.0.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ comma-separated-tokens: 2.0.3
+ hast-util-parse-selector: 4.0.0
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+
+ highlight.js@11.11.1: {}
+
+ hono@4.11.5: {}
+
+ hosted-git-info@4.1.0:
+ dependencies:
+ lru-cache: 6.0.0
+
+ html-url-attributes@3.0.1: {}
+
+ html-void-elements@3.0.0: {}
+
+ http-cache-semantics@4.2.0: {}
+
+ http-errors@2.0.1:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.2
+ toidentifier: 1.0.1
+
+ http-proxy-agent@5.0.0:
+ dependencies:
+ '@tootallnate/once': 2.0.0
+ agent-base: 6.0.2
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ http2-wrapper@1.0.3:
+ dependencies:
+ quick-lru: 5.1.1
+ resolve-alpn: 1.2.1
+
+ https-proxy-agent@5.0.1:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ humanize-ms@1.2.1:
+ dependencies:
+ ms: 2.1.3
+
+ iconv-corefoundation@1.1.7:
+ dependencies:
+ cli-truncate: 2.1.0
+ node-addon-api: 1.7.2
+ optional: true
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ iconv-lite@0.7.2:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ieee754@1.2.1: {}
+
+ import-in-the-middle@2.0.5:
+ dependencies:
+ acorn: 8.15.0
+ acorn-import-attributes: 1.9.5(acorn@8.15.0)
+ cjs-module-lexer: 2.2.0
+ module-details-from-path: 1.0.4
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ infer-owner@1.0.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ inline-style-parser@0.2.7: {}
+
+ internmap@1.0.1: {}
+
+ internmap@2.0.3: {}
+
+ ip-address@10.1.0: {}
+
+ ipaddr.js@1.9.1: {}
+
+ is-alphabetical@2.0.1: {}
+
+ is-alphanumerical@2.0.1:
+ dependencies:
+ is-alphabetical: 2.0.1
+ is-decimal: 2.0.1
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-ci@3.0.1:
+ dependencies:
+ ci-info: 3.9.0
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-decimal@2.0.1: {}
+
+ is-extendable@0.1.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hexadecimal@2.0.1: {}
+
+ is-interactive@1.0.0: {}
+
+ is-lambda@1.0.1: {}
+
+ is-number@7.0.0: {}
+
+ is-plain-obj@4.1.0: {}
+
+ is-promise@4.0.0: {}
+
+ is-unicode-supported@0.1.0: {}
+
+ is-what@5.5.0: {}
+
+ isarray@1.0.0: {}
+
+ isbinaryfile@4.0.10: {}
+
+ isbinaryfile@5.0.7: {}
+
+ isexe@2.0.0: {}
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jake@10.9.4:
+ dependencies:
+ async: 3.2.6
+ filelist: 1.0.4
+ picocolors: 1.1.1
+
+ jiti@1.21.7: {}
+
+ jose@6.1.3: {}
+
+ jotai@2.16.2(@babel/core@7.28.6)(@babel/template@7.28.6)(@types/react@19.2.9)(react@19.2.1):
+ optionalDependencies:
+ '@babel/core': 7.28.6
+ '@babel/template': 7.28.6
+ '@types/react': 19.2.9
+ react: 19.2.1
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.2:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.1:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-schema-typed@8.0.2: {}
+
+ json-schema@0.4.0: {}
+
+ json-stringify-safe@5.0.1:
+ optional: true
+
+ json5@2.2.3: {}
+
+ jsonc-parser@3.3.1: {}
+
+ jsonfile@4.0.0:
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ katex@0.16.28:
+ dependencies:
+ commander: 8.3.0
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ khroma@2.1.0: {}
+
+ kind-of@6.0.3: {}
+
+ langium@3.3.1:
+ dependencies:
+ chevrotain: 11.0.3
+ chevrotain-allstar: 0.3.1(chevrotain@11.0.3)
+ vscode-languageserver: 9.0.1
+ vscode-languageserver-textdocument: 1.0.12
+ vscode-uri: 3.0.8
+
+ layout-base@1.0.2: {}
+
+ layout-base@2.0.1: {}
+
+ lazy-val@1.0.5: {}
+
+ lazystream@1.0.1:
+ dependencies:
+ readable-stream: 2.3.8
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lodash-es@4.17.21: {}
+
+ lodash-es@4.17.23: {}
+
+ lodash.defaults@4.2.0: {}
+
+ lodash.difference@4.5.0: {}
+
+ lodash.escaperegexp@4.1.2: {}
+
+ lodash.flatten@4.4.0: {}
+
+ lodash.isequal@4.5.0: {}
+
+ lodash.isplainobject@4.0.6: {}
+
+ lodash.union@4.6.0: {}
+
+ lodash@4.17.23: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ long@5.3.2: {}
+
+ longest-streak@3.1.0: {}
+
+ lowercase-keys@2.0.0: {}
+
+ lowlight@3.3.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ devlop: 1.1.0
+ highlight.js: 11.11.1
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lru-cache@6.0.0:
+ dependencies:
+ yallist: 4.0.0
+
+ lru-cache@7.18.3: {}
+
+ lru_map@0.4.1: {}
+
+ lucide-react@0.468.0(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+
+ lzma-native@8.0.6:
+ dependencies:
+ node-addon-api: 3.2.1
+ node-gyp-build: 4.8.4
+ readable-stream: 3.6.2
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ make-fetch-happen@10.2.1:
+ dependencies:
+ agentkeepalive: 4.6.0
+ cacache: 16.1.3
+ http-cache-semantics: 4.2.0
+ http-proxy-agent: 5.0.0
+ https-proxy-agent: 5.0.1
+ is-lambda: 1.0.1
+ lru-cache: 7.18.3
+ minipass: 3.3.6
+ minipass-collect: 1.0.2
+ minipass-fetch: 2.1.2
+ minipass-flush: 1.0.5
+ minipass-pipeline: 1.2.4
+ negotiator: 0.6.4
+ promise-retry: 2.0.1
+ socks-proxy-agent: 7.0.0
+ ssri: 9.0.1
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+
+ markdown-table@3.0.4: {}
+
+ marked@14.0.0: {}
+
+ marked@16.4.2: {}
+
+ marked@17.0.1: {}
+
+ matcher@3.0.0:
+ dependencies:
+ escape-string-regexp: 4.0.0
+ optional: true
+
+ math-intrinsics@1.1.0: {}
+
+ 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.2:
+ 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.2
+ 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.2
+ 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.2
+ 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.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.2
+ 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.2
+ 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.2
+ 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.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-newline-to-break@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-find-and-replace: 3.0.2
+
+ 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.0
+ 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
+
+ media-typer@1.1.0: {}
+
+ merge-descriptors@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ mermaid@11.12.2:
+ dependencies:
+ '@braintree/sanitize-url': 7.1.1
+ '@iconify/utils': 3.1.0
+ '@mermaid-js/parser': 0.6.3
+ '@types/d3': 7.4.3
+ cytoscape: 3.33.1
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1)
+ cytoscape-fcose: 2.2.0(cytoscape@3.33.1)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.13
+ dayjs: 1.11.19
+ dompurify: 3.3.1
+ katex: 0.16.28
+ khroma: 2.1.0
+ lodash-es: 4.17.23
+ marked: 16.4.2
+ roughjs: 4.6.6
+ stylis: 4.3.6
+ ts-dedent: 2.2.0
+ uuid: 11.1.0
+
+ 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.12
+ 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
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-db@1.54.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mime-types@3.0.2:
+ dependencies:
+ mime-db: 1.54.0
+
+ mime@2.6.0: {}
+
+ mimic-fn@2.1.0: {}
+
+ mimic-response@1.0.1: {}
+
+ mimic-response@3.1.0: {}
+
+ minimatch@10.1.1:
+ dependencies:
+ '@isaacs/brace-expansion': 5.0.0
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@5.1.6:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ minipass-collect@1.0.2:
+ dependencies:
+ minipass: 3.3.6
+
+ minipass-fetch@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ minipass-sized: 1.0.3
+ minizlib: 2.1.2
+ optionalDependencies:
+ encoding: 0.1.13
+
+ minipass-flush@1.0.5:
+ dependencies:
+ minipass: 3.3.6
+
+ minipass-pipeline@1.2.4:
+ dependencies:
+ minipass: 3.3.6
+
+ minipass-sized@1.0.3:
+ dependencies:
+ minipass: 3.3.6
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp-classic@0.5.3: {}
+
+ mkdirp@1.0.4: {}
+
+ mlly@1.8.0:
+ dependencies:
+ acorn: 8.15.0
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.6.3
+
+ module-details-from-path@1.0.4: {}
+
+ monaco-editor@0.55.1:
+ dependencies:
+ dompurify: 3.2.7
+ marked: 14.0.0
+
+ motion-dom@11.18.1:
+ dependencies:
+ motion-utils: 11.18.1
+
+ motion-utils@11.18.1: {}
+
+ motion@11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ framer-motion: 11.18.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.11: {}
+
+ napi-build-utils@2.0.0: {}
+
+ negotiator@0.6.4: {}
+
+ negotiator@1.0.0: {}
+
+ next-themes@0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ node-abi@3.87.0:
+ dependencies:
+ semver: 7.7.3
+
+ node-addon-api@1.7.2:
+ optional: true
+
+ node-addon-api@3.2.1: {}
+
+ node-addon-api@7.1.1: {}
+
+ node-api-version@0.1.4:
+ dependencies:
+ semver: 7.7.3
+
+ node-api-version@0.2.1:
+ dependencies:
+ semver: 7.7.3
+
+ node-gyp-build@4.8.4: {}
+
+ node-gyp@9.4.1:
+ dependencies:
+ env-paths: 2.2.1
+ exponential-backoff: 3.1.3
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ make-fetch-happen: 10.2.1
+ nopt: 6.0.0
+ npmlog: 6.0.2
+ rimraf: 3.0.2
+ semver: 7.7.3
+ tar: 6.2.1
+ which: 2.0.2
+ transitivePeerDependencies:
+ - bluebird
+ - supports-color
+
+ node-pty@1.1.0:
+ dependencies:
+ node-addon-api: 7.1.1
+
+ node-releases@2.0.27: {}
+
+ nopt@6.0.0:
+ dependencies:
+ abbrev: 1.1.1
+
+ normalize-path@3.0.0: {}
+
+ normalize-url@6.1.0: {}
+
+ npmlog@6.0.2:
+ dependencies:
+ are-we-there-yet: 3.0.1
+ console-control-strings: 1.1.0
+ gauge: 4.0.4
+ set-blocking: 2.0.0
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ object-inspect@1.13.4: {}
+
+ object-keys@1.1.1:
+ optional: true
+
+ on-finished@2.4.1:
+ dependencies:
+ ee-first: 1.1.1
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ oniguruma-parser@0.12.1: {}
+
+ oniguruma-to-es@2.3.0:
+ dependencies:
+ emoji-regex-xs: 1.0.0
+ regex: 5.1.1
+ regex-recursion: 5.1.1
+
+ oniguruma-to-es@4.3.4:
+ dependencies:
+ oniguruma-parser: 0.12.1
+ regex: 6.1.0
+ regex-recursion: 6.0.2
+
+ ora@5.4.1:
+ dependencies:
+ bl: 4.1.0
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-spinners: 2.9.2
+ is-interactive: 1.0.0
+ is-unicode-supported: 0.1.0
+ log-symbols: 4.1.0
+ strip-ansi: 6.0.1
+ wcwidth: 1.0.1
+
+ p-cancelable@2.1.1: {}
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-map@4.0.0:
+ dependencies:
+ aggregate-error: 3.1.0
+
+ package-json-from-dist@1.0.1: {}
+
+ package-manager-detector@1.6.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
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.1
+
+ parseurl@1.3.3: {}
+
+ path-data-parser@0.1.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ path-to-regexp@8.3.0: {}
+
+ pathe@2.0.3: {}
+
+ pe-library@0.4.1: {}
+
+ pend@1.2.0: {}
+
+ pg-int8@1.0.1: {}
+
+ pg-protocol@1.11.0: {}
+
+ pg-types@2.2.0:
+ dependencies:
+ pg-int8: 1.0.1
+ postgres-array: 2.0.0
+ postgres-bytea: 1.0.1
+ postgres-date: 1.0.7
+ postgres-interval: 1.2.0
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pidtree@0.6.0: {}
+
+ pify@2.3.0: {}
+
+ pirates@4.0.7: {}
+
+ pkce-challenge@5.0.1: {}
+
+ pkg-types@1.3.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.8.0
+ pathe: 2.0.3
+
+ plist@3.1.0:
+ dependencies:
+ '@xmldom/xmldom': 0.8.11
+ base64-js: 1.5.1
+ xmlbuilder: 15.1.1
+
+ points-on-curve@0.2.0: {}
+
+ points-on-path@0.2.1:
+ dependencies:
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+
+ postcss-import@15.1.0(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.11
+
+ postcss-js@4.1.0(postcss@8.5.6):
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.5.6
+
+ postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6):
+ dependencies:
+ lilconfig: 3.1.3
+ optionalDependencies:
+ jiti: 1.21.7
+ postcss: 8.5.6
+
+ postcss-nested@6.2.0(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+ postcss-selector-parser: 6.1.2
+
+ postcss-selector-parser@6.0.10:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postgres-array@2.0.0: {}
+
+ postgres-bytea@1.0.1: {}
+
+ postgres-date@1.0.7: {}
+
+ postgres-interval@1.2.0:
+ dependencies:
+ xtend: 4.0.2
+
+ posthog-js@1.335.2:
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.208.0
+ '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0)
+ '@posthog/core': 1.14.0
+ '@posthog/types': 1.335.2
+ core-js: 3.48.0
+ dompurify: 3.3.1
+ fflate: 0.4.8
+ preact: 10.28.2
+ query-selector-shadow-dom: 1.0.1
+ web-vitals: 5.1.0
+
+ posthog-node@5.24.2:
+ dependencies:
+ '@posthog/core': 1.14.0
+
+ preact@10.28.2: {}
+
+ prebuild-install@7.1.3:
+ dependencies:
+ detect-libc: 2.1.2
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.8
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 2.0.0
+ node-abi: 3.87.0
+ pump: 3.0.3
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.4
+ tunnel-agent: 0.6.0
+
+ process-nextick-args@2.0.1: {}
+
+ progress@2.0.3: {}
+
+ promise-inflight@1.0.1: {}
+
+ promise-retry@2.0.1:
+ dependencies:
+ err-code: 2.0.3
+ retry: 0.12.0
+
+ property-information@7.1.0: {}
+
+ protobufjs@7.5.4:
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/base64': 1.1.2
+ '@protobufjs/codegen': 2.0.4
+ '@protobufjs/eventemitter': 1.1.0
+ '@protobufjs/fetch': 1.1.0
+ '@protobufjs/float': 1.0.2
+ '@protobufjs/inquire': 1.1.0
+ '@protobufjs/path': 1.1.2
+ '@protobufjs/pool': 1.1.0
+ '@protobufjs/utf8': 1.1.0
+ '@types/node': 20.19.30
+ long: 5.3.2
+
+ proxy-addr@2.0.7:
+ dependencies:
+ forwarded: 0.2.0
+ ipaddr.js: 1.9.1
+
+ pump@3.0.3:
+ dependencies:
+ end-of-stream: 1.4.5
+ once: 1.4.0
+
+ punycode@2.3.1: {}
+
+ qs@6.14.1:
+ dependencies:
+ side-channel: 1.1.0
+
+ query-selector-shadow-dom@1.0.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ quick-lru@5.1.1: {}
+
+ range-parser@1.2.1: {}
+
+ raw-body@3.0.2:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.1
+ iconv-lite: 0.7.2
+ unpipe: 1.0.0
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
+ react-dom@19.2.1(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ scheduler: 0.27.0
+
+ react-hotkeys-hook@4.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ react-icons@5.5.0(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+
+ react-refresh@0.17.0: {}
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.9)(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-style-singleton: 2.2.3(@types/react@19.2.9)(react@19.2.1)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ react-remove-scroll@2.7.2(@types/react@19.2.9)(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.9)(react@19.2.1)
+ react-style-singleton: 2.2.3(@types/react@19.2.9)(react@19.2.1)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.2.9)(react@19.2.1)
+ use-sidecar: 1.1.3(@types/react@19.2.9)(react@19.2.1)
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ react-style-singleton@2.2.3(@types/react@19.2.9)(react@19.2.1):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.1
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ react-zoom-pan-pinch@3.7.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ react@19.2.1: {}
+
+ reactivity-store@0.3.12(react@19.2.1):
+ dependencies:
+ '@vue/reactivity': 3.5.27
+ '@vue/shared': 3.5.27
+ react: 19.2.1
+ use-sync-external-store: 1.6.0(react@19.2.1)
+
+ read-binary-file-arch@1.0.6:
+ dependencies:
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdir-glob@1.1.3:
+ dependencies:
+ minimatch: 5.1.6
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@5.0.0: {}
+
+ regex-recursion@5.1.1:
+ dependencies:
+ regex: 5.1.1
+ regex-utilities: 2.3.0
+
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@5.1.1:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex@6.1.0:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ rehype-harden@1.1.7:
+ dependencies:
+ unist-util-visit: 5.1.0
+
+ rehype-raw@7.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ hast-util-raw: 9.1.0
+ vfile: 6.0.3
+
+ rehype-sanitize@6.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ hast-util-sanitize: 5.0.2
+
+ remark-breaks@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-newline-to-break: 2.0.0
+ unified: 11.0.5
+
+ 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.2
+ 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
+
+ remend@1.1.0: {}
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ require-in-the-middle@8.0.1:
+ dependencies:
+ debug: 4.4.3
+ module-details-from-path: 1.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ resedit@1.7.2:
+ dependencies:
+ pe-library: 0.4.1
+
+ resolve-alpn@1.2.1: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.11:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ responselike@2.0.1:
+ dependencies:
+ lowercase-keys: 2.0.0
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
+ retry@0.12.0: {}
+
+ reusify@1.1.0: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ roarr@2.15.4:
+ dependencies:
+ boolean: 3.2.0
+ detect-node: 2.1.0
+ globalthis: 1.0.4
+ json-stringify-safe: 5.0.1
+ semver-compare: 1.0.0
+ sprintf-js: 1.1.3
+ optional: true
+
+ robust-predicates@3.0.2: {}
+
+ rollup@4.56.0:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.56.0
+ '@rollup/rollup-android-arm64': 4.56.0
+ '@rollup/rollup-darwin-arm64': 4.56.0
+ '@rollup/rollup-darwin-x64': 4.56.0
+ '@rollup/rollup-freebsd-arm64': 4.56.0
+ '@rollup/rollup-freebsd-x64': 4.56.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.56.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.56.0
+ '@rollup/rollup-linux-arm64-gnu': 4.56.0
+ '@rollup/rollup-linux-arm64-musl': 4.56.0
+ '@rollup/rollup-linux-loong64-gnu': 4.56.0
+ '@rollup/rollup-linux-loong64-musl': 4.56.0
+ '@rollup/rollup-linux-ppc64-gnu': 4.56.0
+ '@rollup/rollup-linux-ppc64-musl': 4.56.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.56.0
+ '@rollup/rollup-linux-riscv64-musl': 4.56.0
+ '@rollup/rollup-linux-s390x-gnu': 4.56.0
+ '@rollup/rollup-linux-x64-gnu': 4.56.0
+ '@rollup/rollup-linux-x64-musl': 4.56.0
+ '@rollup/rollup-openbsd-x64': 4.56.0
+ '@rollup/rollup-openharmony-arm64': 4.56.0
+ '@rollup/rollup-win32-arm64-msvc': 4.56.0
+ '@rollup/rollup-win32-ia32-msvc': 4.56.0
+ '@rollup/rollup-win32-x64-gnu': 4.56.0
+ '@rollup/rollup-win32-x64-msvc': 4.56.0
+ fsevents: 2.3.3
+
+ roughjs@4.6.6:
+ dependencies:
+ hachure-fill: 0.5.2
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+ points-on-path: 0.2.1
+
+ router@2.2.0:
+ dependencies:
+ debug: 4.4.3
+ depd: 2.0.0
+ is-promise: 4.0.0
+ parseurl: 1.3.3
+ path-to-regexp: 8.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ rw@1.3.3: {}
+
+ safe-buffer@5.1.2: {}
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ sanitize-filename@1.6.3:
+ dependencies:
+ truncate-utf8-bytes: 1.0.2
+
+ sax@1.4.4: {}
+
+ scheduler@0.27.0: {}
+
+ section-matter@1.0.0:
+ dependencies:
+ extend-shallow: 2.0.1
+ kind-of: 6.0.3
+
+ semver-compare@1.0.0:
+ optional: true
+
+ semver@6.3.1: {}
+
+ semver@7.7.3: {}
+
+ send@1.2.1:
+ dependencies:
+ debug: 4.4.3
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 2.0.0
+ http-errors: 2.0.1
+ mime-types: 3.0.2
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ serialize-error@7.0.1:
+ dependencies:
+ type-fest: 0.13.1
+ optional: true
+
+ serve-static@2.2.1:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 1.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ set-blocking@2.0.0: {}
+
+ setprototypeof@1.2.0: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shiki@1.29.2:
+ dependencies:
+ '@shikijs/core': 1.29.2
+ '@shikijs/engine-javascript': 1.29.2
+ '@shikijs/engine-oniguruma': 1.29.2
+ '@shikijs/langs': 1.29.2
+ '@shikijs/themes': 1.29.2
+ '@shikijs/types': 1.29.2
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ shiki@3.21.0:
+ dependencies:
+ '@shikijs/core': 3.21.0
+ '@shikijs/engine-javascript': 3.21.0
+ '@shikijs/engine-oniguruma': 3.21.0
+ '@shikijs/langs': 3.21.0
+ '@shikijs/themes': 3.21.0
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ simple-concat@1.0.1: {}
+
+ simple-get@4.0.1:
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+
+ simple-git@3.30.0:
+ dependencies:
+ '@kwsites/file-exists': 1.1.1
+ '@kwsites/promise-deferred': 1.1.1
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ simple-update-notifier@2.0.0:
+ dependencies:
+ semver: 7.7.3
+
+ slice-ansi@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+ optional: true
+
+ smart-buffer@4.2.0: {}
+
+ socks-proxy-agent@7.0.0:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.3
+ socks: 2.8.7
+ transitivePeerDependencies:
+ - supports-color
+
+ socks@2.8.7:
+ dependencies:
+ ip-address: 10.1.0
+ smart-buffer: 4.2.0
+
+ sonner@1.7.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ sprintf-js@1.0.3: {}
+
+ sprintf-js@1.1.3:
+ optional: true
+
+ ssri@9.0.1:
+ dependencies:
+ minipass: 3.3.6
+
+ stat-mode@1.0.0: {}
+
+ state-local@1.0.7: {}
+
+ statuses@2.0.2: {}
+
+ streamdown@2.1.0(react@19.2.1):
+ dependencies:
+ clsx: 2.1.1
+ hast-util-to-jsx-runtime: 2.3.6
+ html-url-attributes: 3.0.1
+ marked: 17.0.1
+ react: 19.2.1
+ rehype-harden: 1.1.7
+ rehype-raw: 7.0.0
+ rehype-sanitize: 6.0.0
+ remark-gfm: 4.0.1
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ remend: 1.1.0
+ tailwind-merge: 3.4.0
+ unified: 11.0.5
+ unist-util-visit: 5.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.2
+
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
+ 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
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.2:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom-string@1.0.0: {}
+
+ strip-json-comments@2.0.1: {}
+
+ 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
+
+ stylis@4.3.6: {}
+
+ sucrase@3.35.1:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ commander: 4.1.1
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.7
+ tinyglobby: 0.2.15
+ ts-interface-checker: 0.1.13
+
+ sumchecker@3.0.1:
+ dependencies:
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ superjson@2.2.6:
+ dependencies:
+ copy-anything: 4.0.5
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ swr@2.3.8(react@19.2.1):
+ dependencies:
+ dequal: 2.0.3
+ react: 19.2.1
+ use-sync-external-store: 1.6.0(react@19.2.1)
+
+ tailwind-merge@2.6.0: {}
+
+ tailwind-merge@3.4.0: {}
+
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.19):
+ dependencies:
+ tailwindcss: 3.4.19
+
+ tailwindcss@3.4.19:
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.3
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.7
+ lilconfig: 3.1.3
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.6
+ postcss-import: 15.1.0(postcss@8.5.6)
+ postcss-js: 4.1.0(postcss@8.5.6)
+ postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)
+ postcss-nested: 6.2.0(postcss@8.5.6)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.11
+ sucrase: 3.35.1
+ transitivePeerDependencies:
+ - tsx
+ - yaml
+
+ tar-fs@2.1.4:
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.3
+ tar-stream: 2.2.0
+
+ tar-stream@2.2.0:
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.5
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
+ temp-file@3.4.0:
+ dependencies:
+ async-exit-hook: 2.0.1
+ fs-extra: 10.1.0
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ throttleit@2.1.0: {}
+
+ tiny-typed-emitter@2.1.0: {}
+
+ tinyexec@1.0.2: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tmp-promise@3.0.3:
+ dependencies:
+ tmp: 0.2.5
+
+ tmp@0.2.5: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toidentifier@1.0.1: {}
+
+ trim-lines@3.0.1: {}
+
+ trough@2.2.0: {}
+
+ trpc-electron@0.1.2(@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.8.1(typescript@5.9.3))(electron@33.4.5):
+ dependencies:
+ '@trpc/client': 11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)
+ '@trpc/server': 11.8.1(typescript@5.9.3)
+ electron: 33.4.5
+
+ truncate-utf8-bytes@1.0.2:
+ dependencies:
+ utf8-byte-length: 1.0.5
+
+ ts-dedent@2.2.0: {}
+
+ ts-interface-checker@0.1.13: {}
+
+ tslib@2.8.1: {}
+
+ tunnel-agent@0.6.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ type-fest@0.13.1:
+ optional: true
+
+ type-is@2.0.1:
+ dependencies:
+ content-type: 1.0.5
+ media-typer: 1.1.0
+ mime-types: 3.0.2
+
+ typescript@5.9.3: {}
+
+ ufo@1.6.3: {}
+
+ undici-types@6.21.0: {}
+
+ 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
+
+ unique-filename@2.0.1:
+ dependencies:
+ unique-slug: 3.0.0
+
+ unique-names-generator@4.7.1: {}
+
+ unique-slug@3.0.0:
+ dependencies:
+ imurmurhash: 0.1.4
+
+ 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
+
+ universalify@0.1.2: {}
+
+ universalify@2.0.1: {}
+
+ unpipe@1.0.0: {}
+
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ 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.9)(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ use-sidecar@1.1.3(@types/react@19.2.9)(react@19.2.1):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.1
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.2.9
+
+ use-sync-external-store@1.6.0(react@19.2.1):
+ dependencies:
+ react: 19.2.1
+
+ utf8-byte-length@1.0.5: {}
+
+ util-deprecate@1.0.2: {}
+
+ uuid@11.1.0: {}
+
+ vary@1.1.2: {}
+
+ verror@1.10.1:
+ dependencies:
+ assert-plus: 1.0.0
+ core-util-is: 1.0.2
+ extsprintf: 1.4.1
+ optional: true
+
+ vfile-location@5.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile: 6.0.3
+
+ 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@6.4.1(@types/node@20.19.30)(jiti@1.21.7):
+ dependencies:
+ esbuild: 0.25.12
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.56.0
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 20.19.30
+ fsevents: 2.3.3
+ jiti: 1.21.7
+
+ vscode-jsonrpc@8.2.0: {}
+
+ vscode-languageserver-protocol@3.17.5:
+ dependencies:
+ vscode-jsonrpc: 8.2.0
+ vscode-languageserver-types: 3.17.5
+
+ vscode-languageserver-textdocument@1.0.12: {}
+
+ vscode-languageserver-types@3.17.5: {}
+
+ vscode-languageserver@9.0.1:
+ dependencies:
+ vscode-languageserver-protocol: 3.17.5
+
+ vscode-uri@3.0.8: {}
+
+ wcwidth@1.0.1:
+ dependencies:
+ defaults: 1.0.4
+
+ web-namespaces@2.0.1: {}
+
+ web-vitals@5.1.0: {}
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ wide-align@1.1.5:
+ dependencies:
+ string-width: 4.2.3
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 5.1.2
+ strip-ansi: 7.1.2
+
+ wrappy@1.0.2: {}
+
+ xmlbuilder@15.1.1: {}
+
+ xtend@4.0.2: {}
+
+ xterm@5.3.0: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yallist@4.0.0: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yauzl@2.10.0:
+ dependencies:
+ buffer-crc32: 0.2.13
+ fd-slicer: 1.1.0
+
+ yocto-queue@0.1.0: {}
+
+ zip-stream@4.1.1:
+ dependencies:
+ archiver-utils: 3.0.4
+ compress-commons: 4.1.2
+ readable-stream: 3.6.2
+
+ zod-to-json-schema@3.25.1(zod@3.25.76):
+ dependencies:
+ zod: 3.25.76
+
+ zod@3.25.76: {}
+
+ zustand@5.0.10(@types/react@19.2.9)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1)):
+ optionalDependencies:
+ '@types/react': 19.2.9
+ react: 19.2.1
+ use-sync-external-store: 1.6.0(react@19.2.1)
+
+ zwitch@2.0.4: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 000000000..5a3dd5920
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,10 @@
+ignoredBuiltDependencies:
+ - better-sqlite3
+ - core-js
+ - esbuild
+ - lzma-native
+ - node-pty
+ - protobufjs
+
+onlyBuiltDependencies:
+ - electron
diff --git a/src/main/index.ts b/src/main/index.ts
index 32bf51eb7..9729cfe79 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -28,6 +28,7 @@ import {
} from "./lib/cli"
import { cleanupGitWatchers } from "./lib/git/watcher"
import { cancelAllPendingOAuth, handleMcpOAuthCallback } from "./lib/mcp-auth"
+import { initTray, destroyTray } from "./lib/tray"
import {
createMainWindow,
createWindow,
@@ -852,9 +853,13 @@ if (gotTheLock) {
console.error("[App] Failed to initialize database:", error)
}
- // Create main window
+ // Create main window FIRST (before tray initialization)
+ // This prevents tray from creating a duplicate window
createMainWindow()
+ // Initialize system tray (after main window exists)
+ initTray()
+
// Initialize auto-updater (production only)
if (app.isPackaged) {
await initAutoUpdater(getAllWindows)
@@ -903,9 +908,25 @@ if (gotTheLock) {
}
})
+ // Track when app is intentionally quitting (vs just closing windows)
+ let isQuitting = false
+ ;(global as any).__isAppQuitting = () => isQuitting
+
// Cleanup before quit
app.on("before-quit", async () => {
console.log("[App] Shutting down...")
+ isQuitting = true
+ ;(global as any).__isAppQuitting = () => true
+
+ // Disable remote access first
+ try {
+ const { disableRemoteAccess } = await import("./lib/remote-access")
+ await disableRemoteAccess()
+ } catch (error) {
+ console.warn("[App] Failed to disable remote access:", error)
+ }
+
+ destroyTray()
cancelAllPendingOAuth()
await cleanupGitWatchers()
await shutdownAnalytics()
diff --git a/src/main/lib/claude/env.ts b/src/main/lib/claude/env.ts
index e181e7895..5b0e5a187 100644
--- a/src/main/lib/claude/env.ts
+++ b/src/main/lib/claude/env.ts
@@ -137,6 +137,41 @@ function stripSensitiveKeys(env: Record): void {
}
}
+/**
+ * Load Claude Code CLI settings from disk (~/.claude/settings.json)
+ * This is needed because some users configure Claude Code via 'claude config'
+ * which writes to this file, and these settings might not be in the shell env.
+ */
+function loadClaudeCliSettings(): Record {
+ try {
+ const homeDir = os.homedir()
+ const settingsPath = path.join(homeDir, ".claude", "settings.json")
+
+ console.log(`[claude-env] Looking for settings at: ${settingsPath}`)
+
+ if (!fs.existsSync(settingsPath)) {
+ console.log(`[claude-env] No settings.json found at ${settingsPath}`)
+ return {}
+ }
+
+ const content = fs.readFileSync(settingsPath, 'utf-8')
+ const settings = JSON.parse(content)
+
+ // Config values are stored in the 'env' object within settings.json
+ if (settings && typeof settings === 'object' && settings.env && typeof settings.env === 'object') {
+ console.log(`[claude-env] Loaded ${Object.keys(settings.env).length} vars from ${settingsPath}`)
+ console.log(`[claude-env] Detected keys: ${Object.keys(settings.env).join(', ')}`)
+ return settings.env as Record
+ } else {
+ console.log(`[claude-env] settings.json found but no 'env' object inside`)
+ }
+ } catch (error) {
+ console.warn("[claude-env] Failed to load settings.json:", error)
+ }
+
+ return {}
+}
+
/**
* Load full shell environment.
* - Windows: Derives PATH from process.env + common install locations (no shell spawn)
@@ -148,62 +183,79 @@ export function getClaudeShellEnvironment(): Record {
return { ...cachedShellEnv }
}
- // Windows: use platform provider to build environment
+ let env: Record = {}
+
+ // 1. Load settings from ~/.claude/settings.json
+ // These are the lowest priority of the "external" configs, but higher than defaults.
+ // We start with these, then overlay shell env on top.
+ const fileSettings = loadClaudeCliSettings()
+ Object.assign(env, fileSettings)
+
+ // 2. Load shell environment
if (isWindows()) {
console.log(
"[claude-env] Windows detected, deriving PATH without shell invocation"
)
// Use platform provider to build environment
- const env = platform.buildEnvironment()
-
- // Strip sensitive keys
- stripSensitiveKeys(env)
+ const platformEnv = platform.buildEnvironment()
+ stripSensitiveKeys(platformEnv)
+
+ Object.assign(env, platformEnv)
console.log(
`[claude-env] Built Windows environment with ${Object.keys(env).length} vars`
)
- cachedShellEnv = env
- return { ...env }
+ } else {
+ // macOS/Linux: spawn interactive login shell to get full environment
+ const shell = getDefaultShell()
+ const command = `echo -n "${DELIMITER}"; env; echo -n "${DELIMITER}"; exit`
+
+ try {
+ const output = execSync(`${shell} -ilc '${command}'`, {
+ encoding: "utf8",
+ timeout: 5000,
+ env: {
+ // Prevent Oh My Zsh from blocking with auto-update prompts
+ DISABLE_AUTO_UPDATE: "true",
+ // Minimal env to bootstrap the shell
+ HOME: os.homedir(),
+ USER: os.userInfo().username,
+ SHELL: shell,
+ },
+ })
+
+ const shellEnv = parseEnvOutput(output)
+ stripSensitiveKeys(shellEnv)
+
+ console.log(
+ `[claude-env] Loaded ${Object.keys(shellEnv).length} environment variables from shell`
+ )
+
+ // Overlay shell env on top of file settings
+ Object.assign(env, shellEnv)
+ } catch (error) {
+ console.error("[claude-env] Failed to load shell environment:", error)
+
+ // Fallback: use platform provider
+ const platformEnv = platform.buildEnvironment()
+ stripSensitiveKeys(platformEnv)
+
+ console.log("[claude-env] Using fallback environment from platform provider")
+ Object.assign(env, platformEnv)
+ }
}
- // macOS/Linux: spawn interactive login shell to get full environment
- const shell = getDefaultShell()
- const command = `echo -n "${DELIMITER}"; env; echo -n "${DELIMITER}"; exit`
+ // Log detected config for debugging
+ console.log(`[claude-env] Final config detection:`)
+ console.log(` - ANTHROPIC_API_KEY: ${env.ANTHROPIC_API_KEY ? '***' : 'not set'}`)
+ console.log(` - ANTHROPIC_AUTH_TOKEN: ${env.ANTHROPIC_AUTH_TOKEN ? '***' : 'not set'}`)
+ console.log(` - ANTHROPIC_BASE_URL: ${env.ANTHROPIC_BASE_URL || 'not set'}`)
+ console.log(` - ANTHROPIC_MODEL: ${env.ANTHROPIC_MODEL || 'not set'}`)
- try {
- const output = execSync(`${shell} -ilc '${command}'`, {
- encoding: "utf8",
- timeout: 5000,
- env: {
- // Prevent Oh My Zsh from blocking with auto-update prompts
- DISABLE_AUTO_UPDATE: "true",
- // Minimal env to bootstrap the shell
- HOME: os.homedir(),
- USER: os.userInfo().username,
- SHELL: shell,
- },
- })
-
- const env = parseEnvOutput(output)
- stripSensitiveKeys(env)
-
- console.log(
- `[claude-env] Loaded ${Object.keys(env).length} environment variables from shell`
- )
- cachedShellEnv = env
- return { ...env }
- } catch (error) {
- console.error("[claude-env] Failed to load shell environment:", error)
-
- // Fallback: use platform provider
- const env = platform.buildEnvironment()
- stripSensitiveKeys(env)
-
- console.log("[claude-env] Using fallback environment from platform provider")
- cachedShellEnv = env
- return { ...env }
- }
+ // 3. Cache and return
+ cachedShellEnv = env
+ return { ...env }
}
/**
diff --git a/src/main/lib/claude/offline-handler.ts b/src/main/lib/claude/offline-handler.ts
index 292279ac2..5254ecd2a 100644
--- a/src/main/lib/claude/offline-handler.ts
+++ b/src/main/lib/claude/offline-handler.ts
@@ -8,6 +8,11 @@ export type CustomClaudeConfig = {
model: string
token: string
baseUrl: string
+ // Additional model configuration (optional)
+ defaultOpusModel?: string // ANTHROPIC_DEFAULT_OPUS_MODEL
+ defaultSonnetModel?: string // ANTHROPIC_DEFAULT_SONNET_MODEL
+ defaultHaikuModel?: string // ANTHROPIC_DEFAULT_HAIKU_MODEL
+ subagentModel?: string // CLAUDE_CODE_SUBAGENT_MODEL
}
export type OfflineCheckResult = {
diff --git a/src/main/lib/db/schema/index.ts b/src/main/lib/db/schema/index.ts
index fe6aa3490..cae063987 100644
--- a/src/main/lib/db/schema/index.ts
+++ b/src/main/lib/db/schema/index.ts
@@ -51,6 +51,9 @@ export const chats = sqliteTable("chats", {
// PR tracking fields
prUrl: text("pr_url"),
prNumber: integer("pr_number"),
+ // Model profile fields (per-workspace model configuration)
+ modelProfileId: text("model_profile_id"), // References profile ID from localStorage
+ selectedModelId: text("selected_model_id"), // Which model within the profile
}, (table) => [
index("chats_worktree_path_idx").on(table.worktreePath),
])
@@ -128,6 +131,35 @@ export const anthropicSettings = sqliteTable("anthropic_settings", {
),
})
+// ============ MODEL PROFILES ============
+// Stores model profiles for Claude API configuration
+// Allows syncing profiles between desktop and web app
+export const modelProfiles = sqliteTable("model_profiles", {
+ id: text("id").primaryKey().$defaultFn(() => createId()),
+ name: text("name").notNull(),
+ // JSON string of CustomClaudeConfig (model, token, baseUrl, etc.)
+ config: text("config").notNull(),
+ // JSON string of ModelMapping[] (available models in this profile)
+ models: text("models").notNull().default("[]"),
+ // Whether this is an offline/Ollama profile
+ isOffline: integer("is_offline", { mode: "boolean" }).notNull().default(false),
+ createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
+ () => new Date(),
+ ),
+ updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(
+ () => new Date(),
+ ),
+})
+
+// Settings for model profiles (stores last used profile ID, etc.)
+export const modelProfileSettings = sqliteTable("model_profile_settings", {
+ id: text("id").primaryKey().default("singleton"), // Single row
+ lastUsedProfileId: text("last_used_profile_id"), // References modelProfiles.id
+ updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(
+ () => new Date(),
+ ),
+})
+
// ============ TYPE EXPORTS ============
export type Project = typeof projects.$inferSelect
export type NewProject = typeof projects.$inferInsert
@@ -140,3 +172,7 @@ export type NewClaudeCodeCredential = typeof claudeCodeCredentials.$inferInsert
export type AnthropicAccount = typeof anthropicAccounts.$inferSelect
export type NewAnthropicAccount = typeof anthropicAccounts.$inferInsert
export type AnthropicSettings = typeof anthropicSettings.$inferSelect
+export type ModelProfile = typeof modelProfiles.$inferSelect
+export type NewModelProfile = typeof modelProfiles.$inferInsert
+export type ModelProfileSettings = typeof modelProfileSettings.$inferSelect
+export type NewModelProfileSettings = typeof modelProfileSettings.$inferInsert
diff --git a/src/main/lib/remote-access/cloudflared.ts b/src/main/lib/remote-access/cloudflared.ts
new file mode 100644
index 000000000..9d515bf54
--- /dev/null
+++ b/src/main/lib/remote-access/cloudflared.ts
@@ -0,0 +1,213 @@
+// src/main/lib/remote-access/cloudflared.ts
+
+import { spawn, ChildProcess } from "child_process"
+import { createWriteStream, existsSync, chmodSync } from "fs"
+import { mkdir, unlink } from "fs/promises"
+import { join } from "path"
+import { app } from "electron"
+import { extract } from "tar"
+
+const CLOUDFLARED_VERSION = "2026.1.1"
+
+const DOWNLOAD_URLS: Record = {
+ "darwin-arm64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-arm64.tgz`,
+ "darwin-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-darwin-amd64.tgz`,
+ "win32-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-windows-amd64.exe`,
+ "linux-x64": `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64`,
+}
+
+let cloudflaredProcess: ChildProcess | null = null
+
+/**
+ * Get the path where cloudflared binary should be stored
+ */
+export function getCloudflaredPath(): string {
+ const binDir = join(app.getPath("userData"), "bin")
+ const ext = process.platform === "win32" ? ".exe" : ""
+ return join(binDir, `cloudflared${ext}`)
+}
+
+/**
+ * Check if cloudflared is already downloaded
+ */
+export function isCloudflaredInstalled(): boolean {
+ return existsSync(getCloudflaredPath())
+}
+
+/**
+ * Download cloudflared binary for current platform
+ */
+export async function downloadCloudflared(
+ onProgress?: (percent: number) => void
+): Promise {
+ const platform = process.platform
+ const arch = process.arch
+ const key = `${platform}-${arch}`
+
+ const url = DOWNLOAD_URLS[key]
+ if (!url) {
+ throw new Error(`Unsupported platform: ${key}`)
+ }
+
+ const binDir = join(app.getPath("userData"), "bin")
+ await mkdir(binDir, { recursive: true })
+
+ const cloudflaredPath = getCloudflaredPath()
+ const isTgz = url.endsWith(".tgz")
+
+ console.log(`[Cloudflared] Downloading from ${url}`)
+
+ const response = await fetch(url)
+ if (!response.ok) {
+ throw new Error(`Failed to download: ${response.statusText}`)
+ }
+
+ const contentLength = parseInt(response.headers.get("content-length") || "0")
+ let downloaded = 0
+
+ if (isTgz) {
+ // For .tgz files, extract directly
+ const tempPath = join(binDir, "cloudflared.tgz")
+ const fileStream = createWriteStream(tempPath)
+
+ const reader = response.body?.getReader()
+ if (!reader) throw new Error("No response body")
+
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ fileStream.write(value)
+ downloaded += value.length
+ if (contentLength && onProgress) {
+ onProgress(Math.round((downloaded / contentLength) * 100))
+ }
+ }
+ fileStream.end()
+
+ // Wait for file to be written
+ await new Promise((resolve) => fileStream.on("finish", resolve))
+
+ // Extract tar.gz
+ await extract({
+ file: tempPath,
+ cwd: binDir,
+ })
+
+ // Clean up temp file
+ await unlink(tempPath)
+ } else {
+ // For direct executables (Windows, Linux)
+ const fileStream = createWriteStream(cloudflaredPath)
+ const reader = response.body?.getReader()
+ if (!reader) throw new Error("No response body")
+
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ fileStream.write(value)
+ downloaded += value.length
+ if (contentLength && onProgress) {
+ onProgress(Math.round((downloaded / contentLength) * 100))
+ }
+ }
+ fileStream.end()
+ await new Promise((resolve) => fileStream.on("finish", resolve))
+ }
+
+ // Make executable on Unix
+ if (process.platform !== "win32") {
+ chmodSync(cloudflaredPath, 0o755)
+ }
+
+ console.log(`[Cloudflared] Downloaded to ${cloudflaredPath}`)
+}
+
+/**
+ * Start cloudflared tunnel
+ * Returns the public URL when ready
+ */
+export function startTunnel(localPort: number): Promise {
+ return new Promise((resolve, reject) => {
+ const cloudflaredPath = getCloudflaredPath()
+
+ if (!existsSync(cloudflaredPath)) {
+ reject(new Error("Cloudflared not installed"))
+ return
+ }
+
+ console.log(`[Cloudflared] Starting tunnel to localhost:${localPort}`)
+
+ cloudflaredProcess = spawn(cloudflaredPath, [
+ "tunnel",
+ "--url",
+ `http://localhost:${localPort}`,
+ ])
+
+ let urlFound = false
+ let outputBuffer = ""
+
+ cloudflaredProcess.stdout?.on("data", (data: Buffer) => {
+ const output = data.toString()
+ console.log(`[Cloudflared] stdout: ${output}`)
+ })
+
+ cloudflaredProcess.stderr?.on("data", (data: Buffer) => {
+ const output = data.toString()
+ console.log(`[Cloudflared] ${output}`)
+ outputBuffer += output
+
+ // Parse URL from output
+ // Example: "INF +-----------------------------------------------------------+"
+ // followed by "INF | https://random-words.trycloudflare.com |"
+ const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i)
+ if (urlMatch && !urlFound) {
+ urlFound = true
+ const url = urlMatch[0]
+ console.log(`[Cloudflared] Tunnel URL found: ${url}`)
+ resolve(url)
+ }
+ })
+
+ cloudflaredProcess.on("error", (err) => {
+ console.error("[Cloudflared] Process error:", err)
+ reject(err)
+ })
+
+ cloudflaredProcess.on("exit", (code, signal) => {
+ console.log(`[Cloudflared] Process exited with code ${code}, signal: ${signal}`)
+ cloudflaredProcess = null
+ if (!urlFound) {
+ // Include output buffer for debugging
+ console.error(`[Cloudflared] Output before exit:\n${outputBuffer}`)
+ reject(new Error(`Cloudflared exited with code ${code} before URL was found`))
+ }
+ })
+
+ // Timeout after 45 seconds (increased from 30)
+ setTimeout(() => {
+ if (!urlFound) {
+ console.error(`[Cloudflared] Timeout waiting for tunnel URL. Output so far:\n${outputBuffer}`)
+ stopTunnel()
+ reject(new Error("Timeout waiting for tunnel URL"))
+ }
+ }, 45000)
+ })
+}
+
+/**
+ * Stop cloudflared tunnel
+ */
+export function stopTunnel(): void {
+ if (cloudflaredProcess) {
+ console.log("[Cloudflared] Stopping tunnel")
+ cloudflaredProcess.kill()
+ cloudflaredProcess = null
+ }
+}
+
+/**
+ * Check if tunnel is running
+ */
+export function isTunnelRunning(): boolean {
+ return cloudflaredProcess !== null
+}
diff --git a/src/main/lib/remote-access/index.ts b/src/main/lib/remote-access/index.ts
new file mode 100644
index 000000000..4c893e764
--- /dev/null
+++ b/src/main/lib/remote-access/index.ts
@@ -0,0 +1,153 @@
+// src/main/lib/remote-access/index.ts
+
+import {
+ createSession,
+ endSession,
+ getSession,
+ setSessionUrl,
+ isSessionActive,
+} from "./session"
+import {
+ isCloudflaredInstalled,
+ downloadCloudflared,
+ startTunnel,
+ stopTunnel,
+ isTunnelRunning,
+} from "./cloudflared"
+import {
+ startWSServer,
+ stopWSServer,
+ getConnectedClientCount,
+ broadcast,
+} from "./ws-server"
+import { BrowserWindow } from "electron"
+
+export type RemoteAccessStatus =
+ | { status: "disabled" }
+ | { status: "downloading"; progress: number }
+ | { status: "starting" }
+ | { status: "active"; url: string; pin: string; clients: number }
+ | { status: "error"; message: string }
+
+let currentStatus: RemoteAccessStatus = { status: "disabled" }
+let statusListeners: ((status: RemoteAccessStatus) => void)[] = []
+
+/**
+ * Get current status
+ */
+export function getRemoteAccessStatus(): RemoteAccessStatus {
+ if (isSessionActive() && isTunnelRunning()) {
+ const session = getSession()
+ if (session?.url) {
+ return {
+ status: "active",
+ url: session.url,
+ pin: session.pin,
+ clients: getConnectedClientCount(),
+ }
+ }
+ }
+ return currentStatus
+}
+
+/**
+ * Set status and notify listeners
+ */
+function setStatus(status: RemoteAccessStatus): void {
+ currentStatus = status
+ for (const listener of statusListeners) {
+ listener(status)
+ }
+ // Also notify via IPC to all windows
+ for (const win of BrowserWindow.getAllWindows()) {
+ if (!win.isDestroyed()) {
+ win.webContents.send("remote-access:status", status)
+ }
+ }
+}
+
+/**
+ * Subscribe to status changes
+ */
+export function onStatusChange(
+ listener: (status: RemoteAccessStatus) => void
+): () => void {
+ statusListeners.push(listener)
+ return () => {
+ statusListeners = statusListeners.filter((l) => l !== listener)
+ }
+}
+
+/**
+ * Enable remote access
+ */
+export async function enableRemoteAccess(): Promise {
+ if (isSessionActive()) {
+ throw new Error("Remote access already enabled")
+ }
+
+ try {
+ // Check if cloudflared is installed
+ if (!isCloudflaredInstalled()) {
+ setStatus({ status: "downloading", progress: 0 })
+ await downloadCloudflared((progress) => {
+ setStatus({ status: "downloading", progress })
+ })
+ }
+
+ setStatus({ status: "starting" })
+
+ // Create session
+ const session = createSession()
+
+ // Start WebSocket server
+ const port = await startWSServer()
+
+ // Start cloudflared tunnel
+ const url = await startTunnel(port)
+ setSessionUrl(url)
+
+ setStatus({
+ status: "active",
+ url,
+ pin: session.pin,
+ clients: 0,
+ })
+
+ console.log(`[RemoteAccess] Enabled - URL: ${url}, PIN: ${session.pin}`)
+ } catch (error) {
+ await disableRemoteAccess()
+ const message = error instanceof Error ? error.message : "Unknown error"
+ setStatus({ status: "error", message })
+ throw error
+ }
+}
+
+/**
+ * Disable remote access
+ */
+export async function disableRemoteAccess(): Promise {
+ stopTunnel()
+ await stopWSServer()
+ endSession()
+ setStatus({ status: "disabled" })
+ console.log("[RemoteAccess] Disabled")
+}
+
+/**
+ * Refresh client count (called periodically or on connection change)
+ */
+export function refreshClientCount(): void {
+ if (currentStatus.status === "active") {
+ setStatus({
+ ...currentStatus,
+ clients: getConnectedClientCount(),
+ })
+ }
+}
+
+// Re-export broadcast for use by other modules
+export { broadcast }
+
+// Re-export for type checking
+export type { RemoteAccessStatus as RemoteAccessState }
diff --git a/src/main/lib/remote-access/session.ts b/src/main/lib/remote-access/session.ts
new file mode 100644
index 000000000..d87286ed9
--- /dev/null
+++ b/src/main/lib/remote-access/session.ts
@@ -0,0 +1,90 @@
+// src/main/lib/remote-access/session.ts
+
+import { randomInt } from "crypto"
+
+export interface RemoteSession {
+ pin: string
+ url: string | null
+ createdAt: Date
+ clients: Set // client IDs
+}
+
+let currentSession: RemoteSession | null = null
+
+/**
+ * Generate a 6-digit PIN
+ */
+export function generatePin(): string {
+ return randomInt(100000, 999999).toString()
+}
+
+/**
+ * Start a new remote session
+ */
+export function createSession(): RemoteSession {
+ currentSession = {
+ pin: generatePin(),
+ url: null,
+ createdAt: new Date(),
+ clients: new Set(),
+ }
+ return currentSession
+}
+
+/**
+ * Get current session
+ */
+export function getSession(): RemoteSession | null {
+ return currentSession
+}
+
+/**
+ * Set tunnel URL after cloudflared starts
+ */
+export function setSessionUrl(url: string): void {
+ if (currentSession) {
+ currentSession.url = url
+ }
+}
+
+/**
+ * Validate PIN
+ */
+export function validatePin(pin: string): boolean {
+ return currentSession?.pin === pin
+}
+
+/**
+ * Add a connected client
+ */
+export function addClient(clientId: string): void {
+ currentSession?.clients.add(clientId)
+}
+
+/**
+ * Remove a connected client
+ */
+export function removeClient(clientId: string): void {
+ currentSession?.clients.delete(clientId)
+}
+
+/**
+ * Get connected client count
+ */
+export function getClientCount(): number {
+ return currentSession?.clients.size ?? 0
+}
+
+/**
+ * End current session
+ */
+export function endSession(): void {
+ currentSession = null
+}
+
+/**
+ * Check if session is active
+ */
+export function isSessionActive(): boolean {
+ return currentSession !== null
+}
diff --git a/src/main/lib/remote-access/ws-server.ts b/src/main/lib/remote-access/ws-server.ts
new file mode 100644
index 000000000..870e2ad14
--- /dev/null
+++ b/src/main/lib/remote-access/ws-server.ts
@@ -0,0 +1,859 @@
+// src/main/lib/remote-access/ws-server.ts
+
+import { WebSocketServer, WebSocket } from "ws"
+import { createServer, Server, IncomingMessage, ServerResponse } from "http"
+import { request as httpRequest } from "http"
+import { randomUUID } from "crypto"
+import { validatePin, addClient, removeClient } from "./session"
+import { createAppRouter } from "../trpc/routers"
+import { BrowserWindow, app } from "electron"
+import superjson from "superjson"
+import { join, extname } from "path"
+import { readFile, stat } from "fs/promises"
+import { existsSync } from "fs"
+
+// Vite dev server port
+const VITE_DEV_PORT = 5173
+
+// Message types
+interface WSRequest {
+ id: string
+ type: "auth" | "trpc" | "api" | "subscribe" | "unsubscribe"
+ method?: string
+ params?: unknown
+ pin?: string
+ channel?: string
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "result" | "error" | "subscription"
+ data?: unknown
+ error?: string
+ channel?: string
+}
+
+interface AuthenticatedClient {
+ ws: WebSocket
+ id: string
+ subscriptions: Set
+}
+
+let httpServer: Server | null = null
+let wss: WebSocketServer | null = null
+const authenticatedClients = new Map()
+
+// Subscription handlers - map of channel pattern to active subscriptions
+const subscriptionCleanups = new Map void>()
+
+// Shared chat subscriptions - one Observable per subChatId, broadcast to all clients
+interface SharedChatSubscription {
+ observable: any
+ subscription: any
+ clients: Map // Map of WebSocket to subscription ID
+ // Event emitter for IPC clients to listen
+ listeners: Set<(data: any) => void>
+}
+const sharedChatSubscriptions = new Map()
+
+// MIME types for static files
+const MIME_TYPES: Record = {
+ ".html": "text/html",
+ ".js": "application/javascript",
+ ".css": "text/css",
+ ".json": "application/json",
+ ".png": "image/png",
+ ".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".gif": "image/gif",
+ ".svg": "image/svg+xml",
+ ".ico": "image/x-icon",
+ ".woff": "font/woff",
+ ".woff2": "font/woff2",
+ ".ttf": "font/ttf",
+ ".eot": "application/vnd.ms-fontobject",
+}
+
+/**
+ * Get the renderer output directory
+ */
+function getRendererDir(): string {
+ // In dev: out/renderer, in production: resources/app.asar/out/renderer
+ if (app.isPackaged) {
+ return join(process.resourcesPath, "app.asar", "out", "renderer")
+ }
+ return join(app.getAppPath(), "out", "renderer")
+}
+
+/**
+ * PIN auth page HTML
+ */
+function getPinAuthPage(): string {
+ return `
+
+
+
+
+ 1Code Remote Access
+
+
+
+
+
+
+`
+}
+
+/**
+ * Proxy request to Vite dev server
+ */
+function proxyToVite(req: IncomingMessage, res: ServerResponse): void {
+ // Map /app to / for Vite (Vite serves at root)
+ let path = req.url || "/"
+ if (path === "/app" || path.startsWith("/app?")) {
+ path = "/" + path.slice(4) // Remove "/app" prefix, keep query string
+ }
+
+ const proxyReq = httpRequest(
+ {
+ hostname: "localhost",
+ port: VITE_DEV_PORT,
+ path: path,
+ method: req.method,
+ headers: {
+ ...req.headers,
+ host: `localhost:${VITE_DEV_PORT}`,
+ },
+ },
+ (proxyRes) => {
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers)
+ proxyRes.pipe(res)
+ }
+ )
+
+ proxyReq.on("error", (err) => {
+ console.error("[WS] Proxy error:", err)
+ res.writeHead(502, { "Content-Type": "text/plain" })
+ res.end("Vite dev server not available. Make sure bun run dev is running.")
+ })
+
+ req.pipe(proxyReq)
+}
+
+/**
+ * Serve static files or PIN auth page
+ * In dev mode, proxy to Vite dev server for hot reload
+ */
+async function handleHttpRequest(req: IncomingMessage, res: ServerResponse): Promise {
+ const url = req.url || "/"
+
+ // In dev mode, only serve PIN auth page directly
+ // All other requests proxy to Vite dev server
+ const isDev = process.env.NODE_ENV !== "production"
+
+ if (isDev) {
+ // Root or /login -> PIN auth page (even in dev)
+ if (url === "/" || url === "/login") {
+ res.writeHead(200, { "Content-Type": "text/html" })
+ res.end(getPinAuthPage())
+ return
+ }
+
+ // Proxy all other requests to Vite dev server
+ proxyToVite(req, res)
+ return
+ }
+
+ // Production mode: serve static files
+ const rendererDir = getRendererDir()
+
+ // Root or /login -> PIN auth page
+ if (url === "/" || url === "/login") {
+ res.writeHead(200, { "Content-Type": "text/html" })
+ res.end(getPinAuthPage())
+ return
+ }
+
+ // /app -> serve renderer index.html
+ if (url === "/app" || url.startsWith("/app?")) {
+ const indexPath = join(rendererDir, "index.html")
+ try {
+ const content = await readFile(indexPath)
+ res.writeHead(200, { "Content-Type": "text/html" })
+ res.end(content)
+ } catch {
+ res.writeHead(404)
+ res.end("Not found")
+ }
+ return
+ }
+
+ // Static assets
+ let filePath = join(rendererDir, url.split("?")[0])
+
+ // Security: prevent directory traversal
+ if (!filePath.startsWith(rendererDir)) {
+ res.writeHead(403)
+ res.end("Forbidden")
+ return
+ }
+
+ try {
+ const fileStat = await stat(filePath)
+ if (fileStat.isDirectory()) {
+ filePath = join(filePath, "index.html")
+ }
+
+ const content = await readFile(filePath)
+ const ext = extname(filePath).toLowerCase()
+ const contentType = MIME_TYPES[ext] || "application/octet-stream"
+
+ res.writeHead(200, { "Content-Type": contentType })
+ res.end(content)
+ } catch {
+ res.writeHead(404)
+ res.end("Not found")
+ }
+}
+
+/**
+ * Send message to a WebSocket client
+ */
+function send(ws: WebSocket, message: WSResponse): void {
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify(message))
+ }
+}
+
+/**
+ * Broadcast to all authenticated clients
+ */
+export function broadcast(channel: string, data: unknown): void {
+ const message: WSResponse = {
+ id: null,
+ type: "subscription",
+ channel,
+ data,
+ }
+
+ for (const [ws, client] of authenticatedClients) {
+ if (client.subscriptions.has(channel)) {
+ send(ws, message)
+ }
+ }
+}
+
+/**
+ * Broadcast chat data to all clients subscribed to a specific subChat
+ * This is called when chat updates come from IPC (desktop) or WebSocket (web)
+ */
+export function broadcastChatData(subChatId: string, data: any): void {
+ const sharedSub = sharedChatSubscriptions.get(subChatId)
+ if (!sharedSub) {
+ // No subscription exists for this chat
+ return
+ }
+
+ console.log(`[WS] Broadcasting chat data to ${sharedSub.clients.size} WebSocket clients and ${sharedSub.listeners.size} IPC listeners for subChatId: ${subChatId}`)
+
+ // Broadcast to WebSocket clients
+ for (const [clientWs, subId] of sharedSub.clients) {
+ send(clientWs, {
+ id: subId,
+ type: "subscription",
+ data: data,
+ })
+ }
+
+ // Broadcast to IPC listeners (desktop clients)
+ for (const listener of sharedSub.listeners) {
+ try {
+ listener(data)
+ } catch (err) {
+ console.error(`[WS] Error in IPC listener:`, err)
+ }
+ }
+}
+
+/**
+ * Subscribe to chat data from IPC (desktop client)
+ * Returns unsubscribe function
+ */
+export function subscribeToChatData(subChatId: string, callback: (data: any) => void): () => void {
+ let sharedSub = sharedChatSubscriptions.get(subChatId)
+
+ if (!sharedSub) {
+ // Create a placeholder subscription without an Observable
+ // This allows IPC clients to register listeners before any WebSocket client connects
+ sharedSub = {
+ observable: null,
+ subscription: null,
+ clients: new Map(),
+ listeners: new Set(),
+ }
+ sharedChatSubscriptions.set(subChatId, sharedSub)
+ }
+
+ sharedSub.listeners.add(callback)
+ console.log(`[WS] IPC listener added for subChatId: ${subChatId}. Total listeners: ${sharedSub.listeners.size}`)
+
+ // Return unsubscribe function
+ return () => {
+ sharedSub!.listeners.delete(callback)
+ console.log(`[WS] IPC listener removed for subChatId: ${subChatId}. Remaining listeners: ${sharedSub!.listeners.size}`)
+
+ // Clean up if no clients or listeners left
+ if (sharedSub!.clients.size === 0 && sharedSub!.listeners.size === 0) {
+ if (sharedSub!.subscription) {
+ sharedSub!.subscription.unsubscribe()
+ }
+ sharedChatSubscriptions.delete(subChatId)
+ }
+ }
+}
+
+/**
+ * Handle incoming WebSocket message
+ */
+async function handleMessage(
+ ws: WebSocket,
+ client: AuthenticatedClient | undefined,
+ message: WSRequest,
+ router: ReturnType
+): Promise {
+ // Auth message
+ if (message.type === "auth") {
+ if (message.pin && validatePin(message.pin)) {
+ const clientId = randomUUID()
+ const newClient: AuthenticatedClient = {
+ ws,
+ id: clientId,
+ subscriptions: new Set(),
+ }
+ authenticatedClients.set(ws, newClient)
+ addClient(clientId)
+ send(ws, { id: message.id, type: "auth_success" })
+ console.log(`[WS] Client authenticated: ${clientId}`)
+ } else {
+ send(ws, { id: message.id, type: "auth_failed", error: "Invalid PIN" })
+ }
+ return
+ }
+
+ // All other messages require authentication
+ if (!client) {
+ send(ws, { id: message.id, type: "auth_required" })
+ return
+ }
+
+ // tRPC call
+ if (message.type === "trpc" && message.method) {
+ try {
+ // Parse the method path (e.g., "claude.chat" or "external.getAppVersion")
+ const pathParts = message.method.split(".")
+ const routerName = pathParts[0]
+ const procedureName = pathParts[pathParts.length - 1]
+
+ // Get the router
+ const routerObj = (router as any)[routerName]
+ if (!routerObj) {
+ throw new Error(`Router not found: ${routerName}`)
+ }
+
+ // Handle subscriptions (streaming)
+ // For claude.chat, routerName is "claude" and procedureName is "chat"
+ if (routerName === "claude" && procedureName === "chat") {
+ const subChatId = message.params?.subChatId
+ if (!subChatId) {
+ throw new Error(`subChatId is required for chat subscription`)
+ }
+
+ console.log(`[WS] Starting claude.chat subscription for subChatId: ${subChatId}`)
+
+ // Check if there's already a shared subscription for this subChatId
+ let sharedSub = sharedChatSubscriptions.get(subChatId)
+
+ if (!sharedSub) {
+ // Create new shared subscription
+ console.log(`[WS] Creating new shared subscription for ${subChatId}`)
+
+ // Access the claude router
+ const claudeRouter = (router as any).claude
+ if (!claudeRouter) {
+ throw new Error(`claude router not found`)
+ }
+
+ const chatProcedure = claudeRouter.chat
+ if (!chatProcedure) {
+ throw new Error(`chat procedure not found`)
+ }
+
+ const procedureDef = chatProcedure._def
+ if (!procedureDef) {
+ throw new Error(`chat procedure _def not found`)
+ }
+
+ const subscriptionFn = procedureDef.resolver
+ if (!subscriptionFn || typeof subscriptionFn !== 'function') {
+ throw new Error(`chat procedure resolver not found`)
+ }
+
+ const ctx = {
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ }
+
+ // Create the Observable
+ const observable = subscriptionFn({
+ input: message.params,
+ type: 'subscription',
+ ctx,
+ path: 'claude.chat',
+ })
+
+ if (typeof observable?.subscribe !== 'function') {
+ throw new Error(`chat subscription did not return Observable`)
+ }
+
+ // Subscribe and broadcast to all clients (WebSocket + IPC)
+ const subscription = observable.subscribe({
+ next: (data: any) => {
+ console.log(`[WS] Broadcasting chat data to ${sharedSub?.clients.size || 0} WS clients, ${sharedSub?.listeners.size || 0} IPC listeners:`, data.type || "no-type")
+ // Broadcast to all WebSocket clients subscribed to this subChat
+ if (sharedSub) {
+ for (const [clientWs, subId] of sharedSub.clients) {
+ send(clientWs, {
+ id: subId,
+ type: "subscription",
+ data: data,
+ })
+ }
+ // Also notify IPC listeners (desktop clients)
+ for (const listener of sharedSub.listeners) {
+ try {
+ listener(data)
+ } catch (err) {
+ console.error(`[WS] Error in IPC listener:`, err)
+ }
+ }
+ }
+ },
+ error: (err: any) => {
+ console.error(`[WS] Chat subscription error:`, err)
+ if (sharedSub) {
+ for (const [clientWs, subId] of sharedSub.clients) {
+ send(clientWs, {
+ id: subId,
+ type: "error",
+ error: err.message,
+ })
+ }
+ for (const listener of sharedSub.listeners) {
+ try {
+ listener({ type: "error", error: err.message })
+ } catch (err) {
+ console.error(`[WS] Error in IPC listener:`, err)
+ }
+ }
+ }
+ },
+ complete: () => {
+ console.log(`[WS] Chat subscription complete for ${subChatId}`)
+ if (sharedSub) {
+ for (const [clientWs, subId] of sharedSub.clients) {
+ send(clientWs, {
+ id: subId,
+ type: "result",
+ data: { completed: true },
+ })
+ }
+ for (const listener of sharedSub.listeners) {
+ try {
+ listener({ type: "complete" })
+ } catch (err) {
+ console.error(`[WS] Error in IPC listener:`, err)
+ }
+ }
+ }
+ }
+ })
+
+ // Create shared subscription object with listeners set
+ // Preserve existing listeners if this is being recreated
+ const existingListeners = sharedChatSubscriptions.get(subChatId)?.listeners || new Set()
+ sharedSub = {
+ observable,
+ subscription,
+ clients: new Map(),
+ listeners: existingListeners,
+ }
+ sharedChatSubscriptions.set(subChatId, sharedSub)
+ }
+
+ // Add this client to the shared subscription with their subscription ID
+ sharedSub.clients.set(ws, message.id)
+ console.log(`[WS] Client added to shared subscription ${subChatId}. Total clients: ${sharedSub.clients.size}`)
+
+ // Send success response
+ send(ws, {
+ id: message.id,
+ type: "result",
+ data: { subscribed: true, subChatId },
+ })
+
+ // Clean up client from subscription on disconnect
+ ws.on("close", () => {
+ if (sharedSub) {
+ sharedSub.clients.delete(ws)
+ console.log(`[WS] Client removed from shared subscription ${subChatId}. Remaining clients: ${sharedSub.clients.size}`)
+
+ // Clean up subscription if no clients left
+ if (sharedSub.clients.size === 0) {
+ console.log(`[WS] No clients left for ${subChatId}, cleaning up subscription`)
+ sharedSub.subscription.unsubscribe()
+ sharedChatSubscriptions.delete(subChatId)
+ }
+ }
+ })
+
+ return
+ }
+
+ // Create a caller for regular procedures
+ const caller = router.createCaller({
+ getWindow: () => BrowserWindow.getFocusedWindow(),
+ })
+
+ // Navigate to the procedure using the caller
+ // Handle nested paths like "external.getAppVersion" or "projects.list" or "chats.list"
+ let current: any = caller
+
+ for (let i = 0; i < pathParts.length - 1; i++) {
+ current = current[pathParts[i]]
+ if (!current) {
+ throw new Error(`Router not found: ${pathParts.slice(0, i + 1).join(".")}`)
+ }
+ }
+
+ if (!current[procedureName]) {
+ throw new Error(`Procedure not found: ${message.method}`)
+ }
+
+ const result = await current[procedureName](message.params)
+
+ send(ws, {
+ id: message.id,
+ type: "result",
+ data: superjson.serialize(result),
+ })
+ } catch (error) {
+ send(ws, {
+ id: message.id,
+ type: "error",
+ error: error instanceof Error ? error.message : "Unknown error",
+ })
+ }
+ return
+ }
+
+ // Subscribe
+ if (message.type === "subscribe" && message.channel) {
+ client.subscriptions.add(message.channel)
+ send(ws, { id: message.id, type: "result", data: { subscribed: message.channel } })
+ return
+ }
+
+ // Unsubscribe
+ if (message.type === "unsubscribe" && message.channel) {
+ client.subscriptions.delete(message.channel)
+ send(ws, { id: message.id, type: "result", data: { unsubscribed: message.channel } })
+ return
+ }
+
+ // Unknown message type
+ send(ws, { id: message.id, type: "error", error: "Unknown message type" })
+}
+
+/**
+ * Start WebSocket server
+ * Returns the port number
+ */
+export function startWSServer(): Promise {
+ return new Promise((resolve, reject) => {
+ // Create HTTP server for WebSocket upgrade and static files
+ httpServer = createServer((req, res) => {
+ handleHttpRequest(req, res).catch((err) => {
+ console.error("[WS] HTTP request error:", err)
+ res.writeHead(500)
+ res.end("Internal Server Error")
+ })
+ })
+
+ wss = new WebSocketServer({ server: httpServer })
+
+ // Create tRPC router
+ const router = createAppRouter(() => BrowserWindow.getFocusedWindow())
+
+ wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
+ console.log("[WS] New connection from:", req.socket.remoteAddress)
+
+ // Send auth required message
+ send(ws, { id: null, type: "auth_required" })
+
+ ws.on("message", async (data: Buffer) => {
+ try {
+ const message = JSON.parse(data.toString()) as WSRequest
+ const client = authenticatedClients.get(ws)
+ await handleMessage(ws, client, message, router)
+ } catch (error) {
+ console.error("[WS] Message parse error:", error)
+ send(ws, { id: null, type: "error", error: "Invalid message format" })
+ }
+ })
+
+ ws.on("close", () => {
+ const client = authenticatedClients.get(ws)
+ if (client) {
+ removeClient(client.id)
+ authenticatedClients.delete(ws)
+ console.log(`[WS] Client disconnected: ${client.id}`)
+ }
+ })
+
+ ws.on("error", (error) => {
+ console.error("[WS] WebSocket error:", error)
+ })
+ })
+
+ // Listen on random available port
+ httpServer.listen(0, () => {
+ const address = httpServer?.address()
+ if (address && typeof address === "object") {
+ console.log(`[WS] Server listening on port ${address.port}`)
+ resolve(address.port)
+ } else {
+ reject(new Error("Failed to get server port"))
+ }
+ })
+
+ httpServer.on("error", reject)
+ })
+}
+
+/**
+ * Stop WebSocket server
+ */
+export function stopWSServer(): Promise {
+ return new Promise((resolve) => {
+ // Close all client connections
+ for (const [ws] of authenticatedClients) {
+ ws.close()
+ }
+ authenticatedClients.clear()
+
+ // Clean up subscriptions
+ for (const cleanup of subscriptionCleanups.values()) {
+ cleanup()
+ }
+ subscriptionCleanups.clear()
+
+ // Close servers
+ if (wss) {
+ wss.close(() => {
+ if (httpServer) {
+ httpServer.close(() => {
+ wss = null
+ httpServer = null
+ console.log("[WS] Server stopped")
+ resolve()
+ })
+ } else {
+ wss = null
+ resolve()
+ }
+ })
+ } else {
+ resolve()
+ }
+ })
+}
+
+/**
+ * Check if server is running
+ */
+export function isWSServerRunning(): boolean {
+ return httpServer !== null
+}
+
+/**
+ * Get connected client count
+ */
+export function getConnectedClientCount(): number {
+ return authenticatedClients.size
+}
diff --git a/src/main/lib/tray.ts b/src/main/lib/tray.ts
new file mode 100644
index 000000000..ce5dd2a4a
--- /dev/null
+++ b/src/main/lib/tray.ts
@@ -0,0 +1,464 @@
+import { app, Menu, Tray, nativeImage } from "electron"
+import { join } from "path"
+import { existsSync } from "fs"
+import { eq } from "drizzle-orm"
+import { IS_DEV } from "../constants"
+import { getWindow, getAllWindows, createMainWindow } from "../windows/main"
+import { getDatabase } from "./db"
+import { projects } from "./db/schema"
+
+let tray: Tray | null = null
+
+/**
+ * Get path to tray icon based on platform and theme
+ * Returns template image for macOS (auto-adapts to light/dark mode)
+ */
+function getTrayIconPath(): string {
+ // macOS uses trayTemplate.png which auto-adapts to light/dark mode
+ const iconName = "trayTemplate.png"
+
+ // In dev mode, __dirname is in out/main, so go up to project root
+ // In production, icon is in app resources
+ if (IS_DEV) {
+ // __dirname = /path/to/project/out/main
+ // We need: /path/to/project/build/trayTemplate.png
+ const devPath = join(__dirname, "../../build", iconName)
+ console.log("[Tray] Dev icon path:", devPath)
+ if (existsSync(devPath)) {
+ return devPath
+ }
+ // Fallback: try app.getAppPath()
+ const fallbackPath = join(app.getAppPath(), "build", iconName)
+ console.log("[Tray] Fallback icon path:", fallbackPath)
+ return fallbackPath
+ }
+ return join(process.resourcesPath, iconName)
+}
+
+/**
+ * Initialize system tray
+ * Should be called after app is ready
+ */
+export function initTray(): void {
+ if (tray) {
+ console.log("[Tray] Already initialized")
+ return
+ }
+
+ try {
+ const iconPath = getTrayIconPath()
+ console.log("[Tray] Creating tray with icon:", iconPath)
+ console.log("[Tray] Icon exists:", existsSync(iconPath))
+
+ if (!existsSync(iconPath)) {
+ console.error("[Tray] Icon file not found at:", iconPath)
+ return
+ }
+
+ // Create native image and set as template for macOS (auto dark/light mode)
+ const icon = nativeImage.createFromPath(iconPath)
+ if (process.platform === "darwin") {
+ icon.setTemplateImage(true)
+ }
+
+ // Resize for proper display (16x16 for macOS menu bar)
+ const resizedIcon = icon.resize({ width: 16, height: 16 })
+
+ tray = new Tray(resizedIcon)
+ tray.setToolTip("1Code")
+
+ // Left-click: restore or create window
+ tray.on("click", () => {
+ console.log("[Tray] Click event - restoring window")
+ restoreOrCreateWindow()
+ })
+
+ // Double-click on Windows also shows window
+ tray.on("double-click", () => {
+ console.log("[Tray] Double-click event - restoring window")
+ restoreOrCreateWindow()
+ })
+
+ // Right-click: rebuild menu before showing (ensures dynamic data is current)
+ tray.on("right-click", () => {
+ console.log("[Tray] Right-click - rebuilding menu for dynamic data")
+ buildTrayMenu().catch(err => {
+ console.error("[Tray] Failed to rebuild menu on right-click:", err)
+ })
+ })
+
+ // Build initial menu (async, don't await)
+ buildTrayMenu().catch(err => {
+ console.error("[Tray] Failed to build initial menu:", err)
+ })
+
+ console.log("[Tray] Initialized successfully")
+ } catch (error) {
+ console.error("[Tray] Failed to initialize:", error)
+ }
+}
+
+/**
+ * Restore an existing window or create a new one
+ */
+function restoreOrCreateWindow(): void {
+ // On macOS, show dock icon when restoring window
+ if (process.platform === "darwin" && app.dock) {
+ app.dock.show()
+ }
+
+ const windows = getAllWindows()
+ if (windows.length > 0) {
+ // Show and focus first window
+ const window = windows[0]!
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+ } else {
+ // No windows exist, create new one
+ createMainWindow()
+ }
+}
+
+/**
+ * Build and set the tray context menu
+ * Fetches current profiles and projects from database/localStorage
+ */
+export async function buildTrayMenu(): Promise {
+ if (!tray) {
+ console.warn("[Tray] Cannot build menu - tray not initialized")
+ return
+ }
+
+ let allProjects: { id: string; name: string; path: string }[] = []
+
+ // Try to fetch projects from database (may fail if DB not ready)
+ try {
+ const db = getDatabase()
+ allProjects = db.select().from(projects).all()
+ } catch (dbError) {
+ console.warn("[Tray] Could not fetch projects from database:", dbError)
+ // Continue with empty projects - show basic menu
+ }
+
+ try {
+
+ // Get active profile from first window's localStorage
+ let activeProfileId: string | null = null
+ let allProfiles: { id: string; name: string }[] = []
+
+ // Get or create a window to read localStorage
+ let windows = getAllWindows()
+ let window = windows.find(w => !w.isDestroyed())
+
+ // First try to get the main window by ID
+ if (!window) {
+ window = getWindow()
+ }
+
+ // If still no window, create one temporarily
+ let createdHiddenWindow = false
+ if (!window) {
+ console.log("[Tray] No window available, creating hidden window to read profiles")
+ window = createMainWindow()
+ createdHiddenWindow = true
+ // Wait for window to load
+ await new Promise((resolve) => {
+ window!.webContents.once('did-finish-load', () => resolve())
+ })
+ }
+
+ if (window && !window.isDestroyed()) {
+ try {
+ // Get profiles from localStorage
+ const profilesJson = await window.webContents.executeJavaScript(
+ `localStorage.getItem('agents:model-profiles')`
+ )
+ console.log("[Tray] Raw profiles from localStorage:", profilesJson)
+ if (profilesJson) {
+ const parsed = JSON.parse(profilesJson)
+ console.log("[Tray] Parsed profiles:", parsed)
+ allProfiles = Array.isArray(parsed) ? parsed : []
+ }
+
+ // Get active profile ID
+ const activeIdJson = await window.webContents.executeJavaScript(
+ `localStorage.getItem('agents:active-profile-id')`
+ )
+ console.log("[Tray] Raw active profile ID:", activeIdJson)
+ activeProfileId = activeIdJson ? JSON.parse(activeIdJson) : null
+ console.log("[Tray] Parsed active profile ID:", activeProfileId)
+ } catch (e) {
+ console.warn("[Tray] Could not get profile data from renderer:", e)
+ }
+
+ // If we created a hidden window just to read, close it
+ if (createdHiddenWindow && window && !window.isDestroyed()) {
+ window.close()
+ }
+ }
+
+ // Filter out offline profiles (matching UI behavior)
+ const customProfiles = allProfiles.filter((p: any) => !p.isOffline)
+ console.log("[Tray] Custom profiles (non-offline):", customProfiles.map((p: any) => p.name))
+
+ // Build profile menu items
+ // First item: "Use Claude Default" option (when no custom profile is active)
+ const profileMenuItems: Electron.MenuItemConstructorOptions[] = [
+ {
+ label: "Use Claude Default",
+ type: "checkbox" as const,
+ checked: activeProfileId === null,
+ click: () => {
+ handleProfileSwitch(null)
+ },
+ },
+ ...(customProfiles.length > 0 ? [{ type: "separator" as const }] : []),
+ ...customProfiles.map((profile: any) => ({
+ label: profile.name,
+ type: "checkbox" as const,
+ checked: profile.id === activeProfileId,
+ click: () => {
+ handleProfileSwitch(profile.id)
+ },
+ }))
+ ]
+
+ // Build workspace menu items
+ const workspaceMenuItems: Electron.MenuItemConstructorOptions[] = allProjects.map(project => ({
+ label: project.name || project.path.split("/").pop() || project.path,
+ click: () => {
+ handleWorkspaceClick(project.id)
+ },
+ }))
+
+ const template: Electron.MenuItemConstructorOptions[] = []
+
+ // macOS: Add "Show 1Code" as first item
+ if (process.platform === "darwin") {
+ template.push({
+ label: "Show 1Code",
+ click: () => {
+ restoreOrCreateWindow()
+ },
+ })
+ template.push({ type: "separator" })
+ }
+
+ template.push(
+ {
+ label: "Profiles",
+ submenu: profileMenuItems,
+ },
+ { type: "separator" },
+ {
+ label: "Workspaces",
+ submenu: workspaceMenuItems.length > 0
+ ? workspaceMenuItems
+ : [{ label: "No workspaces", enabled: false }],
+ },
+ { type: "separator" },
+ {
+ label: "New Workspace...",
+ click: () => {
+ handleNewWorkspace()
+ },
+ },
+ {
+ label: "Settings...",
+ click: () => {
+ handleOpenSettings()
+ },
+ },
+ { type: "separator" },
+ {
+ label: "Quit",
+ click: () => {
+ app.quit()
+ },
+ },
+ )
+
+ const contextMenu = Menu.buildFromTemplate(template)
+ tray.setContextMenu(contextMenu)
+ } catch (error) {
+ console.error("[Tray] Failed to build menu:", error)
+ }
+}
+
+/**
+ * Handle profile switch from tray menu
+ * Updates localStorage in all windows and reloads them
+ */
+async function handleProfileSwitch(profileId: string | null): Promise {
+ console.log("[Tray] Switching to profile:", profileId)
+
+ const windows = getAllWindows()
+ for (const window of windows) {
+ try {
+ if (window.isDestroyed()) continue
+
+ // Update localStorage
+ if (profileId === null) {
+ // Clear profile to use default Claude
+ await window.webContents.executeJavaScript(
+ `localStorage.removeItem('agents:active-profile-id')`
+ )
+ } else {
+ await window.webContents.executeJavaScript(
+ `localStorage.setItem('agents:active-profile-id', ${JSON.stringify(JSON.stringify(profileId))})`
+ )
+ }
+
+ // Reload window to apply new profile
+ window.reload()
+ } catch (error) {
+ console.error("[Tray] Failed to switch profile for window:", error)
+ }
+ }
+
+ // Rebuild menu to update checkmarks
+ await buildTrayMenu()
+}
+
+/**
+ * Handle workspace click from tray menu
+ * Sets active workspace and opens window
+ */
+async function handleWorkspaceClick(projectId: string): Promise {
+ console.log("[Tray] Opening workspace:", projectId)
+
+ // Get or create main window
+ let window = getWindow()
+ if (!window) {
+ window = createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Set selected project in localStorage and trigger reload
+ try {
+ const db = getDatabase()
+ const projectData = db.select().from(projects).where(
+ eq(projects.id, projectId)
+ ).get()
+
+ if (projectData) {
+ await window.webContents.executeJavaScript(`
+ localStorage.setItem('agents:selected-project', ${JSON.stringify(JSON.stringify(projectData))});
+ window.location.reload();
+ `)
+ }
+ } catch (error) {
+ console.error("[Tray] Failed to open workspace:", error)
+ }
+}
+
+/**
+ * Handle "New Workspace" click
+ * Opens select-repo-page by clearing selected project
+ */
+async function handleNewWorkspace(): Promise {
+ console.log("[Tray] Opening new workspace dialog")
+
+ // Get or create main window
+ let window = getWindow()
+ if (!window) {
+ window = createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Wait a bit for window to be fully visible and ready
+ await new Promise(resolve => setTimeout(resolve, 100))
+
+ // Navigate to select-repo page by clearing selected project
+ try {
+ await window.webContents.executeJavaScript(`
+ localStorage.removeItem('agents:selected-project');
+ window.location.reload();
+ `)
+ } catch (error) {
+ console.error("[Tray] Failed to open new workspace:", error)
+ }
+}
+
+/**
+ * Handle "Settings" click
+ * Opens settings by dispatching custom event
+ */
+async function handleOpenSettings(): Promise {
+ console.log("[Tray] Opening settings")
+
+ // On macOS, show dock icon when showing window
+ if (process.platform === "darwin" && app.dock) {
+ app.dock.show()
+ }
+
+ // Get or create main window
+ let window = getWindow()
+ const isNewWindow = !window
+ if (!window) {
+ window = createMainWindow()
+ }
+
+ // Show and focus window
+ if (window.isMinimized()) {
+ window.restore()
+ }
+ window.show()
+ window.focus()
+
+ // Wait for window to be fully loaded before dispatching event
+ const dispatchSettingsEvent = async () => {
+ try {
+ await window!.webContents.executeJavaScript(`
+ window.dispatchEvent(new CustomEvent('tray:open-settings'));
+ `)
+ } catch (error) {
+ console.error("[Tray] Failed to open settings:", error)
+ }
+ }
+
+ if (isNewWindow) {
+ // New window: wait for did-finish-load to ensure React is mounted
+ window.webContents.once('did-finish-load', () => {
+ // Additional delay to ensure React hooks are registered
+ setTimeout(dispatchSettingsEvent, 300)
+ })
+ } else {
+ // Existing window: dispatch immediately
+ await dispatchSettingsEvent()
+ }
+}
+
+/**
+ * Destroy the tray icon
+ * Should be called on app quit
+ */
+export function destroyTray(): void {
+ if (tray) {
+ tray.destroy()
+ tray = null
+ console.log("[Tray] Destroyed")
+ }
+}
+
+/**
+ * Get the tray instance (for testing or advanced use)
+ */
+export function getTray(): Tray | null {
+ return tray
+}
diff --git a/src/main/lib/trpc/routers/chats.ts b/src/main/lib/trpc/routers/chats.ts
index c21c793e8..9297c65be 100644
--- a/src/main/lib/trpc/routers/chats.ts
+++ b/src/main/lib/trpc/routers/chats.ts
@@ -425,6 +425,48 @@ export const chatsRouter = router({
.get()
}),
+ /**
+ * Update chat's model profile (per-workspace model configuration)
+ */
+ updateModelProfile: publicProcedure
+ .input(z.object({
+ id: z.string(),
+ modelProfileId: z.string().nullable(),
+ }))
+ .mutation(({ input }) => {
+ const db = getDatabase()
+ return db
+ .update(chats)
+ .set({
+ modelProfileId: input.modelProfileId,
+ updatedAt: new Date(),
+ })
+ .where(eq(chats.id, input.id))
+ .returning()
+ .get()
+ }),
+
+ /**
+ * Update chat's selected model within the current profile
+ */
+ updateSelectedModel: publicProcedure
+ .input(z.object({
+ id: z.string(),
+ selectedModelId: z.string().nullable(),
+ }))
+ .mutation(({ input }) => {
+ const db = getDatabase()
+ return db
+ .update(chats)
+ .set({
+ selectedModelId: input.selectedModelId,
+ updatedAt: new Date(),
+ })
+ .where(eq(chats.id, input.id))
+ .returning()
+ .get()
+ }),
+
/**
* Archive a chat (also kills any terminal processes in the workspace)
* Optionally deletes the worktree to free disk space
diff --git a/src/main/lib/trpc/routers/claude-code.ts b/src/main/lib/trpc/routers/claude-code.ts
index 78766a88b..174b795d5 100644
--- a/src/main/lib/trpc/routers/claude-code.ts
+++ b/src/main/lib/trpc/routers/claude-code.ts
@@ -118,7 +118,13 @@ export const claudeCodeRouter = router({
return {
hasConfig,
hasApiKey: !!shellEnv.ANTHROPIC_API_KEY,
+ apiKey: shellEnv.ANTHROPIC_API_KEY || shellEnv.ANTHROPIC_AUTH_TOKEN || null,
baseUrl: shellEnv.ANTHROPIC_BASE_URL || null,
+ model: shellEnv.ANTHROPIC_MODEL || null,
+ defaultOpusModel: shellEnv.ANTHROPIC_DEFAULT_OPUS_MODEL || null,
+ defaultSonnetModel: shellEnv.ANTHROPIC_DEFAULT_SONNET_MODEL || null,
+ defaultHaikuModel: shellEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL || null,
+ subagentModel: shellEnv.CLAUDE_CODE_SUBAGENT_MODEL || null,
}
}),
diff --git a/src/main/lib/trpc/routers/claude.ts b/src/main/lib/trpc/routers/claude.ts
index 7be49b8b0..08748384c 100644
--- a/src/main/lib/trpc/routers/claude.ts
+++ b/src/main/lib/trpc/routers/claude.ts
@@ -553,8 +553,13 @@ export const claudeRouter = router({
customConfig: z
.object({
model: z.string().min(1),
- token: z.string().min(1),
+ token: z.string().optional(), // Token can be empty for proxy configs
baseUrl: z.string().min(1),
+ // Additional model configuration (optional)
+ defaultOpusModel: z.string().optional(),
+ defaultSonnetModel: z.string().optional(),
+ defaultHaikuModel: z.string().optional(),
+ subagentModel: z.string().optional(),
})
.optional(),
maxThinkingTokens: z.number().optional(), // Enable extended thinking
@@ -828,6 +833,12 @@ export const claudeRouter = router({
customEnv: {
ANTHROPIC_AUTH_TOKEN: finalCustomConfig.token,
ANTHROPIC_BASE_URL: finalCustomConfig.baseUrl,
+ // Additional model configuration from profile
+ ...(finalCustomConfig.model && { ANTHROPIC_MODEL: finalCustomConfig.model }),
+ ...(finalCustomConfig.defaultOpusModel && { ANTHROPIC_DEFAULT_OPUS_MODEL: finalCustomConfig.defaultOpusModel }),
+ ...(finalCustomConfig.defaultSonnetModel && { ANTHROPIC_DEFAULT_SONNET_MODEL: finalCustomConfig.defaultSonnetModel }),
+ ...(finalCustomConfig.defaultHaikuModel && { ANTHROPIC_DEFAULT_HAIKU_MODEL: finalCustomConfig.defaultHaikuModel }),
+ ...(finalCustomConfig.subagentModel && { CLAUDE_CODE_SUBAGENT_MODEL: finalCustomConfig.subagentModel }),
},
}),
enableTasks: input.enableTasks ?? true,
@@ -891,6 +902,64 @@ export const claudeRouter = router({
symlinksCreated.add(cacheKey)
}
+ // Write settings.json to isolated config dir with profile's env settings
+ // This ensures the CLI uses the selected profile instead of global ~/.claude/settings.json
+ try {
+ const isolatedSettingsPath = path.join(isolatedConfigDir, "settings.json")
+ const homeSettingsPath = path.join(os.homedir(), ".claude", "settings.json")
+
+ // Read existing global settings (for non-env settings like permissions, plugins, etc.)
+ let baseSettings: Record = {}
+ try {
+ const globalSettings = await fs.readFile(homeSettingsPath, "utf-8")
+ baseSettings = JSON.parse(globalSettings)
+ } catch {
+ // No global settings file, start fresh
+ }
+
+ // Build env section from profile config (overrides global env)
+ const profileEnv: Record = {}
+ if (finalCustomConfig) {
+ if (finalCustomConfig.token) {
+ profileEnv.ANTHROPIC_AUTH_TOKEN = finalCustomConfig.token
+ }
+ if (finalCustomConfig.baseUrl) {
+ profileEnv.ANTHROPIC_BASE_URL = finalCustomConfig.baseUrl
+ }
+ if (finalCustomConfig.model) {
+ profileEnv.ANTHROPIC_MODEL = finalCustomConfig.model
+ }
+ if (finalCustomConfig.defaultOpusModel) {
+ profileEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = finalCustomConfig.defaultOpusModel
+ }
+ if (finalCustomConfig.defaultSonnetModel) {
+ profileEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = finalCustomConfig.defaultSonnetModel
+ }
+ if (finalCustomConfig.defaultHaikuModel) {
+ profileEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = finalCustomConfig.defaultHaikuModel
+ }
+ if (finalCustomConfig.subagentModel) {
+ profileEnv.CLAUDE_CODE_SUBAGENT_MODEL = finalCustomConfig.subagentModel
+ }
+ }
+
+ // Merge: keep global non-env settings, but replace env with profile settings
+ const isolatedSettings = {
+ ...baseSettings,
+ env: Object.keys(profileEnv).length > 0 ? profileEnv : undefined,
+ }
+
+ // Remove env key entirely if no profile config (use OAuth)
+ if (!isolatedSettings.env) {
+ delete isolatedSettings.env
+ }
+
+ await fs.writeFile(isolatedSettingsPath, JSON.stringify(isolatedSettings, null, 2))
+ console.log(`[claude] Wrote profile settings to ${isolatedSettingsPath}`)
+ } catch (settingsErr) {
+ console.error(`[claude] Failed to write isolated settings.json:`, settingsErr)
+ }
+
// Read MCP servers from ~/.claude.json for the original project path
// These will be passed directly to the SDK via options.mcpServers
// OPTIMIZATION: Cache MCP config by file mtime to avoid re-parsing on every message
@@ -1072,13 +1141,14 @@ export const claudeRouter = router({
// Log SDK configuration for debugging
if (isUsingOllama) {
+ const envRecord = finalEnv as Record
console.log('[Ollama Debug] SDK Configuration:', {
model: resolvedModel,
- baseUrl: finalEnv.ANTHROPIC_BASE_URL,
+ baseUrl: envRecord.ANTHROPIC_BASE_URL,
cwd: input.cwd,
configDir: isolatedConfigDir,
- hasAuthToken: !!finalEnv.ANTHROPIC_AUTH_TOKEN,
- tokenPreview: finalEnv.ANTHROPIC_AUTH_TOKEN?.slice(0, 10) + '...',
+ hasAuthToken: !!envRecord.ANTHROPIC_AUTH_TOKEN,
+ tokenPreview: envRecord.ANTHROPIC_AUTH_TOKEN?.slice(0, 10) + '...',
})
console.log('[Ollama Debug] Session settings:', {
resumeSessionId: resumeSessionId || 'none (first message)',
diff --git a/src/main/lib/trpc/routers/index.ts b/src/main/lib/trpc/routers/index.ts
index 833026a98..de79abcfa 100644
--- a/src/main/lib/trpc/routers/index.ts
+++ b/src/main/lib/trpc/routers/index.ts
@@ -18,6 +18,8 @@ import { commandsRouter } from "./commands"
import { voiceRouter } from "./voice"
import { pluginsRouter } from "./plugins"
import { createGitRouter } from "../../git"
+import { remoteAccessRouter } from "./remote-access"
+import { modelProfilesRouter } from "./model-profiles"
import { BrowserWindow } from "electron"
/**
@@ -46,6 +48,8 @@ export function createAppRouter(getWindow: () => BrowserWindow | null) {
plugins: pluginsRouter,
// Git operations - named "changes" to match Superset API
changes: createGitRouter(),
+ remoteAccess: remoteAccessRouter,
+ modelProfiles: modelProfilesRouter,
})
}
diff --git a/src/main/lib/trpc/routers/model-profiles.ts b/src/main/lib/trpc/routers/model-profiles.ts
new file mode 100644
index 000000000..5cc74c3da
--- /dev/null
+++ b/src/main/lib/trpc/routers/model-profiles.ts
@@ -0,0 +1,184 @@
+import { eq } from "drizzle-orm"
+import { z } from "zod"
+import { publicProcedure, router } from "../index"
+import {
+ modelProfiles,
+ modelProfileSettings,
+ getDatabase,
+ type NewModelProfile,
+} from "../../db"
+
+/**
+ * Model Profiles Router
+ * Provides CRUD operations for model profiles stored in the database.
+ * This allows syncing profiles between desktop and web app.
+ */
+
+// Schema for creating/updating a model profile
+const modelProfileInputSchema = z.object({
+ name: z.string().min(1, "Name is required"),
+ // CustomClaudeConfig structure
+ config: z.object({
+ model: z.string().optional(),
+ token: z.string().optional(), // Token can be empty for proxy configs
+ baseUrl: z.string().url("Base URL must be a valid URL"),
+ defaultOpusModel: z.string().optional(),
+ defaultSonnetModel: z.string().optional(),
+ defaultHaikuModel: z.string().optional(),
+ subagentModel: z.string().optional(),
+ }),
+ // ModelMapping array
+ models: z.array(z.object({
+ id: z.string(),
+ displayName: z.string(),
+ modelId: z.string(),
+ supportsThinking: z.boolean(),
+ })).optional(),
+ isOffline: z.boolean().optional().default(false),
+})
+
+export const modelProfilesRouter = router({
+ /**
+ * Get all model profiles
+ */
+ list: publicProcedure.query(async () => {
+ const db = getDatabase()
+ const profiles = await db.select().from(modelProfiles).orderBy(modelProfiles.createdAt)
+
+ // Parse JSON fields
+ return profiles.map((profile) => ({
+ ...profile,
+ config: JSON.parse(profile.config),
+ models: JSON.parse(profile.models),
+ }))
+ }),
+
+ /**
+ * Get a single model profile by ID
+ */
+ get: publicProcedure
+ .input(z.object({ id: z.string() }))
+ .query(async ({ input }) => {
+ const db = getDatabase()
+ const profile = await db.select().from(modelProfiles).where(eq(modelProfiles.id, input.id)).get()
+
+ if (!profile) {
+ throw new Error("Model profile not found")
+ }
+
+ return {
+ ...profile,
+ config: JSON.parse(profile.config),
+ models: JSON.parse(profile.models),
+ }
+ }),
+
+ /**
+ * Create a new model profile
+ */
+ create: publicProcedure
+ .input(modelProfileInputSchema)
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+
+ const newProfile: NewModelProfile = {
+ name: input.name,
+ config: JSON.stringify(input.config),
+ models: JSON.stringify(input.models || []),
+ isOffline: input.isOffline || false,
+ }
+
+ const result = await db.insert(modelProfiles).values(newProfile).returning()
+ const profile = result[0]
+
+ return {
+ ...profile,
+ config: JSON.parse(profile.config),
+ models: JSON.parse(profile.models),
+ }
+ }),
+
+ /**
+ * Update an existing model profile
+ */
+ update: publicProcedure
+ .input(z.object({
+ id: z.string(),
+ ...modelProfileInputSchema.partial(),
+ }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+
+ // Get existing profile
+ const existing = await db.select().from(modelProfiles).where(eq(modelProfiles.id, input.id)).get()
+ if (!existing) {
+ throw new Error("Model profile not found")
+ }
+
+ // Build update object
+ const update: Partial = {}
+ if (input.name !== undefined) update.name = input.name
+ if (input.config !== undefined) update.config = JSON.stringify(input.config)
+ if (input.models !== undefined) update.models = JSON.stringify(input.models)
+ if (input.isOffline !== undefined) update.isOffline = input.isOffline
+
+ const result = await db.update(modelProfiles)
+ .set(update)
+ .where(eq(modelProfiles.id, input.id))
+ .returning()
+
+ const profile = result[0]
+ return {
+ ...profile,
+ config: JSON.parse(profile.config),
+ models: JSON.parse(profile.models),
+ }
+ }),
+
+ /**
+ * Delete a model profile
+ */
+ delete: publicProcedure
+ .input(z.object({ id: z.string() }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+
+ await db.delete(modelProfiles).where(eq(modelProfiles.id, input.id))
+ return { success: true }
+ }),
+
+ /**
+ * Get or create model profile settings (last used profile, etc.)
+ */
+ getSettings: publicProcedure.query(async () => {
+ const db = getDatabase()
+ let settings = await db.select().from(modelProfileSettings).where(eq(modelProfileSettings.id, "singleton")).get()
+
+ // Create settings if they don't exist
+ if (!settings) {
+ const result = await db.insert(modelProfileSettings).values({}).returning()
+ settings = result[0]
+ }
+
+ return settings
+ }),
+
+ /**
+ * Update model profile settings (e.g., last used profile ID)
+ */
+ updateSettings: publicProcedure
+ .input(z.object({
+ lastUsedProfileId: z.string().optional(),
+ }))
+ .mutation(async ({ input }) => {
+ const db = getDatabase()
+
+ // Update settings
+ const result = await db.update(modelProfileSettings)
+ .set(input)
+ .where(eq(modelProfileSettings.id, "singleton"))
+ .returning()
+
+ return result[0]
+ }),
+})
diff --git a/src/main/lib/trpc/routers/remote-access.ts b/src/main/lib/trpc/routers/remote-access.ts
new file mode 100644
index 000000000..0f6514818
--- /dev/null
+++ b/src/main/lib/trpc/routers/remote-access.ts
@@ -0,0 +1,33 @@
+// src/main/lib/trpc/routers/remote-access.ts
+
+import { router, publicProcedure } from "../index"
+import {
+ enableRemoteAccess,
+ disableRemoteAccess,
+ getRemoteAccessStatus,
+} from "../../remote-access"
+
+export const remoteAccessRouter = router({
+ /**
+ * Get current remote access status
+ */
+ getStatus: publicProcedure.query(() => {
+ return getRemoteAccessStatus()
+ }),
+
+ /**
+ * Enable remote access
+ */
+ enable: publicProcedure.mutation(async () => {
+ await enableRemoteAccess()
+ return getRemoteAccessStatus()
+ }),
+
+ /**
+ * Disable remote access
+ */
+ disable: publicProcedure.mutation(async () => {
+ await disableRemoteAccess()
+ return getRemoteAccessStatus()
+ }),
+})
diff --git a/src/main/windows/main.ts b/src/main/windows/main.ts
index 36c88af32..9248eaba6 100644
--- a/src/main/windows/main.ts
+++ b/src/main/windows/main.ts
@@ -16,6 +16,7 @@ import { getAuthManager, handleAuthCode, getBaseUrl } from "../index"
import { registerGitWatcherIPC } from "../lib/git/watcher"
import { registerThemeScannerIPC } from "../lib/vscode-theme-scanner"
import { windowManager } from "./window-manager"
+import { subscribeToChatData } from "../lib/remote-access/ws-server"
// Helper to get window from IPC event
function getWindowFromEvent(
@@ -452,6 +453,58 @@ function registerIpcHandlers(): void {
// Register VS Code theme scanner IPC handlers
registerThemeScannerIPC()
+
+ // Remote access IPC handlers
+ ipcMain.handle("remote-access:get-status", async () => {
+ const { getRemoteAccessStatus } = await import("../lib/remote-access")
+ return getRemoteAccessStatus()
+ })
+
+ ipcMain.handle("remote-access:enable", async () => {
+ const { enableRemoteAccess, getRemoteAccessStatus } = await import("../lib/remote-access")
+ await enableRemoteAccess()
+ return getRemoteAccessStatus()
+ })
+
+ ipcMain.handle("remote-access:disable", async () => {
+ const { disableRemoteAccess, getRemoteAccessStatus } = await import("../lib/remote-access")
+ await disableRemoteAccess()
+ return getRemoteAccessStatus()
+ })
+
+ // Chat data sync - subscribe to broadcasts from WebSocket server
+ // This allows desktop clients to receive messages from web clients
+ const chatSubscriptions = new Map void>()
+
+ ipcMain.handle("chat:subscribe", (event, subChatId: string) => {
+ // Clean up existing subscription if any
+ const existing = chatSubscriptions.get(subChatId)
+ if (existing) {
+ existing()
+ chatSubscriptions.delete(subChatId)
+ }
+
+ const unsubscribe = subscribeToChatData(subChatId, (data: any) => {
+ // Send chat data to this renderer process
+ event.sender.send("chat:data", subChatId, data)
+ })
+
+ // Store subscription for later cleanup
+ chatSubscriptions.set(subChatId, unsubscribe)
+
+ // Return success (not the function - functions can't be cloned over IPC)
+ return { success: true, subChatId }
+ })
+
+ ipcMain.handle("chat:unsubscribe", (_event, subChatId: string) => {
+ const unsubscribe = chatSubscriptions.get(subChatId)
+ if (unsubscribe) {
+ unsubscribe()
+ chatSubscriptions.delete(subChatId)
+ return { success: true }
+ }
+ return { success: false, error: "No subscription found" }
+ })
}
/**
@@ -630,7 +683,26 @@ export function createWindow(options?: { chatId?: string; subChatId?: string }):
return { action: "deny" }
})
- // Handle window close
+ // Handle window close - hide to tray instead of closing
+ window.on("close", (event) => {
+ const isQuitting = (global as any).__isAppQuitting?.() || false
+ if (!isQuitting) {
+ event.preventDefault()
+ window.hide()
+ console.log(`[Main] Window ${window.id} hidden to tray`)
+
+ // On macOS, hide dock icon when all windows are hidden
+ if (process.platform === "darwin" && app.dock) {
+ const allWindows = BrowserWindow.getAllWindows()
+ const visibleWindows = allWindows.filter(w => w.isVisible() && !w.isDestroyed())
+ if (visibleWindows.length === 0) {
+ app.dock.hide()
+ console.log("[Main] Dock icon hidden")
+ }
+ }
+ }
+ })
+
window.on("closed", () => {
console.log(`[Main] Window ${window.id} closed`)
// windowManager handles cleanup via 'closed' event listener
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 5152745df..a3653bf6c 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -221,6 +221,40 @@ contextBridge.exposeInMainWorld("desktopApi", {
// VS Code theme scanning
scanVSCodeThemes: () => ipcRenderer.invoke("vscode:scan-themes"),
loadVSCodeTheme: (themePath: string) => ipcRenderer.invoke("vscode:load-theme", themePath),
+
+ // Remote access
+ getRemoteAccessStatus: () => ipcRenderer.invoke("remote-access:get-status"),
+ enableRemoteAccess: () => ipcRenderer.invoke("remote-access:enable"),
+ disableRemoteAccess: () => ipcRenderer.invoke("remote-access:disable"),
+ onRemoteAccessStatusChange: (callback: (status: any) => void) => {
+ const handler = (_event: unknown, status: any) => callback(status)
+ ipcRenderer.on("remote-access:status", handler)
+ return () => ipcRenderer.removeListener("remote-access:status", handler)
+ },
+
+ // Chat data sync - subscribe to broadcasts from WebSocket server
+ subscribeToChatData: (subChatId: string, callback: (data: any) => void) => {
+ const dataHandler = (_event: unknown, chatId: string, data: any) => {
+ if (chatId === subChatId) {
+ callback(data)
+ }
+ }
+ ipcRenderer.on("chat:data", dataHandler)
+
+ // Register subscription with main process
+ ipcRenderer.invoke("chat:subscribe", subChatId).catch((err) => {
+ console.error("[preload] Failed to subscribe to chat:", err)
+ })
+
+ // Return cleanup function
+ return () => {
+ ipcRenderer.removeListener("chat:data", dataHandler)
+ // Call unsubscribe IPC handler (functions can't be passed over IPC)
+ ipcRenderer.invoke("chat:unsubscribe", subChatId).catch((err) => {
+ console.error("[preload] Failed to unsubscribe from chat:", err)
+ })
+ }
+ },
})
// Type definitions
@@ -354,6 +388,18 @@ export interface DesktopApi {
// VS Code theme scanning
scanVSCodeThemes: () => Promise
loadVSCodeTheme: (themePath: string) => Promise
+ // Remote access
+ getRemoteAccessStatus: () => Promise<{
+ status: "disabled" | "downloading" | "starting" | "active" | "error"
+ progress?: number
+ url?: string
+ pin?: string
+ clients?: number
+ message?: string
+ }>
+ enableRemoteAccess: () => Promise
+ disableRemoteAccess: () => Promise
+ onRemoteAccessStatusChange: (callback: (status: any) => void) => () => void
}
declare global {
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index 9e3c90106..3d73e3179 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -1,33 +1,48 @@
-import { Provider as JotaiProvider, useAtomValue, useSetAtom } from "jotai"
-import { ThemeProvider, useTheme } from "next-themes"
-import { useEffect, useMemo } from "react"
-import { Toaster } from "sonner"
-import { TooltipProvider } from "./components/ui/tooltip"
-import { TRPCProvider } from "./contexts/TRPCProvider"
-import { WindowProvider, getInitialWindowParams } from "./contexts/WindowContext"
-import { selectedProjectAtom, selectedAgentChatIdAtom } from "./features/agents/atoms"
-import { useAgentSubChatStore } from "./features/agents/stores/sub-chat-store"
-import { AgentsLayout } from "./features/layout/agents-layout"
+import { Provider as JotaiProvider, useAtomValue, useSetAtom } from "jotai";
+import { ThemeProvider, useTheme } from "next-themes";
+import { useEffect, useMemo, useState } from "react";
+import { Toaster } from "sonner";
+import { TooltipProvider } from "./components/ui/tooltip";
+import { TRPCProvider } from "./contexts/TRPCProvider";
+import {
+ WindowProvider,
+ getInitialWindowParams,
+} from "./contexts/WindowContext";
+import {
+ selectedProjectAtom,
+ selectedAgentChatIdAtom,
+} from "./features/agents/atoms";
+import { useAgentSubChatStore } from "./features/agents/stores/sub-chat-store";
+import { AgentsLayout } from "./features/layout/agents-layout";
import {
AnthropicOnboardingPage,
ApiKeyOnboardingPage,
BillingMethodPage,
+ CliConfigDetectedPage,
SelectRepoPage,
-} from "./features/onboarding"
-import { identify, initAnalytics, shutdown } from "./lib/analytics"
+} from "./features/onboarding";
+import { identify, initAnalytics, shutdown } from "./lib/analytics";
import {
- anthropicOnboardingCompletedAtom, apiKeyOnboardingCompletedAtom,
- billingMethodAtom
-} from "./lib/atoms"
-import { appStore } from "./lib/jotai-store"
-import { VSCodeThemeProvider } from "./lib/themes/theme-provider"
-import { trpc } from "./lib/trpc"
+ anthropicOnboardingCompletedAtom,
+ apiKeyOnboardingCompletedAtom,
+ billingMethodAtom,
+ cliConfigDetectedShownAtom,
+} from "./lib/atoms";
+import { appStore } from "./lib/jotai-store";
+import { VSCodeThemeProvider } from "./lib/themes/theme-provider";
+import { useTrayEvents } from "./lib/tray-events";
+import { trpc } from "./lib/trpc";
+import { initRemoteTransport } from "./lib/remote-transport/init";
+import { isRemoteMode } from "./lib/remote-transport";
+import { ModelProfilesSync } from "./components/model-profiles-sync";
+import { ConnectionErrorOverlay } from "./components/ui/connection-error-overlay";
+import { useRemoteConnection } from "./lib/hooks/use-remote-connection";
/**
* Custom Toaster that adapts to theme
*/
function ThemedToaster() {
- const { resolvedTheme } = useTheme()
+ const { resolvedTheme } = useTheme();
return (
- )
+ );
}
/**
* Main content router - decides which page to show based on onboarding state
*/
function AppContent() {
- const billingMethod = useAtomValue(billingMethodAtom)
- const setBillingMethod = useSetAtom(billingMethodAtom)
+ const billingMethod = useAtomValue(billingMethodAtom);
+ const setBillingMethod = useSetAtom(billingMethodAtom);
const anthropicOnboardingCompleted = useAtomValue(
- anthropicOnboardingCompletedAtom
- )
- const setAnthropicOnboardingCompleted = useSetAtom(anthropicOnboardingCompletedAtom)
- const apiKeyOnboardingCompleted = useAtomValue(apiKeyOnboardingCompletedAtom)
- const setApiKeyOnboardingCompleted = useSetAtom(apiKeyOnboardingCompletedAtom)
- const selectedProject = useAtomValue(selectedProjectAtom)
- const setSelectedChatId = useSetAtom(selectedAgentChatIdAtom)
- const { setActiveSubChat, addToOpenSubChats, setChatId } = useAgentSubChatStore()
+ anthropicOnboardingCompletedAtom,
+ );
+ const setAnthropicOnboardingCompleted = useSetAtom(
+ anthropicOnboardingCompletedAtom,
+ );
+ const apiKeyOnboardingCompleted = useAtomValue(apiKeyOnboardingCompletedAtom);
+ const setApiKeyOnboardingCompleted = useSetAtom(
+ apiKeyOnboardingCompletedAtom,
+ );
+ const cliConfigDetectedShown = useAtomValue(cliConfigDetectedShownAtom);
+ const setCliConfigDetectedShown = useSetAtom(cliConfigDetectedShownAtom);
+ const selectedProject = useAtomValue(selectedProjectAtom);
+ const setSelectedChatId = useSetAtom(selectedAgentChatIdAtom);
+ const { setActiveSubChat, addToOpenSubChats, setChatId } =
+ useAgentSubChatStore();
// Apply initial window params (chatId/subChatId) when opening via "Open in new window"
useEffect(() => {
- const params = getInitialWindowParams()
+ const params = getInitialWindowParams();
if (params.chatId) {
- console.log("[App] Opening chat from window params:", params.chatId, params.subChatId)
- setSelectedChatId(params.chatId)
- setChatId(params.chatId)
+ console.log(
+ "[App] Opening chat from window params:",
+ params.chatId,
+ params.subChatId,
+ );
+ setSelectedChatId(params.chatId);
+ setChatId(params.chatId);
if (params.subChatId) {
- addToOpenSubChats(params.subChatId)
- setActiveSubChat(params.subChatId)
+ addToOpenSubChats(params.subChatId);
+ setActiveSubChat(params.subChatId);
}
}
- }, [setSelectedChatId, setChatId, addToOpenSubChats, setActiveSubChat])
+ }, [setSelectedChatId, setChatId, addToOpenSubChats, setActiveSubChat]);
// Check if user has existing CLI config (API key or proxy)
// Based on PR #29 by @sa4hnd
const { data: cliConfig, isLoading: isLoadingCliConfig } =
- trpc.claudeCode.hasExistingCliConfig.useQuery()
+ trpc.claudeCode.hasExistingCliConfig.useQuery();
// Migration: If user already completed Anthropic onboarding but has no billing method set,
// automatically set it to "claude-subscription" (legacy users before billing method was added)
useEffect(() => {
if (!billingMethod && anthropicOnboardingCompleted) {
- setBillingMethod("claude-subscription")
+ setBillingMethod("claude-subscription");
}
- }, [billingMethod, anthropicOnboardingCompleted, setBillingMethod])
+ }, [billingMethod, anthropicOnboardingCompleted, setBillingMethod]);
// Auto-skip onboarding if user has existing CLI config (API key or proxy)
// This allows users with ANTHROPIC_API_KEY to use the app without OAuth
+ // Only auto-skip if the user has already seen the CLI config detected page
useEffect(() => {
- if (cliConfig?.hasConfig && !billingMethod) {
- console.log("[App] Detected existing CLI config, auto-completing onboarding")
- setBillingMethod("api-key")
- setApiKeyOnboardingCompleted(true)
+ if (cliConfig?.hasConfig && !billingMethod && cliConfigDetectedShown) {
+ console.log(
+ "[App] Detected existing CLI config, auto-completing onboarding",
+ );
+ setBillingMethod("api-key");
+ setApiKeyOnboardingCompleted(true);
}
- }, [cliConfig?.hasConfig, billingMethod, setBillingMethod, setApiKeyOnboardingCompleted])
+ }, [
+ cliConfig?.hasConfig,
+ billingMethod,
+ cliConfigDetectedShown,
+ setBillingMethod,
+ setApiKeyOnboardingCompleted,
+ ]);
// Fetch projects to validate selectedProject exists
const { data: projects, isLoading: isLoadingProjects } =
- trpc.projects.list.useQuery()
+ trpc.projects.list.useQuery();
+
+ const isRemote = useMemo(() => isRemoteMode(), []);
// Validated project - only valid if exists in DB
const validatedProject = useMemo(() => {
- if (!selectedProject) return null
+ if (!selectedProject) return null;
// While loading, trust localStorage value to prevent flicker
- if (isLoadingProjects) return selectedProject
+ if (isLoadingProjects) return selectedProject;
// After loading, validate against DB
- if (!projects) return null
- const exists = projects.some((p) => p.id === selectedProject.id)
- return exists ? selectedProject : null
- }, [selectedProject, projects, isLoadingProjects])
+ if (!projects) return null;
+ const exists = projects.some((p) => p.id === selectedProject.id);
+ return exists ? selectedProject : null;
+ }, [selectedProject, projects, isLoadingProjects]);
+
+ // If in remote mode, bypass all onboarding and go straight to layout
+ // The desktop app handles auth/config, and we can select projects from the sidebar
+ if (isRemote) {
+ return ;
+ }
// Determine which page to show:
- // 1. No billing method selected -> BillingMethodPage
+ // 1. No billing method selected -> BillingMethodPage OR CliConfigDetectedPage
// 2. Claude subscription selected but not completed -> AnthropicOnboardingPage
// 3. API key or custom model selected but not completed -> ApiKeyOnboardingPage
// 4. No valid project selected -> SelectRepoPage
// 5. Otherwise -> AgentsLayout
+
+ // Show CLI config detected page if config exists and user hasn't seen it yet
+ if (
+ !billingMethod &&
+ cliConfig?.hasConfig &&
+ !cliConfigDetectedShown &&
+ !isLoadingCliConfig
+ ) {
+ return ;
+ }
+
if (!billingMethod) {
- return
+ return ;
}
- if (billingMethod === "claude-subscription" && !anthropicOnboardingCompleted) {
- return
+ if (
+ billingMethod === "claude-subscription" &&
+ !anthropicOnboardingCompleted
+ ) {
+ return ;
}
if (
(billingMethod === "api-key" || billingMethod === "custom-model") &&
!apiKeyOnboardingCompleted
) {
- return
+ return ;
}
if (!validatedProject && !isLoadingProjects) {
- return
+ return ;
}
- return
+ return ;
}
export function App() {
+ const [remoteReady, setRemoteReady] = useState(!isRemoteMode());
+
+ // Track remote connection state (for connection error overlay)
+ useRemoteConnection();
+
+ // Initialize remote transport if running in browser
+ useEffect(() => {
+ if (isRemoteMode()) {
+ initRemoteTransport().then((success) => {
+ if (success) {
+ setRemoteReady(true);
+ }
+ });
+ }
+ }, []);
+
+ // Listen for tray events (settings, workspace selection)
+ useTrayEvents();
+
// Initialize analytics on mount
useEffect(() => {
- initAnalytics()
+ initAnalytics();
// Sync analytics opt-out status to main process
const syncOptOutStatus = async () => {
try {
const optOut =
- localStorage.getItem("preferences:analytics-opt-out") === "true"
- await window.desktopApi?.setAnalyticsOptOut(optOut)
+ localStorage.getItem("preferences:analytics-opt-out") === "true";
+ await window.desktopApi?.setAnalyticsOptOut(optOut);
} catch (error) {
- console.warn("[Analytics] Failed to sync opt-out status:", error)
+ console.warn("[Analytics] Failed to sync opt-out status:", error);
}
- }
- syncOptOutStatus()
+ };
+ syncOptOutStatus();
// Identify user if already authenticated
const identifyUser = async () => {
try {
- const user = await window.desktopApi?.getUser()
+ const user = await window.desktopApi?.getUser();
if (user?.id) {
- identify(user.id, { email: user.email, name: user.name })
+ identify(user.id, { email: user.email, name: user.name });
}
} catch (error) {
- console.warn("[Analytics] Failed to identify user:", error)
+ console.warn("[Analytics] Failed to identify user:", error);
}
- }
- identifyUser()
+ };
+ identifyUser();
// Cleanup on unmount
return () => {
- shutdown()
- }
- }, [])
+ shutdown();
+ };
+ }, []);
+
+ // Show loading while remote transport initializes
+ if (!remoteReady) {
+ return (
+
+ );
+ }
return (
@@ -177,6 +262,8 @@ export function App() {
+
+
- )
+ );
}
diff --git a/src/renderer/components/dialogs/remote-access-dialog.tsx b/src/renderer/components/dialogs/remote-access-dialog.tsx
new file mode 100644
index 000000000..7702d2355
--- /dev/null
+++ b/src/renderer/components/dialogs/remote-access-dialog.tsx
@@ -0,0 +1,165 @@
+// src/renderer/components/dialogs/remote-access-dialog.tsx
+
+import { useAtom } from "jotai"
+import { useEffect, useCallback } from "react"
+import { Globe, Copy, Loader2, X } from "lucide-react"
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
+import { Button } from "../ui/button"
+import {
+ remoteAccessStatusAtom,
+ remoteAccessDialogOpenAtom,
+ type RemoteAccessStatus,
+} from "../../lib/atoms/remote-access"
+import { toast } from "sonner"
+
+export function RemoteAccessDialog() {
+ const [open, setOpen] = useAtom(remoteAccessDialogOpenAtom)
+ const [status, setStatus] = useAtom(remoteAccessStatusAtom)
+
+ // Fetch initial status and subscribe to changes
+ useEffect(() => {
+ if (!window.desktopApi) return
+
+ // Get initial status
+ window.desktopApi.getRemoteAccessStatus?.().then(setStatus)
+
+ // Subscribe to status changes
+ const unsubscribe = window.desktopApi.onRemoteAccessStatusChange?.(setStatus)
+ return unsubscribe
+ }, [setStatus])
+
+ const handleEnable = useCallback(async () => {
+ try {
+ await window.desktopApi?.enableRemoteAccess?.()
+ } catch (error) {
+ toast.error("Failed to enable remote access")
+ }
+ }, [])
+
+ const handleDisable = useCallback(async () => {
+ try {
+ await window.desktopApi?.disableRemoteAccess?.()
+ } catch (error) {
+ toast.error("Failed to disable remote access")
+ }
+ }, [])
+
+ const handleCopyUrl = useCallback(() => {
+ if (status.status === "active") {
+ navigator.clipboard.writeText(status.url)
+ toast.success("URL copied to clipboard")
+ }
+ }, [status])
+
+ const handleCopyPin = useCallback(() => {
+ if (status.status === "active") {
+ navigator.clipboard.writeText(status.pin)
+ toast.success("PIN copied to clipboard")
+ }
+ }, [status])
+
+ return (
+
+
+
+
+
+ Remote Access
+
+
+
+
+ {status.status === "disabled" && (
+ <>
+
+ Access your desktop from any browser.
+
+
+ Enable Remote Access
+
+ >
+ )}
+
+ {status.status === "downloading" && (
+
+
+
+ Downloading cloudflared...
+
+
+
+ )}
+
+ {status.status === "starting" && (
+
+
+ Starting tunnel...
+
+ )}
+
+ {status.status === "active" && (
+ <>
+
+
+
+
URL
+
+
+ {status.url}
+
+
+
+
+
+
+
+
+
PIN
+
+
+ {status.pin}
+
+
+
+
+
+
+
+
+ Connected clients: {status.clients}
+
+
+
+ Disable Remote Access
+
+ >
+ )}
+
+ {status.status === "error" && (
+ <>
+
+
+ {status.message}
+
+
+ Try Again
+
+ >
+ )}
+
+
+
+ )
+}
diff --git a/src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx b/src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx
index 66ffebb4b..4180f4b04 100644
--- a/src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx
+++ b/src/renderer/components/dialogs/settings-tabs/agents-models-tab.tsx
@@ -1,13 +1,11 @@
import { useAtom, useSetAtom } from "jotai"
import { MoreHorizontal, Plus } from "lucide-react"
-import { useCallback, useEffect, useRef, useState } from "react"
+import { useEffect, useState } from "react"
import { toast } from "sonner"
import {
agentsSettingsDialogOpenAtom,
anthropicOnboardingCompletedAtom,
- customClaudeConfigAtom,
openaiApiKeyAtom,
- type CustomClaudeConfig,
} from "../../../lib/atoms"
import { trpc } from "../../../lib/trpc"
import { Badge } from "../../ui/badge"
@@ -20,6 +18,7 @@ import {
} from "../../ui/dropdown-menu"
import { Input } from "../../ui/input"
import { Label } from "../../ui/label"
+import { ModelProfilesSection } from "./model-profiles-section"
// Hook to detect narrow screen
function useIsNarrowScreen(): boolean {
@@ -38,12 +37,6 @@ function useIsNarrowScreen(): boolean {
return isNarrow
}
-const EMPTY_CONFIG: CustomClaudeConfig = {
- model: "",
- token: "",
- baseUrl: "",
-}
-
// Account row component
function AccountRow({
account,
@@ -247,10 +240,6 @@ function AnthropicAccountsSection() {
}
export function AgentsModelsTab() {
- const [storedConfig, setStoredConfig] = useAtom(customClaudeConfigAtom)
- const [model, setModel] = useState(storedConfig.model)
- const [baseUrl, setBaseUrl] = useState(storedConfig.baseUrl)
- const [token, setToken] = useState(storedConfig.token)
const setAnthropicOnboardingCompleted = useSetAtom(
anthropicOnboardingCompletedAtom,
)
@@ -266,58 +255,10 @@ export function AgentsModelsTab() {
const setOpenAIKeyMutation = trpc.voice.setOpenAIKey.useMutation()
const trpcUtils = trpc.useUtils()
- useEffect(() => {
- setModel(storedConfig.model)
- setBaseUrl(storedConfig.baseUrl)
- setToken(storedConfig.token)
- }, [storedConfig.model, storedConfig.baseUrl, storedConfig.token])
-
useEffect(() => {
setOpenaiKey(storedOpenAIKey)
}, [storedOpenAIKey])
- const savedConfigRef = useRef(storedConfig)
-
- const handleBlurSave = useCallback(() => {
- const trimmedModel = model.trim()
- const trimmedBaseUrl = baseUrl.trim()
- const trimmedToken = token.trim()
-
- // Only save if all fields are filled
- if (trimmedModel && trimmedBaseUrl && trimmedToken) {
- const next: CustomClaudeConfig = {
- model: trimmedModel,
- token: trimmedToken,
- baseUrl: trimmedBaseUrl,
- }
- if (
- next.model !== savedConfigRef.current.model ||
- next.token !== savedConfigRef.current.token ||
- next.baseUrl !== savedConfigRef.current.baseUrl
- ) {
- setStoredConfig(next)
- savedConfigRef.current = next
- }
- } else if (!trimmedModel && !trimmedBaseUrl && !trimmedToken) {
- // All cleared — reset
- if (savedConfigRef.current.model || savedConfigRef.current.token || savedConfigRef.current.baseUrl) {
- setStoredConfig(EMPTY_CONFIG)
- savedConfigRef.current = EMPTY_CONFIG
- }
- }
- }, [model, baseUrl, token, setStoredConfig])
-
- const handleReset = () => {
- setStoredConfig(EMPTY_CONFIG)
- savedConfigRef.current = EMPTY_CONFIG
- setModel("")
- setBaseUrl("")
- setToken("")
- toast.success("Model settings reset")
- }
-
- const canReset = Boolean(model.trim() || baseUrl.trim() || token.trim())
-
const handleClaudeCodeSetup = () => {
// Don't disconnect - just open onboarding to add a new account
// The previous code was calling disconnectClaudeCode.mutate() which
@@ -398,74 +339,20 @@ export function AgentsModelsTab() {
+ {/* Model Profiles */}
-
- Override Model
-
- {canReset && (
-
- Reset
-
- )}
-
-
-
-
-
Model name
-
- Model identifier to use for requests
-
-
-
- setModel(e.target.value)}
- onBlur={handleBlurSave}
- className="w-full"
- placeholder="claude-3-7-sonnet-20250219"
- />
-
-
-
-
-
-
API token
-
- ANTHROPIC_AUTH_TOKEN env
-
-
-
- setToken(e.target.value)}
- onBlur={handleBlurSave}
- className="w-full"
- placeholder="sk-ant-..."
- />
-
-
-
-
-
-
Base URL
-
- ANTHROPIC_BASE_URL env
-
-
-
- setBaseUrl(e.target.value)}
- onBlur={handleBlurSave}
- className="w-full"
- placeholder="https://api.anthropic.com"
- />
-
+
+
+ Model Profiles
+
+
+ Manage your proxy configurations
+
-
+
+
{/* OpenAI API Key for Voice Input */}
diff --git a/src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx b/src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx
new file mode 100644
index 000000000..c7644d497
--- /dev/null
+++ b/src/renderer/components/dialogs/settings-tabs/model-profile-form.tsx
@@ -0,0 +1,201 @@
+import { X } from "lucide-react"
+import { useState } from "react"
+import { type CustomClaudeConfig, type ModelProfile } from "../../../lib/atoms"
+import { Button } from "../../ui/button"
+import { Input } from "../../ui/input"
+import { Label } from "../../ui/label"
+
+const EMPTY_CONFIG: CustomClaudeConfig = {
+ model: "",
+ token: "",
+ baseUrl: "",
+ defaultOpusModel: "",
+ defaultSonnetModel: "",
+ defaultHaikuModel: "",
+ subagentModel: "",
+}
+
+interface ProfileFormProps {
+ mode: "add" | "edit"
+ existingProfile?: ModelProfile
+ onSave: (profile: Omit
) => void
+ onCancel: () => void
+}
+
+export function ModelProfileForm({
+ mode,
+ existingProfile,
+ onSave,
+ onCancel,
+}: ProfileFormProps) {
+ const [name, setName] = useState(existingProfile?.name || "")
+ const [model, setModel] = useState(existingProfile?.config.model || "")
+ const [token, setToken] = useState(existingProfile?.config.token || "")
+ const [baseUrl, setBaseUrl] = useState(existingProfile?.config.baseUrl || "")
+ const [defaultOpusModel, setDefaultOpusModel] = useState(existingProfile?.config.defaultOpusModel || "")
+ const [defaultSonnetModel, setDefaultSonnetModel] = useState(existingProfile?.config.defaultSonnetModel || "")
+ const [defaultHaikuModel, setDefaultHaikuModel] = useState(existingProfile?.config.defaultHaikuModel || "")
+ const [subagentModel, setSubagentModel] = useState(existingProfile?.config.subagentModel || "")
+
+ const isValid = name.trim() && model.trim() && token.trim() && baseUrl.trim()
+
+ const handleSave = () => {
+ if (!isValid) return
+
+ onSave({
+ name: name.trim(),
+ config: {
+ model: model.trim(),
+ token: token.trim(),
+ baseUrl: baseUrl.trim(),
+ defaultOpusModel: defaultOpusModel.trim() || undefined,
+ defaultSonnetModel: defaultSonnetModel.trim() || undefined,
+ defaultHaikuModel: defaultHaikuModel.trim() || undefined,
+ subagentModel: subagentModel.trim() || undefined,
+ },
+ })
+ }
+
+ return (
+
+
+
+ {mode === "add" ? "Add Profile" : "Edit Profile"}
+
+
+
+
+
+
+
+
+
+ Profile Name *
+
+ setName(e.target.value)}
+ placeholder="e.g., Local Proxy, Cloud Gateway"
+ className="h-8"
+ />
+
+
+
+
+ Base URL *
+
+ setBaseUrl(e.target.value)}
+ placeholder="https://api.anthropic.com"
+ className="h-8"
+ />
+
+
+
+
+ API Token *
+
+ setToken(e.target.value)}
+ placeholder="sk-ant-..."
+ className="h-8"
+ />
+
+
+
+
+ Default Model *
+
+ setModel(e.target.value)}
+ placeholder="claude-sonnet-4-5-thinking"
+ className="h-8"
+ />
+
+
+
+
+ Additional Model Configuration (Optional)
+
+
+
+ {/* 2-column grid for optional model fields */}
+
+
+
+ {mode === "add" ? "Add Profile" : "Save Changes"}
+
+
+
+ )
+}
diff --git a/src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx b/src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx
new file mode 100644
index 000000000..d2f2cd3c3
--- /dev/null
+++ b/src/renderer/components/dialogs/settings-tabs/model-profile-row.tsx
@@ -0,0 +1,104 @@
+import { Check, MoreHorizontal, Pencil, Trash2 } from "lucide-react"
+import { type ModelProfile } from "../../../lib/atoms"
+import { Badge } from "../../ui/badge"
+import { Button } from "../../ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "../../ui/dropdown-menu"
+
+interface ProfileRowProps {
+ profile: ModelProfile
+ isActive: boolean
+ onActivate: () => void
+ onEdit: () => void
+ onDelete: () => void
+}
+
+export function ModelProfileRow({
+ profile,
+ isActive,
+ onActivate,
+ onEdit,
+ onDelete,
+}: ProfileRowProps) {
+ return (
+
+
+
+
+ {profile.name}
+ {isActive && (
+
+ Active
+
+ )}
+
+
+ Model: {profile.config.model || "Not set"}
+
+
+ Proxy: {profile.config.baseUrl || "Default (api.anthropic.com)"}
+
+ {(profile.config.defaultOpusModel ||
+ profile.config.defaultSonnetModel ||
+ profile.config.defaultHaikuModel ||
+ profile.config.subagentModel) && (
+
+ {profile.config.defaultOpusModel && (
+
+ OPUS
+
+ )}
+ {profile.config.defaultSonnetModel && (
+
+ SONNET
+
+ )}
+ {profile.config.defaultHaikuModel && (
+
+ HAIKU
+
+ )}
+ {profile.config.subagentModel && (
+
+ SUBAGENT
+
+ )}
+
+ )}
+
+
+
+
+ {!isActive && (
+
+ Use
+
+ )}
+
+
+
+
+
+
+
+
+
+ Edit
+
+
+
+ Delete
+
+
+
+
+
+ )
+}
diff --git a/src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx b/src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx
new file mode 100644
index 000000000..0a20caa13
--- /dev/null
+++ b/src/renderer/components/dialogs/settings-tabs/model-profiles-section.tsx
@@ -0,0 +1,144 @@
+import { useAtom } from "jotai"
+import { Plus } from "lucide-react"
+import { useState } from "react"
+import { toast } from "sonner"
+import {
+ activeProfileIdAtom,
+ modelProfilesAtom,
+ type ModelProfile,
+} from "../../../lib/atoms"
+import { Button } from "../../ui/button"
+import { ModelProfileForm } from "./model-profile-form"
+import { ModelProfileRow } from "./model-profile-row"
+
+type FormState = {
+ mode: "add" | "edit" | null
+ editingProfile?: ModelProfile
+}
+
+export function ModelProfilesSection() {
+ const [profiles, setProfiles] = useAtom(modelProfilesAtom)
+ const [activeProfileId, setActiveProfileId] = useAtom(activeProfileIdAtom)
+ const [formState, setFormState] = useState({ mode: null })
+
+ // Filter out offline profile from display
+ const customProfiles = profiles.filter((p) => !p.isOffline)
+
+ const handleAddProfile = (profileData: Omit) => {
+ const newProfile: ModelProfile = {
+ ...profileData,
+ id: crypto.randomUUID(),
+ }
+
+ setProfiles([...profiles, newProfile])
+
+ // If this is the first custom profile, auto-activate it
+ if (customProfiles.length === 0) {
+ setActiveProfileId(newProfile.id)
+ }
+
+ setFormState({ mode: null })
+ toast.success("Profile added")
+ }
+
+ const handleEditProfile = (profileData: Omit) => {
+ if (!formState.editingProfile) return
+
+ setProfiles(
+ profiles.map((p) =>
+ p.id === formState.editingProfile!.id
+ ? { ...profileData, id: p.id }
+ : p,
+ ),
+ )
+
+ setFormState({ mode: null })
+ toast.success("Profile updated")
+ }
+
+ const handleDeleteProfile = (profileId: string) => {
+ const confirmed = window.confirm(
+ "Are you sure you want to delete this profile?",
+ )
+ if (!confirmed) return
+
+ const newProfiles = profiles.filter((p) => p.id !== profileId)
+ setProfiles(newProfiles)
+
+ // If we deleted the active profile, clear the active ID
+ if (activeProfileId === profileId) {
+ const remainingCustom = newProfiles.filter((p) => !p.isOffline)
+ setActiveProfileId(remainingCustom[0]?.id || null)
+ }
+
+ toast.success("Profile deleted")
+ }
+
+ const handleStartEdit = (profile: ModelProfile) => {
+ setFormState({ mode: "edit", editingProfile: profile })
+ }
+
+ const handleCancelForm = () => {
+ setFormState({ mode: null })
+ }
+
+ return (
+
+ {customProfiles.length === 0 && !formState.mode ? (
+
+
+ No custom profiles yet. Add your first proxy configuration.
+
+
setFormState({ mode: "add" })}
+ >
+
+ Add Profile
+
+
+ ) : (
+ <>
+ {customProfiles.map((profile) => (
+
setActiveProfileId(profile.id)}
+ onEdit={() => handleStartEdit(profile)}
+ onDelete={() => handleDeleteProfile(profile.id)}
+ />
+ ))}
+
+ {formState.mode && (
+
+ )}
+
+ {!formState.mode && (
+
+
setFormState({ mode: "add" })}
+ >
+
+ Add Profile
+
+
+ )}
+ >
+ )}
+
+ )
+}
diff --git a/src/renderer/components/model-profiles-sync.tsx b/src/renderer/components/model-profiles-sync.tsx
new file mode 100644
index 000000000..688d118b5
--- /dev/null
+++ b/src/renderer/components/model-profiles-sync.tsx
@@ -0,0 +1,13 @@
+import { useEffect } from "react"
+import { useSyncModelProfiles } from "../lib/atoms/model-profiles-sync"
+
+/**
+ * Component that initializes model profiles sync between localStorage and database.
+ * Should be rendered once in the app root.
+ */
+export function ModelProfilesSync() {
+ const { isLoading } = useSyncModelProfiles()
+
+ // This component doesn't render anything - it just handles the sync
+ return null
+}
diff --git a/src/renderer/components/ui/connection-error-overlay.tsx b/src/renderer/components/ui/connection-error-overlay.tsx
new file mode 100644
index 000000000..b6527cd93
--- /dev/null
+++ b/src/renderer/components/ui/connection-error-overlay.tsx
@@ -0,0 +1,54 @@
+// src/renderer/components/ui/connection-error-overlay.tsx
+
+import { useMemo } from "react"
+import { useAtomValue } from "jotai"
+import { remoteConnectionStateAtom } from "../../lib/atoms/remote-access"
+import { WifiOff, RefreshCw } from "lucide-react"
+import { cn } from "../../lib/utils"
+
+export function ConnectionErrorOverlay() {
+ const connectionState = useAtomValue(remoteConnectionStateAtom)
+
+ // Only show overlay in disconnected state
+ const isVisible = connectionState === "disconnected"
+
+ // Don't render if not visible (save DOM nodes)
+ if (!isVisible) return null
+
+ return (
+
+
+ {/* Icon */}
+
+
+
+
+ {/* Text */}
+
+
Connection Lost
+
+ Unable to connect to the desktop app. Please make sure the desktop app is running and remote access is enabled.
+
+
+
+ {/* Status */}
+
+
+ {/* Refresh button */}
+
window.location.reload()}
+ className="inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors text-sm font-medium"
+ >
+
+ Reconnect
+
+
+
+ )
+}
diff --git a/src/renderer/contexts/TRPCProvider.tsx b/src/renderer/contexts/TRPCProvider.tsx
index f3d11be44..a0916b186 100644
--- a/src/renderer/contexts/TRPCProvider.tsx
+++ b/src/renderer/contexts/TRPCProvider.tsx
@@ -1,8 +1,8 @@
-import { useState } from "react"
+import { useState, useEffect } from "react"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
-import { ipcLink } from "trpc-electron/renderer"
import { trpc } from "../lib/trpc"
import superjson from "superjson"
+import { wsLink } from "../lib/remote-transport/ws-link"
interface TRPCProviderProps {
children: React.ReactNode
@@ -15,6 +15,15 @@ export function getQueryClient(): QueryClient | null {
return globalQueryClient
}
+/**
+ * Check if we're in remote/browser mode (no electronTRPC global)
+ * Must be called at runtime, not at module load time
+ */
+function checkIsRemoteMode(): boolean {
+ // @ts-ignore
+ return typeof window !== "undefined" && !window.electronTRPC
+}
+
export function TRPCProvider({ children }: TRPCProviderProps) {
const [queryClient] = useState(() => {
const client = new QueryClient({
@@ -35,15 +44,75 @@ export function TRPCProvider({ children }: TRPCProviderProps) {
return client
})
- const [trpcClient] = useState(() => {
- const client = trpc.createClient({
- links: [ipcLink({ transformer: superjson })],
- })
- return client
+ // Combine loading and client state to ensure atomic updates
+ const [state, setState] = useState<{
+ isLoading: boolean
+ trpcClient: ReturnType | null
+ }>({
+ isLoading: true,
+ trpcClient: null,
})
+ // Log render state
+ console.log("[TRPCProvider] Render - isLoading:", state.isLoading, "trpcClient:", state.trpcClient)
+
+ useEffect(() => {
+ let mounted = true
+
+ async function initClient() {
+ try {
+ let link: any
+
+ // Check at runtime to ensure electronTRPC has been set by preload
+ const isRemote = checkIsRemoteMode()
+ console.log("[TRPCProvider] isRemote:", isRemote, "electronTRPC:", !!(window as any).electronTRPC)
+
+ if (isRemote) {
+ // Browser mode - use WebSocket link
+ link = wsLink()
+ } else {
+ // Electron mode - dynamically import ipcLink
+ console.log("[TRPCProvider] Loading ipcLink...")
+ const module = await import("trpc-electron/renderer")
+ console.log("[TRPCProvider] ipcLink module loaded:", module)
+ link = module.ipcLink({ transformer: superjson })
+ console.log("[TRPCProvider] ipcLink created:", link)
+ }
+
+ console.log("[TRPCProvider] Creating trpc client...")
+ const client = trpc.createClient({ links: [link] })
+ console.log("[TRPCProvider] Client created:", client)
+
+ if (mounted) {
+ setState({ isLoading: false, trpcClient: client })
+ console.log("[TRPCProvider] State updated with client")
+ }
+ } catch (error) {
+ console.error("[TRPCProvider] Error initializing client:", error)
+ if (mounted) {
+ setState((prev) => ({ ...prev, isLoading: false }))
+ }
+ }
+ }
+
+ initClient()
+
+ return () => {
+ mounted = false
+ }
+ }, [])
+
+ // Show loading state while initializing client
+ if (state.isLoading || !state.trpcClient) {
+ return (
+
+ )
+ }
+
return (
-
+
{children}
)
diff --git a/src/renderer/features/agents/atoms/index.ts b/src/renderer/features/agents/atoms/index.ts
index 6a6ed4c88..2bd2547fe 100644
--- a/src/renderer/features/agents/atoms/index.ts
+++ b/src/renderer/features/agents/atoms/index.ts
@@ -238,7 +238,13 @@ export const subChatModeAtomFamily = atomFamily((subChatId: string) =>
)
// Model ID to full Claude model string mapping
+// When a custom profile is active, these map to the profile's model settings:
+// - default: uses profile.model (ANTHROPIC_MODEL)
+// - opus: uses profile.defaultOpusModel (ANTHROPIC_DEFAULT_OPUS_MODEL)
+// - sonnet: uses profile.defaultSonnetModel (ANTHROPIC_DEFAULT_SONNET_MODEL)
+// - haiku: uses profile.defaultHaikuModel (ANTHROPIC_DEFAULT_HAIKU_MODEL)
export const MODEL_ID_MAP: Record = {
+ default: "default",
opus: "opus",
sonnet: "sonnet",
haiku: "haiku",
diff --git a/src/renderer/features/agents/lib/ipc-chat-transport.ts b/src/renderer/features/agents/lib/ipc-chat-transport.ts
index 9a36a7544..05084958e 100644
--- a/src/renderer/features/agents/lib/ipc-chat-transport.ts
+++ b/src/renderer/features/agents/lib/ipc-chat-transport.ts
@@ -2,14 +2,15 @@ import * as Sentry from "@sentry/electron/renderer"
import type { ChatTransport, UIMessage } from "ai"
import { toast } from "sonner"
import {
+ activeConfigAtom,
agentsLoginModalOpenAtom,
autoOfflineModeAtom,
- type CustomClaudeConfig,
- customClaudeConfigAtom,
enableTasksAtom,
extendedThinkingEnabledAtom,
historyEnabledAtom,
- normalizeCustomClaudeConfig,
+ modelProfilesAtom,
+ lastUsedProfileIdAtom,
+ selectedProfileModelIdAtom,
selectedOllamaModelAtom,
sessionInfoAtom,
showOfflineModeFeaturesAtom,
@@ -170,13 +171,63 @@ export class IPCChatTransport implements ChatTransport {
const enableTasks = appStore.get(enableTasksAtom)
// Read model selection dynamically (so model changes apply to existing chats)
- const selectedModelId = appStore.get(lastSelectedModelIdAtom)
- const modelString = MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"]
+ // First try to get model from profile system, then fall back to legacy MODEL_ID_MAP
+ const profiles = appStore.get(modelProfilesAtom)
+ const lastUsedProfileId = appStore.get(lastUsedProfileIdAtom)
+ const selectedProfileModelId = appStore.get(selectedProfileModelIdAtom)
+
+ // Find the effective profile
+ const effectiveProfile = lastUsedProfileId
+ ? profiles.find(p => p.id === lastUsedProfileId)
+ : profiles.filter(p => !p.isOffline)[0]
+
+ // Resolve model from profile config based on selected model ID
+ let modelString: string | undefined
+ if (effectiveProfile?.config) {
+ const config = effectiveProfile.config
+ switch (selectedProfileModelId) {
+ case "main":
+ modelString = config.model
+ break
+ case "opus":
+ modelString = config.defaultOpusModel
+ break
+ case "sonnet":
+ modelString = config.defaultSonnetModel
+ break
+ case "haiku":
+ modelString = config.defaultHaikuModel
+ break
+ case "subagent":
+ modelString = config.subagentModel
+ break
+ default:
+ // Default to main model if no specific selection
+ modelString = config.model
+ }
+ }
- const storedCustomConfig = appStore.get(
- customClaudeConfigAtom,
- ) as CustomClaudeConfig
- const customConfig = normalizeCustomClaudeConfig(storedCustomConfig)
+ // Fall back to legacy model selection if no profile model found
+ if (!modelString) {
+ const selectedModelId = appStore.get(lastSelectedModelIdAtom)
+ modelString = MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"]
+ }
+
+ console.log(`[IPC Transport] Using model: ${modelString} (profile: ${effectiveProfile?.name || 'none'}, selectedProfileModelId: ${selectedProfileModelId})`)
+
+ // Read active profile config (from profile system, supports multiple profiles)
+ // Override the model in customConfig with the selected model from profile
+ let baseCustomConfig: any
+ try {
+ baseCustomConfig = appStore.get(activeConfigAtom)
+ console.log('[IPC Transport] baseCustomConfig:', baseCustomConfig ? 'FOUND' : 'NOT FOUND', baseCustomConfig)
+ } catch (error) {
+ console.error('[IPC Transport] ERROR getting activeConfigAtom:', error)
+ baseCustomConfig = undefined
+ }
+ const customConfig = baseCustomConfig && modelString
+ ? { ...baseCustomConfig, model: modelString }
+ : baseCustomConfig
// Get selected Ollama model for offline mode
const selectedOllamaModel = appStore.get(selectedOllamaModelAtom)
@@ -463,10 +514,47 @@ export class IPCChatTransport implements ChatTransport {
},
)
+ // Also subscribe to broadcasts from WebSocket server (for sync with web clients)
+ // This allows desktop to receive messages sent from web clients
+ let broadcastUnsubscribe: (() => void) | null = null
+ if (window.desktopApi?.subscribeToChatData) {
+ try {
+ broadcastUnsubscribe = window.desktopApi.subscribeToChatData(
+ this.config.subChatId,
+ (data: any) => {
+ // Received broadcast from web client, forward to controller
+ console.log(`[SD] Broadcast from web:`, data.type || "no-type")
+ if (data.type === "complete") {
+ try {
+ controller.close()
+ } catch {
+ // Already closed
+ }
+ } else if (data.type === "error") {
+ controller.error(new Error(data.error || "Unknown error"))
+ } else {
+ // Regular data chunk
+ try {
+ controller.enqueue(data)
+ } catch {
+ // Controller closed, ignore
+ }
+ }
+ }
+ )
+ } catch (err) {
+ console.error(`[SD] Failed to subscribe to chat broadcasts:`, err)
+ }
+ }
+
// Handle abort
options.abortSignal?.addEventListener("abort", () => {
console.log(`[SD] R:ABORT sub=${subId} n=${chunkCount} last=${lastChunkType}`)
sub.unsubscribe()
+ // Clean up broadcast subscription
+ if (broadcastUnsubscribe) {
+ broadcastUnsubscribe()
+ }
// trpcClient.claude.cancel.mutate({ subChatId: this.config.subChatId })
try {
controller.close()
diff --git a/src/renderer/features/agents/lib/models.ts b/src/renderer/features/agents/lib/models.ts
index fb43b940d..a9b8448a2 100644
--- a/src/renderer/features/agents/lib/models.ts
+++ b/src/renderer/features/agents/lib/models.ts
@@ -1,5 +1,12 @@
-export const CLAUDE_MODELS = [
- { id: "opus", name: "Opus" },
- { id: "sonnet", name: "Sonnet" },
- { id: "haiku", name: "Haiku" },
+export interface ClaudeModel {
+ id: string
+ name: string
+ supportsThinking: boolean
+}
+
+export const CLAUDE_MODELS: ClaudeModel[] = [
+ { id: "default", name: "Default", supportsThinking: true },
+ { id: "opus", name: "Opus", supportsThinking: true },
+ { id: "sonnet", name: "Sonnet", supportsThinking: true },
+ { id: "haiku", name: "Haiku", supportsThinking: false },
]
diff --git a/src/renderer/features/agents/main/active-chat.tsx b/src/renderer/features/agents/main/active-chat.tsx
index e57be3598..ed3f54412 100644
--- a/src/renderer/features/agents/main/active-chat.tsx
+++ b/src/renderer/features/agents/main/active-chat.tsx
@@ -1,9 +1,14 @@
-"use client"
+"use client";
+import { stripEmojis } from "../../../components/chat-markdown-renderer";
+import { Button } from "../../../components/ui/button";
import {
- stripEmojis
-} from "../../../components/chat-markdown-renderer"
-import { Button } from "../../../components/ui/button"
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "../../../components/ui/dropdown-menu";
+import { Badge } from "../../../components/ui/badge";
import {
AgentIcon,
AttachIcon,
@@ -18,32 +23,32 @@ import {
IconSpinner,
IconTextUndo,
PauseIcon,
- VolumeIcon
-} from "../../../components/ui/icons"
-import { Kbd } from "../../../components/ui/kbd"
+ VolumeIcon,
+} from "../../../components/ui/icons";
+import { Kbd } from "../../../components/ui/kbd";
import {
PromptInput,
- PromptInputActions
-} from "../../../components/ui/prompt-input"
-import { ResizableSidebar } from "../../../components/ui/resizable-sidebar"
+ PromptInputActions,
+} from "../../../components/ui/prompt-input";
+import { ResizableSidebar } from "../../../components/ui/resizable-sidebar";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
-} from "../../../components/ui/tooltip"
+} from "../../../components/ui/tooltip";
// e2b API routes are used instead of useSandboxManager for agents
// import { clearSubChatSelectionAtom, isSubChatMultiSelectModeAtom, selectedSubChatIdsAtom } from "@/lib/atoms/agent-subchat-selection"
-import { Chat, useChat } from "@ai-sdk/react"
-import type { DiffViewMode } from "../ui/agent-diff-view"
-import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"
+import { Chat, useChat } from "@ai-sdk/react";
+import type { DiffViewMode } from "../ui/agent-diff-view";
+import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import {
ArrowDown,
ChevronDown,
GitFork,
ListTree,
- TerminalSquare
-} from "lucide-react"
-import { AnimatePresence, motion } from "motion/react"
+ TerminalSquare,
+} from "lucide-react";
+import { AnimatePresence, motion } from "motion/react";
import {
createContext,
memo,
@@ -53,47 +58,60 @@ import {
useLayoutEffect,
useMemo,
useRef,
- useState
-} from "react"
-import { flushSync } from "react-dom"
-import { toast } from "sonner"
-import { useShallow } from "zustand/react/shallow"
-import type { FileStatus } from "../../../../shared/changes-types"
-import { getQueryClient } from "../../../contexts/TRPCProvider"
-import { trackMessageSent } from "../../../lib/analytics"
-import { apiFetch } from "../../../lib/api-fetch"
+ useState,
+} from "react";
+import { flushSync } from "react-dom";
+import { toast } from "sonner";
+import { useShallow } from "zustand/react/shallow";
+import type { FileStatus } from "../../../../shared/changes-types";
+import { getQueryClient } from "../../../contexts/TRPCProvider";
+import { trackMessageSent } from "../../../lib/analytics";
+import { apiFetch } from "../../../lib/api-fetch";
import {
+ activeProfileIdAtom,
chatSourceModeAtom,
customClaudeConfigAtom,
defaultAgentModeAtom,
- isDesktopAtom, isFullscreenAtom,
+ isDesktopAtom,
+ isFullscreenAtom,
+ modelProfilesAtom,
normalizeCustomClaudeConfig,
selectedOllamaModelAtom,
- soundNotificationsEnabledAtom
-} from "../../../lib/atoms"
-import { useFileChangeListener, useGitWatcher } from "../../../lib/hooks/use-file-change-listener"
-import { useRemoteChat } from "../../../lib/hooks/use-remote-chats"
-import { useResolvedHotkeyDisplay } from "../../../lib/hotkeys"
-import { appStore } from "../../../lib/jotai-store"
-import { api } from "../../../lib/mock-api"
-import { trpc, trpcClient } from "../../../lib/trpc"
-import { cn } from "../../../lib/utils"
-import { isDesktopApp } from "../../../lib/utils/platform"
-import { ChangesPanel } from "../../changes"
-import { DiffCenterPeekDialog } from "../../changes/components/diff-center-peek-dialog"
-import { DiffFullPageView } from "../../changes/components/diff-full-page-view"
-import { DiffSidebarHeader } from "../../changes/components/diff-sidebar-header"
-import { getStatusIndicator } from "../../changes/utils/status"
+ soundNotificationsEnabledAtom,
+} from "../../../lib/atoms";
+import {
+ useFileChangeListener,
+ useGitWatcher,
+} from "../../../lib/hooks/use-file-change-listener";
+import { useRemoteChat } from "../../../lib/hooks/use-remote-chats";
+import { useResolvedHotkeyDisplay } from "../../../lib/hotkeys";
+import { appStore } from "../../../lib/jotai-store";
+import { api } from "../../../lib/mock-api";
+import { trpc, trpcClient } from "../../../lib/trpc";
+import { cn } from "../../../lib/utils";
+import { isDesktopApp } from "../../../lib/utils/platform";
+import { ChangesPanel } from "../../changes";
+import { DiffCenterPeekDialog } from "../../changes/components/diff-center-peek-dialog";
+import { DiffFullPageView } from "../../changes/components/diff-full-page-view";
+import { DiffSidebarHeader } from "../../changes/components/diff-sidebar-header";
+import { getStatusIndicator } from "../../changes/utils/status";
import {
detailsSidebarOpenAtom,
unifiedSidebarEnabledAtom,
-} from "../../details-sidebar/atoms"
-import { DetailsSidebar } from "../../details-sidebar/details-sidebar"
-import { FileViewerSidebar } from "../../file-viewer"
-import { FileSearchDialog } from "../../file-viewer/components/file-search-dialog"
-import { terminalSidebarOpenAtomFamily, terminalDisplayModeAtom, terminalBottomHeightAtom } from "../../terminal/atoms"
-import { TerminalSidebar, TerminalBottomPanelContent } from "../../terminal/terminal-sidebar"
-import { ResizableBottomPanel } from "@/components/ui/resizable-bottom-panel"
+} from "../../details-sidebar/atoms";
+import { DetailsSidebar } from "../../details-sidebar/details-sidebar";
+import { FileViewerSidebar } from "../../file-viewer";
+import { FileSearchDialog } from "../../file-viewer/components/file-search-dialog";
+import {
+ terminalSidebarOpenAtomFamily,
+ terminalDisplayModeAtom,
+ terminalBottomHeightAtom,
+} from "../../terminal/atoms";
+import {
+ TerminalSidebar,
+ TerminalBottomPanelContent,
+} from "../../terminal/terminal-sidebar";
+import { ResizableBottomPanel } from "@/components/ui/resizable-bottom-panel";
import {
agentsChangesPanelCollapsedAtom,
agentsChangesPanelWidthAtom,
@@ -142,126 +160,140 @@ import {
pendingMentionAtom,
suppressInputFocusAtom,
type AgentMode,
- type SelectedCommit
-} from "../atoms"
-import { BUILTIN_SLASH_COMMANDS } from "../commands"
-import { AgentSendButton } from "../components/agent-send-button"
-import { OpenLocallyDialog } from "../components/open-locally-dialog"
-import { PreviewSetupHoverCard } from "../components/preview-setup-hover-card"
-import type { TextSelectionSource } from "../context/text-selection-context"
-import { TextSelectionProvider } from "../context/text-selection-context"
-import { useAgentsFileUpload } from "../hooks/use-agents-file-upload"
-import { useAutoImport } from "../hooks/use-auto-import"
-import { useChangedFilesTracking } from "../hooks/use-changed-files-tracking"
-import { useDesktopNotifications } from "../hooks/use-desktop-notifications"
-import { useFocusInputOnEnter } from "../hooks/use-focus-input-on-enter"
-import { useHaptic } from "../hooks/use-haptic"
-import { usePastedTextFiles } from "../hooks/use-pasted-text-files"
-import { useTextContextSelection } from "../hooks/use-text-context-selection"
-import { useToggleFocusOnCmdEsc } from "../hooks/use-toggle-focus-on-cmd-esc"
-import {
- clearSubChatDraft,
- getSubChatDraftFull
-} from "../lib/drafts"
-import { IPCChatTransport } from "../lib/ipc-chat-transport"
+ type SelectedCommit,
+} from "../atoms";
+import { BUILTIN_SLASH_COMMANDS } from "../commands";
+import { AgentSendButton } from "../components/agent-send-button";
+import { OpenLocallyDialog } from "../components/open-locally-dialog";
+import { PreviewSetupHoverCard } from "../components/preview-setup-hover-card";
+import type { TextSelectionSource } from "../context/text-selection-context";
+import { TextSelectionProvider } from "../context/text-selection-context";
+import { useAgentsFileUpload } from "../hooks/use-agents-file-upload";
+import { useAutoImport } from "../hooks/use-auto-import";
+import { useChangedFilesTracking } from "../hooks/use-changed-files-tracking";
+import { useDesktopNotifications } from "../hooks/use-desktop-notifications";
+import { useFocusInputOnEnter } from "../hooks/use-focus-input-on-enter";
+import { useHaptic } from "../hooks/use-haptic";
+import { usePastedTextFiles } from "../hooks/use-pasted-text-files";
+import { useTextContextSelection } from "../hooks/use-text-context-selection";
+import { useToggleFocusOnCmdEsc } from "../hooks/use-toggle-focus-on-cmd-esc";
+import { clearSubChatDraft, getSubChatDraftFull } from "../lib/drafts";
+import { IPCChatTransport } from "../lib/ipc-chat-transport";
import {
createQueueItem,
generateQueueId,
toQueuedFile,
toQueuedImage,
toQueuedTextContext,
-} from "../lib/queue-utils"
-import { RemoteChatTransport } from "../lib/remote-chat-transport"
+} from "../lib/queue-utils";
+import { RemoteChatTransport } from "../lib/remote-chat-transport";
import {
FileOpenProvider,
MENTION_PREFIXES,
type AgentsMentionsEditorHandle,
-} from "../mentions"
+} from "../mentions";
import {
ChatSearchBar,
chatSearchCurrentMatchAtom,
- SearchHighlightProvider
-} from "../search"
-import { agentChatStore } from "../stores/agent-chat-store"
-import { EMPTY_QUEUE, useMessageQueueStore } from "../stores/message-queue-store"
-import { clearSubChatCaches, isRollingBackAtom, rollbackHandlerAtom, syncMessagesWithStatusAtom } from "../stores/message-store"
-import { useStreamingStatusStore } from "../stores/streaming-status-store"
+ SearchHighlightProvider,
+} from "../search";
+import { agentChatStore } from "../stores/agent-chat-store";
+import {
+ EMPTY_QUEUE,
+ useMessageQueueStore,
+} from "../stores/message-queue-store";
+import {
+ clearSubChatCaches,
+ isRollingBackAtom,
+ rollbackHandlerAtom,
+ syncMessagesWithStatusAtom,
+} from "../stores/message-store";
+import { useStreamingStatusStore } from "../stores/streaming-status-store";
import {
useAgentSubChatStore,
type SubChatMeta,
-} from "../stores/sub-chat-store"
+} from "../stores/sub-chat-store";
import {
AgentDiffView,
diffViewModeAtom,
splitUnifiedDiffByFile,
type AgentDiffViewRef,
type ParsedDiffFile,
-} from "../ui/agent-diff-view"
-import { AgentPlanSidebar } from "../ui/agent-plan-sidebar"
-import { AgentPreview } from "../ui/agent-preview"
-import { AgentQueueIndicator } from "../ui/agent-queue-indicator"
-import { AgentToolCall } from "../ui/agent-tool-call"
-import { AgentToolRegistry } from "../ui/agent-tool-registry"
-import { isPlanFile } from "../ui/agent-tool-utils"
-import { AgentUserMessageBubble } from "../ui/agent-user-message-bubble"
-import { AgentUserQuestion, type AgentUserQuestionHandle } from "../ui/agent-user-question"
-import { AgentsHeaderControls } from "../ui/agents-header-controls"
-import { ChatTitleEditor } from "../ui/chat-title-editor"
-import { MobileChatHeader } from "../ui/mobile-chat-header"
-import { QuickCommentInput } from "../ui/quick-comment-input"
-import { SubChatSelector } from "../ui/sub-chat-selector"
-import { SubChatStatusCard } from "../ui/sub-chat-status-card"
-import { TextSelectionPopover } from "../ui/text-selection-popover"
-import { autoRenameAgentChat } from "../utils/auto-rename"
-import { generateCommitToPrMessage, generatePrMessage, generateReviewMessage } from "../utils/pr-message"
-import { ChatInputArea } from "./chat-input-area"
-import { IsolatedMessagesSection } from "./isolated-messages-section"
-const clearSubChatSelectionAtom = atom(null, () => {})
-const isSubChatMultiSelectModeAtom = atom(false)
-const selectedSubChatIdsAtom = atom(new Set())
+} from "../ui/agent-diff-view";
+import { AgentPlanSidebar } from "../ui/agent-plan-sidebar";
+import { AgentPreview } from "../ui/agent-preview";
+import { AgentQueueIndicator } from "../ui/agent-queue-indicator";
+import { AgentToolCall } from "../ui/agent-tool-call";
+import { AgentToolRegistry } from "../ui/agent-tool-registry";
+import { isPlanFile } from "../ui/agent-tool-utils";
+import { AgentUserMessageBubble } from "../ui/agent-user-message-bubble";
+import {
+ AgentUserQuestion,
+ type AgentUserQuestionHandle,
+} from "../ui/agent-user-question";
+import { AgentsHeaderControls } from "../ui/agents-header-controls";
+import { ChatTitleEditor } from "../ui/chat-title-editor";
+import { MobileChatHeader } from "../ui/mobile-chat-header";
+import { QuickCommentInput } from "../ui/quick-comment-input";
+import { SubChatSelector } from "../ui/sub-chat-selector";
+import { SubChatStatusCard } from "../ui/sub-chat-status-card";
+import { TextSelectionPopover } from "../ui/text-selection-popover";
+import { autoRenameAgentChat } from "../utils/auto-rename";
+import {
+ generateCommitToPrMessage,
+ generatePrMessage,
+ generateReviewMessage,
+} from "../utils/pr-message";
+import { ChatInputArea } from "./chat-input-area";
+import { IsolatedMessagesSection } from "./isolated-messages-section";
+const clearSubChatSelectionAtom = atom(null, () => {});
+const isSubChatMultiSelectModeAtom = atom(false);
+const selectedSubChatIdsAtom = atom(new Set());
// import { selectedTeamIdAtom } from "@/lib/atoms/team"
-const selectedTeamIdAtom = atom(null)
+const selectedTeamIdAtom = atom(null);
// import type { PlanType } from "@/lib/config/subscription-plans"
-type PlanType = string
+type PlanType = string;
// UTF-8 safe base64 encoding (btoa doesn't support Unicode)
function utf8ToBase64(str: string): string {
- const bytes = new TextEncoder().encode(str)
- const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("")
- return btoa(binString)
+ const bytes = new TextEncoder().encode(str);
+ const binString = Array.from(bytes, (byte) =>
+ String.fromCodePoint(byte),
+ ).join("");
+ return btoa(binString);
}
/** Wait for streaming to finish by subscribing to the status store.
* Includes a 30s safety timeout — if the store never transitions to "ready",
* the promise resolves anyway to prevent hanging the UI indefinitely. */
-const STREAMING_READY_TIMEOUT_MS = 30_000
+const STREAMING_READY_TIMEOUT_MS = 30_000;
function waitForStreamingReady(subChatId: string): Promise {
return new Promise((resolve) => {
if (!useStreamingStatusStore.getState().isStreaming(subChatId)) {
- resolve()
- return
+ resolve();
+ return;
}
const timeout = setTimeout(() => {
console.warn(
- `[waitForStreamingReady] Timed out after ${STREAMING_READY_TIMEOUT_MS}ms for subChat ${subChatId.slice(-8)}, proceeding anyway`
- )
- unsub()
- resolve()
- }, STREAMING_READY_TIMEOUT_MS)
+ `[waitForStreamingReady] Timed out after ${STREAMING_READY_TIMEOUT_MS}ms for subChat ${subChatId.slice(-8)}, proceeding anyway`,
+ );
+ unsub();
+ resolve();
+ }, STREAMING_READY_TIMEOUT_MS);
const unsub = useStreamingStatusStore.subscribe(
(state) => state.statuses[subChatId],
(status) => {
if (status === "ready" || status === undefined) {
- clearTimeout(timeout)
- unsub()
- resolve()
+ clearTimeout(timeout);
+ unsub();
+ resolve();
}
- }
- )
- })
+ },
+ );
+ });
}
// Exploring tools - these get grouped when 2+ consecutive
@@ -271,37 +303,37 @@ const EXPLORING_TOOLS = new Set([
"tool-Glob",
"tool-WebSearch",
"tool-WebFetch",
-])
+]);
// Group consecutive exploring tools into exploring-group
function groupExploringTools(parts: any[], nestedToolIds: Set): any[] {
- const result: any[] = []
- let currentGroup: any[] = []
+ const result: any[] = [];
+ let currentGroup: any[] = [];
for (const part of parts) {
// Skip nested tools - they shouldn't be grouped, they render inside parent
- const isNested = part.toolCallId && nestedToolIds.has(part.toolCallId)
+ const isNested = part.toolCallId && nestedToolIds.has(part.toolCallId);
if (EXPLORING_TOOLS.has(part.type) && !isNested) {
- currentGroup.push(part)
+ currentGroup.push(part);
} else {
// Flush group if 3+
if (currentGroup.length >= 3) {
- result.push({ type: "exploring-group", parts: currentGroup })
+ result.push({ type: "exploring-group", parts: currentGroup });
} else {
- result.push(...currentGroup)
+ result.push(...currentGroup);
}
- currentGroup = []
- result.push(part)
+ currentGroup = [];
+ result.push(part);
}
}
// Flush remaining
if (currentGroup.length >= 3) {
- result.push({ type: "exploring-group", parts: currentGroup })
+ result.push({ type: "exploring-group", parts: currentGroup });
} else {
- result.push(...currentGroup)
+ result.push(...currentGroup);
}
- return result
+ return result;
}
// Get the ID of the first sub-chat by creation date
@@ -310,13 +342,13 @@ function getFirstSubChatId(
| Array<{ id: string; created_at?: Date | string | null }>
| undefined,
): string | null {
- if (!subChats?.length) return null
+ if (!subChats?.length) return null;
const sorted = [...subChats].sort(
(a, b) =>
(a.created_at ? new Date(a.created_at).getTime() : 0) -
(b.created_at ? new Date(b.created_at).getTime() : 0),
- )
- return sorted[0]?.id ?? null
+ );
+ return sorted[0]?.id ?? null;
}
// Layout constants for chat header and sticky messages
@@ -332,41 +364,79 @@ const CHAT_LAYOUT = {
// Header padding when absolute
headerPaddingSidebarOpen: "pt-1.5 pb-12 px-3 pl-2",
headerPaddingSidebarClosed: "p-2 pt-1.5",
-} as const
+} as const;
// Codex icon (OpenAI style)
const CodexIcon = (props: React.SVGProps) => (
-)
+);
// Model options for Claude Code
const claudeModels = [
{ id: "opus", name: "Opus" },
{ id: "sonnet", name: "Sonnet" },
{ id: "haiku", name: "Haiku" },
-]
+];
// Agent providers
const agents = [
{ id: "claude-code", name: "Claude Code", hasModels: true },
{ id: "cursor", name: "Cursor CLI", disabled: true },
{ id: "codex", name: "OpenAI Codex", disabled: true },
-]
+];
// Helper function to get agent icon
const getAgentIcon = (agentId: string, className?: string) => {
switch (agentId) {
case "claude-code":
- return
+ return ;
case "cursor":
- return
+ return ;
case "codex":
- return
+ return ;
default:
- return null
+ return null;
}
+};
+
+// Model Profile Selector component for quick profile switching
+function ModelProfileSelector() {
+ const [profiles] = useAtom(modelProfilesAtom);
+ const [activeProfileId, setActiveProfileId] = useAtom(activeProfileIdAtom);
+
+ const activeProfile = profiles.find((p) => p.id === activeProfileId);
+ const customProfiles = profiles.filter((p) => !p.isOffline);
+
+ if (customProfiles.length === 0) return null;
+
+ return (
+
+
+
+
+ {activeProfile ? activeProfile.name : "System"}
+
+
+
+
+
+ {/* System (No Profile) option removed as per request - forcing use of named profiles */}
+ {customProfiles.map((profile) => (
+ setActiveProfileId(profile.id)}
+ >
+ {profile.name}
+ {activeProfileId === profile.id && (
+ Active
+ )}
+
+ ))}
+
+
+ );
}
// Copy button component with tooltip feedback (matches project style)
@@ -374,18 +444,18 @@ function CopyButton({
onCopy,
isMobile = false,
}: {
- onCopy: () => void
- isMobile?: boolean
+ onCopy: () => void;
+ isMobile?: boolean;
}) {
- const [copied, setCopied] = useState(false)
- const { trigger: triggerHaptic } = useHaptic()
+ const [copied, setCopied] = useState(false);
+ const { trigger: triggerHaptic } = useHaptic();
const handleCopy = () => {
- onCopy()
- triggerHaptic("medium")
- setCopied(true)
- setTimeout(() => setCopied(false), 2000)
- }
+ onCopy();
+ triggerHaptic("medium");
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
return (
- )
+ );
}
// Play button component for TTS (text-to-speech) with streaming support
-type PlayButtonState = "idle" | "loading" | "playing"
+type PlayButtonState = "idle" | "loading" | "playing";
-const PLAYBACK_SPEEDS = [1, 2, 3] as const
-type PlaybackSpeed = (typeof PLAYBACK_SPEEDS)[number]
+const PLAYBACK_SPEEDS = [1, 2, 3] as const;
+type PlaybackSpeed = (typeof PLAYBACK_SPEEDS)[number];
function PlayButton({
text,
@@ -423,34 +493,34 @@ function PlayButton({
playbackRate = 1,
onPlaybackRateChange,
}: {
- text: string
- isMobile?: boolean
- playbackRate?: PlaybackSpeed
- onPlaybackRateChange?: (rate: PlaybackSpeed) => void
+ text: string;
+ isMobile?: boolean;
+ playbackRate?: PlaybackSpeed;
+ onPlaybackRateChange?: (rate: PlaybackSpeed) => void;
}) {
- const [state, setState] = useState("idle")
- const audioRef = useRef(null)
- const mediaSourceRef = useRef(null)
- const sourceBufferRef = useRef(null)
- const abortControllerRef = useRef(null)
- const chunkCountRef = useRef(0)
+ const [state, setState] = useState("idle");
+ const audioRef = useRef(null);
+ const mediaSourceRef = useRef(null);
+ const sourceBufferRef = useRef(null);
+ const abortControllerRef = useRef(null);
+ const chunkCountRef = useRef(0);
// Update playback rate when it changes
useEffect(() => {
if (audioRef.current) {
- audioRef.current.playbackRate = playbackRate
+ audioRef.current.playbackRate = playbackRate;
}
- }, [playbackRate])
+ }, [playbackRate]);
const cleanup = useCallback(() => {
if (abortControllerRef.current) {
- abortControllerRef.current.abort()
- abortControllerRef.current = null
+ abortControllerRef.current.abort();
+ abortControllerRef.current = null;
}
if (audioRef.current) {
- audioRef.current.pause()
+ audioRef.current.pause();
if (audioRef.current.src) {
- URL.revokeObjectURL(audioRef.current.src)
+ URL.revokeObjectURL(audioRef.current.src);
}
}
if (
@@ -458,133 +528,133 @@ function PlayButton({
mediaSourceRef.current.readyState === "open"
) {
try {
- mediaSourceRef.current.endOfStream()
+ mediaSourceRef.current.endOfStream();
} catch {
// Ignore errors during cleanup
}
}
- audioRef.current = null
- mediaSourceRef.current = null
- sourceBufferRef.current = null
- chunkCountRef.current = 0
- }, [])
+ audioRef.current = null;
+ mediaSourceRef.current = null;
+ sourceBufferRef.current = null;
+ chunkCountRef.current = 0;
+ }, []);
const handlePlay = async () => {
// If playing, stop the audio
if (state === "playing") {
- cleanup()
- setState("idle")
- return
+ cleanup();
+ setState("idle");
+ return;
}
// If loading, cancel and reset
if (state === "loading") {
- cleanup()
- setState("idle")
- return
+ cleanup();
+ setState("idle");
+ return;
}
// Start loading
- setState("loading")
- chunkCountRef.current = 0
+ setState("loading");
+ chunkCountRef.current = 0;
try {
// Check if MediaSource is supported for streaming
const supportsMediaSource =
typeof MediaSource !== "undefined" &&
- MediaSource.isTypeSupported("audio/mpeg")
+ MediaSource.isTypeSupported("audio/mpeg");
if (supportsMediaSource) {
// Use streaming approach with MediaSource API
- await playWithStreaming()
+ await playWithStreaming();
} else {
// Fallback: wait for full response (Safari, older browsers)
- await playWithFallback()
+ await playWithFallback();
}
} catch (error) {
if ((error as Error).name !== "AbortError") {
- console.error("[PlayButton] TTS error:", error)
+ console.error("[PlayButton] TTS error:", error);
}
- cleanup()
- setState("idle")
+ cleanup();
+ setState("idle");
}
- }
+ };
const playWithStreaming = async () => {
- const mediaSource = new MediaSource()
- mediaSourceRef.current = mediaSource
+ const mediaSource = new MediaSource();
+ mediaSourceRef.current = mediaSource;
- const audio = new Audio()
- audioRef.current = audio
+ const audio = new Audio();
+ audioRef.current = audio;
- audio.src = URL.createObjectURL(mediaSource)
+ audio.src = URL.createObjectURL(mediaSource);
audio.onended = () => {
- cleanup()
- setState("idle")
- }
+ cleanup();
+ setState("idle");
+ };
audio.onerror = () => {
- cleanup()
- setState("idle")
- }
+ cleanup();
+ setState("idle");
+ };
// Track if we've already started playing
- let hasStartedPlaying = false
+ let hasStartedPlaying = false;
// Start playback when browser has enough data (canplay event)
audio.oncanplay = async () => {
- if (hasStartedPlaying) return
- hasStartedPlaying = true
+ if (hasStartedPlaying) return;
+ hasStartedPlaying = true;
try {
- await audio.play()
- audio.playbackRate = playbackRate
- setState("playing")
+ await audio.play();
+ audio.playbackRate = playbackRate;
+ setState("playing");
} catch {
- cleanup()
- setState("idle")
+ cleanup();
+ setState("idle");
}
- }
+ };
// Wait for MediaSource to open
await new Promise((resolve, reject) => {
mediaSource.addEventListener("sourceopen", () => resolve(), {
once: true,
- })
+ });
mediaSource.addEventListener(
"error",
() => reject(new Error("MediaSource error")),
{
once: true,
},
- )
- })
+ );
+ });
- const sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg")
- sourceBufferRef.current = sourceBuffer
+ const sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg");
+ sourceBufferRef.current = sourceBuffer;
// Create abort controller for this request
- abortControllerRef.current = new AbortController()
+ abortControllerRef.current = new AbortController();
- const fetchStartTime = Date.now()
+ const fetchStartTime = Date.now();
const response = await apiFetch("/api/tts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
signal: abortControllerRef.current.signal,
- })
+ });
if (!response.ok) {
- throw new Error("TTS request failed")
+ throw new Error("TTS request failed");
}
if (!response.body) {
- throw new Error("No response body")
+ throw new Error("No response body");
}
- const reader = response.body.getReader()
- const pendingChunks: Uint8Array[] = []
- let isAppending = false
+ const reader = response.body.getReader();
+ const pendingChunks: Uint8Array[] = [];
+ let isAppending = false;
const appendNextChunk = () => {
if (
@@ -593,101 +663,101 @@ function PlayButton({
!sourceBufferRef.current ||
sourceBufferRef.current.updating
) {
- return
+ return;
}
- isAppending = true
- const chunk = pendingChunks.shift()!
+ isAppending = true;
+ const chunk = pendingChunks.shift()!;
try {
// Use ArrayBuffer.isView to ensure TypeScript knows this is a valid BufferSource
- const buffer = new Uint8Array(chunk.buffer.slice(0)) as BufferSource
- sourceBufferRef.current.appendBuffer(buffer)
+ const buffer = new Uint8Array(chunk.buffer.slice(0)) as BufferSource;
+ sourceBufferRef.current.appendBuffer(buffer);
} catch {
// Buffer might be full or source closed
- isAppending = false
+ isAppending = false;
}
- }
+ };
sourceBuffer.addEventListener("updateend", () => {
- isAppending = false
- appendNextChunk()
- })
+ isAppending = false;
+ appendNextChunk();
+ });
// Read stream chunks
const processStream = async () => {
while (true) {
- const { done, value } = await reader.read()
+ const { done, value } = await reader.read();
if (done) {
// Wait for all pending chunks to be appended
while (pendingChunks.length > 0 || sourceBuffer.updating) {
- await new Promise((r) => setTimeout(r, 50))
+ await new Promise((r) => setTimeout(r, 50));
}
if (mediaSource.readyState === "open") {
try {
- mediaSource.endOfStream()
+ mediaSource.endOfStream();
} catch {
// Ignore
}
}
- break
+ break;
}
if (value) {
- chunkCountRef.current++
- pendingChunks.push(value)
- appendNextChunk()
+ chunkCountRef.current++;
+ pendingChunks.push(value);
+ appendNextChunk();
// Just accumulate data, don't try to play yet
// Playback will start via canplay event listener
}
}
- }
+ };
// Start processing stream - playback will start via canplay event
- processStream()
- }
+ processStream();
+ };
const playWithFallback = async () => {
- abortControllerRef.current = new AbortController()
+ abortControllerRef.current = new AbortController();
const response = await apiFetch("/api/tts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
signal: abortControllerRef.current.signal,
- })
+ });
if (!response.ok) {
- throw new Error("TTS request failed")
+ throw new Error("TTS request failed");
}
- const audioBlob = await response.blob()
- const audioUrl = URL.createObjectURL(audioBlob)
+ const audioBlob = await response.blob();
+ const audioUrl = URL.createObjectURL(audioBlob);
- const audio = new Audio(audioUrl)
- audioRef.current = audio
+ const audio = new Audio(audioUrl);
+ audioRef.current = audio;
audio.onended = () => {
- cleanup()
- setState("idle")
- }
+ cleanup();
+ setState("idle");
+ };
audio.onerror = () => {
- cleanup()
- setState("idle")
- }
+ cleanup();
+ setState("idle");
+ };
- await audio.play()
+ await audio.play();
// Set playback rate AFTER play() - browser resets it when setting src
- audio.playbackRate = playbackRate
- setState("playing")
- }
+ audio.playbackRate = playbackRate;
+ setState("playing");
+ };
// Cleanup on unmount
useEffect(() => {
- return cleanup
- }, [cleanup])
+ return cleanup;
+ }, [cleanup]);
return (
@@ -714,9 +784,9 @@ function PlayButton({
{state === "playing" && (
{
- const currentIndex = PLAYBACK_SPEEDS.indexOf(playbackRate)
- const nextIndex = (currentIndex + 1) % PLAYBACK_SPEEDS.length
- onPlaybackRateChange?.(PLAYBACK_SPEEDS[nextIndex])
+ const currentIndex = PLAYBACK_SPEEDS.indexOf(playbackRate);
+ const nextIndex = (currentIndex + 1) % PLAYBACK_SPEEDS.length;
+ onPlaybackRateChange?.(PLAYBACK_SPEEDS[nextIndex]);
}}
tabIndex={-1}
className={cn(
@@ -744,7 +814,7 @@ function PlayButton({
)}
- )
+ );
}
// Rollback button component for reverting to a previous message state
@@ -753,9 +823,9 @@ function RollbackButton({
onRollback,
isRollingBack = false,
}: {
- disabled?: boolean
- onRollback: () => void
- isRollingBack?: boolean
+ disabled?: boolean;
+ onRollback: () => void;
+ isRollingBack?: boolean;
}) {
return (
@@ -776,7 +846,7 @@ function RollbackButton({
{isRollingBack ? "Rolling back..." : "Rollback to here"}
- )
+ );
}
// Isolated scroll-to-bottom button - uses own scroll listener to avoid re-renders of parent
@@ -787,73 +857,75 @@ const ScrollToBottomButton = memo(function ScrollToBottomButton({
subChatId,
isActive = true,
}: {
- containerRef: React.RefObject
- onScrollToBottom: () => void
- hasStackedCards?: boolean
- subChatId?: string
- isActive?: boolean
+ containerRef: React.RefObject;
+ onScrollToBottom: () => void;
+ hasStackedCards?: boolean;
+ subChatId?: string;
+ isActive?: boolean;
}) {
- const [isVisible, setIsVisible] = useState(false)
+ const [isVisible, setIsVisible] = useState(false);
// Keep isActive in ref for scroll event handler
- const isActiveRef = useRef(isActive)
- isActiveRef.current = isActive
+ const isActiveRef = useRef(isActive);
+ isActiveRef.current = isActive;
useEffect(() => {
// Skip scroll monitoring for inactive tabs (keep-alive)
- if (!isActive) return
+ if (!isActive) return;
- const container = containerRef.current
- if (!container) return
+ const container = containerRef.current;
+ if (!container) return;
// RAF throttle to avoid setState on every scroll event
- let rafId: number | null = null
- let lastAtBottom: boolean | null = null
+ let rafId: number | null = null;
+ let lastAtBottom: boolean | null = null;
const checkVisibility = () => {
// Skip if not active or RAF already pending
- if (!isActiveRef.current || rafId !== null) return
+ if (!isActiveRef.current || rafId !== null) return;
rafId = requestAnimationFrame(() => {
- rafId = null
+ rafId = null;
// Double-check active state in RAF callback
- if (!isActiveRef.current) return
+ if (!isActiveRef.current) return;
- const threshold = 50
+ const threshold = 50;
const atBottom =
- container.scrollHeight - container.scrollTop - container.clientHeight <=
- threshold
+ container.scrollHeight -
+ container.scrollTop -
+ container.clientHeight <=
+ threshold;
// Only update state if value actually changed
if (lastAtBottom !== atBottom) {
- lastAtBottom = atBottom
- setIsVisible(!atBottom)
+ lastAtBottom = atBottom;
+ setIsVisible(!atBottom);
}
- })
- }
+ });
+ };
// Check initial state after a short delay to allow scroll position to be set
// This handles the case when entering a sub-chat that's scrolled to a specific position
const timeoutId = setTimeout(() => {
// Skip if not active
- if (!isActiveRef.current) return
+ if (!isActiveRef.current) return;
// Direct check for initial state (no RAF needed)
- const threshold = 50
+ const threshold = 50;
const atBottom =
container.scrollHeight - container.scrollTop - container.clientHeight <=
- threshold
- lastAtBottom = atBottom
- setIsVisible(!atBottom)
- }, 50)
+ threshold;
+ lastAtBottom = atBottom;
+ setIsVisible(!atBottom);
+ }, 50);
- container.addEventListener("scroll", checkVisibility, { passive: true })
+ container.addEventListener("scroll", checkVisibility, { passive: true });
return () => {
- clearTimeout(timeoutId)
- if (rafId !== null) cancelAnimationFrame(rafId)
- container.removeEventListener("scroll", checkVisibility)
- }
- }, [containerRef, subChatId, isActive])
+ clearTimeout(timeoutId);
+ if (rafId !== null) cancelAnimationFrame(rafId);
+ container.removeEventListener("scroll", checkVisibility);
+ };
+ }, [containerRef, subChatId, isActive]);
return (
@@ -868,7 +940,9 @@ const ScrollToBottomButton = memo(function ScrollToBottomButton({
onClick={onScrollToBottom}
className={cn(
"absolute right-4 p-2 rounded-full bg-background border border-border shadow-md hover:bg-accent active:scale-[0.97] transition-colors z-20",
- hasStackedCards ? "bottom-44 sm:bottom-36" : "bottom-32 sm:bottom-24"
+ hasStackedCards
+ ? "bottom-44 sm:bottom-36"
+ : "bottom-32 sm:bottom-24",
)}
aria-label="Scroll to bottom"
>
@@ -887,42 +961,44 @@ const ScrollToBottomButton = memo(function ScrollToBottomButton({
)}
- )
-})
+ );
+});
// Message group wrapper - measures user message height for sticky todo positioning
interface MessageGroupProps {
- children: React.ReactNode
- isLastGroup?: boolean
+ children: React.ReactNode;
+ isLastGroup?: boolean;
}
function MessageGroup({ children, isLastGroup }: MessageGroupProps) {
- const groupRef = useRef(null)
- const userMessageRef = useRef(null)
+ const groupRef = useRef(null);
+ const userMessageRef = useRef(null);
useEffect(() => {
- const groupEl = groupRef.current
- if (!groupEl) return
+ const groupEl = groupRef.current;
+ if (!groupEl) return;
// Find the actual bubble element (not the wrapper which includes gradient)
- const bubbleEl = groupEl.querySelector('[data-user-bubble]') as HTMLDivElement | null
- if (!bubbleEl) return
+ const bubbleEl = groupEl.querySelector(
+ "[data-user-bubble]",
+ ) as HTMLDivElement | null;
+ if (!bubbleEl) return;
- userMessageRef.current = bubbleEl
+ userMessageRef.current = bubbleEl;
const updateHeight = () => {
- const height = bubbleEl.offsetHeight
+ const height = bubbleEl.offsetHeight;
// Set CSS variable directly on DOM - no React state, no re-renders
- groupEl.style.setProperty('--user-message-height', `${height}px`)
- }
+ groupEl.style.setProperty("--user-message-height", `${height}px`);
+ };
- updateHeight()
+ updateHeight();
- const observer = new ResizeObserver(updateHeight)
- observer.observe(bubbleEl)
+ const observer = new ResizeObserver(updateHeight);
+ observer.observe(bubbleEl);
- return () => observer.disconnect()
- }, [])
+ return () => observer.disconnect();
+ }, []);
return (
{children}
- )
+ );
}
// Collapsible steps component for intermediate content before final response
interface CollapsibleStepsProps {
- stepsCount: number
- children: React.ReactNode
- defaultExpanded?: boolean
+ stepsCount: number;
+ children: React.ReactNode;
+ defaultExpanded?: boolean;
}
function CollapsibleSteps({
@@ -956,9 +1034,9 @@ function CollapsibleSteps({
children,
defaultExpanded = false,
}: CollapsibleStepsProps) {
- const [isExpanded, setIsExpanded] = useState(defaultExpanded)
+ const [isExpanded, setIsExpanded] = useState(defaultExpanded);
- if (stepsCount === 0) return null
+ if (stepsCount === 0) return null;
return (
@@ -976,8 +1054,8 @@ function CollapsibleSteps({
{
- e.stopPropagation()
- setIsExpanded(!isExpanded)
+ e.stopPropagation();
+ setIsExpanded(!isExpanded);
}}
>
@@ -998,7 +1076,7 @@ function CollapsibleSteps({
{isExpanded && {children}
}
- )
+ );
}
// ============================================================================
@@ -1006,60 +1084,84 @@ function CollapsibleSteps({
// ============================================================================
interface DiffStateContextValue {
- selectedFilePath: string | null
- filteredSubChatId: string | null
- viewedCount: number
- handleDiffFileSelect: (file: { path: string }, category: string) => void
- handleSelectNextFile: (filePath: string) => void
- handleCommitSuccess: () => void
- handleCloseDiff: () => void
- handleViewedCountChange: (count: number) => void
+ selectedFilePath: string | null;
+ filteredSubChatId: string | null;
+ viewedCount: number;
+ handleDiffFileSelect: (file: { path: string }, category: string) => void;
+ handleSelectNextFile: (filePath: string) => void;
+ handleCommitSuccess: () => void;
+ handleCloseDiff: () => void;
+ handleViewedCountChange: (count: number) => void;
/** Ref to register a function that resets activeTab to "changes" before closing */
- resetActiveTabRef: React.MutableRefObject<(() => void) | null>
+ resetActiveTabRef: React.MutableRefObject<(() => void) | null>;
}
-const DiffStateContext = createContext(null)
+const DiffStateContext = createContext(null);
function useDiffState() {
- const ctx = useContext(DiffStateContext)
- if (!ctx) throw new Error('useDiffState must be used within DiffStateProvider')
- return ctx
+ const ctx = useContext(DiffStateContext);
+ if (!ctx)
+ throw new Error("useDiffState must be used within DiffStateProvider");
+ return ctx;
}
// Diff sidebar content component with responsive layout
interface DiffSidebarContentProps {
- worktreePath: string | null
- selectedFilePath: string | null
- onFileSelect: (file: { path: string }, category: string) => void
- chatId: string
- sandboxId: string | null
- repository: { owner: string; name: string } | null
- diffStats: { isLoading: boolean; hasChanges: boolean; fileCount: number; additions: number; deletions: number }
- setDiffStats: (stats: { isLoading: boolean; hasChanges: boolean; fileCount: number; additions: number; deletions: number }) => void
- diffContent: string | null
- parsedFileDiffs: unknown
- prefetchedFileContents: Record | undefined
- setDiffCollapseState: (state: Map) => void
- diffViewRef: React.RefObject<{ expandAll: () => void; collapseAll: () => void; getViewedCount: () => number; markAllViewed: () => void; markAllUnviewed: () => void } | null>
- agentChat: { prUrl?: string; prNumber?: number } | null | undefined
+ worktreePath: string | null;
+ selectedFilePath: string | null;
+ onFileSelect: (file: { path: string }, category: string) => void;
+ chatId: string;
+ sandboxId: string | null;
+ repository: { owner: string; name: string } | null;
+ diffStats: {
+ isLoading: boolean;
+ hasChanges: boolean;
+ fileCount: number;
+ additions: number;
+ deletions: number;
+ };
+ setDiffStats: (stats: {
+ isLoading: boolean;
+ hasChanges: boolean;
+ fileCount: number;
+ additions: number;
+ deletions: number;
+ }) => void;
+ diffContent: string | null;
+ parsedFileDiffs: unknown;
+ prefetchedFileContents: Record | undefined;
+ setDiffCollapseState: (state: Map) => void;
+ diffViewRef: React.RefObject<{
+ expandAll: () => void;
+ collapseAll: () => void;
+ getViewedCount: () => number;
+ markAllViewed: () => void;
+ markAllUnviewed: () => void;
+ } | null>;
+ agentChat: { prUrl?: string; prNumber?: number } | null | undefined;
// Real-time sidebar width for responsive layout during resize
- sidebarWidth: number
+ sidebarWidth: number;
// Commit with AI
- onCommitWithAI?: () => void
- isCommittingWithAI?: boolean
+ onCommitWithAI?: () => void;
+ isCommittingWithAI?: boolean;
// Diff view mode
- diffMode: DiffViewMode
- setDiffMode: (mode: DiffViewMode) => void
+ diffMode: DiffViewMode;
+ setDiffMode: (mode: DiffViewMode) => void;
// Create PR callback
- onCreatePr?: () => void
+ onCreatePr?: () => void;
// Called after successful commit to reset diff view state
- onCommitSuccess?: () => void
+ onCommitSuccess?: () => void;
// Subchats with changed files for filtering
- subChats?: Array<{ id: string; name: string; filePaths: string[]; fileCount: number }>
+ subChats?: Array<{
+ id: string;
+ name: string;
+ filePaths: string[];
+ fileCount: number;
+ }>;
// Initial subchat filter (e.g., from Review button)
- initialSubChatFilter?: string | null
+ initialSubChatFilter?: string | null;
// Callback when marking file as viewed to select next file
- onSelectNextFile?: (filePath: string) => void
+ onSelectNextFile?: (filePath: string) => void;
}
// Memoized commit file item for History tab
@@ -1067,17 +1169,19 @@ const CommitFileItem = memo(function CommitFileItem({
file,
onClick,
}: {
- file: { path: string; status: FileStatus }
- onClick: () => void
+ file: { path: string; status: FileStatus };
+ onClick: () => void;
}) {
- const fileName = file.path.split('/').pop() || file.path
- const dirPath = file.path.includes('/') ? file.path.substring(0, file.path.lastIndexOf('/')) : ''
+ const fileName = file.path.split("/").pop() || file.path;
+ const dirPath = file.path.includes("/")
+ ? file.path.substring(0, file.path.lastIndexOf("/"))
+ : "";
return (
@@ -1091,12 +1195,10 @@ const CommitFileItem = memo(function CommitFileItem({
{fileName}
-
- {getStatusIndicator(file.status)}
-
+ {getStatusIndicator(file.status)}
- )
-})
+ );
+});
const DiffSidebarContent = memo(function DiffSidebarContent({
worktreePath,
@@ -1118,7 +1220,14 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
setDiffMode,
onCreatePr,
subChats = [],
-}: Omit) {
+}: Omit<
+ DiffSidebarContentProps,
+ | "selectedFilePath"
+ | "onFileSelect"
+ | "onCommitSuccess"
+ | "initialSubChatFilter"
+ | "onSelectNextFile"
+>) {
// Get values from context instead of props
const {
selectedFilePath,
@@ -1128,104 +1237,117 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
handleCommitSuccess,
handleViewedCountChange,
resetActiveTabRef,
- } = useDiffState()
+ } = useDiffState();
// Compute initial selected file synchronously for first render
// This prevents AgentDiffView from rendering all files before filter kicks in
const initialSelectedFile = useMemo(() => {
- if (selectedFilePath) return selectedFilePath
+ if (selectedFilePath) return selectedFilePath;
if (parsedFileDiffs && parsedFileDiffs.length > 0) {
- const firstFile = parsedFileDiffs[0]
- const filePath = firstFile.newPath !== '/dev/null' ? firstFile.newPath : firstFile.oldPath
- if (filePath && filePath !== '/dev/null') {
- return filePath
+ const firstFile = parsedFileDiffs[0];
+ const filePath =
+ firstFile.newPath !== "/dev/null"
+ ? firstFile.newPath
+ : firstFile.oldPath;
+ if (filePath && filePath !== "/dev/null") {
+ return filePath;
}
}
- return null
- }, [selectedFilePath, parsedFileDiffs])
- const [changesPanelWidth, setChangesPanelWidth] = useAtom(agentsChangesPanelWidthAtom)
- const [isChangesPanelCollapsed, setIsChangesPanelCollapsed] = useAtom(agentsChangesPanelCollapsedAtom)
- const [isResizing, setIsResizing] = useState(false)
+ return null;
+ }, [selectedFilePath, parsedFileDiffs]);
+ const [changesPanelWidth, setChangesPanelWidth] = useAtom(
+ agentsChangesPanelWidthAtom,
+ );
+ const [isChangesPanelCollapsed, setIsChangesPanelCollapsed] = useAtom(
+ agentsChangesPanelCollapsedAtom,
+ );
+ const [isResizing, setIsResizing] = useState(false);
// Active tab state (Changes/History)
- const [activeTab, setActiveTab] = useState<"changes" | "history">("changes")
+ const [activeTab, setActiveTab] = useState<"changes" | "history">("changes");
// Register the reset function so handleCloseDiff can reset to "changes" tab before closing
// This prevents React 19 ref cleanup issues with HistoryView's ContextMenu components
useEffect(() => {
- resetActiveTabRef.current = () => setActiveTab("changes")
+ resetActiveTabRef.current = () => setActiveTab("changes");
return () => {
- resetActiveTabRef.current = null
- }
- }, [resetActiveTabRef])
+ resetActiveTabRef.current = null;
+ };
+ }, [resetActiveTabRef]);
// Selected commit for History tab
- const [selectedCommit, setSelectedCommit] = useAtom(selectedCommitAtom)
+ const [selectedCommit, setSelectedCommit] = useAtom(selectedCommitAtom);
// When sidebar is narrow (< 500px), use vertical layout
- const isNarrow = sidebarWidth < 500
+ const isNarrow = sidebarWidth < 500;
// Get diff stats for collapsed header display
const { data: diffStatus } = trpc.changes.getStatus.useQuery(
{ worktreePath: worktreePath || "" },
- { enabled: !!worktreePath && isNarrow }
- )
+ { enabled: !!worktreePath && isNarrow },
+ );
// Handle resize drag
const handleResizePointerDown = useCallback(
(event: React.PointerEvent) => {
- if (event.button !== 0) return
+ if (event.button !== 0) return;
- event.preventDefault()
- event.stopPropagation()
+ event.preventDefault();
+ event.stopPropagation();
- const startX = event.clientX
- const startWidth = changesPanelWidth
- const pointerId = event.pointerId
- const handleElement = event.currentTarget as HTMLElement
+ const startX = event.clientX;
+ const startWidth = changesPanelWidth;
+ const pointerId = event.pointerId;
+ const handleElement = event.currentTarget as HTMLElement;
- const minWidth = 200
- const maxWidth = 450
+ const minWidth = 200;
+ const maxWidth = 450;
const clampWidth = (width: number) =>
- Math.max(minWidth, Math.min(maxWidth, width))
+ Math.max(minWidth, Math.min(maxWidth, width));
- handleElement.setPointerCapture?.(pointerId)
- setIsResizing(true)
+ handleElement.setPointerCapture?.(pointerId);
+ setIsResizing(true);
const handlePointerMove = (e: PointerEvent) => {
- const delta = e.clientX - startX
- const newWidth = clampWidth(startWidth + delta)
- setChangesPanelWidth(newWidth)
- }
+ const delta = e.clientX - startX;
+ const newWidth = clampWidth(startWidth + delta);
+ setChangesPanelWidth(newWidth);
+ };
const handlePointerUp = () => {
if (handleElement.hasPointerCapture?.(pointerId)) {
- handleElement.releasePointerCapture(pointerId)
+ handleElement.releasePointerCapture(pointerId);
}
- document.removeEventListener("pointermove", handlePointerMove)
- document.removeEventListener("pointerup", handlePointerUp)
- setIsResizing(false)
- }
+ document.removeEventListener("pointermove", handlePointerMove);
+ document.removeEventListener("pointerup", handlePointerUp);
+ setIsResizing(false);
+ };
- document.addEventListener("pointermove", handlePointerMove)
- document.addEventListener("pointerup", handlePointerUp, { once: true })
+ document.addEventListener("pointermove", handlePointerMove);
+ document.addEventListener("pointerup", handlePointerUp, { once: true });
},
- [changesPanelWidth, setChangesPanelWidth]
- )
+ [changesPanelWidth, setChangesPanelWidth],
+ );
// Handle commit selection in History tab
- const handleCommitSelect = useCallback((commit: SelectedCommit) => {
- setSelectedCommit(commit)
- // Reset file selection when changing commits
- // The HistoryView will auto-select first file
- }, [setSelectedCommit])
+ const handleCommitSelect = useCallback(
+ (commit: SelectedCommit) => {
+ setSelectedCommit(commit);
+ // Reset file selection when changing commits
+ // The HistoryView will auto-select first file
+ },
+ [setSelectedCommit],
+ );
// Handle file selection in commit (History tab)
- const handleCommitFileSelect = useCallback((file: { path: string }, commitHash: string) => {
- // Set selected file path for highlighting
- handleDiffFileSelect(file, "")
- }, [handleDiffFileSelect])
+ const handleCommitFileSelect = useCallback(
+ (file: { path: string }, commitHash: string) => {
+ // Set selected file path for highlighting
+ handleDiffFileSelect(file, "");
+ },
+ [handleDiffFileSelect],
+ );
// Fetch commit files when a commit is selected
const { data: commitFiles } = trpc.changes.getCommitFiles.useQuery(
@@ -1236,8 +1358,8 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
{
enabled: !!worktreePath && !!selectedCommit,
staleTime: 60000, // Cache for 1 minute
- }
- )
+ },
+ );
// Fetch commit file diff when a commit is selected
const { data: commitFileDiff } = trpc.changes.getCommitFileDiff.useQuery(
@@ -1249,32 +1371,39 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
{
enabled: !!worktreePath && !!selectedCommit && !!selectedFilePath,
staleTime: 60000, // Cache for 1 minute
- }
- )
+ },
+ );
// Use commit diff or regular diff based on selection
// Only use commit data when in History tab, otherwise always use regular diff
- const shouldUseCommitDiff = activeTab === "history" && selectedCommit
- const effectiveDiff = shouldUseCommitDiff && commitFileDiff ? commitFileDiff : diffContent
- const effectiveParsedFiles = shouldUseCommitDiff ? null : parsedFileDiffs
- const effectivePrefetchedContents = shouldUseCommitDiff ? {} : prefetchedFileContents
+ const shouldUseCommitDiff = activeTab === "history" && selectedCommit;
+ const effectiveDiff =
+ shouldUseCommitDiff && commitFileDiff ? commitFileDiff : diffContent;
+ const effectiveParsedFiles = shouldUseCommitDiff ? null : parsedFileDiffs;
+ const effectivePrefetchedContents = shouldUseCommitDiff
+ ? {}
+ : prefetchedFileContents;
if (isNarrow) {
// Count changed files for collapsed header
const changedFilesCount = diffStatus
- ? (diffStatus.staged?.length || 0) + (diffStatus.unstaged?.length || 0) + (diffStatus.untracked?.length || 0)
- : 0
- const stagedCount = diffStatus?.staged?.length || 0
+ ? (diffStatus.staged?.length || 0) +
+ (diffStatus.unstaged?.length || 0) +
+ (diffStatus.untracked?.length || 0)
+ : 0;
+ const stagedCount = diffStatus?.staged?.length || 0;
// Vertical layout: ChangesPanel on top, diff/file list below
return (
{/* Top: ChangesPanel (file list + commit) */}
{worktreePath && (
-
+
{/* History view - files in commit */}
-
- {selectedCommit && (
- !commitFiles ? (
+
+ {selectedCommit &&
+ (!commitFiles ? (
Loading files...
@@ -1320,8 +1453,8 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
{
- navigator.clipboard.writeText(selectedCommit.hash)
- toast.success('Copied SHA to clipboard')
+ navigator.clipboard.writeText(selectedCommit.hash);
+ toast.success("Copied SHA to clipboard");
}}
className="text-xs font-mono text-muted-foreground hover:text-foreground underline cursor-pointer shrink-0"
>
@@ -1334,7 +1467,10 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
)}
- {selectedCommit.author} • {selectedCommit.date ? new Date(selectedCommit.date).toLocaleString() : 'Unknown date'}
+ {selectedCommit.author} •{" "}
+ {selectedCommit.date
+ ? new Date(selectedCommit.date).toLocaleString()
+ : "Unknown date"}
@@ -1349,14 +1485,17 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
/>
))}
>
- )
- )}
+ ))}
{/* Diff view - always mounted to prevent expensive re-initialization */}
-
- )
+ );
}
// Horizontal layout: files on left, diff on right
@@ -1418,17 +1557,23 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
)}
{/* Right: File list (when History tab) or AgentDiffView (when Changes tab) */}
{/* Both views are always mounted but hidden via CSS to prevent expensive re-mounts */}
-
+
{/* History view - files in commit */}
-
- {selectedCommit && (
- !commitFiles ? (
+
+ {selectedCommit &&
+ (!commitFiles ? (
Loading files...
@@ -1446,8 +1591,8 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
{
- navigator.clipboard.writeText(selectedCommit.hash)
- toast.success('Copied SHA to clipboard')
+ navigator.clipboard.writeText(selectedCommit.hash);
+ toast.success("Copied SHA to clipboard");
}}
className="text-xs font-mono text-muted-foreground hover:text-foreground underline cursor-pointer shrink-0"
>
@@ -1460,7 +1605,10 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
)}
- {selectedCommit.author} • {selectedCommit.date ? new Date(selectedCommit.date).toLocaleString() : 'Unknown date'}
+ {selectedCommit.author} •{" "}
+ {selectedCommit.date
+ ? new Date(selectedCommit.date).toLocaleString()
+ : "Unknown date"}
@@ -1475,14 +1623,17 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
/>
))}
>
- )
- )}
+ ))}
{/* Diff view - always mounted to prevent expensive re-initialization */}
-
- )
-})
+ );
+});
// ============================================================================
// DiffStateProvider - manages diff state in isolation from ChatView
@@ -1511,16 +1662,22 @@ const DiffSidebarContent = memo(function DiffSidebarContent({
// ============================================================================
interface DiffStateProviderProps {
- isDiffSidebarOpen: boolean
- parsedFileDiffs: ParsedDiffFile[] | null
- isDiffSidebarNarrow: boolean
- setIsDiffSidebarOpen: (open: boolean) => void
- setDiffStats: (stats: { isLoading: boolean; hasChanges: boolean; fileCount: number; additions: number; deletions: number }) => void
- setDiffContent: (content: string | null) => void
- setParsedFileDiffs: (files: ParsedDiffFile[] | null) => void
- setPrefetchedFileContents: (contents: Record
) => void
- fetchDiffStats: () => void
- children: React.ReactNode
+ isDiffSidebarOpen: boolean;
+ parsedFileDiffs: ParsedDiffFile[] | null;
+ isDiffSidebarNarrow: boolean;
+ setIsDiffSidebarOpen: (open: boolean) => void;
+ setDiffStats: (stats: {
+ isLoading: boolean;
+ hasChanges: boolean;
+ fileCount: number;
+ additions: number;
+ deletions: number;
+ }) => void;
+ setDiffContent: (content: string | null) => void;
+ setParsedFileDiffs: (files: ParsedDiffFile[] | null) => void;
+ setPrefetchedFileContents: (contents: Record) => void;
+ fetchDiffStats: () => void;
+ children: React.ReactNode;
}
const DiffStateProvider = memo(function DiffStateProvider({
@@ -1536,111 +1693,152 @@ const DiffStateProvider = memo(function DiffStateProvider({
children,
}: DiffStateProviderProps) {
// Viewed count state - kept here to avoid re-rendering ChatView
- const [viewedCount, setViewedCount] = useState(0)
+ const [viewedCount, setViewedCount] = useState(0);
// Ref for resetting activeTab to "changes" before closing
// This prevents React 19 ref cleanup issues with HistoryView's ContextMenu components
- const resetActiveTabRef = useRef<(() => void) | null>(null)
+ const resetActiveTabRef = useRef<(() => void) | null>(null);
// All diff-related atoms are read HERE, not in ChatView
- const [selectedFilePath, setSelectedFilePath] = useAtom(selectedDiffFilePathAtom)
- const [, setFilteredDiffFiles] = useAtom(filteredDiffFilesAtom)
- const [filteredSubChatId, setFilteredSubChatId] = useAtom(filteredSubChatIdAtom)
- const isChangesPanelCollapsed = useAtomValue(agentsChangesPanelCollapsedAtom)
+ const [selectedFilePath, setSelectedFilePath] = useAtom(
+ selectedDiffFilePathAtom,
+ );
+ const [, setFilteredDiffFiles] = useAtom(filteredDiffFilesAtom);
+ const [filteredSubChatId, setFilteredSubChatId] = useAtom(
+ filteredSubChatIdAtom,
+ );
+ const isChangesPanelCollapsed = useAtomValue(agentsChangesPanelCollapsedAtom);
// Auto-select first file when diff sidebar opens - use useLayoutEffect for synchronous update
// This prevents the initial render from showing all 11 files before filter kicks in
useLayoutEffect(() => {
if (!isDiffSidebarOpen) {
- setSelectedFilePath(null)
- setFilteredDiffFiles(null)
- return
+ setSelectedFilePath(null);
+ setFilteredDiffFiles(null);
+ return;
}
// Determine which file to select
- let fileToSelect = selectedFilePath
+ let fileToSelect = selectedFilePath;
if (!fileToSelect && parsedFileDiffs && parsedFileDiffs.length > 0) {
- const firstFile = parsedFileDiffs[0]
- fileToSelect = firstFile.newPath !== '/dev/null' ? firstFile.newPath : firstFile.oldPath
- if (fileToSelect && fileToSelect !== '/dev/null') {
- setSelectedFilePath(fileToSelect)
+ const firstFile = parsedFileDiffs[0];
+ fileToSelect =
+ firstFile.newPath !== "/dev/null"
+ ? firstFile.newPath
+ : firstFile.oldPath;
+ if (fileToSelect && fileToSelect !== "/dev/null") {
+ setSelectedFilePath(fileToSelect);
}
}
// Filter logic based on layout mode
- const shouldShowAllFiles = isDiffSidebarNarrow && isChangesPanelCollapsed
+ const shouldShowAllFiles = isDiffSidebarNarrow && isChangesPanelCollapsed;
if (shouldShowAllFiles) {
- setFilteredDiffFiles(null)
+ setFilteredDiffFiles(null);
} else if (fileToSelect) {
- setFilteredDiffFiles([fileToSelect])
+ setFilteredDiffFiles([fileToSelect]);
} else {
- setFilteredDiffFiles(null)
+ setFilteredDiffFiles(null);
}
- }, [isDiffSidebarOpen, selectedFilePath, parsedFileDiffs, isDiffSidebarNarrow, isChangesPanelCollapsed, setFilteredDiffFiles, setSelectedFilePath])
+ }, [
+ isDiffSidebarOpen,
+ selectedFilePath,
+ parsedFileDiffs,
+ isDiffSidebarNarrow,
+ isChangesPanelCollapsed,
+ setFilteredDiffFiles,
+ setSelectedFilePath,
+ ]);
// Stable callbacks
- const handleDiffFileSelect = useCallback((file: { path: string }, _category: string) => {
- setSelectedFilePath(file.path)
- setFilteredDiffFiles([file.path])
- }, [setSelectedFilePath, setFilteredDiffFiles])
+ const handleDiffFileSelect = useCallback(
+ (file: { path: string }, _category: string) => {
+ setSelectedFilePath(file.path);
+ setFilteredDiffFiles([file.path]);
+ },
+ [setSelectedFilePath, setFilteredDiffFiles],
+ );
- const handleSelectNextFile = useCallback((filePath: string) => {
- setSelectedFilePath(filePath)
- setFilteredDiffFiles([filePath])
- }, [setSelectedFilePath, setFilteredDiffFiles])
+ const handleSelectNextFile = useCallback(
+ (filePath: string) => {
+ setSelectedFilePath(filePath);
+ setFilteredDiffFiles([filePath]);
+ },
+ [setSelectedFilePath, setFilteredDiffFiles],
+ );
const handleCommitSuccess = useCallback(() => {
- setSelectedFilePath(null)
- setFilteredDiffFiles(null)
- setParsedFileDiffs(null)
- setDiffContent(null)
- setPrefetchedFileContents({})
+ setSelectedFilePath(null);
+ setFilteredDiffFiles(null);
+ setParsedFileDiffs(null);
+ setDiffContent(null);
+ setPrefetchedFileContents({});
setDiffStats({
fileCount: 0,
additions: 0,
deletions: 0,
isLoading: true,
hasChanges: false,
- })
+ });
setTimeout(() => {
- fetchDiffStats()
- }, 500)
- }, [setSelectedFilePath, setFilteredDiffFiles, setParsedFileDiffs, setDiffContent, setPrefetchedFileContents, setDiffStats, fetchDiffStats])
+ fetchDiffStats();
+ }, 500);
+ }, [
+ setSelectedFilePath,
+ setFilteredDiffFiles,
+ setParsedFileDiffs,
+ setDiffContent,
+ setPrefetchedFileContents,
+ setDiffStats,
+ fetchDiffStats,
+ ]);
const handleCloseDiff = useCallback(() => {
// Use flushSync to reset activeTab synchronously before closing.
// This unmounts HistoryView's ContextMenu components in a single commit,
// preventing React 19 ref cleanup "Maximum update depth exceeded" error.
flushSync(() => {
- resetActiveTabRef.current?.()
- })
- setIsDiffSidebarOpen(false)
- setFilteredSubChatId(null)
- }, [setIsDiffSidebarOpen, setFilteredSubChatId])
+ resetActiveTabRef.current?.();
+ });
+ setIsDiffSidebarOpen(false);
+ setFilteredSubChatId(null);
+ }, [setIsDiffSidebarOpen, setFilteredSubChatId]);
const handleViewedCountChange = useCallback((count: number) => {
- setViewedCount(count)
- }, [])
-
- const contextValue = useMemo(() => ({
- selectedFilePath,
- filteredSubChatId,
- viewedCount,
- handleDiffFileSelect,
- handleSelectNextFile,
- handleCommitSuccess,
- handleCloseDiff,
- handleViewedCountChange,
- resetActiveTabRef,
- }), [selectedFilePath, filteredSubChatId, viewedCount, handleDiffFileSelect, handleSelectNextFile, handleCommitSuccess, handleCloseDiff, handleViewedCountChange])
+ setViewedCount(count);
+ }, []);
+
+ const contextValue = useMemo(
+ () => ({
+ selectedFilePath,
+ filteredSubChatId,
+ viewedCount,
+ handleDiffFileSelect,
+ handleSelectNextFile,
+ handleCommitSuccess,
+ handleCloseDiff,
+ handleViewedCountChange,
+ resetActiveTabRef,
+ }),
+ [
+ selectedFilePath,
+ filteredSubChatId,
+ viewedCount,
+ handleDiffFileSelect,
+ handleSelectNextFile,
+ handleCommitSuccess,
+ handleCloseDiff,
+ handleViewedCountChange,
+ ],
+ );
return (
{children}
- )
-})
+ );
+});
// ============================================================================
// DiffSidebarRenderer - renders the diff sidebar using context for state
@@ -1648,49 +1846,80 @@ const DiffStateProvider = memo(function DiffStateProvider({
// ============================================================================
interface DiffSidebarRendererProps {
- worktreePath: string | null
- chatId: string
- sandboxId: string | null
- repository: { owner: string; name: string } | null
- diffStats: { isLoading: boolean; hasChanges: boolean; fileCount: number; additions: number; deletions: number }
- diffContent: string | null
- parsedFileDiffs: ParsedDiffFile[] | null
- prefetchedFileContents: Record
- setDiffCollapseState: (state: { allCollapsed: boolean; allExpanded: boolean }) => void
- diffViewRef: React.RefObject
- diffSidebarRef: React.RefObject
- agentChat: { prUrl?: string; prNumber?: number } | null | undefined
- branchData: { current: string } | undefined
- gitStatus: { pushCount?: number; pullCount?: number; hasUpstream?: boolean; ahead?: number; behind?: number; staged?: any[]; unstaged?: any[]; untracked?: any[] } | undefined
- isGitStatusLoading: boolean
- isDiffSidebarOpen: boolean
- diffDisplayMode: "side-peek" | "center-peek" | "full-page"
- diffSidebarWidth: number
- handleReview: () => void
- isReviewing: boolean
- handleCreatePrDirect: () => void
- handleCreatePr: () => void
- isCreatingPr: boolean
- handleMergePr: () => void
- mergePrMutation: { isPending: boolean }
- handleRefreshGitStatus: () => void
- hasPrNumber: boolean
- isPrOpen: boolean
- hasMergeConflicts: boolean
- handleFixConflicts: () => void
- handleExpandAll: () => void
- handleCollapseAll: () => void
- diffMode: DiffViewMode
- setDiffMode: (mode: DiffViewMode) => void
- handleMarkAllViewed: () => void
- handleMarkAllUnviewed: () => void
- isDesktop: boolean
- isFullscreen: boolean
- setDiffDisplayMode: (mode: "side-peek" | "center-peek" | "full-page") => void
- handleCommitToPr: (selectedPaths?: string[]) => void
- isCommittingToPr: boolean
- subChatsWithFiles: Array<{ id: string; name: string; filePaths: string[]; fileCount: number }>
- setDiffStats: (stats: { isLoading: boolean; hasChanges: boolean; fileCount: number; additions: number; deletions: number }) => void
+ worktreePath: string | null;
+ chatId: string;
+ sandboxId: string | null;
+ repository: { owner: string; name: string } | null;
+ diffStats: {
+ isLoading: boolean;
+ hasChanges: boolean;
+ fileCount: number;
+ additions: number;
+ deletions: number;
+ };
+ diffContent: string | null;
+ parsedFileDiffs: ParsedDiffFile[] | null;
+ prefetchedFileContents: Record;
+ setDiffCollapseState: (state: {
+ allCollapsed: boolean;
+ allExpanded: boolean;
+ }) => void;
+ diffViewRef: React.RefObject;
+ diffSidebarRef: React.RefObject;
+ agentChat: { prUrl?: string; prNumber?: number } | null | undefined;
+ branchData: { current: string } | undefined;
+ gitStatus:
+ | {
+ pushCount?: number;
+ pullCount?: number;
+ hasUpstream?: boolean;
+ ahead?: number;
+ behind?: number;
+ staged?: any[];
+ unstaged?: any[];
+ untracked?: any[];
+ }
+ | undefined;
+ isGitStatusLoading: boolean;
+ isDiffSidebarOpen: boolean;
+ diffDisplayMode: "side-peek" | "center-peek" | "full-page";
+ diffSidebarWidth: number;
+ handleReview: () => void;
+ isReviewing: boolean;
+ handleCreatePrDirect: () => void;
+ handleCreatePr: () => void;
+ isCreatingPr: boolean;
+ handleMergePr: () => void;
+ mergePrMutation: { isPending: boolean };
+ handleRefreshGitStatus: () => void;
+ hasPrNumber: boolean;
+ isPrOpen: boolean;
+ hasMergeConflicts: boolean;
+ handleFixConflicts: () => void;
+ handleExpandAll: () => void;
+ handleCollapseAll: () => void;
+ diffMode: DiffViewMode;
+ setDiffMode: (mode: DiffViewMode) => void;
+ handleMarkAllViewed: () => void;
+ handleMarkAllUnviewed: () => void;
+ isDesktop: boolean;
+ isFullscreen: boolean;
+ setDiffDisplayMode: (mode: "side-peek" | "center-peek" | "full-page") => void;
+ handleCommitToPr: (selectedPaths?: string[]) => void;
+ isCommittingToPr: boolean;
+ subChatsWithFiles: Array<{
+ id: string;
+ name: string;
+ filePaths: string[];
+ fileCount: number;
+ }>;
+ setDiffStats: (stats: {
+ isLoading: boolean;
+ hasChanges: boolean;
+ fileCount: number;
+ additions: number;
+ deletions: number;
+ }) => void;
}
const DiffSidebarRenderer = memo(function DiffSidebarRenderer({
@@ -1739,14 +1968,18 @@ const DiffSidebarRenderer = memo(function DiffSidebarRenderer({
setDiffStats,
}: DiffSidebarRendererProps) {
// Get callbacks and state from context
- const { handleCloseDiff, viewedCount, handleViewedCountChange } = useDiffState()
+ const { handleCloseDiff, viewedCount, handleViewedCountChange } =
+ useDiffState();
// Width for responsive layouts - use stored width for sidebar, fixed for dialog/fullpage
- const effectiveWidth = diffDisplayMode === "side-peek"
- ? diffSidebarWidth
- : diffDisplayMode === "center-peek"
- ? 1200
- : typeof window !== 'undefined' ? window.innerWidth : 1200
+ const effectiveWidth =
+ diffDisplayMode === "side-peek"
+ ? diffSidebarWidth
+ : diffDisplayMode === "center-peek"
+ ? 1200
+ : typeof window !== "undefined"
+ ? window.innerWidth
+ : 1200;
const diffViewContent = (
- )
+ );
// Render based on display mode
if (diffDisplayMode === "side-peek") {
@@ -1849,7 +2082,7 @@ const DiffSidebarRenderer = memo(function DiffSidebarRenderer({
>
{diffViewContent}
- )
+ );
}
if (diffDisplayMode === "center-peek") {
@@ -1860,22 +2093,19 @@ const DiffSidebarRenderer = memo(function DiffSidebarRenderer({
>
{diffViewContent}
- )
+ );
}
if (diffDisplayMode === "full-page") {
return (
-
+
{diffViewContent}
- )
+ );
}
- return null
-})
+ return null;
+});
// Inner chat component - only rendered when chat object is ready
// Memoized to prevent re-renders when parent state changes (e.g., selectedFilePath)
@@ -1902,259 +2132,261 @@ const ChatViewInner = memo(function ChatViewInner({
existingPrUrl,
isActive = true,
}: {
- chat: Chat
- subChatId: string
- parentChatId: string
- isFirstSubChat: boolean
- onAutoRename: (userMessage: string, subChatId: string) => void
- onCreateNewSubChat?: () => void
- refreshDiff?: () => void
- teamId?: string
- repository?: string
- streamId?: string | null
- isMobile?: boolean
- sandboxSetupStatus?: "cloning" | "ready" | "error"
- sandboxSetupError?: string
- onRetrySetup?: () => void
- isSubChatsSidebarOpen?: boolean
- sandboxId?: string
- projectPath?: string
- isArchived?: boolean
- onRestoreWorkspace?: () => void
- existingPrUrl?: string | null
- isActive?: boolean
+ chat: Chat;
+ subChatId: string;
+ parentChatId: string;
+ isFirstSubChat: boolean;
+ onAutoRename: (userMessage: string, subChatId: string) => void;
+ onCreateNewSubChat?: () => void;
+ refreshDiff?: () => void;
+ teamId?: string;
+ repository?: string;
+ streamId?: string | null;
+ isMobile?: boolean;
+ sandboxSetupStatus?: "cloning" | "ready" | "error";
+ sandboxSetupError?: string;
+ onRetrySetup?: () => void;
+ isSubChatsSidebarOpen?: boolean;
+ sandboxId?: string;
+ projectPath?: string;
+ isArchived?: boolean;
+ onRestoreWorkspace?: () => void;
+ existingPrUrl?: string | null;
+ isActive?: boolean;
}) {
- const hasTriggeredRenameRef = useRef(false)
- const hasTriggeredAutoGenerateRef = useRef(false)
+ const hasTriggeredRenameRef = useRef(false);
+ const hasTriggeredAutoGenerateRef = useRef(false);
// Keep isActive in ref for use in callbacks (avoid stale closures)
- const isActiveRef = useRef(isActive)
- isActiveRef.current = isActive
+ const isActiveRef = useRef(isActive);
+ isActiveRef.current = isActive;
// Scroll management state (like canvas chat)
// Using only ref to avoid re-renders on scroll
- const shouldAutoScrollRef = useRef(true)
- const isAutoScrollingRef = useRef(false) // Flag to ignore scroll events caused by auto-scroll
- const isInitializingScrollRef = useRef(false) // Flag to ignore scroll events during scroll initialization (content loading)
- const hasUnapprovedPlanRef = useRef(false) // Track unapproved plan state for scroll initialization
- const chatContainerRef = useRef(null)
+ const shouldAutoScrollRef = useRef(true);
+ const isAutoScrollingRef = useRef(false); // Flag to ignore scroll events caused by auto-scroll
+ const isInitializingScrollRef = useRef(false); // Flag to ignore scroll events during scroll initialization (content loading)
+ const hasUnapprovedPlanRef = useRef(false); // Track unapproved plan state for scroll initialization
+ const chatContainerRef = useRef(null);
// Cleanup isAutoScrollingRef on unmount to prevent stuck state
useEffect(() => {
return () => {
- isAutoScrollingRef.current = false
- }
- }, [])
+ isAutoScrollingRef.current = false;
+ };
+ }, []);
// Track chat container height via CSS custom property (no re-renders)
- const chatContainerObserverRef = useRef(null)
+ const chatContainerObserverRef = useRef(null);
- const editorRef = useRef(null)
- const fileInputRef = useRef(null)
- const questionRef = useRef(null)
- const prevChatKeyRef = useRef(null)
- const prevSubChatIdRef = useRef(null)
+ const editorRef = useRef(null);
+ const fileInputRef = useRef(null);
+ const questionRef = useRef(null);
+ const prevChatKeyRef = useRef(null);
+ const prevSubChatIdRef = useRef(null);
// Consume pending mentions from external components (e.g. MCP widget in sidebar)
- const [pendingMention, setPendingMention] = useAtom(pendingMentionAtom)
+ const [pendingMention, setPendingMention] = useAtom(pendingMentionAtom);
useEffect(() => {
if (pendingMention) {
- editorRef.current?.insertMention(pendingMention)
- editorRef.current?.focus()
- setPendingMention(null)
+ editorRef.current?.insertMention(pendingMention);
+ editorRef.current?.focus();
+ setPendingMention(null);
}
- }, [pendingMention, setPendingMention])
+ }, [pendingMention, setPendingMention]);
// TTS playback rate state (persists across messages and sessions via localStorage)
const [ttsPlaybackRate, setTtsPlaybackRate] = useState(() => {
if (typeof window !== "undefined") {
- const saved = localStorage.getItem("tts-playback-rate")
+ const saved = localStorage.getItem("tts-playback-rate");
if (saved && PLAYBACK_SPEEDS.includes(Number(saved) as PlaybackSpeed)) {
- return Number(saved) as PlaybackSpeed
+ return Number(saved) as PlaybackSpeed;
}
}
- return 1
- })
+ return 1;
+ });
// Save playback rate to localStorage when it changes
const handlePlaybackRateChange = useCallback((rate: PlaybackSpeed) => {
- setTtsPlaybackRate(rate)
- localStorage.setItem("tts-playback-rate", String(rate))
- }, [])
+ setTtsPlaybackRate(rate);
+ localStorage.setItem("tts-playback-rate", String(rate));
+ }, []);
// PR creation loading state - from atom to allow resetting after message sent
- const setIsCreatingPr = useSetAtom(isCreatingPrAtom)
+ const setIsCreatingPr = useSetAtom(isCreatingPrAtom);
// Rollback state
- const [isRollingBack, setIsRollingBack] = useState(false)
+ const [isRollingBack, setIsRollingBack] = useState(false);
// Check if user is at bottom of chat (like canvas)
const isAtBottom = useCallback(() => {
- const container = chatContainerRef.current
- if (!container) return true
- const threshold = 50 // pixels from bottom
+ const container = chatContainerRef.current;
+ if (!container) return true;
+ const threshold = 50; // pixels from bottom
return (
container.scrollHeight - container.scrollTop - container.clientHeight <=
threshold
- )
- }, [])
-
+ );
+ }, []);
// Track previous scroll position to detect scroll direction
- const prevScrollTopRef = useRef(0)
+ const prevScrollTopRef = useRef(0);
// Handle scroll events to detect user scrolling
// Updates shouldAutoScrollRef based on scroll direction
// Using refs only to avoid re-renders on scroll
const handleScroll = useCallback(() => {
// Skip scroll handling for inactive tabs (keep-alive)
- if (!isActiveRef.current) return
+ if (!isActiveRef.current) return;
- const container = chatContainerRef.current
- if (!container) return
+ const container = chatContainerRef.current;
+ if (!container) return;
- const currentScrollTop = container.scrollTop
- const prevScrollTop = prevScrollTopRef.current
- prevScrollTopRef.current = currentScrollTop
+ const currentScrollTop = container.scrollTop;
+ const prevScrollTop = prevScrollTopRef.current;
+ prevScrollTopRef.current = currentScrollTop;
// Ignore scroll events during initialization (content loading)
- if (isInitializingScrollRef.current) return
+ if (isInitializingScrollRef.current) return;
// If user scrolls UP - disable auto-scroll immediately
// This works even during auto-scroll animation (user intent takes priority)
if (currentScrollTop < prevScrollTop) {
- shouldAutoScrollRef.current = false
- return
+ shouldAutoScrollRef.current = false;
+ return;
}
// Ignore other scroll direction checks during auto-scroll animation
- if (isAutoScrollingRef.current) return
+ if (isAutoScrollingRef.current) return;
// If user scrolls DOWN and reaches bottom - enable auto-scroll
- shouldAutoScrollRef.current = isAtBottom()
- }, [isAtBottom])
+ shouldAutoScrollRef.current = isAtBottom();
+ }, [isAtBottom]);
// Scroll to bottom handler with ease-in-out animation
const scrollToBottom = useCallback(() => {
- const container = chatContainerRef.current
- if (!container) return
+ const container = chatContainerRef.current;
+ if (!container) return;
- isAutoScrollingRef.current = true
- shouldAutoScrollRef.current = true
+ isAutoScrollingRef.current = true;
+ shouldAutoScrollRef.current = true;
- const start = container.scrollTop
- const duration = 300 // ms
- const startTime = performance.now()
+ const start = container.scrollTop;
+ const duration = 300; // ms
+ const startTime = performance.now();
// Ease-in-out cubic function
const easeInOutCubic = (t: number) =>
- t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2
+ t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
const animateScroll = (currentTime: number) => {
- const elapsed = currentTime - startTime
- const progress = Math.min(elapsed / duration, 1)
- const easedProgress = easeInOutCubic(progress)
+ const elapsed = currentTime - startTime;
+ const progress = Math.min(elapsed / duration, 1);
+ const easedProgress = easeInOutCubic(progress);
// Calculate end on each frame to handle dynamic content
- const end = container.scrollHeight - container.clientHeight
- container.scrollTop = start + (end - start) * easedProgress
+ const end = container.scrollHeight - container.clientHeight;
+ container.scrollTop = start + (end - start) * easedProgress;
if (progress < 1) {
- requestAnimationFrame(animateScroll)
+ requestAnimationFrame(animateScroll);
} else {
// Ensure we're at the absolute bottom
- container.scrollTop = container.scrollHeight
- isAutoScrollingRef.current = false
+ container.scrollTop = container.scrollHeight;
+ isAutoScrollingRef.current = false;
}
- }
+ };
- requestAnimationFrame(animateScroll)
- }, [])
+ requestAnimationFrame(animateScroll);
+ }, []);
// tRPC utils for cache invalidation
- const utils = api.useUtils()
+ const utils = api.useUtils();
// Get sub-chat name from store
const subChatName = useAgentSubChatStore(
(state) => state.allSubChats.find((sc) => sc.id === subChatId)?.name || "",
- )
+ );
// Mutation for renaming sub-chat
const renameSubChatMutation = api.agents.renameSubChat.useMutation({
onError: (error) => {
if (error.data?.code === "NOT_FOUND") {
- toast.error("Send a message first before renaming this chat")
+ toast.error("Send a message first before renaming this chat");
} else {
- toast.error("Failed to rename chat")
+ toast.error("Failed to rename chat");
}
},
- })
+ });
// Handler for renaming sub-chat
// Using ref for mutation to avoid callback recreation
- const renameSubChatMutationRef = useRef(renameSubChatMutation)
- renameSubChatMutationRef.current = renameSubChatMutation
- const subChatNameRef = useRef(subChatName)
- subChatNameRef.current = subChatName
+ const renameSubChatMutationRef = useRef(renameSubChatMutation);
+ renameSubChatMutationRef.current = renameSubChatMutation;
+ const subChatNameRef = useRef(subChatName);
+ subChatNameRef.current = subChatName;
const handleRenameSubChat = useCallback(
async (newName: string) => {
// Optimistic update in store
- useAgentSubChatStore.getState().updateSubChatName(subChatId, newName)
+ useAgentSubChatStore.getState().updateSubChatName(subChatId, newName);
// Save to database
try {
await renameSubChatMutationRef.current.mutateAsync({
subChatId,
name: newName,
- })
+ });
} catch {
// Revert on error (toast shown by mutation onError)
useAgentSubChatStore
.getState()
- .updateSubChatName(subChatId, subChatNameRef.current || "New Chat")
+ .updateSubChatName(subChatId, subChatNameRef.current || "New Chat");
}
},
[subChatId],
- )
+ );
// Plan mode state (per-subChat using atomFamily)
- const [subChatMode, setSubChatMode] = useAtom(subChatModeAtomFamily(subChatId))
+ const [subChatMode, setSubChatMode] = useAtom(
+ subChatModeAtomFamily(subChatId),
+ );
// Mutation for updating sub-chat mode in database
const updateSubChatModeMutation = api.agents.updateSubChatMode.useMutation({
onSuccess: () => {
// Invalidate to refetch with new mode from DB
- utils.agents.getAgentChat.invalidate({ chatId: parentChatId })
+ utils.agents.getAgentChat.invalidate({ chatId: parentChatId });
},
onError: (error, variables) => {
// Don't revert if sub-chat not found in DB - it may not be persisted yet
// This is expected for new sub-chats that haven't been saved to DB
if (error.message === "Sub-chat not found") {
- console.warn("Sub-chat not found in DB, keeping local mode state")
- return
+ console.warn("Sub-chat not found in DB, keeping local mode state");
+ return;
}
// Revert local state on error to maintain sync with database
- const revertedMode: AgentMode = variables.mode === "plan" ? "agent" : "plan"
- setSubChatMode(revertedMode)
+ const revertedMode: AgentMode =
+ variables.mode === "plan" ? "agent" : "plan";
+ setSubChatMode(revertedMode);
// Also update store for consistency
useAgentSubChatStore
.getState()
- .updateSubChatMode(variables.subChatId, revertedMode)
- console.error("Failed to update sub-chat mode:", error.message)
+ .updateSubChatMode(variables.subChatId, revertedMode);
+ console.error("Failed to update sub-chat mode:", error.message);
},
- })
+ });
// Sync atomFamily mode to Zustand store on mount/subChatId change
// This ensures the sidebar shows the correct mode icon
useEffect(() => {
if (subChatId) {
// Read mode directly from atomFamily to ensure we get the correct value
- const mode = appStore.get(subChatModeAtomFamily(subChatId))
- useAgentSubChatStore.getState().updateSubChatMode(subChatId, mode)
+ const mode = appStore.get(subChatModeAtomFamily(subChatId));
+ useAgentSubChatStore.getState().updateSubChatMode(subChatId, mode);
}
- }, [subChatId])
+ }, [subChatId]);
// NOTE: We no longer clear caches on deactivation.
// With proper subChatId isolation, each chat's caches are separate.
@@ -2164,45 +2396,51 @@ const ChatViewInner = memo(function ChatViewInner({
// CRITICAL: Use a delayed cleanup to avoid clearing caches during temporary unmount/remount
// (e.g., React StrictMode, HMR, or parent re-render causing component remount)
useEffect(() => {
- const currentSubChatId = subChatId
+ const currentSubChatId = subChatId;
return () => {
// Delay cache clearing to allow remount to happen first
// If the component remounts with the same subChatId, the sync will repopulate the atoms
// If it truly unmounts, the timeout will clear the caches
const timeoutId = setTimeout(() => {
- clearSubChatCaches(currentSubChatId)
- }, 100)
+ clearSubChatCaches(currentSubChatId);
+ }, 100);
// Store the timeout so it can be cancelled if the component remounts
// We use a global map to track pending cleanups
- ;(window as any).__pendingCacheCleanups = (window as any).__pendingCacheCleanups || new Map()
- ;(window as any).__pendingCacheCleanups.set(currentSubChatId, timeoutId)
- }
- }, [subChatId])
+ (window as any).__pendingCacheCleanups =
+ (window as any).__pendingCacheCleanups || new Map();
+ (window as any).__pendingCacheCleanups.set(currentSubChatId, timeoutId);
+ };
+ }, [subChatId]);
// Cancel pending cleanup if we remount with the same subChatId
useEffect(() => {
- const pendingCleanups = (window as any).__pendingCacheCleanups as Map | undefined
+ const pendingCleanups = (window as any).__pendingCacheCleanups as
+ | Map
+ | undefined;
if (pendingCleanups?.has(subChatId)) {
- clearTimeout(pendingCleanups.get(subChatId))
- pendingCleanups.delete(subChatId)
+ clearTimeout(pendingCleanups.get(subChatId));
+ pendingCleanups.delete(subChatId);
}
- }, [subChatId])
+ }, [subChatId]);
// Handle mode changes - updates atomFamily, store, and database together
// No effect needed - this is called directly when user toggles mode
- const handleModeChange = useCallback((newMode: AgentMode) => {
- // Update atomFamily (source of truth for UI)
- setSubChatMode(newMode)
+ const handleModeChange = useCallback(
+ (newMode: AgentMode) => {
+ // Update atomFamily (source of truth for UI)
+ setSubChatMode(newMode);
- // Update Zustand store (for other components that read from store)
- useAgentSubChatStore.getState().updateSubChatMode(subChatId, newMode)
+ // Update Zustand store (for other components that read from store)
+ useAgentSubChatStore.getState().updateSubChatMode(subChatId, newMode);
- // Save to database (skip temp IDs that haven't been persisted yet)
- if (!subChatId.startsWith("temp-")) {
- updateSubChatModeMutation.mutate({ subChatId, mode: newMode })
- }
- }, [subChatId, setSubChatMode, updateSubChatModeMutation])
+ // Save to database (skip temp IDs that haven't been persisted yet)
+ if (!subChatId.startsWith("temp-")) {
+ updateSubChatModeMutation.mutate({ subChatId, mode: newMode });
+ }
+ },
+ [subChatId, setSubChatMode, updateSubChatModeMutation],
+ );
// File/image upload hook
const {
@@ -2215,7 +2453,7 @@ const ChatViewInner = memo(function ChatViewInner({
isUploading,
setImagesFromDraft,
setFilesFromDraft,
- } = useAgentsFileUpload()
+ } = useAgentsFileUpload();
// Text context selection hook (for selecting text from assistant messages and diff)
const {
@@ -2231,7 +2469,7 @@ const ChatViewInner = memo(function ChatViewInner({
diffTextContextsRef,
setTextContextsFromDraft,
setDiffTextContextsFromDraft,
- } = useTextContextSelection()
+ } = useTextContextSelection();
// Pasted text files (large pasted text saved as files)
const {
@@ -2240,93 +2478,93 @@ const ChatViewInner = memo(function ChatViewInner({
removePastedText,
clearPastedTexts,
pastedTextsRef,
- } = usePastedTextFiles(subChatId)
+ } = usePastedTextFiles(subChatId);
// File contents cache - stores content for file mentions (keyed by mentionId)
// This content gets added to the prompt when sending, without showing a separate card
- const fileContentsRef = useRef>(new Map())
+ const fileContentsRef = useRef>(new Map());
const cacheFileContent = useCallback((mentionId: string, content: string) => {
- fileContentsRef.current.set(mentionId, content)
- }, [])
+ fileContentsRef.current.set(mentionId, content);
+ }, []);
const clearFileContents = useCallback(() => {
- fileContentsRef.current.clear()
- }, [])
+ fileContentsRef.current.clear();
+ }, []);
// Clear file contents cache when switching subChats to prevent stale data
useEffect(() => {
- fileContentsRef.current.clear()
- }, [subChatId])
+ fileContentsRef.current.clear();
+ }, [subChatId]);
// Quick comment state
const [quickCommentState, setQuickCommentState] = useState<{
- selectedText: string
- source: TextSelectionSource
- rect: DOMRect
- } | null>(null)
+ selectedText: string;
+ source: TextSelectionSource;
+ rect: DOMRect;
+ } | null>(null);
// Message queue for sending messages while streaming
- const queue = useMessageQueueStore((s) => s.queues[subChatId] ?? EMPTY_QUEUE)
- const addToQueue = useMessageQueueStore((s) => s.addToQueue)
- const removeFromQueue = useMessageQueueStore((s) => s.removeFromQueue)
- const popItemFromQueue = useMessageQueueStore((s) => s.popItem)
+ const queue = useMessageQueueStore((s) => s.queues[subChatId] ?? EMPTY_QUEUE);
+ const addToQueue = useMessageQueueStore((s) => s.addToQueue);
+ const removeFromQueue = useMessageQueueStore((s) => s.removeFromQueue);
+ const popItemFromQueue = useMessageQueueStore((s) => s.popItem);
// Plan approval pending state (for tool approval loading)
const [planApprovalPending, setPlanApprovalPending] = useState<
Record
- >({})
+ >({});
// Track chat changes for rename trigger reset
- const chatRef = useRef | null>(null)
+ const chatRef = useRef | null>(null);
if (prevSubChatIdRef.current !== subChatId) {
- hasTriggeredRenameRef.current = false // Reset on sub-chat change
- hasTriggeredAutoGenerateRef.current = false // Reset auto-generate on sub-chat change
- prevSubChatIdRef.current = subChatId
+ hasTriggeredRenameRef.current = false; // Reset on sub-chat change
+ hasTriggeredAutoGenerateRef.current = false; // Reset auto-generate on sub-chat change
+ prevSubChatIdRef.current = subChatId;
}
- chatRef.current = chat
+ chatRef.current = chat;
// Restore draft when subChatId changes (switching between sub-chats)
- const prevSubChatIdForDraftRef = useRef(null)
+ const prevSubChatIdForDraftRef = useRef(null);
useEffect(() => {
// Restore full draft (text + attachments + text contexts) for new sub-chat
const savedDraft = parentChatId
? getSubChatDraftFull(parentChatId, subChatId)
- : null
+ : null;
if (savedDraft) {
// Restore text
if (savedDraft.text) {
- editorRef.current?.setValue(savedDraft.text)
+ editorRef.current?.setValue(savedDraft.text);
} else {
- editorRef.current?.clear()
+ editorRef.current?.clear();
}
// Restore images
if (savedDraft.images.length > 0) {
- setImagesFromDraft(savedDraft.images)
+ setImagesFromDraft(savedDraft.images);
} else {
- clearAll()
+ clearAll();
}
// Restore files
if (savedDraft.files.length > 0) {
- setFilesFromDraft(savedDraft.files)
+ setFilesFromDraft(savedDraft.files);
}
// Restore text contexts
if (savedDraft.textContexts.length > 0) {
- setTextContextsFromDraft(savedDraft.textContexts)
+ setTextContextsFromDraft(savedDraft.textContexts);
} else {
- clearTextContexts()
+ clearTextContexts();
}
} else if (
prevSubChatIdForDraftRef.current &&
prevSubChatIdForDraftRef.current !== subChatId
) {
// Clear everything when switching to a sub-chat with no draft
- editorRef.current?.clear()
- clearAll()
- clearTextContexts()
+ editorRef.current?.clear();
+ clearAll();
+ clearTextContexts();
}
- prevSubChatIdForDraftRef.current = subChatId
+ prevSubChatIdForDraftRef.current = subChatId;
}, [
subChatId,
parentChatId,
@@ -2335,279 +2573,320 @@ const ChatViewInner = memo(function ChatViewInner({
setTextContextsFromDraft,
clearAll,
clearTextContexts,
- ])
+ ]);
// Use subChatId as stable key to prevent HMR-induced duplicate resume requests
// resume: !!streamId to reconnect to active streams (background streaming support)
- const { messages, sendMessage, status, stop, regenerate, setMessages } = useChat({
- id: subChatId,
- chat,
- resume: !!streamId,
- experimental_throttle: 50, // Throttle updates to reduce re-renders during streaming
- })
+ const { messages, sendMessage, status, stop, regenerate, setMessages } =
+ useChat({
+ id: subChatId,
+ chat,
+ resume: !!streamId,
+ experimental_throttle: 50, // Throttle updates to reduce re-renders during streaming
+ });
// Refs for useChat functions to keep callbacks stable across renders
- const sendMessageRef = useRef(sendMessage)
- sendMessageRef.current = sendMessage
- const stopRef = useRef(stop)
- stopRef.current = stop
+ const sendMessageRef = useRef(sendMessage);
+ sendMessageRef.current = sendMessage;
+ const stopRef = useRef(stop);
+ stopRef.current = stop;
- const isStreaming = status === "streaming" || status === "submitted"
+ const isStreaming = status === "streaming" || status === "submitted";
// Ref for isStreaming to use in callbacks/effects that need fresh value
- const isStreamingRef = useRef(isStreaming)
- isStreamingRef.current = isStreaming
+ const isStreamingRef = useRef(isStreaming);
+ isStreamingRef.current = isStreaming;
// Track compacting status from SDK
- const compactingSubChats = useAtomValue(compactingSubChatsAtom)
- const isCompacting = compactingSubChats.has(subChatId)
+ const compactingSubChats = useAtomValue(compactingSubChatsAtom);
+ const isCompacting = compactingSubChats.has(subChatId);
// Desktop/fullscreen state for window drag region
- const isDesktop = useAtomValue(isDesktopAtom)
- const isFullscreen = useAtomValue(isFullscreenAtom)
+ const isDesktop = useAtomValue(isDesktopAtom);
+ const isFullscreen = useAtomValue(isFullscreenAtom);
// Handler to trigger manual context compaction
const handleCompact = useCallback(() => {
- if (isStreamingRef.current) return // Can't compact while streaming
+ if (isStreamingRef.current) return; // Can't compact while streaming
sendMessageRef.current({
role: "user",
parts: [{ type: "text", text: "/compact" }],
- })
- }, [])
+ });
+ }, []);
// Handler to stop streaming - memoized to prevent ChatInputArea re-renders
const handleStop = useCallback(async () => {
// Mark as manually aborted to prevent completion sound
- agentChatStore.setManuallyAborted(subChatId, true)
- await stopRef.current()
- }, [subChatId])
+ agentChatStore.setManuallyAborted(subChatId, true);
+ await stopRef.current();
+ }, [subChatId]);
// Wrapper for addTextContext that handles TextSelectionSource
- const addTextContext = useCallback((text: string, source: TextSelectionSource) => {
- if (source.type === "assistant-message") {
- addTextContextOriginal(text, source.messageId)
- } else if (source.type === "diff") {
- addDiffTextContext(text, source.filePath, source.lineNumber, source.lineType)
- } else if (source.type === "tool-edit") {
- // Tool edit selections are treated as code selections (similar to diff)
- addDiffTextContext(text, source.filePath)
- } else if (source.type === "plan") {
- // Plan selections are treated as code selections (similar to diff)
- addDiffTextContext(text, source.planPath)
- } else if (source.type === "file-viewer") {
- // File viewer selections are treated as code selections
- addDiffTextContext(text, source.filePath)
- }
- }, [addTextContextOriginal, addDiffTextContext])
+ const addTextContext = useCallback(
+ (text: string, source: TextSelectionSource) => {
+ if (source.type === "assistant-message") {
+ addTextContextOriginal(text, source.messageId);
+ } else if (source.type === "diff") {
+ addDiffTextContext(
+ text,
+ source.filePath,
+ source.lineNumber,
+ source.lineType,
+ );
+ } else if (source.type === "tool-edit") {
+ // Tool edit selections are treated as code selections (similar to diff)
+ addDiffTextContext(text, source.filePath);
+ } else if (source.type === "plan") {
+ // Plan selections are treated as code selections (similar to diff)
+ addDiffTextContext(text, source.planPath);
+ } else if (source.type === "file-viewer") {
+ // File viewer selections are treated as code selections
+ addDiffTextContext(text, source.filePath);
+ }
+ },
+ [addTextContextOriginal, addDiffTextContext],
+ );
// Focus handler for text selection popover - focus chat input after adding to context
const handleFocusInput = useCallback(() => {
- editorRef.current?.focus()
- }, [])
+ editorRef.current?.focus();
+ }, []);
// Listen for file-viewer "Add to Context" from the custom context menu
useEffect(() => {
const handler = (e: Event) => {
const detail = (e as CustomEvent).detail as {
- text: string
- source: TextSelectionSource
- }
+ text: string;
+ source: TextSelectionSource;
+ };
if (detail.text && detail.source) {
- addTextContext(detail.text, detail.source)
- editorRef.current?.focus()
+ addTextContext(detail.text, detail.source);
+ editorRef.current?.focus();
}
- }
- window.addEventListener("file-viewer-add-to-context", handler)
- return () => window.removeEventListener("file-viewer-add-to-context", handler)
- }, [addTextContext])
+ };
+ window.addEventListener("file-viewer-add-to-context", handler);
+ return () =>
+ window.removeEventListener("file-viewer-add-to-context", handler);
+ }, [addTextContext]);
// Handler for quick comment trigger from popover
- const handleQuickComment = useCallback((text: string, source: TextSelectionSource, rect: DOMRect) => {
- setQuickCommentState({ selectedText: text, source, rect })
- }, [])
+ const handleQuickComment = useCallback(
+ (text: string, source: TextSelectionSource, rect: DOMRect) => {
+ setQuickCommentState({ selectedText: text, source, rect });
+ },
+ [],
+ );
// Handler for quick comment submission
- const handleQuickCommentSubmit = useCallback((comment: string, selectedText: string, source: TextSelectionSource) => {
- // Format message with mention token + comment
- const preview = selectedText.slice(0, 50).replace(/[:\[\]]/g, "")
- const encodedText = utf8ToBase64(selectedText)
-
- let mentionToken: string
- if (source.type === "diff") {
- const lineNum = source.lineNumber || 0
- mentionToken = `@[${MENTION_PREFIXES.DIFF}${source.filePath}:${lineNum}:${preview}:${encodedText}]`
- } else if (source.type === "tool-edit") {
- // Tool edit is treated as code/diff context
- mentionToken = `@[${MENTION_PREFIXES.DIFF}${source.filePath}:0:${preview}:${encodedText}]`
- } else {
- mentionToken = `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`
- }
+ const handleQuickCommentSubmit = useCallback(
+ (comment: string, selectedText: string, source: TextSelectionSource) => {
+ // Format message with mention token + comment
+ const preview = selectedText.slice(0, 50).replace(/[:\[\]]/g, "");
+ const encodedText = utf8ToBase64(selectedText);
+
+ let mentionToken: string;
+ if (source.type === "diff") {
+ const lineNum = source.lineNumber || 0;
+ mentionToken = `@[${MENTION_PREFIXES.DIFF}${source.filePath}:${lineNum}:${preview}:${encodedText}]`;
+ } else if (source.type === "tool-edit") {
+ // Tool edit is treated as code/diff context
+ mentionToken = `@[${MENTION_PREFIXES.DIFF}${source.filePath}:0:${preview}:${encodedText}]`;
+ } else {
+ mentionToken = `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`;
+ }
- const message = `${mentionToken} ${comment}`
+ const message = `${mentionToken} ${comment}`;
- // If streaming, add to queue
- if (isStreamingRef.current) {
- const item = createQueueItem(generateQueueId(), message)
- addToQueue(subChatId, item)
- toast.success("Reply queued", { description: "Will be sent when current response completes" })
- } else {
- // Send directly
- sendMessageRef.current({
- role: "user",
- parts: [{ type: "text", text: message }],
- })
- toast.success("Reply sent")
- }
+ // If streaming, add to queue
+ if (isStreamingRef.current) {
+ const item = createQueueItem(generateQueueId(), message);
+ addToQueue(subChatId, item);
+ toast.success("Reply queued", {
+ description: "Will be sent when current response completes",
+ });
+ } else {
+ // Send directly
+ sendMessageRef.current({
+ role: "user",
+ parts: [{ type: "text", text: message }],
+ });
+ toast.success("Reply sent");
+ }
- // Clear state and selection
- setQuickCommentState(null)
- window.getSelection()?.removeAllRanges()
- }, [addToQueue, subChatId])
+ // Clear state and selection
+ setQuickCommentState(null);
+ window.getSelection()?.removeAllRanges();
+ },
+ [addToQueue, subChatId],
+ );
// Handler for quick comment cancel
const handleQuickCommentCancel = useCallback(() => {
- setQuickCommentState(null)
- }, [])
+ setQuickCommentState(null);
+ }, []);
// Sync loading status to atom for UI indicators
// When streaming starts, set loading. When it stops, clear loading.
// Unseen changes, sound notification, and sidebar refresh are handled in onFinish callback
- const setLoadingSubChats = useSetAtom(loadingSubChatsAtom)
+ const setLoadingSubChats = useSetAtom(loadingSubChatsAtom);
useEffect(() => {
- const storedParentChatId = agentChatStore.getParentChatId(subChatId)
- if (!storedParentChatId) return
+ const storedParentChatId = agentChatStore.getParentChatId(subChatId);
+ if (!storedParentChatId) return;
if (isStreaming) {
- setLoading(setLoadingSubChats, subChatId, storedParentChatId)
+ setLoading(setLoadingSubChats, subChatId, storedParentChatId);
} else {
- clearLoading(setLoadingSubChats, subChatId)
+ clearLoading(setLoadingSubChats, subChatId);
}
- }, [isStreaming, subChatId, setLoadingSubChats])
+ }, [isStreaming, subChatId, setLoadingSubChats]);
// Watch for pending PR message and send it
// Only the active tab should consume pending messages to prevent
// inactive ChatViewInner instances from stealing the message
- const [pendingPrMessage, setPendingPrMessage] = useAtom(pendingPrMessageAtom)
+ const [pendingPrMessage, setPendingPrMessage] = useAtom(pendingPrMessageAtom);
useEffect(() => {
if (pendingPrMessage && !isStreaming && isActive) {
// Clear the pending message immediately to prevent double-sending
- setPendingPrMessage(null)
+ setPendingPrMessage(null);
// Send the message to Claude
sendMessage({
role: "user",
parts: [{ type: "text", text: pendingPrMessage }],
- })
+ });
// Reset creating PR state after message is sent
- setIsCreatingPr(false)
+ setIsCreatingPr(false);
}
- }, [pendingPrMessage, isStreaming, isActive, sendMessage, setPendingPrMessage])
+ }, [
+ pendingPrMessage,
+ isStreaming,
+ isActive,
+ sendMessage,
+ setPendingPrMessage,
+ ]);
// Watch for pending Review message and send it
const [pendingReviewMessage, setPendingReviewMessage] = useAtom(
pendingReviewMessageAtom,
- )
+ );
useEffect(() => {
if (pendingReviewMessage && !isStreaming && isActive) {
// Clear the pending message immediately to prevent double-sending
- setPendingReviewMessage(null)
+ setPendingReviewMessage(null);
// Send the message to Claude
sendMessage({
role: "user",
parts: [{ type: "text", text: pendingReviewMessage }],
- })
+ });
}
- }, [pendingReviewMessage, isStreaming, isActive, sendMessage, setPendingReviewMessage])
+ }, [
+ pendingReviewMessage,
+ isStreaming,
+ isActive,
+ sendMessage,
+ setPendingReviewMessage,
+ ]);
// Watch for pending conflict resolution message and send it
const [pendingConflictMessage, setPendingConflictMessage] = useAtom(
pendingConflictResolutionMessageAtom,
- )
+ );
useEffect(() => {
if (pendingConflictMessage && !isStreaming && isActive) {
// Clear the pending message immediately to prevent double-sending
- setPendingConflictMessage(null)
+ setPendingConflictMessage(null);
// Send the message to Claude
sendMessage({
role: "user",
parts: [{ type: "text", text: pendingConflictMessage }],
- })
+ });
}
- }, [pendingConflictMessage, isStreaming, isActive, sendMessage, setPendingConflictMessage])
+ }, [
+ pendingConflictMessage,
+ isStreaming,
+ isActive,
+ sendMessage,
+ setPendingConflictMessage,
+ ]);
// Handle pending "Build plan" from sidebar (atom - effect is defined after handleApprovePlan)
const [pendingBuildPlanSubChatId, setPendingBuildPlanSubChatId] = useAtom(
pendingBuildPlanSubChatIdAtom,
- )
+ );
// Pending user questions from AskUserQuestion tool
const [pendingQuestionsMap, setPendingQuestionsMap] = useAtom(
pendingUserQuestionsAtom,
- )
+ );
// Get pending questions for this specific subChat
- const pendingQuestions = pendingQuestionsMap.get(subChatId) ?? null
+ const pendingQuestions = pendingQuestionsMap.get(subChatId) ?? null;
// Expired user questions (timed out but still answerable as normal messages)
const [expiredQuestionsMap, setExpiredQuestionsMap] = useAtom(
expiredUserQuestionsAtom,
- )
- const expiredQuestions = expiredQuestionsMap.get(subChatId) ?? null
+ );
+ const expiredQuestions = expiredQuestionsMap.get(subChatId) ?? null;
// Unified display questions: prefer pending (live), fall back to expired
- const displayQuestions = pendingQuestions ?? expiredQuestions
- const isQuestionExpired = !pendingQuestions && !!expiredQuestions
+ const displayQuestions = pendingQuestions ?? expiredQuestions;
+ const isQuestionExpired = !pendingQuestions && !!expiredQuestions;
// Track whether chat input has content (for custom text with questions)
- const [inputHasContent, setInputHasContent] = useState(false)
+ const [inputHasContent, setInputHasContent] = useState(false);
// Memoize the last assistant message to avoid unnecessary recalculations
const lastAssistantMessage = useMemo(
() => messages.findLast((m) => m.role === "assistant"),
[messages],
- )
+ );
// Pre-compute token data for ChatInputArea to avoid passing unstable messages array
// This prevents ChatInputArea from re-rendering on every streaming chunk
const messageTokenData = useMemo(() => {
- let totalInputTokens = 0
- let totalOutputTokens = 0
- let totalCostUsd = 0
+ let totalInputTokens = 0;
+ let totalOutputTokens = 0;
+ let totalCostUsd = 0;
for (const msg of messages) {
if (msg.metadata) {
- totalInputTokens += msg.metadata.inputTokens || 0
- totalOutputTokens += msg.metadata.outputTokens || 0
- totalCostUsd += msg.metadata.totalCostUsd || 0
+ totalInputTokens += msg.metadata.inputTokens || 0;
+ totalOutputTokens += msg.metadata.outputTokens || 0;
+ totalCostUsd += msg.metadata.totalCostUsd || 0;
}
}
- return { totalInputTokens, totalOutputTokens, totalCostUsd, messageCount: messages.length }
- }, [messages])
+ return {
+ totalInputTokens,
+ totalOutputTokens,
+ totalCostUsd,
+ messageCount: messages.length,
+ };
+ }, [messages]);
// Track previous streaming state to detect stream stop
- const prevIsStreamingRef = useRef(isStreaming)
+ const prevIsStreamingRef = useRef(isStreaming);
// Track if we recently stopped streaming (to prevent sync effect from restoring)
- const recentlyStoppedStreamRef = useRef(false)
+ const recentlyStoppedStreamRef = useRef(false);
// Clear pending questions when streaming is aborted
// This effect runs when isStreaming transitions from true to false
useEffect(() => {
- const wasStreaming = prevIsStreamingRef.current
- prevIsStreamingRef.current = isStreaming
+ const wasStreaming = prevIsStreamingRef.current;
+ prevIsStreamingRef.current = isStreaming;
// Detect streaming stop transition
if (wasStreaming && !isStreaming) {
// Mark that we recently stopped streaming
- recentlyStoppedStreamRef.current = true
+ recentlyStoppedStreamRef.current = true;
// Clear the flag after a delay
const flagTimeout = setTimeout(() => {
- recentlyStoppedStreamRef.current = false
- }, 500)
+ recentlyStoppedStreamRef.current = false;
+ }, 500);
// Streaming just stopped - if there's a pending question for this chat,
// clear it after a brief delay (backend already handled the abort)
@@ -2616,21 +2895,21 @@ const ChatViewInner = memo(function ChatViewInner({
// Re-check if still showing the same question (might have been cleared by other means)
setPendingQuestionsMap((current) => {
if (current.has(subChatId)) {
- const newMap = new Map(current)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(current);
+ newMap.delete(subChatId);
+ return newMap;
}
- return current
- })
- }, 150) // Small delay to allow for race conditions with transport chunks
+ return current;
+ });
+ }, 150); // Small delay to allow for race conditions with transport chunks
return () => {
- clearTimeout(timeout)
- clearTimeout(flagTimeout)
- }
+ clearTimeout(timeout);
+ clearTimeout(flagTimeout);
+ };
}
- return () => clearTimeout(flagTimeout)
+ return () => clearTimeout(flagTimeout);
}
- }, [isStreaming, subChatId, pendingQuestions, setPendingQuestionsMap])
+ }, [isStreaming, subChatId, pendingQuestions, setPendingQuestionsMap]);
// Sync pending questions with messages state
// This handles: 1) restoring on chat switch, 2) clearing when question is answered/timed out
@@ -2643,20 +2922,19 @@ const ChatViewInner = memo(function ChatViewInner({
part.state !== "output-error" &&
part.state !== "result" &&
part.input?.questions,
- ) as any | undefined
-
+ ) as any | undefined;
// Helper to clear pending question for this subChat
const clearPendingQuestion = () => {
setPendingQuestionsMap((current) => {
if (current.has(subChatId)) {
- const newMap = new Map(current)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(current);
+ newMap.delete(subChatId);
+ return newMap;
}
- return current
- })
- }
+ return current;
+ });
+ };
// If streaming and we already have a pending question for this chat, keep it
// (transport will manage it via chunks)
@@ -2671,12 +2949,12 @@ const ChatViewInner = memo(function ChatViewInner({
(part.state === "output-available" ||
part.state === "output-error" ||
part.state === "result"),
- )
+ );
if (answeredPart) {
- clearPendingQuestion()
+ clearPendingQuestion();
}
}
- return
+ return;
}
// Not streaming - DON'T restore pending questions from messages
@@ -2689,35 +2967,41 @@ const ChatViewInner = memo(function ChatViewInner({
if (pendingQuestionPart) {
// Don't restore - if there's an existing pending question for this chat, clear it
if (pendingQuestions) {
- clearPendingQuestion()
+ clearPendingQuestion();
}
} else {
// No pending question - clear if belongs to this sub-chat
if (pendingQuestions) {
- clearPendingQuestion()
+ clearPendingQuestion();
}
}
- }, [subChatId, lastAssistantMessage, isStreaming, pendingQuestions, setPendingQuestionsMap])
+ }, [
+ subChatId,
+ lastAssistantMessage,
+ isStreaming,
+ pendingQuestions,
+ setPendingQuestionsMap,
+ ]);
// Helper to clear pending and expired questions for this subChat (used in callbacks)
const clearPendingQuestionCallback = useCallback(() => {
setPendingQuestionsMap((current) => {
if (current.has(subChatId)) {
- const newMap = new Map(current)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(current);
+ newMap.delete(subChatId);
+ return newMap;
}
- return current
- })
+ return current;
+ });
setExpiredQuestionsMap((current) => {
if (current.has(subChatId)) {
- const newMap = new Map(current)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(current);
+ newMap.delete(subChatId);
+ return newMap;
}
- return current
- })
- }, [subChatId, setPendingQuestionsMap, setExpiredQuestionsMap])
+ return current;
+ });
+ }, [subChatId, setPendingQuestionsMap, setExpiredQuestionsMap]);
// Shared helpers for question answer handlers
const formatAnswersAsText = useCallback(
@@ -2726,60 +3010,66 @@ const ChatViewInner = memo(function ChatViewInner({
.map(([question, answer]) => `${question}: ${answer}`)
.join("\n"),
[],
- )
+ );
const clearInputAndDraft = useCallback(() => {
- editorRef.current?.clear()
+ editorRef.current?.clear();
if (parentChatId) {
- clearSubChatDraft(parentChatId, subChatId)
+ clearSubChatDraft(parentChatId, subChatId);
}
- }, [parentChatId, subChatId])
+ }, [parentChatId, subChatId]);
const sendUserMessage = useCallback(async (text: string) => {
- shouldAutoScrollRef.current = true
+ shouldAutoScrollRef.current = true;
await sendMessageRef.current({
role: "user",
parts: [{ type: "text", text }],
- })
- }, [])
+ });
+ }, []);
// Handle answering questions
const handleQuestionsAnswer = useCallback(
async (answers: Record) => {
- if (!displayQuestions) return
+ if (!displayQuestions) return;
if (isQuestionExpired) {
// Question timed out - send answers as a normal user message
- clearPendingQuestionCallback()
- await sendUserMessage(formatAnswersAsText(answers))
+ clearPendingQuestionCallback();
+ await sendUserMessage(formatAnswersAsText(answers));
} else {
// Question is still live - use tool approval path
await trpcClient.claude.respondToolApproval.mutate({
toolUseId: displayQuestions.toolUseId,
approved: true,
updatedInput: { questions: displayQuestions.questions, answers },
- })
- clearPendingQuestionCallback()
+ });
+ clearPendingQuestionCallback();
}
},
- [displayQuestions, isQuestionExpired, clearPendingQuestionCallback, sendUserMessage, formatAnswersAsText],
- )
+ [
+ displayQuestions,
+ isQuestionExpired,
+ clearPendingQuestionCallback,
+ sendUserMessage,
+ formatAnswersAsText,
+ ],
+ );
// Handle skipping questions
const handleQuestionsSkip = useCallback(async () => {
- if (!displayQuestions) return
+ if (!displayQuestions) return;
if (isQuestionExpired) {
// Expired question - just clear the UI, no backend call needed
- clearPendingQuestionCallback()
- return
+ clearPendingQuestionCallback();
+ return;
}
- const toolUseId = displayQuestions.toolUseId
+ const toolUseId = displayQuestions.toolUseId;
// Clear UI immediately - don't wait for backend
// This ensures dialog closes even if stream was already aborted
- clearPendingQuestionCallback()
+ clearPendingQuestionCallback();
// Try to notify backend (may fail if already aborted - that's ok)
try {
@@ -2787,96 +3077,99 @@ const ChatViewInner = memo(function ChatViewInner({
toolUseId,
approved: false,
message: QUESTIONS_SKIPPED_MESSAGE,
- })
+ });
} catch {
// Stream likely already aborted - ignore
}
- }, [displayQuestions, isQuestionExpired, clearPendingQuestionCallback])
+ }, [displayQuestions, isQuestionExpired, clearPendingQuestionCallback]);
// Ref to prevent double submit of question answer
- const isSubmittingQuestionAnswerRef = useRef(false)
+ const isSubmittingQuestionAnswerRef = useRef(false);
// Handle answering questions with custom text from input (called on Enter in input)
- const handleSubmitWithQuestionAnswer = useCallback(
- async () => {
- if (!displayQuestions) return
- if (isSubmittingQuestionAnswerRef.current) return
- isSubmittingQuestionAnswerRef.current = true
-
- try {
- // 1. Get custom text from input
- const customText = editorRef.current?.getValue()?.trim() || ""
- if (!customText) {
- isSubmittingQuestionAnswerRef.current = false
- return
- }
+ const handleSubmitWithQuestionAnswer = useCallback(async () => {
+ if (!displayQuestions) return;
+ if (isSubmittingQuestionAnswerRef.current) return;
+ isSubmittingQuestionAnswerRef.current = true;
- // 2. Get already selected answers from question component
- const selectedAnswers = questionRef.current?.getAnswers() || {}
- const formattedAnswers: Record = { ...selectedAnswers }
-
- // 3. Add custom text to the last question as "Other"
- const lastQuestion =
- displayQuestions.questions[displayQuestions.questions.length - 1]
- if (lastQuestion) {
- const existingAnswer = formattedAnswers[lastQuestion.question]
- if (existingAnswer) {
- // Append to existing answer
- formattedAnswers[lastQuestion.question] = `${existingAnswer}, Other: ${customText}`
- } else {
- formattedAnswers[lastQuestion.question] = `Other: ${customText}`
- }
- }
+ try {
+ // 1. Get custom text from input
+ const customText = editorRef.current?.getValue()?.trim() || "";
+ if (!customText) {
+ isSubmittingQuestionAnswerRef.current = false;
+ return;
+ }
- if (isQuestionExpired) {
- // Expired: send user's custom text as-is (don't format)
- clearPendingQuestionCallback()
- clearInputAndDraft()
- // await sendUserMessage(formatAnswersAsText(formattedAnswers))
- await sendUserMessage(customText)
+ // 2. Get already selected answers from question component
+ const selectedAnswers = questionRef.current?.getAnswers() || {};
+ const formattedAnswers: Record = { ...selectedAnswers };
+
+ // 3. Add custom text to the last question as "Other"
+ const lastQuestion =
+ displayQuestions.questions[displayQuestions.questions.length - 1];
+ if (lastQuestion) {
+ const existingAnswer = formattedAnswers[lastQuestion.question];
+ if (existingAnswer) {
+ // Append to existing answer
+ formattedAnswers[lastQuestion.question] =
+ `${existingAnswer}, Other: ${customText}`;
} else {
- // Live: use existing tool approval flow
- await trpcClient.claude.respondToolApproval.mutate({
- toolUseId: displayQuestions.toolUseId,
- approved: true,
- updatedInput: {
- questions: displayQuestions.questions,
- answers: formattedAnswers,
- },
- })
- clearPendingQuestionCallback()
-
- // Stop stream if currently streaming
- if (isStreamingRef.current) {
- agentChatStore.setManuallyAborted(subChatId, true)
- await stopRef.current()
- await new Promise((resolve) => setTimeout(resolve, 100))
- }
+ formattedAnswers[lastQuestion.question] = `Other: ${customText}`;
+ }
+ }
- clearInputAndDraft()
- await sendUserMessage(customText)
+ if (isQuestionExpired) {
+ // Expired: send user's custom text as-is (don't format)
+ clearPendingQuestionCallback();
+ clearInputAndDraft();
+ // await sendUserMessage(formatAnswersAsText(formattedAnswers))
+ await sendUserMessage(customText);
+ } else {
+ // Live: use existing tool approval flow
+ await trpcClient.claude.respondToolApproval.mutate({
+ toolUseId: displayQuestions.toolUseId,
+ approved: true,
+ updatedInput: {
+ questions: displayQuestions.questions,
+ answers: formattedAnswers,
+ },
+ });
+ clearPendingQuestionCallback();
+
+ // Stop stream if currently streaming
+ if (isStreamingRef.current) {
+ agentChatStore.setManuallyAborted(subChatId, true);
+ await stopRef.current();
+ await new Promise((resolve) => setTimeout(resolve, 100));
}
- } finally {
- isSubmittingQuestionAnswerRef.current = false
+
+ clearInputAndDraft();
+ await sendUserMessage(customText);
}
- },
- [displayQuestions, isQuestionExpired, clearPendingQuestionCallback, clearInputAndDraft, sendUserMessage, formatAnswersAsText, subChatId],
- )
+ } finally {
+ isSubmittingQuestionAnswerRef.current = false;
+ }
+ }, [
+ displayQuestions,
+ isQuestionExpired,
+ clearPendingQuestionCallback,
+ clearInputAndDraft,
+ sendUserMessage,
+ formatAnswersAsText,
+ subChatId,
+ ]);
// Memoize the callback to prevent ChatInputArea re-renders
// Only provide callback when there's a pending or expired question for this subChat
const submitWithQuestionAnswerCallback = useMemo(
- () =>
- displayQuestions
- ? handleSubmitWithQuestionAnswer
- : undefined,
+ () => (displayQuestions ? handleSubmitWithQuestionAnswer : undefined),
[displayQuestions, handleSubmitWithQuestionAnswer],
- )
+ );
// Watch for pending auth retry message (after successful OAuth flow)
const [pendingAuthRetry, setPendingAuthRetry] = useAtom(
pendingAuthRetryMessageAtom,
- )
+ );
useEffect(() => {
// Only retry when:
@@ -2891,12 +3184,12 @@ const ChatViewInner = memo(function ChatViewInner({
!isStreaming
) {
// Clear the pending message immediately to prevent double-sending
- setPendingAuthRetry(null)
+ setPendingAuthRetry(null);
// Build message parts
const parts: Array<
{ type: "text"; text: string } | { type: "data-image"; data: any }
- > = [{ type: "text", text: pendingAuthRetry.prompt }]
+ > = [{ type: "text", text: pendingAuthRetry.prompt }];
// Add images if present
if (pendingAuthRetry.images && pendingAuthRetry.images.length > 0) {
@@ -2908,7 +3201,7 @@ const ChatViewInner = memo(function ChatViewInner({
mediaType: img.mediaType,
filename: img.filename,
},
- })
+ });
}
}
@@ -2916,7 +3209,7 @@ const ChatViewInner = memo(function ChatViewInner({
sendMessage({
role: "user",
parts,
- })
+ });
}
}, [
pendingAuthRetry,
@@ -2924,119 +3217,125 @@ const ChatViewInner = memo(function ChatViewInner({
sendMessage,
setPendingAuthRetry,
subChatId,
- ])
+ ]);
const handlePlanApproval = useCallback(
async (toolUseId: string, approved: boolean) => {
- if (!toolUseId) return
- setPlanApprovalPending((prev) => ({ ...prev, [toolUseId]: true }))
+ if (!toolUseId) return;
+ setPlanApprovalPending((prev) => ({ ...prev, [toolUseId]: true }));
try {
await trpcClient.claude.respondToolApproval.mutate({
toolUseId,
approved,
- })
+ });
} catch (error) {
- console.error("[plan-approval] Failed to respond:", error)
- toast.error("Failed to send plan approval. Please try again.")
+ console.error("[plan-approval] Failed to respond:", error);
+ toast.error("Failed to send plan approval. Please try again.");
} finally {
setPlanApprovalPending((prev) => {
- const next = { ...prev }
- delete next[toolUseId]
- return next
- })
+ const next = { ...prev };
+ delete next[toolUseId];
+ return next;
+ });
}
},
[],
- )
+ );
// Handle plan approval - sends "Build plan" message and switches to agent mode
const handleApprovePlan = useCallback(() => {
// Update store mode synchronously BEFORE sending (transport reads from store)
- useAgentSubChatStore.getState().updateSubChatMode(subChatId, "agent")
+ useAgentSubChatStore.getState().updateSubChatMode(subChatId, "agent");
// Sync mode to database for sidebar indicator (getPendingPlanApprovals)
if (!subChatId.startsWith("temp-")) {
- updateSubChatModeMutation.mutate({ subChatId, mode: "agent" })
+ updateSubChatModeMutation.mutate({ subChatId, mode: "agent" });
}
// Update atomFamily state (for UI) - this also syncs to store via effect
- setSubChatMode("agent")
+ setSubChatMode("agent");
// Enable auto-scroll and immediately scroll to bottom
- shouldAutoScrollRef.current = true
- scrollToBottom()
+ shouldAutoScrollRef.current = true;
+ scrollToBottom();
// Send "Build plan" message (now in agent mode)
sendMessageRef.current({
role: "user",
parts: [{ type: "text", text: "Implement plan" }],
- })
- }, [subChatId, setSubChatMode, scrollToBottom, updateSubChatModeMutation])
+ });
+ }, [subChatId, setSubChatMode, scrollToBottom, updateSubChatModeMutation]);
// Handle pending "Build plan" from sidebar
useEffect(() => {
// Only trigger if this is the target sub-chat and we're active
if (pendingBuildPlanSubChatId === subChatId && isActive) {
- setPendingBuildPlanSubChatId(null) // Clear immediately to prevent double-trigger
- handleApprovePlan()
+ setPendingBuildPlanSubChatId(null); // Clear immediately to prevent double-trigger
+ handleApprovePlan();
}
- }, [pendingBuildPlanSubChatId, subChatId, isActive, setPendingBuildPlanSubChatId, handleApprovePlan])
+ }, [
+ pendingBuildPlanSubChatId,
+ subChatId,
+ isActive,
+ setPendingBuildPlanSubChatId,
+ handleApprovePlan,
+ ]);
// Detect PR URLs in assistant messages and store them
// Initialize with existing PR URL to prevent duplicate toast on re-mount
- const detectedPrUrlRef = useRef(existingPrUrl ?? null)
+ const detectedPrUrlRef = useRef(existingPrUrl ?? null);
useEffect(() => {
// Only check after streaming ends
- if (isStreaming) return
+ if (isStreaming) return;
// Look through messages for PR URLs
for (const msg of messages) {
- if (msg.role !== "assistant") continue
+ if (msg.role !== "assistant") continue;
// Extract text content from message
const textContent =
msg.parts
?.filter((p: any) => p.type === "text")
.map((p: any) => p.text)
- .join(" ") || ""
+ .join(" ") || "";
// Match GitHub PR URL pattern
const prUrlMatch = textContent.match(
/https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/(\d+)/,
- )
+ );
if (prUrlMatch && prUrlMatch[0] !== detectedPrUrlRef.current) {
- const prUrl = prUrlMatch[0]
- const prNumber = parseInt(prUrlMatch[1], 10)
+ const prUrl = prUrlMatch[0];
+ const prNumber = parseInt(prUrlMatch[1], 10);
// Store to prevent duplicate calls
- detectedPrUrlRef.current = prUrl
+ detectedPrUrlRef.current = prUrl;
// Update database
trpcClient.chats.updatePrInfo
.mutate({ chatId: parentChatId, prUrl, prNumber })
.then(() => {
// Invalidate the agentChat query to refetch with new PR info
- utils.agents.getAgentChat.invalidate({ chatId: parentChatId })
- })
+ utils.agents.getAgentChat.invalidate({ chatId: parentChatId });
+ });
- break // Only process first PR URL found
+ break; // Only process first PR URL found
}
}
- }, [messages, isStreaming, parentChatId])
+ }, [messages, isStreaming, parentChatId]);
// Track plan Edit completions to trigger sidebar refetch
const triggerPlanEditRefetch = useSetAtom(
- useMemo(() => planEditRefetchTriggerAtomFamily(subChatId), [subChatId])
- )
- const lastPlanEditCountRef = useRef(0)
+ useMemo(() => planEditRefetchTriggerAtomFamily(subChatId), [subChatId]),
+ );
+ const lastPlanEditCountRef = useRef(0);
useEffect(() => {
// Count completed plan Edits
- let completedPlanEdits = 0
+ let completedPlanEdits = 0;
for (const msg of messages) {
- if (msg.role !== "assistant" || !(msg as any).parts) continue
+ if (msg.role !== "assistant" || !(msg as any).parts) continue;
for (const part of (msg as any).parts as any[]) {
if (
part.type === "tool-Edit" &&
@@ -3044,68 +3343,64 @@ const ChatViewInner = memo(function ChatViewInner({
part.state !== "pending" &&
isPlanFile(part.input?.file_path || "")
) {
- completedPlanEdits++
+ completedPlanEdits++;
}
}
}
// Trigger refetch if count increased (new Edit completed)
if (completedPlanEdits > lastPlanEditCountRef.current) {
- lastPlanEditCountRef.current = completedPlanEdits
- triggerPlanEditRefetch()
+ lastPlanEditCountRef.current = completedPlanEdits;
+ triggerPlanEditRefetch();
}
- }, [messages, triggerPlanEditRefetch])
+ }, [messages, triggerPlanEditRefetch]);
- const { changedFiles: changedFilesForSubChat, recomputeChangedFiles } = useChangedFilesTracking(
- messages,
- subChatId,
- isStreaming,
- parentChatId,
- )
+ const { changedFiles: changedFilesForSubChat, recomputeChangedFiles } =
+ useChangedFilesTracking(messages, subChatId, isStreaming, parentChatId);
// Rollback handler - truncates messages to the clicked assistant message and restores git state
// The SDK UUID from the last assistant message will be used for resumeSessionAt on next send
const handleRollback = useCallback(
async (assistantMsg: (typeof messages)[0]) => {
if (isRollingBack) {
- toast.error("Rollback already in progress")
- return
+ toast.error("Rollback already in progress");
+ return;
}
if (isStreaming) {
- toast.error("Cannot rollback while streaming")
- return
+ toast.error("Cannot rollback while streaming");
+ return;
}
- const sdkUuid = (assistantMsg.metadata as any)?.sdkMessageUuid
+ const sdkUuid = (assistantMsg.metadata as any)?.sdkMessageUuid;
if (!sdkUuid) {
- toast.error("Cannot rollback: message has no SDK UUID")
- return
+ toast.error("Cannot rollback: message has no SDK UUID");
+ return;
}
- setIsRollingBack(true)
+ setIsRollingBack(true);
try {
// Single call handles both message truncation and git rollback
const result = await trpcClient.chats.rollbackToMessage.mutate({
subChatId,
sdkMessageUuid: sdkUuid,
- })
+ });
if (!result.success) {
- toast.error(`Failed to rollback: ${result.error}`)
- setIsRollingBack(false)
- return
+ toast.error(`Failed to rollback: ${result.error}`);
+ setIsRollingBack(false);
+ return;
}
// Update local state with truncated messages from server
- setMessages(result.messages)
- recomputeChangedFiles(result.messages)
- refreshDiff?.()
+ setMessages(result.messages);
+ recomputeChangedFiles(result.messages);
+ refreshDiff?.();
} catch (error) {
- console.error("[handleRollback] Error:", error)
- toast.error("Failed to rollback")
+ console.error("[handleRollback] Error:", error);
+ toast.error("Failed to rollback");
} finally {
- setIsRollingBack(false)
+ setIsRollingBack(false);
}
},
[
@@ -3116,28 +3411,28 @@ const ChatViewInner = memo(function ChatViewInner({
recomputeChangedFiles,
refreshDiff,
],
- )
+ );
// Expose rollback handler/state via atoms for message action bar
- const setRollbackHandler = useSetAtom(rollbackHandlerAtom)
+ const setRollbackHandler = useSetAtom(rollbackHandlerAtom);
useEffect(() => {
- setRollbackHandler(() => handleRollback)
- return () => setRollbackHandler(null)
- }, [handleRollback, setRollbackHandler])
+ setRollbackHandler(() => handleRollback);
+ return () => setRollbackHandler(null);
+ }, [handleRollback, setRollbackHandler]);
- const setIsRollingBackAtom = useSetAtom(isRollingBackAtom)
+ const setIsRollingBackAtom = useSetAtom(isRollingBackAtom);
useEffect(() => {
- setIsRollingBackAtom(isRollingBack)
- }, [isRollingBack, setIsRollingBackAtom])
+ setIsRollingBackAtom(isRollingBack);
+ }, [isRollingBack, setIsRollingBackAtom]);
// ESC, Ctrl+C and Cmd+Shift+Backspace handler for stopping stream
useEffect(() => {
// Skip keyboard handlers for inactive tabs (keep-alive)
- if (!isActive) return
+ if (!isActive) return;
const handleKeyDown = async (e: KeyboardEvent) => {
- let shouldStop = false
- let shouldSkipQuestions = false
+ let shouldStop = false;
+ let shouldSkipQuestions = false;
// Check for Escape key without modifiers (works even from input fields, like terminal Ctrl+C)
// Ignore if Cmd/Ctrl is pressed (reserved for Cmd+Esc to focus input)
@@ -3149,40 +3444,40 @@ const ChatViewInner = memo(function ChatViewInner({
!e.altKey &&
isStreaming
) {
- const target = e.target as HTMLElement
+ const target = e.target as HTMLElement;
// Allow ESC to propagate if it originated from a modal/dialog/dropdown
const isInsideOverlay = target.closest(
'[role="dialog"], [role="alertdialog"], [role="menu"], [role="listbox"], [data-radix-popper-content-wrapper], [data-state="open"]',
- )
+ );
// Also check if any dialog/modal is open anywhere in the document (not just at event target)
// This prevents stopping stream when settings dialog is open but not focused
const hasOpenDialog = document.querySelector(
'[role="dialog"][aria-modal="true"], [data-modal="agents-settings"]',
- )
+ );
if (!isInsideOverlay && !hasOpenDialog) {
// If there are pending/expired questions for this chat, skip/dismiss them instead of stopping stream
if (displayQuestions) {
- shouldSkipQuestions = true
+ shouldSkipQuestions = true;
} else {
- shouldStop = true
+ shouldStop = true;
}
}
}
// Check for Ctrl+C (only Ctrl, not Cmd on Mac)
if (e.ctrlKey && !e.metaKey && e.code === "KeyC") {
- if (!isStreaming) return
+ if (!isStreaming) return;
- const selection = window.getSelection()
- const hasSelection = selection && selection.toString().length > 0
+ const selection = window.getSelection();
+ const hasSelection = selection && selection.toString().length > 0;
// If there's a text selection, let browser handle copy
- if (hasSelection) return
+ if (hasSelection) return;
- shouldStop = true
+ shouldStop = true;
}
// Check for Cmd+Shift+Backspace (Mac) or Ctrl+Shift+Backspace (Windows/Linux)
@@ -3192,29 +3487,36 @@ const ChatViewInner = memo(function ChatViewInner({
e.key === "Backspace" &&
isStreaming
) {
- shouldStop = true
+ shouldStop = true;
}
if (shouldSkipQuestions) {
- e.preventDefault()
- await handleQuestionsSkip()
+ e.preventDefault();
+ await handleQuestionsSkip();
} else if (shouldStop) {
- e.preventDefault()
+ e.preventDefault();
// Mark as manually aborted to prevent completion sound
- agentChatStore.setManuallyAborted(subChatId, true)
- await stop()
+ agentChatStore.setManuallyAborted(subChatId, true);
+ await stop();
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [isActive, isStreaming, stop, subChatId, displayQuestions, handleQuestionsSkip])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [
+ isActive,
+ isStreaming,
+ stop,
+ subChatId,
+ displayQuestions,
+ handleQuestionsSkip,
+ ]);
// Keyboard shortcut: Enter to focus input when not already focused
- useFocusInputOnEnter(editorRef)
+ useFocusInputOnEnter(editorRef);
// Keyboard shortcut: Cmd+Esc to toggle focus/blur (without stopping generation)
- useToggleFocusOnCmdEsc(editorRef)
+ useToggleFocusOnCmdEsc(editorRef);
// Auto-trigger AI response when we have initial message but no response yet
// Also trigger auto-rename for initial sub-chat with pre-populated message
@@ -3226,19 +3528,19 @@ const ChatViewInner = memo(function ChatViewInner({
!streamId &&
!hasTriggeredAutoGenerateRef.current
) {
- hasTriggeredAutoGenerateRef.current = true
+ hasTriggeredAutoGenerateRef.current = true;
// Trigger rename for pre-populated initial message (from createAgentChat)
if (!hasTriggeredRenameRef.current && isFirstSubChat) {
- const firstMsg = messages[0]
+ const firstMsg = messages[0];
if (firstMsg?.role === "user") {
- const textPart = firstMsg.parts?.find((p: any) => p.type === "text")
+ const textPart = firstMsg.parts?.find((p: any) => p.type === "text");
if (textPart && "text" in textPart) {
- hasTriggeredRenameRef.current = true
- onAutoRename(textPart.text, subChatId)
+ hasTriggeredRenameRef.current = true;
+ onAutoRename(textPart.text, subChatId);
}
}
}
- regenerate()
+ regenerate();
}
}, [
status,
@@ -3248,233 +3550,233 @@ const ChatViewInner = memo(function ChatViewInner({
onAutoRename,
streamId,
subChatId,
- ])
+ ]);
// Ref to track if initial scroll has been set for this sub-chat
- const scrollInitializedRef = useRef(false)
+ const scrollInitializedRef = useRef(false);
// Track if this tab has been initialized (for keep-alive)
- const hasInitializedRef = useRef(false)
+ const hasInitializedRef = useRef(false);
// Initialize scroll position on mount (only once per tab with keep-alive)
// Strategy: wait for content to stabilize, then scroll to bottom ONCE
// No jumping around - just wait and scroll when ready
useLayoutEffect(() => {
// Skip if not active (keep-alive: hidden tabs don't need scroll init)
- if (!isActive) return
+ if (!isActive) return;
- const container = chatContainerRef.current
- if (!container) return
+ const container = chatContainerRef.current;
+ if (!container) return;
// With keep-alive, only initialize once per tab mount
- if (hasInitializedRef.current) return
- hasInitializedRef.current = true
+ if (hasInitializedRef.current) return;
+ hasInitializedRef.current = true;
// Reset on sub-chat change
- scrollInitializedRef.current = false
- isInitializingScrollRef.current = true
+ scrollInitializedRef.current = false;
+ isInitializingScrollRef.current = true;
// IMMEDIATE scroll to bottom - no waiting
- container.scrollTop = container.scrollHeight
- shouldAutoScrollRef.current = true
+ container.scrollTop = container.scrollHeight;
+ shouldAutoScrollRef.current = true;
// Mark as initialized IMMEDIATELY
- scrollInitializedRef.current = true
- isInitializingScrollRef.current = false
+ scrollInitializedRef.current = true;
+ isInitializingScrollRef.current = false;
// MutationObserver for async content (images, code blocks loading after initial render)
const observer = new MutationObserver((mutations) => {
// Skip if not active (keep-alive: don't scroll hidden tabs)
- if (!isActive) return
- if (!shouldAutoScrollRef.current) return
+ if (!isActive) return;
+ if (!shouldAutoScrollRef.current) return;
// Check if content was added
const hasAddedContent = mutations.some(
- (m) => m.type === "childList" && m.addedNodes.length > 0
- )
+ (m) => m.type === "childList" && m.addedNodes.length > 0,
+ );
if (hasAddedContent) {
requestAnimationFrame(() => {
- isAutoScrollingRef.current = true
- container.scrollTop = container.scrollHeight
+ isAutoScrollingRef.current = true;
+ container.scrollTop = container.scrollHeight;
requestAnimationFrame(() => {
- isAutoScrollingRef.current = false
- })
- })
+ isAutoScrollingRef.current = false;
+ });
+ });
}
- })
+ });
- observer.observe(container, { childList: true, subtree: true })
+ observer.observe(container, { childList: true, subtree: true });
return () => {
- observer.disconnect()
- }
+ observer.disconnect();
+ };
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [subChatId, isActive])
+ }, [subChatId, isActive]);
// Attach scroll listener (separate effect)
useEffect(() => {
- const container = chatContainerRef.current
- if (!container) return
+ const container = chatContainerRef.current;
+ if (!container) return;
- container.addEventListener("scroll", handleScroll, { passive: true })
+ container.addEventListener("scroll", handleScroll, { passive: true });
return () => {
- container.removeEventListener("scroll", handleScroll)
- }
- }, [handleScroll])
+ container.removeEventListener("scroll", handleScroll);
+ };
+ }, [handleScroll]);
// Auto scroll to bottom when messages change during streaming
// Only kicks in after content fills the viewport (overflow behavior)
useEffect(() => {
// Skip if not active (keep-alive: don't scroll hidden tabs)
- if (!isActive) return
+ if (!isActive) return;
// Skip if scroll not yet initialized
- if (!scrollInitializedRef.current) return
+ if (!scrollInitializedRef.current) return;
// Auto-scroll during streaming if user hasn't scrolled up
if (shouldAutoScrollRef.current && status === "streaming") {
- const container = chatContainerRef.current
+ const container = chatContainerRef.current;
if (container) {
// Always scroll during streaming if auto-scroll is enabled
// (user can disable by scrolling up)
requestAnimationFrame(() => {
- isAutoScrollingRef.current = true
- container.scrollTop = container.scrollHeight
+ isAutoScrollingRef.current = true;
+ container.scrollTop = container.scrollHeight;
requestAnimationFrame(() => {
- isAutoScrollingRef.current = false
- })
- })
+ isAutoScrollingRef.current = false;
+ });
+ });
}
}
- }, [isActive, messages, status, subChatId])
+ }, [isActive, messages, status, subChatId]);
// Auto-focus input when switching to this chat (any sub-chat change)
// Skip on mobile to prevent keyboard from opening automatically
useEffect(() => {
// Skip if not active (keep-alive: don't focus hidden tabs)
- if (!isActive) return
- if (isMobile) return // Don't autofocus on mobile
+ if (!isActive) return;
+ if (isMobile) return; // Don't autofocus on mobile
// Use requestAnimationFrame to ensure DOM is ready after render
requestAnimationFrame(() => {
// Skip if sidebar keyboard navigation is active (user is arrowing through sidebar items)
if (appStore.get(suppressInputFocusAtom)) {
- appStore.set(suppressInputFocusAtom, false)
- return
+ appStore.set(suppressInputFocusAtom, false);
+ return;
}
- editorRef.current?.focus()
- })
- }, [isActive, subChatId, isMobile])
+ editorRef.current?.focus();
+ });
+ }, [isActive, subChatId, isMobile]);
// Refs for handleSend to avoid recreating callback on every messages change
- const messagesLengthRef = useRef(messages.length)
- messagesLengthRef.current = messages.length
- const subChatModeRef = useRef(subChatMode)
- subChatModeRef.current = subChatMode
- const imagesRef = useRef(images)
- imagesRef.current = images
- const filesRef = useRef(files)
- filesRef.current = files
+ const messagesLengthRef = useRef(messages.length);
+ messagesLengthRef.current = messages.length;
+ const subChatModeRef = useRef(subChatMode);
+ subChatModeRef.current = subChatMode;
+ const imagesRef = useRef(images);
+ imagesRef.current = images;
+ const filesRef = useRef(files);
+ filesRef.current = files;
const handleSend = useCallback(async () => {
// Block sending while sandbox is still being set up
if (sandboxSetupStatus !== "ready") {
- return
+ return;
}
// Clear any expired questions when user sends a new message
setExpiredQuestionsMap((current) => {
if (current.has(subChatId)) {
- const newMap = new Map(current)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(current);
+ newMap.delete(subChatId);
+ return newMap;
}
- return current
- })
+ return current;
+ });
// Get value from uncontrolled editor
- const inputValue = editorRef.current?.getValue() || ""
- const hasText = inputValue.trim().length > 0
- const currentImages = imagesRef.current
- const currentFiles = filesRef.current
- const currentTextContexts = textContextsRef.current
- const currentPastedTexts = pastedTextsRef.current
+ const inputValue = editorRef.current?.getValue() || "";
+ const hasText = inputValue.trim().length > 0;
+ const currentImages = imagesRef.current;
+ const currentFiles = filesRef.current;
+ const currentTextContexts = textContextsRef.current;
+ const currentPastedTexts = pastedTextsRef.current;
const hasImages =
- currentImages.filter((img) => !img.isLoading && img.url).length > 0
- const hasTextContexts = currentTextContexts.length > 0
- const hasPastedTexts = currentPastedTexts.length > 0
+ currentImages.filter((img) => !img.isLoading && img.url).length > 0;
+ const hasTextContexts = currentTextContexts.length > 0;
+ const hasPastedTexts = currentPastedTexts.length > 0;
- if (!hasText && !hasImages && !hasTextContexts && !hasPastedTexts) return
+ if (!hasText && !hasImages && !hasTextContexts && !hasPastedTexts) return;
// If streaming, add to queue instead of sending directly
if (isStreamingRef.current) {
const queuedImages = currentImages
.filter((img) => !img.isLoading && img.url)
- .map(toQueuedImage)
+ .map(toQueuedImage);
const queuedFiles = currentFiles
.filter((f) => !f.isLoading && f.url)
- .map(toQueuedFile)
- const queuedTextContexts = currentTextContexts.map(toQueuedTextContext)
+ .map(toQueuedFile);
+ const queuedTextContexts = currentTextContexts.map(toQueuedTextContext);
const item = createQueueItem(
generateQueueId(),
inputValue.trim(),
queuedImages.length > 0 ? queuedImages : undefined,
queuedFiles.length > 0 ? queuedFiles : undefined,
- queuedTextContexts.length > 0 ? queuedTextContexts : undefined
- )
- addToQueue(subChatId, item)
+ queuedTextContexts.length > 0 ? queuedTextContexts : undefined,
+ );
+ addToQueue(subChatId, item);
// Clear input and attachments
- editorRef.current?.clear()
+ editorRef.current?.clear();
if (parentChatId) {
- clearSubChatDraft(parentChatId, subChatId)
+ clearSubChatDraft(parentChatId, subChatId);
}
- clearAll()
- clearTextContexts()
- return
+ clearAll();
+ clearTextContexts();
+ return;
}
// Auto-restore archived workspace when sending a message
if (isArchived && onRestoreWorkspace) {
- onRestoreWorkspace()
+ onRestoreWorkspace();
}
- const text = inputValue.trim()
+ const text = inputValue.trim();
// Expand custom slash commands with arguments (e.g. "/Apex my argument")
// This mirrors the logic in new-chat-form.tsx
- let finalText = text
- const slashMatch = text.match(/^\/(\S+)\s*(.*)$/s)
+ let finalText = text;
+ const slashMatch = text.match(/^\/(\S+)\s*(.*)$/s);
if (slashMatch) {
- const [, commandName, args] = slashMatch
+ const [, commandName, args] = slashMatch;
const builtinNames = new Set(
BUILTIN_SLASH_COMMANDS.map((cmd) => cmd.name),
- )
+ );
if (!builtinNames.has(commandName)) {
try {
const commands = await trpcClient.commands.list.query({
projectPath,
- })
+ });
const cmd = commands.find(
(c) => c.name.toLowerCase() === commandName.toLowerCase(),
- )
+ );
if (cmd) {
const { content } = await trpcClient.commands.getContent.query({
path: cmd.path,
- })
- finalText = content.replace(/\$ARGUMENTS/g, args.trim())
+ });
+ finalText = content.replace(/\$ARGUMENTS/g, args.trim());
}
} catch (error) {
- console.error("Failed to expand custom slash command:", error)
+ console.error("Failed to expand custom slash command:", error);
}
}
}
// Clear editor and draft from localStorage
- editorRef.current?.clear()
+ editorRef.current?.clear();
if (parentChatId) {
- clearSubChatDraft(parentChatId, subChatId)
+ clearSubChatDraft(parentChatId, subChatId);
}
// Track message sent
@@ -3482,12 +3784,12 @@ const ChatViewInner = memo(function ChatViewInner({
workspaceId: subChatId,
messageLength: finalText.length,
mode: subChatModeRef.current,
- })
+ });
// Trigger auto-rename on first message in a new sub-chat
if (messagesLengthRef.current === 0 && !hasTriggeredRenameRef.current) {
- hasTriggeredRenameRef.current = true
- onAutoRename(finalText || "Image message", subChatId)
+ hasTriggeredRenameRef.current = true;
+ onAutoRename(finalText || "Image message", subChatId);
}
// Build message parts: images first, then files, then text
@@ -3515,39 +3817,45 @@ const ChatViewInner = memo(function ChatViewInner({
size: f.size,
},
})),
- ]
+ ];
// Add text contexts as mention tokens
- const currentDiffTextContexts = diffTextContextsRef.current
- let mentionPrefix = ""
+ const currentDiffTextContexts = diffTextContextsRef.current;
+ let mentionPrefix = "";
- if (currentTextContexts.length > 0 || currentDiffTextContexts.length > 0 || currentPastedTexts.length > 0) {
+ if (
+ currentTextContexts.length > 0 ||
+ currentDiffTextContexts.length > 0 ||
+ currentPastedTexts.length > 0
+ ) {
const quoteMentions = currentTextContexts.map((tc) => {
- const preview = tc.preview.replace(/[:\[\]]/g, "") // Sanitize preview
- const encodedText = utf8ToBase64(tc.text) // Base64 encode full text
- return `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`
- })
+ const preview = tc.preview.replace(/[:\[\]]/g, ""); // Sanitize preview
+ const encodedText = utf8ToBase64(tc.text); // Base64 encode full text
+ return `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`;
+ });
const diffMentions = currentDiffTextContexts.map((dtc) => {
- const preview = dtc.preview.replace(/[:\[\]]/g, "") // Sanitize preview
- const encodedText = utf8ToBase64(dtc.text) // Base64 encode full text
- const lineNum = dtc.lineNumber || 0
- return `@[${MENTION_PREFIXES.DIFF}${dtc.filePath}:${lineNum}:${preview}:${encodedText}]`
- })
+ const preview = dtc.preview.replace(/[:\[\]]/g, ""); // Sanitize preview
+ const encodedText = utf8ToBase64(dtc.text); // Base64 encode full text
+ const lineNum = dtc.lineNumber || 0;
+ return `@[${MENTION_PREFIXES.DIFF}${dtc.filePath}:${lineNum}:${preview}:${encodedText}]`;
+ });
// Add pasted text as pasted mentions (format: pasted:size:preview|filepath)
// Using | as separator since filepath can contain colons
const pastedTextMentions = currentPastedTexts.map((pt) => {
// Sanitize preview to remove special characters that break mention parsing
- const sanitizedPreview = pt.preview.replace(/[:\[\]|]/g, "")
- return `@[${MENTION_PREFIXES.PASTED}${pt.size}:${sanitizedPreview}|${pt.filePath}]`
- })
+ const sanitizedPreview = pt.preview.replace(/[:\[\]|]/g, "");
+ return `@[${MENTION_PREFIXES.PASTED}${pt.size}:${sanitizedPreview}|${pt.filePath}]`;
+ });
- mentionPrefix = [...quoteMentions, ...diffMentions, ...pastedTextMentions].join(" ") + " "
+ mentionPrefix =
+ [...quoteMentions, ...diffMentions, ...pastedTextMentions].join(" ") +
+ " ";
}
if (finalText || mentionPrefix) {
- parts.push({ type: "text", text: mentionPrefix + (finalText || "") })
+ parts.push({ type: "text", text: mentionPrefix + (finalText || "") });
}
// Add cached file contents as hidden parts (sent to agent but not displayed in UI)
@@ -3555,71 +3863,76 @@ const ChatViewInner = memo(function ChatViewInner({
if (fileContentsRef.current.size > 0) {
for (const [mentionId, content] of fileContentsRef.current.entries()) {
// Extract file path from mentionId (file:local:path or file:external:path)
- const filePath = mentionId.replace(/^file:(local|external):/, "")
+ const filePath = mentionId.replace(/^file:(local|external):/, "");
parts.push({
type: "file-content",
filePath,
content,
- })
+ });
}
}
- clearAll()
- clearTextContexts()
- clearDiffTextContexts()
- clearPastedTexts()
- clearFileContents()
+ clearAll();
+ clearTextContexts();
+ clearDiffTextContexts();
+ clearPastedTexts();
+ clearFileContents();
// Optimistic update: immediately update chat's updated_at and resort array for instant sidebar resorting
if (teamId) {
- const now = new Date()
+ const now = new Date();
utils.agents.getAgentChats.setData({ teamId }, (old: any) => {
- if (!old) return old
+ if (!old) return old;
// Update the timestamp and sort by updated_at descending
const updated = old.map((c: any) =>
c.id === parentChatId ? { ...c, updated_at: now } : c,
- )
+ );
return updated.sort(
(a: any, b: any) =>
new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(),
- )
- })
+ );
+ });
}
// Desktop app: Optimistic update for chats.list to update sidebar immediately
- const queryClient = getQueryClient()
+ const queryClient = getQueryClient();
if (queryClient) {
- const now = new Date()
- const queries = queryClient.getQueryCache().getAll()
- const chatsListQuery = queries.find(q =>
- Array.isArray(q.queryKey) &&
- Array.isArray(q.queryKey[0]) &&
- q.queryKey[0][0] === 'chats' &&
- q.queryKey[0][1] === 'list'
- )
+ const now = new Date();
+ const queries = queryClient.getQueryCache().getAll();
+ const chatsListQuery = queries.find(
+ (q) =>
+ Array.isArray(q.queryKey) &&
+ Array.isArray(q.queryKey[0]) &&
+ q.queryKey[0][0] === "chats" &&
+ q.queryKey[0][1] === "list",
+ );
if (chatsListQuery) {
- queryClient.setQueryData(chatsListQuery.queryKey, (old: any[] | undefined) => {
- if (!old) return old
- // Update the timestamp and sort by updatedAt descending
- const updated = old.map((c: any) =>
- c.id === parentChatId ? { ...c, updatedAt: now } : c,
- )
- return updated.sort(
- (a: any, b: any) =>
- new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),
- )
- })
+ queryClient.setQueryData(
+ chatsListQuery.queryKey,
+ (old: any[] | undefined) => {
+ if (!old) return old;
+ // Update the timestamp and sort by updatedAt descending
+ const updated = old.map((c: any) =>
+ c.id === parentChatId ? { ...c, updatedAt: now } : c,
+ );
+ return updated.sort(
+ (a: any, b: any) =>
+ new Date(b.updatedAt).getTime() -
+ new Date(a.updatedAt).getTime(),
+ );
+ },
+ );
}
}
// Optimistically update sub-chat timestamp to move it to top
- useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId)
+ useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId);
// Enable auto-scroll and immediately scroll to bottom
- shouldAutoScrollRef.current = true
- scrollToBottom()
+ shouldAutoScrollRef.current = true;
+ scrollToBottom();
- await sendMessageRef.current({ role: "user", parts })
+ await sendMessageRef.current({ role: "user", parts });
}, [
sandboxSetupStatus,
isArchived,
@@ -3633,160 +3946,172 @@ const ChatViewInner = memo(function ChatViewInner({
teamId,
addToQueue,
setExpiredQuestionsMap,
- ])
+ ]);
// Queue handlers for sending queued messages
- const handleSendFromQueue = useCallback(async (itemId: string) => {
- const item = popItemFromQueue(subChatId, itemId)
- if (!item) return
+ const handleSendFromQueue = useCallback(
+ async (itemId: string) => {
+ const item = popItemFromQueue(subChatId, itemId);
+ if (!item) return;
- try {
- // Stop current stream if streaming and wait for status to become ready.
- // The server-side save block preserves sessionId on abort, so the next
- // message can resume the session with full conversation context.
- if (isStreamingRef.current) {
- await handleStop()
- await waitForStreamingReady(subChatId)
- }
+ try {
+ // Stop current stream if streaming and wait for status to become ready.
+ // The server-side save block preserves sessionId on abort, so the next
+ // message can resume the session with full conversation context.
+ if (isStreamingRef.current) {
+ await handleStop();
+ await waitForStreamingReady(subChatId);
+ }
- // Build message parts from queued item
- const parts: any[] = [
- ...(item.images || []).map((img) => ({
- type: "data-image" as const,
- data: {
- url: img.url,
- mediaType: img.mediaType,
- filename: img.filename,
- base64Data: img.base64Data,
- },
- })),
- ...(item.files || []).map((f) => ({
- type: "data-file" as const,
- data: {
- url: f.url,
- mediaType: f.mediaType,
- filename: f.filename,
- size: f.size,
- },
- })),
- ]
-
- // Add text contexts as mention tokens
- let mentionPrefix = ""
- if (item.textContexts && item.textContexts.length > 0) {
- const quoteMentions = item.textContexts.map((tc) => {
- const preview = tc.text.slice(0, 50).replace(/[:\[\]]/g, "") // Create and sanitize preview
- const encodedText = utf8ToBase64(tc.text) // Base64 encode full text
- return `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`
- })
- mentionPrefix = quoteMentions.join(" ") + " "
- }
+ // Build message parts from queued item
+ const parts: any[] = [
+ ...(item.images || []).map((img) => ({
+ type: "data-image" as const,
+ data: {
+ url: img.url,
+ mediaType: img.mediaType,
+ filename: img.filename,
+ base64Data: img.base64Data,
+ },
+ })),
+ ...(item.files || []).map((f) => ({
+ type: "data-file" as const,
+ data: {
+ url: f.url,
+ mediaType: f.mediaType,
+ filename: f.filename,
+ size: f.size,
+ },
+ })),
+ ];
+
+ // Add text contexts as mention tokens
+ let mentionPrefix = "";
+ if (item.textContexts && item.textContexts.length > 0) {
+ const quoteMentions = item.textContexts.map((tc) => {
+ const preview = tc.text.slice(0, 50).replace(/[:\[\]]/g, ""); // Create and sanitize preview
+ const encodedText = utf8ToBase64(tc.text); // Base64 encode full text
+ return `@[${MENTION_PREFIXES.QUOTE}${preview}:${encodedText}]`;
+ });
+ mentionPrefix = quoteMentions.join(" ") + " ";
+ }
- // Add diff text contexts as mention tokens
- if (item.diffTextContexts && item.diffTextContexts.length > 0) {
- const diffMentions = item.diffTextContexts.map((dtc) => {
- const preview = dtc.text.slice(0, 50).replace(/[:\[\]]/g, "") // Create and sanitize preview
- const encodedText = utf8ToBase64(dtc.text) // Base64 encode full text
- const lineNum = dtc.lineNumber || 0
- return `@[${MENTION_PREFIXES.DIFF}${dtc.filePath}:${lineNum}:${preview}:${encodedText}]`
- })
- mentionPrefix += diffMentions.join(" ") + " "
- }
+ // Add diff text contexts as mention tokens
+ if (item.diffTextContexts && item.diffTextContexts.length > 0) {
+ const diffMentions = item.diffTextContexts.map((dtc) => {
+ const preview = dtc.text.slice(0, 50).replace(/[:\[\]]/g, ""); // Create and sanitize preview
+ const encodedText = utf8ToBase64(dtc.text); // Base64 encode full text
+ const lineNum = dtc.lineNumber || 0;
+ return `@[${MENTION_PREFIXES.DIFF}${dtc.filePath}:${lineNum}:${preview}:${encodedText}]`;
+ });
+ mentionPrefix += diffMentions.join(" ") + " ";
+ }
- if (item.message || mentionPrefix) {
- parts.push({ type: "text", text: mentionPrefix + (item.message || "") })
- }
+ if (item.message || mentionPrefix) {
+ parts.push({
+ type: "text",
+ text: mentionPrefix + (item.message || ""),
+ });
+ }
- // Track message sent
- trackMessageSent({
- workspaceId: subChatId,
- messageLength: item.message.length,
- mode: subChatModeRef.current,
- })
+ // Track message sent
+ trackMessageSent({
+ workspaceId: subChatId,
+ messageLength: item.message.length,
+ mode: subChatModeRef.current,
+ });
- // Update timestamps
- useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId)
+ // Update timestamps
+ useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId);
- // Enable auto-scroll and immediately scroll to bottom
- shouldAutoScrollRef.current = true
- scrollToBottom()
+ // Enable auto-scroll and immediately scroll to bottom
+ shouldAutoScrollRef.current = true;
+ scrollToBottom();
- await sendMessageRef.current({ role: "user", parts })
- } catch (error) {
- console.error("[handleSendFromQueue] Error sending queued message:", error)
- // Requeue the item at the front so it isn't lost
- useMessageQueueStore.getState().prependItem(subChatId, item)
- }
- }, [subChatId, popItemFromQueue, handleStop])
+ await sendMessageRef.current({ role: "user", parts });
+ } catch (error) {
+ console.error(
+ "[handleSendFromQueue] Error sending queued message:",
+ error,
+ );
+ // Requeue the item at the front so it isn't lost
+ useMessageQueueStore.getState().prependItem(subChatId, item);
+ }
+ },
+ [subChatId, popItemFromQueue, handleStop],
+ );
- const handleRemoveFromQueue = useCallback((itemId: string) => {
- removeFromQueue(subChatId, itemId)
- }, [subChatId, removeFromQueue])
+ const handleRemoveFromQueue = useCallback(
+ (itemId: string) => {
+ removeFromQueue(subChatId, itemId);
+ },
+ [subChatId, removeFromQueue],
+ );
// Force send - stop stream and send immediately, bypassing queue (Opt+Enter)
const handleForceSend = useCallback(async () => {
// Block sending while sandbox is still being set up
if (sandboxSetupStatus !== "ready") {
- return
+ return;
}
// Get value from uncontrolled editor
- const inputValue = editorRef.current?.getValue() || ""
- const hasText = inputValue.trim().length > 0
- const currentImages = imagesRef.current
- const currentFiles = filesRef.current
+ const inputValue = editorRef.current?.getValue() || "";
+ const hasText = inputValue.trim().length > 0;
+ const currentImages = imagesRef.current;
+ const currentFiles = filesRef.current;
const hasImages =
- currentImages.filter((img) => !img.isLoading && img.url).length > 0
+ currentImages.filter((img) => !img.isLoading && img.url).length > 0;
- if (!hasText && !hasImages) return
+ if (!hasText && !hasImages) return;
// Stop current stream if streaming and wait for status to become ready.
// The server-side save block sets sessionId=null on abort, so the next
// message starts fresh without needing an explicit cancel mutation.
if (isStreamingRef.current) {
- await handleStop()
- await waitForStreamingReady(subChatId)
+ await handleStop();
+ await waitForStreamingReady(subChatId);
}
// Auto-restore archived workspace when sending a message
if (isArchived && onRestoreWorkspace) {
- onRestoreWorkspace()
+ onRestoreWorkspace();
}
- const text = inputValue.trim()
+ const text = inputValue.trim();
// Expand custom slash commands with arguments (e.g. "/Apex my argument")
- let finalText = text
- const slashMatch = text.match(/^\/(\S+)\s*(.*)$/s)
+ let finalText = text;
+ const slashMatch = text.match(/^\/(\S+)\s*(.*)$/s);
if (slashMatch) {
- const [, commandName, args] = slashMatch
+ const [, commandName, args] = slashMatch;
const builtinNames = new Set(
BUILTIN_SLASH_COMMANDS.map((cmd) => cmd.name),
- )
+ );
if (!builtinNames.has(commandName)) {
try {
const commands = await trpcClient.commands.list.query({
projectPath,
- })
+ });
const cmd = commands.find(
(c) => c.name.toLowerCase() === commandName.toLowerCase(),
- )
+ );
if (cmd) {
const { content } = await trpcClient.commands.getContent.query({
path: cmd.path,
- })
- finalText = content.replace(/\$ARGUMENTS/g, args.trim())
+ });
+ finalText = content.replace(/\$ARGUMENTS/g, args.trim());
}
} catch (error) {
- console.error("Failed to expand custom slash command:", error)
+ console.error("Failed to expand custom slash command:", error);
}
}
}
// Clear editor and draft from localStorage
- editorRef.current?.clear()
+ editorRef.current?.clear();
if (parentChatId) {
- clearSubChatDraft(parentChatId, subChatId)
+ clearSubChatDraft(parentChatId, subChatId);
}
// Track message sent
@@ -3794,7 +4119,7 @@ const ChatViewInner = memo(function ChatViewInner({
workspaceId: subChatId,
messageLength: finalText.length,
mode: subChatModeRef.current,
- })
+ });
// Build message parts
const parts: any[] = [
@@ -3820,28 +4145,28 @@ const ChatViewInner = memo(function ChatViewInner({
size: f.size,
},
})),
- ]
+ ];
if (finalText) {
- parts.push({ type: "text", text: finalText })
+ parts.push({ type: "text", text: finalText });
}
// Clear attachments
- clearAll()
+ clearAll();
// Update timestamps
- useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId)
+ useAgentSubChatStore.getState().updateSubChatTimestamp(subChatId);
// Force scroll to bottom
- shouldAutoScrollRef.current = true
- scrollToBottom()
+ shouldAutoScrollRef.current = true;
+ scrollToBottom();
try {
- await sendMessageRef.current({ role: "user", parts })
+ await sendMessageRef.current({ role: "user", parts });
} catch (error) {
- console.error("[handleForceSend] Error sending message:", error)
+ console.error("[handleForceSend] Error sending message:", error);
// Restore editor content so the user can retry
- editorRef.current?.setValue(finalText)
+ editorRef.current?.setValue(finalText);
}
}, [
sandboxSetupStatus,
@@ -3851,7 +4176,7 @@ const ChatViewInner = memo(function ChatViewInner({
subChatId,
handleStop,
clearAll,
- ])
+ ]);
// NOTE: Auto-processing of queue is now handled globally by QueueProcessor
// component in agents-layout.tsx. This ensures queues continue processing
@@ -3864,64 +4189,67 @@ const ChatViewInner = memo(function ChatViewInner({
?.filter((p: any) => p.type === "text")
.map((p: any) => p.text)
.join("\n") || ""
- )
- }
+ );
+ };
// Helper to copy message content
const copyMessageContent = (msg: any) => {
- const textContent = getMessageTextContent(msg)
+ const textContent = getMessageTextContent(msg);
if (textContent) {
- navigator.clipboard.writeText(stripEmojis(textContent))
+ navigator.clipboard.writeText(stripEmojis(textContent));
}
- }
+ };
// Check if there's an unapproved plan (in plan mode with completed ExitPlanMode)
const hasUnapprovedPlan = useMemo(() => {
// If already in agent mode, plan is approved (mode is the source of truth)
- if (subChatMode !== "plan") return false
+ if (subChatMode !== "plan") return false;
// Look for completed ExitPlanMode in messages
for (let i = messages.length - 1; i >= 0; i--) {
- const msg = messages[i]
+ const msg = messages[i];
// If assistant message with completed ExitPlanMode, we found an unapproved plan
if (msg.role === "assistant" && msg.parts) {
const exitPlanPart = msg.parts.find(
- (p: any) => p.type === "tool-ExitPlanMode"
- )
+ (p: any) => p.type === "tool-ExitPlanMode",
+ );
// Check if ExitPlanMode is completed (has output, even if empty)
if (exitPlanPart && exitPlanPart.output !== undefined) {
- return true
+ return true;
}
}
}
- return false
- }, [messages, subChatMode])
+ return false;
+ }, [messages, subChatMode]);
// Keep ref in sync for use in initializeScroll (which runs in useLayoutEffect)
- hasUnapprovedPlanRef.current = hasUnapprovedPlan
+ hasUnapprovedPlanRef.current = hasUnapprovedPlan;
// Update pending plan approvals atom for sidebar indicators
- const setPendingPlanApprovals = useSetAtom(pendingPlanApprovalsAtom)
+ const setPendingPlanApprovals = useSetAtom(pendingPlanApprovalsAtom);
useEffect(() => {
setPendingPlanApprovals((prev: Map) => {
- const newMap = new Map(prev)
+ const newMap = new Map(prev);
if (hasUnapprovedPlan) {
- newMap.set(subChatId, parentChatId)
+ newMap.set(subChatId, parentChatId);
} else {
- newMap.delete(subChatId)
+ newMap.delete(subChatId);
}
// Only return new map if it changed
- if (newMap.size !== prev.size || ![...newMap.keys()].every((id) => prev.has(id))) {
- return newMap
+ if (
+ newMap.size !== prev.size ||
+ ![...newMap.keys()].every((id) => prev.has(id))
+ ) {
+ return newMap;
}
- return prev
- })
- }, [hasUnapprovedPlan, subChatId, parentChatId, setPendingPlanApprovals])
+ return prev;
+ });
+ }, [hasUnapprovedPlan, subChatId, parentChatId, setPendingPlanApprovals]);
// Keyboard shortcut: Cmd+Enter to approve plan
useEffect(() => {
- if (!isActive) return
+ if (!isActive) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (
@@ -3931,14 +4259,14 @@ const ChatViewInner = memo(function ChatViewInner({
hasUnapprovedPlan &&
!isStreaming
) {
- e.preventDefault()
- handleApprovePlan()
+ e.preventDefault();
+ handleApprovePlan();
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [isActive, hasUnapprovedPlan, isStreaming, handleApprovePlan])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [isActive, hasUnapprovedPlan, isStreaming, handleApprovePlan]);
// Cmd/Ctrl + Arrow Down to scroll to bottom (works even when focused in input)
// But don't intercept if input has content - let native cursor navigation work
@@ -3951,113 +4279,120 @@ const ChatViewInner = memo(function ChatViewInner({
!e.shiftKey
) {
// Don't intercept if input has content - let native cursor navigation work
- const inputValue = editorRef.current?.getValue() || ""
+ const inputValue = editorRef.current?.getValue() || "";
if (inputValue.trim().length > 0) {
- return
+ return;
}
- e.preventDefault()
- scrollToBottom()
+ e.preventDefault();
+ scrollToBottom();
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [scrollToBottom])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [scrollToBottom]);
// Clean up pending plan approval when unmounting
useEffect(() => {
return () => {
setPendingPlanApprovals((prev: Map) => {
if (prev.has(subChatId)) {
- const newMap = new Map(prev)
- newMap.delete(subChatId)
- return newMap
+ const newMap = new Map(prev);
+ newMap.delete(subChatId);
+ return newMap;
}
- return prev
- })
- }
- }, [subChatId, setPendingPlanApprovals])
+ return prev;
+ });
+ };
+ }, [subChatId, setPendingPlanApprovals]);
// Compute sticky top class for user messages
const stickyTopClass = isMobile
? CHAT_LAYOUT.stickyTopMobile
: isSubChatsSidebarOpen
? CHAT_LAYOUT.stickyTopSidebarOpen
- : CHAT_LAYOUT.stickyTopSidebarClosed
+ : CHAT_LAYOUT.stickyTopSidebarClosed;
// Sync messages to Jotai store for isolated rendering
// CRITICAL: Only sync from the ACTIVE tab to prevent overwriting global atoms
// Each tab has its own useChat() instance, but global atoms (messageIdsAtom, etc.) are shared.
// Only the active tab should update these global atoms.
- const syncMessages = useSetAtom(syncMessagesWithStatusAtom)
+ const syncMessages = useSetAtom(syncMessagesWithStatusAtom);
useLayoutEffect(() => {
// Skip syncing for inactive tabs - they shouldn't update global atoms
- if (!isActive) return
- syncMessages({ messages, status, subChatId })
- }, [messages, status, subChatId, syncMessages, isActive])
+ if (!isActive) return;
+ syncMessages({ messages, status, subChatId });
+ }, [messages, status, subChatId, syncMessages, isActive]);
// Sync status to global streaming status store for queue processing
- const setStreamingStatus = useStreamingStatusStore((s) => s.setStatus)
+ const setStreamingStatus = useStreamingStatusStore((s) => s.setStatus);
useEffect(() => {
- setStreamingStatus(subChatId, status as "ready" | "streaming" | "submitted" | "error")
- }, [subChatId, status, setStreamingStatus])
+ setStreamingStatus(
+ subChatId,
+ status as "ready" | "streaming" | "submitted" | "error",
+ );
+ }, [subChatId, status, setStreamingStatus]);
// Chat search - scroll to current match
// Use ref to track scroll lock and prevent race conditions
- const searchScrollLockRef = useRef(0)
- const currentSearchMatch = useAtomValue(chatSearchCurrentMatchAtom)
+ const searchScrollLockRef = useRef(0);
+ const currentSearchMatch = useAtomValue(chatSearchCurrentMatchAtom);
useEffect(() => {
- if (!currentSearchMatch) return
+ if (!currentSearchMatch) return;
- const container = chatContainerRef.current
- if (!container) return
+ const container = chatContainerRef.current;
+ if (!container) return;
// Increment lock to cancel any pending scroll operations
- const currentLock = ++searchScrollLockRef.current
+ const currentLock = ++searchScrollLockRef.current;
// Use double requestAnimationFrame + small delay to ensure DOM has updated with new highlights
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTimeout(() => {
// Check if this scroll operation is still valid (not superseded by newer one)
- if (searchScrollLockRef.current !== currentLock) return
+ if (searchScrollLockRef.current !== currentLock) return;
// First try to find the highlight mark
- let targetElement: Element | null = container.querySelector(".search-highlight-current")
+ let targetElement: Element | null = container.querySelector(
+ ".search-highlight-current",
+ );
// If no highlight mark, find the message element with matching data attributes
if (!targetElement) {
- const selector = `[data-message-id="${currentSearchMatch.messageId}"][data-part-index="${currentSearchMatch.partIndex}"]`
- targetElement = container.querySelector(selector)
+ const selector = `[data-message-id="${currentSearchMatch.messageId}"][data-part-index="${currentSearchMatch.partIndex}"]`;
+ targetElement = container.querySelector(selector);
}
if (targetElement) {
// Check if this is inside a sticky user message container
- const stickyParent = targetElement.closest("[data-user-message-id]")
+ const stickyParent = targetElement.closest(
+ "[data-user-message-id]",
+ );
if (stickyParent) {
- const messageGroupWrapper = stickyParent.parentElement
+ const messageGroupWrapper = stickyParent.parentElement;
if (messageGroupWrapper) {
messageGroupWrapper.scrollIntoView({
behavior: "smooth",
block: "start",
- })
- return
+ });
+ return;
}
}
targetElement.scrollIntoView({
behavior: "smooth",
block: "center",
- })
+ });
}
- }, 50)
- })
- })
- }, [currentSearchMatch])
+ }, 50);
+ });
+ });
+ }, [currentSearchMatch]);
// Calculate top offset for search bar based on sub-chat selector
- const searchBarTopOffset = isSubChatsSidebarOpen ? "52px" : undefined
+ const searchBarTopOffset = isSubChatsSidebarOpen ? "52px" : undefined;
return (
@@ -4085,176 +4420,179 @@ const ChatViewInner = memo(function ChatViewInner({
{/* Chat title - flex above scroll area (desktop only) */}
{!isMobile && (
-
- 0}
- />
-
- )}
+
+ 0}
+ />
+
+ )}
- {/* Messages */}
- {
- // Cleanup previous observer
- if (chatContainerObserverRef.current) {
- chatContainerObserverRef.current.disconnect()
- chatContainerObserverRef.current = null
- }
+ {/* Messages */}
+
{
+ // Cleanup previous observer
+ if (chatContainerObserverRef.current) {
+ chatContainerObserverRef.current.disconnect();
+ chatContainerObserverRef.current = null;
+ }
- chatContainerRef.current = el
+ chatContainerRef.current = el;
- // Setup ResizeObserver for --chat-container-height CSS variable
- if (el) {
- const observer = new ResizeObserver((entries) => {
- const height = entries[0]?.contentRect.height ?? 0
- el.style.setProperty("--chat-container-height", `${height}px`)
- })
- observer.observe(el)
- chatContainerObserverRef.current = observer
- }
- }}
- className="flex-1 overflow-y-auto w-full relative allow-text-selection outline-none"
- tabIndex={-1}
- data-chat-container
- >
-
{
+ const height = entries[0]?.contentRect.height ?? 0;
+ el.style.setProperty("--chat-container-height", `${height}px`);
+ });
+ observer.observe(el);
+ chatContainerObserverRef.current = observer;
+ }
}}
+ className="flex-1 overflow-y-auto w-full relative allow-text-selection outline-none"
+ tabIndex={-1}
+ data-chat-container
>
-
- {/* ISOLATED: Messages rendered via Jotai atom subscription
+
+
+ {/* ISOLATED: Messages rendered via Jotai atom subscription
Each component subscribes to specific atoms and only re-renders when those change
KEY: Force remount on subChatId change to ensure fresh atom reads after syncMessages */}
-
-
-
-
-
- {/* User questions panel - shows for both live (pending) and expired (timed out) questions */}
- {displayQuestions && (
-
- )}
- {/* Stacked cards container - queue + status */}
- {!displayQuestions &&
- (queue.length > 0 || changedFilesForSubChat.length > 0) && (
-
-
- {/* Queue indicator card - top card */}
- {queue.length > 0 && (
-
0}
- />
- )}
- {/* Status card - bottom card, only when there are changed files */}
- {changedFilesForSubChat.length > 0 && (
- 0}
- />
- )}
+ {/* User questions panel - shows for both live (pending) and expired (timed out) questions */}
+ {displayQuestions && (
+
)}
- {/* Input - isolated component to prevent re-renders */}
-
+ {/* Stacked cards container - queue + status */}
+ {!displayQuestions &&
+ (queue.length > 0 || changedFilesForSubChat.length > 0) && (
+
+
+ {/* Queue indicator card - top card */}
+ {queue.length > 0 && (
+
0}
+ />
+ )}
+ {/* Status card - bottom card, only when there are changed files */}
+ {changedFilesForSubChat.length > 0 && (
+ 0}
+ />
+ )}
+
+
+ )}
+
+ {/* Input - isolated component to prevent re-renders */}
+
{/* Scroll to bottom button - isolated component to avoid re-renders during streaming */}
0 || changedFilesForSubChat.length > 0)}
+ hasStackedCards={
+ !displayQuestions &&
+ (queue.length > 0 || changedFilesForSubChat.length > 0)
+ }
subChatId={subChatId}
isActive={isActive}
/>
- )
-})
+ );
+});
// Chat View wrapper - handles loading and creates chat object
export function ChatView({
@@ -4269,133 +4607,144 @@ export function ChatView({
onOpenDiff,
onOpenTerminal,
}: {
- chatId: string
- isSidebarOpen: boolean
- onToggleSidebar: () => void
- selectedTeamName?: string
- selectedTeamImageUrl?: string
- isMobileFullscreen?: boolean
- onBackToChats?: () => void
- onOpenPreview?: () => void
- onOpenDiff?: () => void
- onOpenTerminal?: () => void
+ chatId: string;
+ isSidebarOpen: boolean;
+ onToggleSidebar: () => void;
+ selectedTeamName?: string;
+ selectedTeamImageUrl?: string;
+ isMobileFullscreen?: boolean;
+ onBackToChats?: () => void;
+ onOpenPreview?: () => void;
+ onOpenDiff?: () => void;
+ onOpenTerminal?: () => void;
}) {
- const [selectedTeamId] = useAtom(selectedTeamIdAtom)
- const [selectedModelId] = useAtom(lastSelectedModelIdAtom)
+ const [selectedTeamId] = useAtom(selectedTeamIdAtom);
+ const [selectedModelId] = useAtom(lastSelectedModelIdAtom);
// Get active sub-chat ID from store for mode tracking (reactive)
- const activeSubChatIdForMode = useAgentSubChatStore((state) => state.activeSubChatId)
+ const activeSubChatIdForMode = useAgentSubChatStore(
+ (state) => state.activeSubChatId,
+ );
// Use per-subChat mode atom - falls back to "agent" if no active sub-chat
const subChatModeAtom = useMemo(
() => subChatModeAtomFamily(activeSubChatIdForMode || ""),
[activeSubChatIdForMode],
- )
- const [subChatMode] = useAtom(subChatModeAtom)
+ );
+ const [subChatMode] = useAtom(subChatModeAtom);
// Default mode for new sub-chats (used as fallback when no active sub-chat)
- const defaultAgentMode = useAtomValue(defaultAgentModeAtom)
+ const defaultAgentMode = useAtomValue(defaultAgentModeAtom);
// Current mode - use subChatMode when there's an active sub-chat, otherwise use user's default preference
- const currentMode: AgentMode = activeSubChatIdForMode ? subChatMode : defaultAgentMode
-
- const isDesktop = useAtomValue(isDesktopAtom)
- const isFullscreen = useAtomValue(isFullscreenAtom)
- const customClaudeConfig = useAtomValue(customClaudeConfigAtom)
- const selectedOllamaModel = useAtomValue(selectedOllamaModelAtom)
+ const currentMode: AgentMode = activeSubChatIdForMode
+ ? subChatMode
+ : defaultAgentMode;
+
+ const isDesktop = useAtomValue(isDesktopAtom);
+ const isFullscreen = useAtomValue(isFullscreenAtom);
+ const customClaudeConfig = useAtomValue(customClaudeConfigAtom);
+ const selectedOllamaModel = useAtomValue(selectedOllamaModelAtom);
const normalizedCustomClaudeConfig =
- normalizeCustomClaudeConfig(customClaudeConfig)
- const hasCustomClaudeConfig = Boolean(normalizedCustomClaudeConfig)
- const setLoadingSubChats = useSetAtom(loadingSubChatsAtom)
- const unseenChanges = useAtomValue(agentsUnseenChangesAtom)
- const setUnseenChanges = useSetAtom(agentsUnseenChangesAtom)
- const setSubChatUnseenChanges = useSetAtom(agentsSubChatUnseenChangesAtom)
- const setJustCreatedIds = useSetAtom(justCreatedIdsAtom)
- const selectedChatId = useAtomValue(selectedAgentChatIdAtom)
- const setUndoStack = useSetAtom(undoStackAtom)
- const setSelectedFilePath = useSetAtom(selectedDiffFilePathAtom)
- const setFilteredDiffFiles = useSetAtom(filteredDiffFilesAtom)
- const { notifyAgentComplete } = useDesktopNotifications()
+ normalizeCustomClaudeConfig(customClaudeConfig);
+ const hasCustomClaudeConfig = Boolean(normalizedCustomClaudeConfig);
+ const setLoadingSubChats = useSetAtom(loadingSubChatsAtom);
+ const unseenChanges = useAtomValue(agentsUnseenChangesAtom);
+ const setUnseenChanges = useSetAtom(agentsUnseenChangesAtom);
+ const setSubChatUnseenChanges = useSetAtom(agentsSubChatUnseenChangesAtom);
+ const setJustCreatedIds = useSetAtom(justCreatedIdsAtom);
+ const selectedChatId = useAtomValue(selectedAgentChatIdAtom);
+ const setUndoStack = useSetAtom(undoStackAtom);
+ const setSelectedFilePath = useSetAtom(selectedDiffFilePathAtom);
+ const setFilteredDiffFiles = useSetAtom(filteredDiffFilesAtom);
+ const { notifyAgentComplete } = useDesktopNotifications();
// Check if any chat has unseen changes
- const hasAnyUnseenChanges = unseenChanges.size > 0
- const [, forceUpdate] = useState({})
+ const hasAnyUnseenChanges = unseenChanges.size > 0;
+ const [, forceUpdate] = useState({});
const [isPreviewSidebarOpen, setIsPreviewSidebarOpen] = useAtom(
agentsPreviewSidebarOpenAtom,
- )
+ );
// Per-chat diff sidebar state - each chat remembers its own open/close state
const diffSidebarAtom = useMemo(
() => diffSidebarOpenAtomFamily(chatId),
[chatId],
- )
- const [isDiffSidebarOpen, setIsDiffSidebarOpen] = useAtom(diffSidebarAtom)
+ );
+ const [isDiffSidebarOpen, setIsDiffSidebarOpen] = useAtom(diffSidebarAtom);
// Subscribe to activeSubChatId for plan sidebar (needs to update when switching sub-chats)
- const activeSubChatIdForPlan = useAgentSubChatStore((state) => state.activeSubChatId)
+ const activeSubChatIdForPlan = useAgentSubChatStore(
+ (state) => state.activeSubChatId,
+ );
// Per-subChat plan sidebar state - each sub-chat remembers its own open/close state
const planSidebarAtom = useMemo(
() => planSidebarOpenAtomFamily(activeSubChatIdForPlan || ""),
[activeSubChatIdForPlan],
- )
- const [isPlanSidebarOpen, setIsPlanSidebarOpen] = useAtom(planSidebarAtom)
+ );
+ const [isPlanSidebarOpen, setIsPlanSidebarOpen] = useAtom(planSidebarAtom);
const currentPlanPathAtom = useMemo(
() => currentPlanPathAtomFamily(activeSubChatIdForPlan || ""),
[activeSubChatIdForPlan],
- )
- const [currentPlanPath, setCurrentPlanPath] = useAtom(currentPlanPathAtom)
+ );
+ const [currentPlanPath, setCurrentPlanPath] = useAtom(currentPlanPathAtom);
// File viewer sidebar state - per-chat open file path
const fileViewerAtom = useMemo(
() => fileViewerOpenAtomFamily(chatId),
[chatId],
- )
- const [fileViewerPath, setFileViewerPath] = useAtom(fileViewerAtom)
- const [fileViewerDisplayMode] = useAtom(fileViewerDisplayModeAtom)
+ );
+ const [fileViewerPath, setFileViewerPath] = useAtom(fileViewerAtom);
+ const [fileViewerDisplayMode] = useAtom(fileViewerDisplayModeAtom);
// File search dialog (Cmd+P)
- const [fileSearchOpen, setFileSearchOpen] = useAtom(fileSearchDialogOpenAtom)
+ const [fileSearchOpen, setFileSearchOpen] = useAtom(fileSearchDialogOpenAtom);
// Details sidebar state (unified sidebar that combines all right sidebars)
- const isUnifiedSidebarEnabled = useAtomValue(unifiedSidebarEnabledAtom)
- const [isDetailsSidebarOpen, setIsDetailsSidebarOpen] = useAtom(detailsSidebarOpenAtom)
+ const isUnifiedSidebarEnabled = useAtomValue(unifiedSidebarEnabledAtom);
+ const [isDetailsSidebarOpen, setIsDetailsSidebarOpen] = useAtom(
+ detailsSidebarOpenAtom,
+ );
// Resolved hotkeys for tooltips
- const toggleDetailsHotkey = useResolvedHotkeyDisplay("toggle-details")
- const toggleTerminalHotkey = useResolvedHotkeyDisplay("toggle-terminal")
+ const toggleDetailsHotkey = useResolvedHotkeyDisplay("toggle-details");
+ const toggleTerminalHotkey = useResolvedHotkeyDisplay("toggle-terminal");
// Close plan sidebar when switching to a sub-chat that has no plan
- const prevSubChatIdRef = useRef(activeSubChatIdForPlan)
+ const prevSubChatIdRef = useRef(activeSubChatIdForPlan);
useEffect(() => {
if (prevSubChatIdRef.current !== activeSubChatIdForPlan) {
// Sub-chat changed - if new one has no plan path, close sidebar
if (!currentPlanPath) {
- setIsPlanSidebarOpen(false)
+ setIsPlanSidebarOpen(false);
}
- prevSubChatIdRef.current = activeSubChatIdForPlan
+ prevSubChatIdRef.current = activeSubChatIdForPlan;
}
- }, [activeSubChatIdForPlan, currentPlanPath, setIsPlanSidebarOpen])
- const setPendingBuildPlanSubChatId = useSetAtom(pendingBuildPlanSubChatIdAtom)
+ }, [activeSubChatIdForPlan, currentPlanPath, setIsPlanSidebarOpen]);
+ const setPendingBuildPlanSubChatId = useSetAtom(
+ pendingBuildPlanSubChatIdAtom,
+ );
// Read plan edit refetch trigger from atom (set by ChatViewInner when Edit completes)
const planEditRefetchTriggerAtom = useMemo(
() => planEditRefetchTriggerAtomFamily(activeSubChatIdForPlan || ""),
[activeSubChatIdForPlan],
- )
- const planEditRefetchTrigger = useAtomValue(planEditRefetchTriggerAtom)
+ );
+ const planEditRefetchTrigger = useAtomValue(planEditRefetchTriggerAtom);
// Handler for plan sidebar "Build plan" button
// Uses getState() to get fresh activeSubChatId (avoids stale closure)
const handleApprovePlanFromSidebar = useCallback(() => {
- const activeSubChatId = useAgentSubChatStore.getState().activeSubChatId
+ const activeSubChatId = useAgentSubChatStore.getState().activeSubChatId;
if (activeSubChatId) {
- setPendingBuildPlanSubChatId(activeSubChatId)
+ setPendingBuildPlanSubChatId(activeSubChatId);
}
- }, [setPendingBuildPlanSubChatId])
+ }, [setPendingBuildPlanSubChatId]);
// Per-chat terminal sidebar state - each chat remembers its own open/close state
const terminalSidebarAtom = useMemo(
() => terminalSidebarOpenAtomFamily(chatId),
[chatId],
- )
- const [isTerminalSidebarOpen, setIsTerminalSidebarOpen] = useAtom(terminalSidebarAtom)
- const terminalDisplayMode = useAtomValue(terminalDisplayModeAtom)
+ );
+ const [isTerminalSidebarOpen, setIsTerminalSidebarOpen] =
+ useAtom(terminalSidebarAtom);
+ const terminalDisplayMode = useAtomValue(terminalDisplayModeAtom);
// Keyboard shortcut: Cmd+J to toggle terminal
useEffect(() => {
@@ -4407,15 +4756,15 @@ export function ChatView({
!e.ctrlKey &&
e.code === "KeyJ"
) {
- e.preventDefault()
- e.stopPropagation()
- setIsTerminalSidebarOpen(!isTerminalSidebarOpen)
+ e.preventDefault();
+ e.stopPropagation();
+ setIsTerminalSidebarOpen(!isTerminalSidebarOpen);
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown, true)
- return () => window.removeEventListener("keydown", handleKeyDown, true)
- }, [isTerminalSidebarOpen, setIsTerminalSidebarOpen])
+ window.addEventListener("keydown", handleKeyDown, true);
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
+ }, [isTerminalSidebarOpen, setIsTerminalSidebarOpen]);
// Mutual exclusion: Details sidebar vs Plan/Terminal/Diff(side-peek) sidebars
// When one opens, close the conflicting ones and remember for restoration
@@ -4423,89 +4772,93 @@ export function ChatView({
// Track what was auto-closed and by whom for restoration
const autoClosedStateRef = useRef<{
// What closed Details
- detailsClosedBy: "plan" | "terminal" | "diff" | null
+ detailsClosedBy: "plan" | "terminal" | "diff" | null;
// What Details closed
- planClosedByDetails: boolean
- terminalClosedByDetails: boolean
- diffClosedByDetails: boolean
+ planClosedByDetails: boolean;
+ terminalClosedByDetails: boolean;
+ diffClosedByDetails: boolean;
}>({
detailsClosedBy: null,
planClosedByDetails: false,
terminalClosedByDetails: false,
diffClosedByDetails: false,
- })
+ });
// Track previous states to detect opens/closes
const prevSidebarStatesRef = useRef({
details: isDetailsSidebarOpen,
plan: isPlanSidebarOpen && !!currentPlanPath,
terminal: isTerminalSidebarOpen,
- })
+ });
useEffect(() => {
- const prev = prevSidebarStatesRef.current
- const auto = autoClosedStateRef.current
- const isPlanOpen = isPlanSidebarOpen && !!currentPlanPath
+ const prev = prevSidebarStatesRef.current;
+ const auto = autoClosedStateRef.current;
+ const isPlanOpen = isPlanSidebarOpen && !!currentPlanPath;
// Detect state changes
- const detailsJustOpened = isDetailsSidebarOpen && !prev.details
- const detailsJustClosed = !isDetailsSidebarOpen && prev.details
- const planJustOpened = isPlanOpen && !prev.plan
- const planJustClosed = !isPlanOpen && prev.plan
- const terminalJustOpened = isTerminalSidebarOpen && !prev.terminal
- const terminalJustClosed = !isTerminalSidebarOpen && prev.terminal
+ const detailsJustOpened = isDetailsSidebarOpen && !prev.details;
+ const detailsJustClosed = !isDetailsSidebarOpen && prev.details;
+ const planJustOpened = isPlanOpen && !prev.plan;
+ const planJustClosed = !isPlanOpen && prev.plan;
+ const terminalJustOpened = isTerminalSidebarOpen && !prev.terminal;
+ const terminalJustClosed = !isTerminalSidebarOpen && prev.terminal;
// Terminal in "bottom" mode doesn't conflict with Details sidebar
- const terminalConflictsWithDetails = terminalDisplayMode === "side-peek"
+ const terminalConflictsWithDetails = terminalDisplayMode === "side-peek";
// Details opened → close conflicting sidebars and remember
if (detailsJustOpened) {
if (isPlanOpen) {
- auto.planClosedByDetails = true
- setIsPlanSidebarOpen(false)
+ auto.planClosedByDetails = true;
+ setIsPlanSidebarOpen(false);
}
if (isTerminalSidebarOpen && terminalConflictsWithDetails) {
- auto.terminalClosedByDetails = true
- setIsTerminalSidebarOpen(false)
+ auto.terminalClosedByDetails = true;
+ setIsTerminalSidebarOpen(false);
}
}
// Details closed → restore what it closed
else if (detailsJustClosed) {
if (auto.planClosedByDetails) {
- auto.planClosedByDetails = false
- setIsPlanSidebarOpen(true)
+ auto.planClosedByDetails = false;
+ setIsPlanSidebarOpen(true);
}
if (auto.terminalClosedByDetails) {
- auto.terminalClosedByDetails = false
- setIsTerminalSidebarOpen(true)
+ auto.terminalClosedByDetails = false;
+ setIsTerminalSidebarOpen(true);
}
}
// Plan opened → close Details and remember
else if (planJustOpened && isDetailsSidebarOpen) {
- auto.detailsClosedBy = "plan"
- setIsDetailsSidebarOpen(false)
+ auto.detailsClosedBy = "plan";
+ setIsDetailsSidebarOpen(false);
}
// Plan closed → restore Details if we closed it
else if (planJustClosed && auto.detailsClosedBy === "plan") {
- auto.detailsClosedBy = null
- setIsDetailsSidebarOpen(true)
+ auto.detailsClosedBy = null;
+ setIsDetailsSidebarOpen(true);
}
// Terminal opened → close Details and remember (only in side-peek mode)
- else if (terminalJustOpened && isDetailsSidebarOpen && terminalConflictsWithDetails) {
- auto.detailsClosedBy = "terminal"
- setIsDetailsSidebarOpen(false)
+ else if (
+ terminalJustOpened &&
+ isDetailsSidebarOpen &&
+ terminalConflictsWithDetails
+ ) {
+ auto.detailsClosedBy = "terminal";
+ setIsDetailsSidebarOpen(false);
}
// Terminal closed → restore Details if we closed it
else if (terminalJustClosed && auto.detailsClosedBy === "terminal") {
- auto.detailsClosedBy = null
- setIsDetailsSidebarOpen(true)
+ auto.detailsClosedBy = null;
+ setIsDetailsSidebarOpen(true);
}
prevSidebarStatesRef.current = {
details: isDetailsSidebarOpen,
plan: isPlanOpen,
terminal: isTerminalSidebarOpen,
- }
+ };
}, [
isDetailsSidebarOpen,
isPlanSidebarOpen,
@@ -4515,260 +4868,289 @@ export function ChatView({
setIsDetailsSidebarOpen,
setIsPlanSidebarOpen,
setIsTerminalSidebarOpen,
- ])
+ ]);
// Diff data cache - stored in atoms to persist across workspace switches
const diffCacheAtom = useMemo(
() => workspaceDiffCacheAtomFamily(chatId),
[chatId],
- )
- const [diffCache, setDiffCache] = useAtom(diffCacheAtom)
+ );
+ const [diffCache, setDiffCache] = useAtom(diffCacheAtom);
// Extract diff data from cache
- const diffStats = diffCache.diffStats
- const parsedFileDiffs = diffCache.parsedFileDiffs as ParsedDiffFile[] | null
- const prefetchedFileContents = diffCache.prefetchedFileContents
- const diffContent = diffCache.diffContent
+ const diffStats = diffCache.diffStats;
+ const parsedFileDiffs = diffCache.parsedFileDiffs as ParsedDiffFile[] | null;
+ const prefetchedFileContents = diffCache.prefetchedFileContents;
+ const diffContent = diffCache.diffContent;
// Smart setters that update the cache
- const setDiffStats = useCallback((val: any) => {
- setDiffCache((prev) => {
- const newVal = typeof val === 'function' ? val(prev.diffStats) : val
- // Only update if something changed
- if (
- prev.diffStats.fileCount === newVal.fileCount &&
- prev.diffStats.additions === newVal.additions &&
- prev.diffStats.deletions === newVal.deletions &&
- prev.diffStats.isLoading === newVal.isLoading &&
- prev.diffStats.hasChanges === newVal.hasChanges
- ) {
- return prev // Return same reference to prevent re-render
- }
- return { ...prev, diffStats: newVal }
- })
- }, [setDiffCache])
-
- const setParsedFileDiffs = useCallback((files: ParsedDiffFile[] | null) => {
- setDiffCache((prev) => ({ ...prev, parsedFileDiffs: files as any }))
- }, [setDiffCache])
+ const setDiffStats = useCallback(
+ (val: any) => {
+ setDiffCache((prev) => {
+ const newVal = typeof val === "function" ? val(prev.diffStats) : val;
+ // Only update if something changed
+ if (
+ prev.diffStats.fileCount === newVal.fileCount &&
+ prev.diffStats.additions === newVal.additions &&
+ prev.diffStats.deletions === newVal.deletions &&
+ prev.diffStats.isLoading === newVal.isLoading &&
+ prev.diffStats.hasChanges === newVal.hasChanges
+ ) {
+ return prev; // Return same reference to prevent re-render
+ }
+ return { ...prev, diffStats: newVal };
+ });
+ },
+ [setDiffCache],
+ );
- const setPrefetchedFileContents = useCallback((contents: Record
) => {
- setDiffCache((prev) => ({ ...prev, prefetchedFileContents: contents }))
- }, [setDiffCache])
+ const setParsedFileDiffs = useCallback(
+ (files: ParsedDiffFile[] | null) => {
+ setDiffCache((prev) => ({ ...prev, parsedFileDiffs: files as any }));
+ },
+ [setDiffCache],
+ );
- const setDiffContent = useCallback((content: string | null) => {
- setDiffCache((prev) => ({ ...prev, diffContent: content }))
- }, [setDiffCache])
- const [diffMode, setDiffMode] = useAtom(diffViewModeAtom)
- const [diffDisplayMode, setDiffDisplayMode] = useAtom(diffViewDisplayModeAtom)
- const subChatsSidebarMode = useAtomValue(agentsSubChatsSidebarModeAtom)
+ const setPrefetchedFileContents = useCallback(
+ (contents: Record) => {
+ setDiffCache((prev) => ({ ...prev, prefetchedFileContents: contents }));
+ },
+ [setDiffCache],
+ );
+ const setDiffContent = useCallback(
+ (content: string | null) => {
+ setDiffCache((prev) => ({ ...prev, diffContent: content }));
+ },
+ [setDiffCache],
+ );
+ const [diffMode, setDiffMode] = useAtom(diffViewModeAtom);
+ const [diffDisplayMode, setDiffDisplayMode] = useAtom(
+ diffViewDisplayModeAtom,
+ );
+ const subChatsSidebarMode = useAtomValue(agentsSubChatsSidebarModeAtom);
// Force narrow width when switching to side-peek mode (from dialog/fullscreen)
useEffect(() => {
if (diffDisplayMode === "side-peek") {
// Set to narrow width (400px) to ensure correct layout
- appStore.set(agentsDiffSidebarWidthAtom, 400)
+ appStore.set(agentsDiffSidebarWidthAtom, 400);
}
- }, [diffDisplayMode])
+ }, [diffDisplayMode]);
// Handle Diff + Details sidebar conflict (side-peek mode only)
// - If Diff opens in side-peek while Details is open: switch Diff to center-peek (dialog) mode
// - If user manually switches Diff to side-peek while Details is open: close Details and remember
// - If Details opens while Diff is in side-peek mode: close Diff and remember
- const prevDiffStateRef = useRef<{ isOpen: boolean; mode: string; detailsOpen: boolean }>({
+ const prevDiffStateRef = useRef<{
+ isOpen: boolean;
+ mode: string;
+ detailsOpen: boolean;
+ }>({
isOpen: isDiffSidebarOpen,
mode: diffDisplayMode,
detailsOpen: isDetailsSidebarOpen,
- })
+ });
// Flag to skip center-peek switch when restoring Diff after Details closes
- const isRestoringDiffRef = useRef(false)
+ const isRestoringDiffRef = useRef(false);
useEffect(() => {
- const prev = prevDiffStateRef.current
- const auto = autoClosedStateRef.current
- const isNowSidePeek = isDiffSidebarOpen && diffDisplayMode === "side-peek"
- const wasSidePeek = prev.isOpen && prev.mode === "side-peek"
- const detailsJustOpened = isDetailsSidebarOpen && !prev.detailsOpen
- const detailsJustClosed = !isDetailsSidebarOpen && prev.detailsOpen
- const diffSidePeekJustClosed = wasSidePeek && !isNowSidePeek
+ const prev = prevDiffStateRef.current;
+ const auto = autoClosedStateRef.current;
+ const isNowSidePeek = isDiffSidebarOpen && diffDisplayMode === "side-peek";
+ const wasSidePeek = prev.isOpen && prev.mode === "side-peek";
+ const detailsJustOpened = isDetailsSidebarOpen && !prev.detailsOpen;
+ const detailsJustClosed = !isDetailsSidebarOpen && prev.detailsOpen;
+ const diffSidePeekJustClosed = wasSidePeek && !isNowSidePeek;
if (isNowSidePeek && isDetailsSidebarOpen) {
// Details just opened while Diff is in side-peek → close Diff and remember
if (detailsJustOpened) {
- auto.diffClosedByDetails = true
- setIsDiffSidebarOpen(false)
+ auto.diffClosedByDetails = true;
+ setIsDiffSidebarOpen(false);
}
// Diff just opened in side-peek mode → switch to dialog (don't close Details)
// Skip if we're restoring Diff after Details closed
else if (!prev.isOpen && !isRestoringDiffRef.current) {
- setDiffDisplayMode("center-peek")
+ setDiffDisplayMode("center-peek");
}
// User manually switched to side-peek while Diff was already open → close Details and remember
else if (prev.isOpen && prev.mode !== "side-peek") {
- auto.detailsClosedBy = "diff"
- setIsDetailsSidebarOpen(false)
+ auto.detailsClosedBy = "diff";
+ setIsDetailsSidebarOpen(false);
}
}
// Diff side-peek closed → restore Details if we closed it
else if (diffSidePeekJustClosed && auto.detailsClosedBy === "diff") {
- auto.detailsClosedBy = null
- setIsDetailsSidebarOpen(true)
+ auto.detailsClosedBy = null;
+ setIsDetailsSidebarOpen(true);
}
// Details closed → restore Diff if we closed it (in side-peek mode, not switching to dialog)
else if (detailsJustClosed && auto.diffClosedByDetails) {
- auto.diffClosedByDetails = false
- isRestoringDiffRef.current = true
- setIsDiffSidebarOpen(true)
+ auto.diffClosedByDetails = false;
+ isRestoringDiffRef.current = true;
+ setIsDiffSidebarOpen(true);
// Reset flag after state update
requestAnimationFrame(() => {
- isRestoringDiffRef.current = false
- })
+ isRestoringDiffRef.current = false;
+ });
}
- prevDiffStateRef.current = { isOpen: isDiffSidebarOpen, mode: diffDisplayMode, detailsOpen: isDetailsSidebarOpen }
- }, [isDiffSidebarOpen, diffDisplayMode, isDetailsSidebarOpen, setDiffDisplayMode, setIsDetailsSidebarOpen, setIsDiffSidebarOpen])
+ prevDiffStateRef.current = {
+ isOpen: isDiffSidebarOpen,
+ mode: diffDisplayMode,
+ detailsOpen: isDetailsSidebarOpen,
+ };
+ }, [
+ isDiffSidebarOpen,
+ diffDisplayMode,
+ isDetailsSidebarOpen,
+ setDiffDisplayMode,
+ setIsDetailsSidebarOpen,
+ setIsDiffSidebarOpen,
+ ]);
// Hide/show traffic lights based on full-page diff or full-page file viewer
useEffect(() => {
- if (!isDesktop || isFullscreen) return
- if (typeof window === "undefined" || !window.desktopApi?.setTrafficLightVisibility) return
+ if (!isDesktop || isFullscreen) return;
+ if (
+ typeof window === "undefined" ||
+ !window.desktopApi?.setTrafficLightVisibility
+ )
+ return;
- const isFullPageDiff = isDiffSidebarOpen && diffDisplayMode === "full-page"
- const isFullPageFileViewer = !!fileViewerPath && fileViewerDisplayMode === "full-page"
- window.desktopApi.setTrafficLightVisibility(!isFullPageDiff && !isFullPageFileViewer)
- }, [isDiffSidebarOpen, diffDisplayMode, fileViewerPath, fileViewerDisplayMode, isDesktop, isFullscreen])
+ const isFullPageDiff = isDiffSidebarOpen && diffDisplayMode === "full-page";
+ const isFullPageFileViewer = !!fileViewerPath && fileViewerDisplayMode === "full-page";
+ window.desktopApi.setTrafficLightVisibility(!isFullPageDiff && !isFullPageFileViewer);
+ }, [isDiffSidebarOpen, diffDisplayMode, fileViewerPath, fileViewerDisplayMode, isDesktop, isFullscreen]);
// Track diff sidebar width for responsive header
- const storedDiffSidebarWidth = useAtomValue(agentsDiffSidebarWidthAtom)
- const diffSidebarRef = useRef(null)
- const diffViewRef = useRef(null)
+ const storedDiffSidebarWidth = useAtomValue(agentsDiffSidebarWidthAtom);
+ const diffSidebarRef = useRef(null);
+ const diffViewRef = useRef(null);
const [diffSidebarWidth, setDiffSidebarWidth] = useState(
storedDiffSidebarWidth,
- )
+ );
// Track if all diff files are collapsed/expanded for button disabled states
const [diffCollapseState, setDiffCollapseState] = useState({
allCollapsed: false,
allExpanded: true,
- })
+ });
// Compute isNarrow for filtering logic (same threshold as DiffSidebarContent)
- const isDiffSidebarNarrow = diffSidebarWidth < 500
+ const isDiffSidebarNarrow = diffSidebarWidth < 500;
// ResizeObserver to track diff sidebar width in real-time (atom only updates after resize ends)
useEffect(() => {
if (!isDiffSidebarOpen) {
- return
+ return;
}
- let observer: ResizeObserver | null = null
- let rafId: number | null = null
+ let observer: ResizeObserver | null = null;
+ let rafId: number | null = null;
const checkRef = () => {
- const element = diffSidebarRef.current
+ const element = diffSidebarRef.current;
if (!element) {
// Retry if ref not ready yet
- rafId = requestAnimationFrame(checkRef)
- return
+ rafId = requestAnimationFrame(checkRef);
+ return;
}
// Set initial width
- setDiffSidebarWidth(element.offsetWidth || storedDiffSidebarWidth)
+ setDiffSidebarWidth(element.offsetWidth || storedDiffSidebarWidth);
observer = new ResizeObserver((entries) => {
for (const entry of entries) {
- const width = entry.contentRect.width
+ const width = entry.contentRect.width;
if (width > 0) {
- setDiffSidebarWidth(width)
+ setDiffSidebarWidth(width);
}
}
- })
+ });
- observer.observe(element)
- }
+ observer.observe(element);
+ };
- checkRef()
+ checkRef();
return () => {
- if (rafId !== null) cancelAnimationFrame(rafId)
- if (observer) observer.disconnect()
- }
- }, [isDiffSidebarOpen, storedDiffSidebarWidth])
+ if (rafId !== null) cancelAnimationFrame(rafId);
+ if (observer) observer.disconnect();
+ };
+ }, [isDiffSidebarOpen, storedDiffSidebarWidth]);
// Track changed files across all sub-chats for throttled diff refresh
- const subChatFiles = useAtomValue(subChatFilesAtom)
+ const subChatFiles = useAtomValue(subChatFilesAtom);
// Initialize to Date.now() to prevent double-fetch on mount
// (the "mount" effect already fetches, throttle should wait)
- const lastDiffFetchTimeRef = useRef(Date.now())
- const DIFF_THROTTLE_MS = 2000 // Max 1 fetch per 2 seconds
+ const lastDiffFetchTimeRef = useRef(Date.now());
+ const DIFF_THROTTLE_MS = 2000; // Max 1 fetch per 2 seconds
// Clear "unseen changes" when chat is opened
useEffect(() => {
setUnseenChanges((prev: Set) => {
if (prev.has(chatId)) {
- const next = new Set(prev)
- next.delete(chatId)
- return next
+ const next = new Set(prev);
+ next.delete(chatId);
+ return next;
}
- return prev
- })
- }, [chatId, setUnseenChanges])
+ return prev;
+ });
+ }, [chatId, setUnseenChanges]);
// Get sub-chat state from store (reactive subscription for tabsToRender)
- const {
- activeSubChatId,
- openSubChatIds,
- pinnedSubChatIds,
- allSubChats,
- } = useAgentSubChatStore(
- useShallow((state) => ({
- activeSubChatId: state.activeSubChatId,
- openSubChatIds: state.openSubChatIds,
- pinnedSubChatIds: state.pinnedSubChatIds,
- allSubChats: state.allSubChats,
- }))
- )
+ const { activeSubChatId, openSubChatIds, pinnedSubChatIds, allSubChats } =
+ useAgentSubChatStore(
+ useShallow((state) => ({
+ activeSubChatId: state.activeSubChatId,
+ openSubChatIds: state.openSubChatIds,
+ pinnedSubChatIds: state.pinnedSubChatIds,
+ allSubChats: state.allSubChats,
+ })),
+ );
// Clear sub-chat "unseen changes" indicator when sub-chat becomes active
useEffect(() => {
- if (!activeSubChatId) return
+ if (!activeSubChatId) return;
setSubChatUnseenChanges((prev: Set) => {
if (prev.has(activeSubChatId)) {
- const next = new Set(prev)
- next.delete(activeSubChatId)
- return next
+ const next = new Set(prev);
+ next.delete(activeSubChatId);
+ return next;
}
- return prev
- })
- }, [activeSubChatId, setSubChatUnseenChanges])
+ return prev;
+ });
+ }, [activeSubChatId, setSubChatUnseenChanges]);
// tRPC utils for optimistic cache updates
- const utils = api.useUtils()
+ const utils = api.useUtils();
// tRPC mutations for renaming
- const renameSubChatMutation = api.agents.renameSubChat.useMutation()
- const renameChatMutation = api.agents.renameChat.useMutation()
+ const renameSubChatMutation = api.agents.renameSubChat.useMutation();
+ const renameChatMutation = api.agents.renameChat.useMutation();
const generateSubChatNameMutation =
- api.agents.generateSubChatName.useMutation()
+ api.agents.generateSubChatName.useMutation();
// PR creation loading state - using atom to allow ChatViewInner to reset it
- const [isCreatingPr, setIsCreatingPr] = useAtom(isCreatingPrAtom)
+ const [isCreatingPr, setIsCreatingPr] = useAtom(isCreatingPrAtom);
// Review loading state
- const [isReviewing, setIsReviewing] = useState(false)
+ const [isReviewing, setIsReviewing] = useState(false);
// Subchat filter setter - used by handleReview to filter by active subchat
- const setFilteredSubChatId = useSetAtom(filteredSubChatIdAtom)
+ const setFilteredSubChatId = useSetAtom(filteredSubChatIdAtom);
// Determine if we're in sandbox mode
- const chatSourceMode = useAtomValue(chatSourceModeAtom)
+ const chatSourceMode = useAtomValue(chatSourceModeAtom);
// Fetch chat data from local or remote based on mode
- const { data: localAgentChat, isLoading: isLocalLoading } = api.agents.getAgentChat.useQuery(
- { chatId },
- { enabled: !!chatId && chatSourceMode === "local" },
- )
+ const { data: localAgentChat, isLoading: isLocalLoading } =
+ api.agents.getAgentChat.useQuery(
+ { chatId },
+ { enabled: !!chatId && chatSourceMode === "local" },
+ );
const { data: remoteAgentChat, isLoading: isRemoteLoading } = useRemoteChat(
chatSourceMode === "sandbox" ? chatId : null,
- )
+ );
// Use the appropriate data source
// IMPORTANT: Must memoize to prevent infinite re-render loop
@@ -4776,7 +5158,7 @@ export function ChatView({
// which triggers the useEffect that calls setAllSubChats(), causing re-renders
const agentChat = useMemo(() => {
if (chatSourceMode === "sandbox") {
- if (!remoteAgentChat) return null
+ if (!remoteAgentChat) return null;
return {
...remoteAgentChat,
// Transform remote chat to match local structure
@@ -4794,70 +5176,75 @@ export function ChatView({
isRemote: true,
// Preserve stats from remote chat for diff display
remoteStats: remoteAgentChat.stats,
- subChats: remoteAgentChat.subChats?.map(sc => ({
- ...sc,
- created_at: new Date(sc.created_at),
- updated_at: new Date(sc.updated_at),
- })) ?? [],
- }
+ subChats:
+ remoteAgentChat.subChats?.map((sc) => ({
+ ...sc,
+ created_at: new Date(sc.created_at),
+ updated_at: new Date(sc.updated_at),
+ })) ?? [],
+ };
}
- return localAgentChat
- }, [chatSourceMode, remoteAgentChat, localAgentChat])
+ return localAgentChat;
+ }, [chatSourceMode, remoteAgentChat, localAgentChat]);
- const isLoading = chatSourceMode === "sandbox" ? isRemoteLoading : isLocalLoading
+ const isLoading =
+ chatSourceMode === "sandbox" ? isRemoteLoading : isLocalLoading;
// Compute if we're waiting for local chat data (used as loading gate)
- const isLocalChatLoading = chatSourceMode === "local" && isLocalLoading
+ const isLocalChatLoading = chatSourceMode === "local" && isLocalLoading;
// Projects query for "Open Locally" functionality
- const { data: projects } = trpc.projects.list.useQuery()
+ const { data: projects } = trpc.projects.list.useQuery();
// Open Locally dialog state
- const [openLocallyDialogOpen, setOpenLocallyDialogOpen] = useState(false)
+ const [openLocallyDialogOpen, setOpenLocallyDialogOpen] = useState(false);
// Auto-import hook for "Open Locally"
- const { getMatchingProjects, autoImport, isImporting } = useAutoImport()
+ const { getMatchingProjects, autoImport, isImporting } = useAutoImport();
// Handler for "Open Locally" button in header
const handleOpenLocally = useCallback(() => {
- if (!remoteAgentChat) return
+ if (!remoteAgentChat) return;
- const matchingProjects = getMatchingProjects(projects ?? [], remoteAgentChat)
+ const matchingProjects = getMatchingProjects(
+ projects ?? [],
+ remoteAgentChat,
+ );
if (matchingProjects.length === 1) {
// Auto-import: single match found
- autoImport(remoteAgentChat, matchingProjects[0]!)
+ autoImport(remoteAgentChat, matchingProjects[0]!);
} else {
// Show dialog: 0 or 2+ matches
- setOpenLocallyDialogOpen(true)
+ setOpenLocallyDialogOpen(true);
}
- }, [remoteAgentChat, projects, getMatchingProjects, autoImport])
+ }, [remoteAgentChat, projects, getMatchingProjects, autoImport]);
// Determine if "Open Locally" button should show
- const showOpenLocally = chatSourceMode === "sandbox" && !!remoteAgentChat
+ const showOpenLocally = chatSourceMode === "sandbox" && !!remoteAgentChat;
// Get matching projects for dialog (only computed when needed)
const openLocallyMatchingProjects = useMemo(() => {
- if (!remoteAgentChat) return []
- return getMatchingProjects(projects ?? [], remoteAgentChat)
- }, [remoteAgentChat, projects, getMatchingProjects])
+ if (!remoteAgentChat) return [];
+ return getMatchingProjects(projects ?? [], remoteAgentChat);
+ }, [remoteAgentChat, projects, getMatchingProjects]);
const agentSubChats = (agentChat?.subChats ?? []) as Array<{
- id: string
- name?: string | null
- mode?: "plan" | "agent" | null
- created_at?: Date | string | null
- updated_at?: Date | string | null
- messages?: any
- stream_id?: string | null
- }>
+ id: string;
+ name?: string | null;
+ mode?: "plan" | "agent" | null;
+ created_at?: Date | string | null;
+ updated_at?: Date | string | null;
+ messages?: any;
+ stream_id?: string | null;
+ }>;
// Workspace isolation: limit mounted tabs to prevent memory growth
// CRITICAL: Filter by workspace to prevent rendering sub-chats from other workspaces
// Always render: active + pinned, then fill with recent up to limit
- const MAX_MOUNTED_TABS = 5
+ const MAX_MOUNTED_TABS = 5;
const tabsToRender = useMemo(() => {
- if (!activeSubChatId) return []
+ if (!activeSubChatId) return [];
// Combine server data (agentSubChats) with local store (allSubChats) for validation.
// This handles:
@@ -4866,125 +5253,151 @@ export function ChatView({
//
// By combining both sources, we validate against all known sub-chats from both server and local state.
const validSubChatIds = new Set([
- ...agentSubChats.map(sc => sc.id),
- ...allSubChats.map(sc => sc.id),
- ])
+ ...agentSubChats.map((sc) => sc.id),
+ ...allSubChats.map((sc) => sc.id),
+ ]);
// If active sub-chat doesn't belong to this workspace → return []
// This prevents rendering sub-chats from another workspace during race condition
if (!validSubChatIds.has(activeSubChatId)) {
- return []
+ return [];
}
// Filter openSubChatIds and pinnedSubChatIds to only valid IDs for this workspace
- const validOpenIds = openSubChatIds.filter(id => validSubChatIds.has(id))
- const validPinnedIds = pinnedSubChatIds.filter(id => validSubChatIds.has(id))
+ const validOpenIds = openSubChatIds.filter((id) => validSubChatIds.has(id));
+ const validPinnedIds = pinnedSubChatIds.filter((id) =>
+ validSubChatIds.has(id),
+ );
// Start with active (must always be mounted)
- const mustRender = new Set([activeSubChatId])
+ const mustRender = new Set([activeSubChatId]);
// Add pinned tabs (only valid ones)
for (const id of validPinnedIds) {
- mustRender.add(id)
+ mustRender.add(id);
}
// If we have room, add recent tabs from openSubChatIds (only valid ones)
if (mustRender.size < MAX_MOUNTED_TABS) {
- const remaining = MAX_MOUNTED_TABS - mustRender.size
+ const remaining = MAX_MOUNTED_TABS - mustRender.size;
const recentTabs = validOpenIds
- .filter(id => !mustRender.has(id))
- .slice(-remaining) // Take the most recent (end of array)
+ .filter((id) => !mustRender.has(id))
+ .slice(-remaining); // Take the most recent (end of array)
for (const id of recentTabs) {
- mustRender.add(id)
+ mustRender.add(id);
}
}
// Return tabs to render
// Always include activeSubChatId even if not in validOpenIds (handles race condition
// where openSubChatIds from localStorage doesn't include the active tab yet)
- const result = validOpenIds.filter(id => mustRender.has(id))
+ const result = validOpenIds.filter((id) => mustRender.has(id));
if (!result.includes(activeSubChatId)) {
- result.unshift(activeSubChatId)
+ result.unshift(activeSubChatId);
}
- return result
- }, [activeSubChatId, pinnedSubChatIds, openSubChatIds, allSubChats, agentSubChats])
+ return result;
+ }, [
+ activeSubChatId,
+ pinnedSubChatIds,
+ openSubChatIds,
+ allSubChats,
+ agentSubChats,
+ ]);
// Get PR status when PR exists (for checking if it's open/merged/closed)
- const hasPrNumber = !!agentChat?.prNumber
- const { data: prStatusData, isLoading: isPrStatusLoading } = trpc.chats.getPrStatus.useQuery(
- { chatId },
- {
- enabled: hasPrNumber,
- refetchInterval: 30000, // Poll every 30 seconds
- }
- )
- const prState = prStatusData?.pr?.state as "open" | "draft" | "merged" | "closed" | undefined
- const prMergeable = prStatusData?.pr?.mergeable
- const hasMergeConflicts = prMergeable === "CONFLICTING"
+ const hasPrNumber = !!agentChat?.prNumber;
+ const { data: prStatusData, isLoading: isPrStatusLoading } =
+ trpc.chats.getPrStatus.useQuery(
+ { chatId },
+ {
+ enabled: hasPrNumber,
+ refetchInterval: 30000, // Poll every 30 seconds
+ },
+ );
+ const prState = prStatusData?.pr?.state as
+ | "open"
+ | "draft"
+ | "merged"
+ | "closed"
+ | undefined;
+ const prMergeable = prStatusData?.pr?.mergeable;
+ const hasMergeConflicts = prMergeable === "CONFLICTING";
// PR is open if state is explicitly "open" or "draft"
// When PR status is still loading, assume open to avoid showing wrong button
- const isPrOpen = hasPrNumber && (isPrStatusLoading || prState === "open" || prState === "draft")
+ const isPrOpen =
+ hasPrNumber &&
+ (isPrStatusLoading || prState === "open" || prState === "draft");
// Merge PR mutation
- const trpcUtils = trpc.useUtils()
+ const trpcUtils = trpc.useUtils();
// Direct PR creation mutation (push branch and open GitHub)
const createPrMutation = trpc.changes.createPR.useMutation({
onSuccess: () => {
- toast.success("Opening GitHub to create PR...", { position: "top-center" })
- refetchGitStatus()
+ toast.success("Opening GitHub to create PR...", {
+ position: "top-center",
+ });
+ refetchGitStatus();
},
onError: (error) => {
- toast.error(error.message || "Failed to create PR", { position: "top-center" })
+ toast.error(error.message || "Failed to create PR", {
+ position: "top-center",
+ });
},
- })
+ });
// Sync from main mutation (for resolving merge conflicts)
const mergeFromDefaultMutation = trpc.changes.mergeFromDefault.useMutation({
onSuccess: () => {
- toast.success("Branch synced with main. You can now merge the PR.", { position: "top-center" })
+ toast.success("Branch synced with main. You can now merge the PR.", {
+ position: "top-center",
+ });
// Invalidate PR status to refresh mergeability
- trpcUtils.chats.getPrStatus.invalidate({ chatId })
+ trpcUtils.chats.getPrStatus.invalidate({ chatId });
},
onError: (error) => {
- toast.error(error.message || "Failed to sync with main", { position: "top-center" })
+ toast.error(error.message || "Failed to sync with main", {
+ position: "top-center",
+ });
},
- })
+ });
const mergePrMutation = trpc.chats.mergePr.useMutation({
onSuccess: () => {
- toast.success("PR merged successfully!", { position: "top-center" })
+ toast.success("PR merged successfully!", { position: "top-center" });
// Invalidate PR status to update button state
- trpcUtils.chats.getPrStatus.invalidate({ chatId })
+ trpcUtils.chats.getPrStatus.invalidate({ chatId });
},
onError: (error) => {
- const errorMsg = error.message || "Failed to merge PR"
+ const errorMsg = error.message || "Failed to merge PR";
// Check if it's a merge conflict error
if (errorMsg.includes("MERGE_CONFLICT")) {
- toast.error(
- "PR has merge conflicts. Sync with main to resolve.",
- {
- position: "top-center",
- duration: 8000,
- action: worktreePath ? {
- label: "Sync with Main",
- onClick: () => {
- mergeFromDefaultMutation.mutate({ worktreePath, useRebase: false })
- },
- } : undefined,
- }
- )
+ toast.error("PR has merge conflicts. Sync with main to resolve.", {
+ position: "top-center",
+ duration: 8000,
+ action: worktreePath
+ ? {
+ label: "Sync with Main",
+ onClick: () => {
+ mergeFromDefaultMutation.mutate({
+ worktreePath,
+ useRebase: false,
+ });
+ },
+ }
+ : undefined,
+ });
} else {
- toast.error(errorMsg, { position: "top-center" })
+ toast.error(errorMsg, { position: "top-center" });
}
},
- })
+ });
const handleMergePr = useCallback(() => {
- mergePrMutation.mutate({ chatId, method: "squash" })
- }, [chatId, mergePrMutation])
+ mergePrMutation.mutate({ chatId, method: "squash" });
+ }, [chatId, mergePrMutation]);
// Restore archived workspace mutation (silent - no toast)
const restoreWorkspaceMutation = trpc.chats.restore.useMutation({
@@ -4992,101 +5405,103 @@ export function ChatView({
if (restoredChat) {
// Update the main chat list cache
trpcUtils.chats.list.setData({}, (oldData) => {
- if (!oldData) return [restoredChat]
- if (oldData.some((c) => c.id === restoredChat.id)) return oldData
- return [restoredChat, ...oldData]
- })
+ if (!oldData) return [restoredChat];
+ if (oldData.some((c) => c.id === restoredChat.id)) return oldData;
+ return [restoredChat, ...oldData];
+ });
}
// Invalidate both lists to refresh
- trpcUtils.chats.list.invalidate()
- trpcUtils.chats.listArchived.invalidate()
+ trpcUtils.chats.list.invalidate();
+ trpcUtils.chats.listArchived.invalidate();
// Invalidate this chat's data to update isArchived state
- utils.agents.getAgentChat.invalidate({ chatId })
+ utils.agents.getAgentChat.invalidate({ chatId });
},
- })
+ });
const handleRestoreWorkspace = useCallback(() => {
- restoreWorkspaceMutation.mutate({ id: chatId })
- }, [chatId, restoreWorkspaceMutation])
+ restoreWorkspaceMutation.mutate({ id: chatId });
+ }, [chatId, restoreWorkspaceMutation]);
// Check if this workspace is archived
- const isArchived = !!agentChat?.archivedAt
+ const isArchived = !!agentChat?.archivedAt;
// Get user usage data for credit checks
- const { data: usageData } = api.usage.getUserUsage.useQuery()
+ const { data: usageData } = api.usage.getUserUsage.useQuery();
// Desktop: use worktreePath instead of sandbox
- const worktreePath = agentChat?.worktreePath as string | null
+ const worktreePath = agentChat?.worktreePath as string | null;
// Desktop: original project path for MCP config lookup
- const originalProjectPath = (agentChat as any)?.project?.path as string | undefined
+ const originalProjectPath = (agentChat as any)?.project?.path as
+ | string
+ | undefined;
// Fallback for web: use sandbox_id
- const sandboxId = agentChat?.sandbox_id
- const sandboxUrl = sandboxId ? `https://3003-${sandboxId}.e2b.app` : null
+ const sandboxId = agentChat?.sandbox_id;
+ const sandboxUrl = sandboxId ? `https://3003-${sandboxId}.e2b.app` : null;
// Desktop uses worktreePath, web uses sandboxUrl
- const chatWorkingDir = worktreePath || sandboxUrl
+ const chatWorkingDir = worktreePath || sandboxUrl;
// Listen for file changes from Claude Write/Edit tools and invalidate git status
- useFileChangeListener(worktreePath)
+ useFileChangeListener(worktreePath);
// Subscribe to GitWatcher for real-time file system monitoring (chokidar on main process)
- useGitWatcher(worktreePath)
+ useGitWatcher(worktreePath);
// Plugin MCP approval - disabled for now since official marketplace plugins
// are trusted by default. Will re-enable when third-party plugin support is added.
// Extract port, repository, and quick setup flag from meta
const meta = agentChat?.meta as {
- sandboxConfig?: { port?: number }
- repository?: string
- branch?: string | null
- isQuickSetup?: boolean
- } | null
- const repository = meta?.repository
+ sandboxConfig?: { port?: number };
+ repository?: string;
+ branch?: string | null;
+ isQuickSetup?: boolean;
+ } | null;
+ const repository = meta?.repository;
// Remote info for Details sidebar (when worktreePath is null but sandboxId exists)
const remoteInfo = useMemo(() => {
- if (worktreePath || !sandboxId) return null
+ if (worktreePath || !sandboxId) return null;
return {
repository: meta?.repository,
branch: meta?.branch,
sandboxId,
- }
- }, [worktreePath, sandboxId, meta?.repository, meta?.branch])
+ };
+ }, [worktreePath, sandboxId, meta?.repository, meta?.branch]);
// Track if we've already triggered sandbox setup for this chat
// Check if this is a quick setup (no preview available)
- const isQuickSetup = meta?.isQuickSetup || !meta?.sandboxConfig?.port
- const previewPort = meta?.sandboxConfig?.port ?? 3000
+ const isQuickSetup = meta?.isQuickSetup || !meta?.sandboxConfig?.port;
+ const previewPort = meta?.sandboxConfig?.port ?? 3000;
// Check if preview can be opened (sandbox with port exists and not quick setup)
const canOpenPreview = !!(
sandboxId &&
!isQuickSetup &&
meta?.sandboxConfig?.port
- )
+ );
// Check if diff button can be shown (stats available)
// This shows the Changes button with stats in header
- const canShowDiffButton = !!worktreePath || !!sandboxId
+ const canShowDiffButton = !!worktreePath || !!sandboxId;
// Check if diff sidebar can be opened (actual diff content available)
// Desktop remote chats (sandboxId without worktree) cannot open diff sidebar - only stats in header
- const canOpenDiff = !!worktreePath || (!!sandboxId && !isDesktopApp())
+ const canOpenDiff = !!worktreePath || (!!sandboxId && !isDesktopApp());
// Create list of subchats with changed files for filtering
// Only include subchats that have uncommitted changes, sorted by most recent first
const subChatsWithFiles = useMemo(() => {
const result: Array<{
- id: string
- name: string
- filePaths: string[]
- fileCount: number
- updatedAt: string
- }> = []
+ id: string;
+ name: string;
+ filePaths: string[];
+ fileCount: number;
+ updatedAt: string;
+ }> = [];
// Only include subchats that have files (uncommitted changes)
for (const subChat of allSubChats) {
- const files = subChatFiles.get(subChat.id) || []
+ const files = subChatFiles.get(subChat.id) || [];
if (files.length > 0) {
result.push({
id: subChat.id,
@@ -5094,70 +5509,75 @@ export function ChatView({
filePaths: files.map((f) => f.filePath),
fileCount: files.length,
updatedAt: subChat.updated_at || subChat.created_at || "",
- })
+ });
}
}
// Sort by most recent first
result.sort((a, b) => {
- if (!a.updatedAt && !b.updatedAt) return 0
- if (!a.updatedAt) return 1
- if (!b.updatedAt) return -1
- return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
- })
+ if (!a.updatedAt && !b.updatedAt) return 0;
+ if (!a.updatedAt) return 1;
+ if (!b.updatedAt) return -1;
+ return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
+ });
- return result
- }, [allSubChats, subChatFiles])
+ return result;
+ }, [allSubChats, subChatFiles]);
// Close preview sidebar if preview becomes unavailable
useEffect(() => {
if (!canOpenPreview && isPreviewSidebarOpen) {
- setIsPreviewSidebarOpen(false)
+ setIsPreviewSidebarOpen(false);
}
- }, [canOpenPreview, isPreviewSidebarOpen, setIsPreviewSidebarOpen])
+ }, [canOpenPreview, isPreviewSidebarOpen, setIsPreviewSidebarOpen]);
// Note: We no longer forcibly close diff sidebar when canOpenDiff is false.
// The sidebar render is guarded by canOpenDiff, so it naturally hides.
// Per-chat state (diffSidebarOpenAtomFamily) preserves each chat's preference.
// Fetch diff stats - extracted as callback for reuse in onFinish
- const fetchDiffStatsDebounceRef = useRef(null)
- const isFetchingDiffRef = useRef(false)
+ const fetchDiffStatsDebounceRef = useRef(null);
+ const isFetchingDiffRef = useRef(false);
const fetchDiffStats = useCallback(async () => {
- console.log("[fetchDiffStats] Called with:", { worktreePath, sandboxId, chatId, isDesktop: isDesktopApp() })
+ console.log("[fetchDiffStats] Called with:", {
+ worktreePath,
+ sandboxId,
+ chatId,
+ isDesktop: isDesktopApp(),
+ });
// Desktop uses worktreePath, web uses sandboxId
// Don't reset stats if worktreePath is temporarily undefined - just skip the fetch
// This prevents the button from becoming disabled when component re-renders
if (!worktreePath && !sandboxId) {
- console.log("[fetchDiffStats] Skipping - no worktreePath or sandboxId")
- return
+ console.log("[fetchDiffStats] Skipping - no worktreePath or sandboxId");
+ return;
}
// Prevent duplicate parallel fetches
if (isFetchingDiffRef.current) {
- console.log("[fetchDiffStats] Skipping - already fetching")
- return
+ console.log("[fetchDiffStats] Skipping - already fetching");
+ return;
}
- isFetchingDiffRef.current = true
- console.log("[fetchDiffStats] Starting fetch...")
+ isFetchingDiffRef.current = true;
+ console.log("[fetchDiffStats] Starting fetch...");
try {
// Desktop: use new getParsedDiff endpoint (all-in-one: parsing + file contents)
if (worktreePath && chatId) {
- const result = await trpcClient.chats.getParsedDiff.query({ chatId })
+ const result = await trpcClient.chats.getParsedDiff.query({ chatId });
if (result.files.length > 0) {
// Store parsed files directly (already parsed on server)
- setParsedFileDiffs(result.files)
+ setParsedFileDiffs(result.files);
// Store prefetched file contents
- setPrefetchedFileContents(result.fileContents)
+ setPrefetchedFileContents(result.fileContents);
// Set diff content to null since we have parsed files
// (AgentDiffView will use parsedFileDiffs when available)
- setDiffContent(null)
+ setDiffContent(null);
setDiffStats({
fileCount: result.files.length,
@@ -5165,7 +5585,7 @@ export function ChatView({
deletions: result.totalDeletions,
isLoading: false,
hasChanges: result.files.length > 0,
- })
+ });
} else {
setDiffStats({
fileCount: 0,
@@ -5173,30 +5593,33 @@ export function ChatView({
deletions: 0,
isLoading: false,
hasChanges: false,
- })
+ });
// Use empty array instead of null to signal "no changes" vs "still loading"
- setParsedFileDiffs([])
- setPrefetchedFileContents({})
- setDiffContent(null)
+ setParsedFileDiffs([]);
+ setPrefetchedFileContents({});
+ setDiffContent(null);
}
- return
+ return;
}
// Desktop without chat (viewing main repo directly)
if (worktreePath && !chatId) {
// TODO: Need to add endpoint that accepts worktreePath directly
- return
+ return;
}
// Remote sandbox: use stats from chat data (desktop) or fetch diff (web)
if (sandboxId) {
- console.log("[fetchDiffStats] Sandbox mode - sandboxId:", sandboxId)
+ console.log("[fetchDiffStats] Sandbox mode - sandboxId:", sandboxId);
// Desktop app: use stats already provided in chat data
// The diff sidebar won't work for remote chats (no worktree), but stats will show
if (isDesktopApp()) {
- const remoteStats = (agentChat as any)?.remoteStats
- console.log("[fetchDiffStats] Desktop remote chat - using remoteStats:", remoteStats)
+ const remoteStats = (agentChat as any)?.remoteStats;
+ console.log(
+ "[fetchDiffStats] Desktop remote chat - using remoteStats:",
+ remoteStats,
+ );
if (remoteStats) {
setDiffStats({
@@ -5205,7 +5628,7 @@ export function ChatView({
deletions: remoteStats.deletions,
isLoading: false,
hasChanges: remoteStats.fileCount > 0,
- })
+ });
} else {
setDiffStats({
fileCount: 0,
@@ -5213,251 +5636,269 @@ export function ChatView({
deletions: 0,
isLoading: false,
hasChanges: false,
- })
+ });
}
// No parsed files for remote chats - diff view not available
- setParsedFileDiffs([])
- setPrefetchedFileContents({})
- setDiffContent(null)
- return
+ setParsedFileDiffs([]);
+ setPrefetchedFileContents({});
+ setDiffContent(null);
+ return;
}
// Web: use relative fetch to get actual diff
- let rawDiff: string | null = null
- const response = await fetch(`/api/agents/sandbox/${sandboxId}/diff`)
+ let rawDiff: string | null = null;
+ const response = await fetch(`/api/agents/sandbox/${sandboxId}/diff`);
if (!response.ok) {
- setDiffStats((prev) => ({ ...prev, isLoading: false }))
- return
+ setDiffStats((prev) => ({ ...prev, isLoading: false }));
+ return;
}
- const data = await response.json()
- rawDiff = data.diff || null
+ const data = await response.json();
+ rawDiff = data.diff || null;
// Store raw diff for AgentDiffView
- console.log("[fetchDiffStats] Setting diff content, length:", rawDiff?.length ?? 0)
- setDiffContent(rawDiff)
+ console.log(
+ "[fetchDiffStats] Setting diff content, length:",
+ rawDiff?.length ?? 0,
+ );
+ setDiffContent(rawDiff);
if (rawDiff && rawDiff.trim()) {
// Parse diff to get file list and stats (client-side for web)
- console.log("[fetchDiffStats] Parsing diff...")
- const parsedFiles = splitUnifiedDiffByFile(rawDiff)
- console.log("[fetchDiffStats] Parsed files:", parsedFiles.length, "files")
- setParsedFileDiffs(parsedFiles)
-
- let additions = 0
- let deletions = 0
+ console.log("[fetchDiffStats] Parsing diff...");
+ const parsedFiles = splitUnifiedDiffByFile(rawDiff);
+ console.log(
+ "[fetchDiffStats] Parsed files:",
+ parsedFiles.length,
+ "files",
+ );
+ setParsedFileDiffs(parsedFiles);
+
+ let additions = 0;
+ let deletions = 0;
for (const file of parsedFiles) {
- additions += file.additions
- deletions += file.deletions
+ additions += file.additions;
+ deletions += file.deletions;
}
- console.log("[fetchDiffStats] Setting stats:", { fileCount: parsedFiles.length, additions, deletions })
+ console.log("[fetchDiffStats] Setting stats:", {
+ fileCount: parsedFiles.length,
+ additions,
+ deletions,
+ });
setDiffStats({
fileCount: parsedFiles.length,
additions,
deletions,
isLoading: false,
hasChanges: parsedFiles.length > 0,
- })
+ });
} else {
- console.log("[fetchDiffStats] No diff content, setting empty stats")
+ console.log("[fetchDiffStats] No diff content, setting empty stats");
setDiffStats({
fileCount: 0,
additions: 0,
deletions: 0,
isLoading: false,
hasChanges: false,
- })
+ });
// Use empty array instead of null to signal "no changes" vs "still loading"
- setParsedFileDiffs([])
- setPrefetchedFileContents({})
+ setParsedFileDiffs([]);
+ setPrefetchedFileContents({});
}
}
} catch (error) {
- console.error("[fetchDiffStats] Error:", error)
- setDiffStats((prev) => ({ ...prev, isLoading: false }))
+ console.error("[fetchDiffStats] Error:", error);
+ setDiffStats((prev) => ({ ...prev, isLoading: false }));
} finally {
- console.log("[fetchDiffStats] Done")
- isFetchingDiffRef.current = false
+ console.log("[fetchDiffStats] Done");
+ isFetchingDiffRef.current = false;
}
- }, [worktreePath, sandboxId, chatId, agentChat]) // Note: activeSubChatId removed - diff is same for whole chat
+ }, [worktreePath, sandboxId, chatId, agentChat]); // Note: activeSubChatId removed - diff is same for whole chat
// Debounced version for calling after stream ends
const fetchDiffStatsDebounced = useCallback(() => {
if (fetchDiffStatsDebounceRef.current) {
- clearTimeout(fetchDiffStatsDebounceRef.current)
+ clearTimeout(fetchDiffStatsDebounceRef.current);
}
fetchDiffStatsDebounceRef.current = setTimeout(() => {
- fetchDiffStats()
- }, 500) // 500ms debounce to avoid spamming if multiple streams end
- }, [fetchDiffStats])
+ fetchDiffStats();
+ }, 500); // 500ms debounce to avoid spamming if multiple streams end
+ }, [fetchDiffStats]);
// Ref to hold the latest fetchDiffStatsDebounced for use in onFinish callbacks
- const fetchDiffStatsRef = useRef(fetchDiffStatsDebounced)
+ const fetchDiffStatsRef = useRef(fetchDiffStatsDebounced);
useEffect(() => {
- fetchDiffStatsRef.current = fetchDiffStatsDebounced
- }, [fetchDiffStatsDebounced])
+ fetchDiffStatsRef.current = fetchDiffStatsDebounced;
+ }, [fetchDiffStatsDebounced]);
// Fetch diff stats on mount and when worktreePath/sandboxId changes
useEffect(() => {
- fetchDiffStats()
- }, [fetchDiffStats])
+ fetchDiffStats();
+ }, [fetchDiffStats]);
// Refresh diff stats when diff sidebar opens (background refresh - don't block UI)
// Keep existing data visible while fetching, only update if data changed
useEffect(() => {
if (isDiffSidebarOpen) {
// Fetch in background - existing parsedFileDiffs will be shown immediately
- fetchDiffStats()
+ fetchDiffStats();
}
- }, [isDiffSidebarOpen, fetchDiffStats])
+ }, [isDiffSidebarOpen, fetchDiffStats]);
// Calculate total file count across all sub-chats for change detection
const totalSubChatFileCount = useMemo(() => {
- let count = 0
+ let count = 0;
subChatFiles.forEach((files) => {
- count += files.length
- })
- return count
- }, [subChatFiles])
+ count += files.length;
+ });
+ return count;
+ }, [subChatFiles]);
// Throttled refetch when sub-chat files change (agent edits/writes files)
// This keeps the top-right diff sidebar in sync with the bottom "Generated X files" bar
useEffect(() => {
// Skip if no files tracked yet (initial state)
- if (totalSubChatFileCount === 0) return
+ if (totalSubChatFileCount === 0) return;
- const now = Date.now()
- const timeSinceLastFetch = now - lastDiffFetchTimeRef.current
+ const now = Date.now();
+ const timeSinceLastFetch = now - lastDiffFetchTimeRef.current;
if (timeSinceLastFetch >= DIFF_THROTTLE_MS) {
// Enough time passed, fetch immediately
- lastDiffFetchTimeRef.current = now
- fetchDiffStats()
+ lastDiffFetchTimeRef.current = now;
+ fetchDiffStats();
} else {
// Schedule fetch for when throttle window ends
- const delay = DIFF_THROTTLE_MS - timeSinceLastFetch
+ const delay = DIFF_THROTTLE_MS - timeSinceLastFetch;
const timer = setTimeout(() => {
- lastDiffFetchTimeRef.current = Date.now()
- fetchDiffStats()
- }, delay)
- return () => clearTimeout(timer)
+ lastDiffFetchTimeRef.current = Date.now();
+ fetchDiffStats();
+ }, delay);
+ return () => clearTimeout(timer);
}
- }, [totalSubChatFileCount, fetchDiffStats])
+ }, [totalSubChatFileCount, fetchDiffStats]);
// Handle Create PR (Direct) - pushes branch and opens GitHub compare URL
const handleCreatePrDirect = useCallback(async () => {
if (!worktreePath) {
- toast.error("No workspace path available", { position: "top-center" })
- return
+ toast.error("No workspace path available", { position: "top-center" });
+ return;
}
- setIsCreatingPr(true)
+ setIsCreatingPr(true);
try {
- await createPrMutation.mutateAsync({ worktreePath })
+ await createPrMutation.mutateAsync({ worktreePath });
} finally {
- setIsCreatingPr(false)
+ setIsCreatingPr(false);
}
- }, [worktreePath, createPrMutation])
+ }, [worktreePath, createPrMutation]);
// Handle Create PR with AI - sends a message to Claude to create the PR
- const setPendingPrMessage = useSetAtom(pendingPrMessageAtom)
+ const setPendingPrMessage = useSetAtom(pendingPrMessageAtom);
const handleCreatePr = useCallback(async () => {
if (!chatId) {
- toast.error("Chat ID is required", { position: "top-center" })
- return
+ toast.error("Chat ID is required", { position: "top-center" });
+ return;
}
- setIsCreatingPr(true)
+ setIsCreatingPr(true);
try {
// Get PR context from backend
- const context = await trpcClient.chats.getPrContext.query({ chatId })
+ const context = await trpcClient.chats.getPrContext.query({ chatId });
if (!context) {
- toast.error("Could not get git context", { position: "top-center" })
- setIsCreatingPr(false)
- return
+ toast.error("Could not get git context", { position: "top-center" });
+ setIsCreatingPr(false);
+ return;
}
// Generate message and set it for ChatViewInner to send
- const message = generatePrMessage(context)
- setPendingPrMessage(message)
+ const message = generatePrMessage(context);
+ setPendingPrMessage(message);
// Don't reset isCreatingPr here - it will be reset after message is sent
} catch (error) {
toast.error(
error instanceof Error ? error.message : "Failed to prepare PR request",
{ position: "top-center" },
- )
- setIsCreatingPr(false)
+ );
+ setIsCreatingPr(false);
}
- }, [chatId, setPendingPrMessage])
+ }, [chatId, setPendingPrMessage]);
// Handle Commit to existing PR - sends a message to Claude to commit and push
// selectedPaths parameter is optional - if provided, only those files will be mentioned
- const [isCommittingToPr, setIsCommittingToPr] = useState(false)
- const handleCommitToPr = useCallback(async (_selectedPaths?: string[]) => {
- if (!chatId) {
- toast.error("Chat ID is required", { position: "top-center" })
- return
- }
-
- try {
- setIsCommittingToPr(true)
- const context = await trpcClient.chats.getPrContext.query({ chatId })
- if (!context) {
- toast.error("Could not get git context", { position: "top-center" })
- return
+ const [isCommittingToPr, setIsCommittingToPr] = useState(false);
+ const handleCommitToPr = useCallback(
+ async (_selectedPaths?: string[]) => {
+ if (!chatId) {
+ toast.error("Chat ID is required", { position: "top-center" });
+ return;
}
- const message = generateCommitToPrMessage(context)
- setPendingPrMessage(message)
- } catch (error) {
- toast.error(
- error instanceof Error ? error.message : "Failed to prepare commit request",
- { position: "top-center" },
- )
- } finally {
- setIsCommittingToPr(false)
- }
- }, [chatId, setPendingPrMessage])
+ try {
+ setIsCommittingToPr(true);
+ const context = await trpcClient.chats.getPrContext.query({ chatId });
+ if (!context) {
+ toast.error("Could not get git context", { position: "top-center" });
+ return;
+ }
+
+ const message = generateCommitToPrMessage(context);
+ setPendingPrMessage(message);
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Failed to prepare commit request",
+ { position: "top-center" },
+ );
+ } finally {
+ setIsCommittingToPr(false);
+ }
+ },
+ [chatId, setPendingPrMessage],
+ );
// Handle Review - sends a message to Claude to review the diff
- const setPendingReviewMessage = useSetAtom(pendingReviewMessageAtom)
+ const setPendingReviewMessage = useSetAtom(pendingReviewMessageAtom);
const handleReview = useCallback(async () => {
if (!chatId) {
- toast.error("Chat ID is required", { position: "top-center" })
- return
+ toast.error("Chat ID is required", { position: "top-center" });
+ return;
}
- setIsReviewing(true)
+ setIsReviewing(true);
try {
// Get PR context from backend
- const context = await trpcClient.chats.getPrContext.query({ chatId })
+ const context = await trpcClient.chats.getPrContext.query({ chatId });
if (!context) {
- toast.error("Could not get git context", { position: "top-center" })
- return
+ toast.error("Could not get git context", { position: "top-center" });
+ return;
}
// Set filter to show only files from the active subchat
if (activeSubChatId) {
- setFilteredSubChatId(activeSubChatId)
+ setFilteredSubChatId(activeSubChatId);
}
// Generate review message and set it for ChatViewInner to send
- const message = generateReviewMessage(context)
- setPendingReviewMessage(message)
+ const message = generateReviewMessage(context);
+ setPendingReviewMessage(message);
} catch (error) {
toast.error(
error instanceof Error ? error.message : "Failed to start review",
{ position: "top-center" },
- )
+ );
} finally {
- setIsReviewing(false)
+ setIsReviewing(false);
}
- }, [chatId, activeSubChatId, setPendingReviewMessage, setFilteredSubChatId])
+ }, [chatId, activeSubChatId, setPendingReviewMessage, setFilteredSubChatId]);
// Handle Fix Conflicts - sends a message to Claude to sync with main and fix merge conflicts
- const setPendingConflictResolutionMessage = useSetAtom(pendingConflictResolutionMessageAtom)
+ const setPendingConflictResolutionMessage = useSetAtom(
+ pendingConflictResolutionMessageAtom,
+ );
const handleFixConflicts = useCallback(() => {
const message = `This PR has merge conflicts with the main branch. Please:
@@ -5467,116 +5908,126 @@ export function ChatView({
3. After resolving conflicts, commit the merge
4. Push the changes to update the PR
-Make sure to preserve all functionality from both branches when resolving conflicts.`
+Make sure to preserve all functionality from both branches when resolving conflicts.`;
- setPendingConflictResolutionMessage(message)
- }, [setPendingConflictResolutionMessage])
+ setPendingConflictResolutionMessage(message);
+ }, [setPendingConflictResolutionMessage]);
// Fetch branch data for diff sidebar header
const { data: branchData } = trpc.changes.getBranches.useQuery(
{ worktreePath: worktreePath || "" },
- { enabled: !!worktreePath }
- )
+ { enabled: !!worktreePath },
+ );
// Fetch git status for sync counts (pushCount, pullCount, hasUpstream)
- const { data: gitStatus, refetch: refetchGitStatus, isLoading: isGitStatusLoading } = trpc.changes.getStatus.useQuery(
+ const {
+ data: gitStatus,
+ refetch: refetchGitStatus,
+ isLoading: isGitStatusLoading,
+ } = trpc.changes.getStatus.useQuery(
{ worktreePath: worktreePath || "" },
- { enabled: !!worktreePath && isDiffSidebarOpen, staleTime: 30000 }
- )
+ { enabled: !!worktreePath && isDiffSidebarOpen, staleTime: 30000 },
+ );
// Refetch git status and diff stats when window gains focus
useEffect(() => {
- if (!worktreePath || !isDiffSidebarOpen) return
+ if (!worktreePath || !isDiffSidebarOpen) return;
const handleWindowFocus = () => {
// Refetch git status
- refetchGitStatus()
+ refetchGitStatus();
// Refetch diff stats to get latest changes
- fetchDiffStats()
- }
+ fetchDiffStats();
+ };
- window.addEventListener('focus', handleWindowFocus)
- return () => window.removeEventListener('focus', handleWindowFocus)
- }, [worktreePath, isDiffSidebarOpen, refetchGitStatus, fetchDiffStats])
+ window.addEventListener("focus", handleWindowFocus);
+ return () => window.removeEventListener("focus", handleWindowFocus);
+ }, [worktreePath, isDiffSidebarOpen, refetchGitStatus, fetchDiffStats]);
// Sync parsedFileDiffs with git status - clear diff data when all files are committed
// This fixes the issue where diff sidebar shows stale files after external git commit
useEffect(() => {
- if (!gitStatus || isGitStatusLoading) return
+ if (!gitStatus || isGitStatusLoading) return;
// Check if git status shows no uncommitted changes
const hasUncommittedChanges =
(gitStatus.staged?.length ?? 0) > 0 ||
(gitStatus.unstaged?.length ?? 0) > 0 ||
- (gitStatus.untracked?.length ?? 0) > 0
+ (gitStatus.untracked?.length ?? 0) > 0;
// If git shows no changes but we still have parsedFileDiffs, clear them
- if (!hasUncommittedChanges && parsedFileDiffs && parsedFileDiffs.length > 0) {
- console.log('[active-chat] Git status empty but parsedFileDiffs has files, refreshing diff data')
- setParsedFileDiffs([])
- setPrefetchedFileContents({})
- setDiffContent(null)
+ if (
+ !hasUncommittedChanges &&
+ parsedFileDiffs &&
+ parsedFileDiffs.length > 0
+ ) {
+ console.log(
+ "[active-chat] Git status empty but parsedFileDiffs has files, refreshing diff data",
+ );
+ setParsedFileDiffs([]);
+ setPrefetchedFileContents({});
+ setDiffContent(null);
setDiffStats({
fileCount: 0,
additions: 0,
deletions: 0,
isLoading: false,
hasChanges: false,
- })
+ });
}
- }, [gitStatus, isGitStatusLoading, parsedFileDiffs])
+ }, [gitStatus, isGitStatusLoading, parsedFileDiffs]);
// Stable callbacks for DiffSidebarHeader to prevent re-renders
const handleRefreshGitStatus = useCallback(() => {
- refetchGitStatus()
- }, [refetchGitStatus])
+ refetchGitStatus();
+ }, [refetchGitStatus]);
const handleExpandAll = useCallback(() => {
- diffViewRef.current?.expandAll()
- }, [])
+ diffViewRef.current?.expandAll();
+ }, []);
const handleCollapseAll = useCallback(() => {
- diffViewRef.current?.collapseAll()
- }, [])
+ diffViewRef.current?.collapseAll();
+ }, []);
const handleMarkAllViewed = useCallback(() => {
- diffViewRef.current?.markAllViewed()
- }, [])
+ diffViewRef.current?.markAllViewed();
+ }, []);
const handleMarkAllUnviewed = useCallback(() => {
- diffViewRef.current?.markAllUnviewed()
- }, [])
+ diffViewRef.current?.markAllUnviewed();
+ }, []);
// Initialize store when chat data loads
useEffect(() => {
- if (!agentChat) return
+ if (!agentChat) return;
- const store = useAgentSubChatStore.getState()
+ const store = useAgentSubChatStore.getState();
// Only initialize if chatId changed
if (store.chatId !== chatId) {
- store.setChatId(chatId)
+ store.setChatId(chatId);
}
// Re-get fresh state after setChatId may have loaded from localStorage
- const freshState = useAgentSubChatStore.getState()
+ const freshState = useAgentSubChatStore.getState();
// Get sub-chats from DB (like Canvas - no isPersistedInDb flag)
// Build a map of existing local sub-chats to preserve their created_at if DB doesn't have it
const existingSubChatsMap = new Map(
freshState.allSubChats.map((sc) => [sc.id, sc]),
- )
+ );
const dbSubChats: SubChatMeta[] = agentSubChats.map((sc) => {
- const existingLocal = existingSubChatsMap.get(sc.id)
+ const existingLocal = existingSubChatsMap.get(sc.id);
const createdAt =
typeof sc.created_at === "string"
? sc.created_at
- : sc.created_at?.toISOString()
+ : sc.created_at?.toISOString();
const updatedAt =
typeof sc.updated_at === "string"
? sc.updated_at
- : sc.updated_at?.toISOString()
+ : sc.updated_at?.toISOString();
return {
id: sc.id,
name: sc.name || "New Chat",
@@ -5588,117 +6039,128 @@ Make sure to preserve all functionality from both branches when resolving confli
(sc.mode as "plan" | "agent" | undefined) ||
existingLocal?.mode ||
"agent",
- }
- })
- const dbSubChatIds = new Set(dbSubChats.map((sc) => sc.id))
+ };
+ });
+ const dbSubChatIds = new Set(dbSubChats.map((sc) => sc.id));
// Start with DB sub-chats
- const allSubChats: SubChatMeta[] = [...dbSubChats]
+ const allSubChats: SubChatMeta[] = [...dbSubChats];
// For each open tab ID that's NOT in DB, add placeholder (like Canvas)
// This prevents losing tabs during race conditions
- const currentOpenIds = freshState.openSubChatIds
+ const currentOpenIds = freshState.openSubChatIds;
currentOpenIds.forEach((id) => {
if (!dbSubChatIds.has(id)) {
allSubChats.push({
id,
name: "New Chat",
created_at: new Date().toISOString(),
- })
+ });
}
- })
+ });
- freshState.setAllSubChats(allSubChats)
+ freshState.setAllSubChats(allSubChats);
// Initialize atomFamily mode for each sub-chat from database
// This ensures new chats with mode="plan" use the correct mode
for (const sc of dbSubChats) {
if (sc.mode) {
- appStore.set(subChatModeAtomFamily(sc.id), sc.mode)
+ appStore.set(subChatModeAtomFamily(sc.id), sc.mode);
}
}
// All open tabs are now valid (we created placeholders for non-DB ones)
- const validOpenIds = currentOpenIds
+ const validOpenIds = currentOpenIds;
if (validOpenIds.length === 0 && allSubChats.length > 0) {
// No valid open tabs, open the first sub-chat
- freshState.addToOpenSubChats(allSubChats[0].id)
- freshState.setActiveSubChat(allSubChats[0].id)
+ freshState.addToOpenSubChats(allSubChats[0].id);
+ freshState.setActiveSubChat(allSubChats[0].id);
} else if (validOpenIds.length > 0) {
// Validate active tab is in open tabs
- const currentActive = freshState.activeSubChatId
+ const currentActive = freshState.activeSubChatId;
if (!currentActive || !validOpenIds.includes(currentActive)) {
- freshState.setActiveSubChat(validOpenIds[0])
+ freshState.setActiveSubChat(validOpenIds[0]);
}
}
- }, [agentChat, chatId])
+ }, [agentChat, chatId]);
// Auto-detect plan path from ACTIVE sub-chat messages when sub-chat changes
// This ensures the plan sidebar shows the correct plan for the active sub-chat only
useEffect(() => {
- if (!agentSubChats || agentSubChats.length === 0 || !activeSubChatIdForPlan) {
- setCurrentPlanPath(null)
- return
+ if (
+ !agentSubChats ||
+ agentSubChats.length === 0 ||
+ !activeSubChatIdForPlan
+ ) {
+ setCurrentPlanPath(null);
+ return;
}
// Find the active sub-chat
- const activeSubChat = agentSubChats.find(sc => sc.id === activeSubChatIdForPlan)
+ const activeSubChat = agentSubChats.find(
+ (sc) => sc.id === activeSubChatIdForPlan,
+ );
if (!activeSubChat) {
- setCurrentPlanPath(null)
- return
+ setCurrentPlanPath(null);
+ return;
}
// Find last plan file path from active sub-chat only
- let lastPlanPath: string | null = null
- const messages = (activeSubChat.messages as any[]) || []
+ let lastPlanPath: string | null = null;
+ const messages = (activeSubChat.messages as any[]) || [];
for (const msg of messages) {
- if (msg.role !== "assistant") continue
- const parts = msg.parts || []
+ if (msg.role !== "assistant") continue;
+ const parts = msg.parts || [];
for (const part of parts) {
if (
part.type === "tool-Write" &&
isPlanFile(part.input?.file_path || "")
) {
- lastPlanPath = part.input.file_path
+ lastPlanPath = part.input.file_path;
}
}
}
- setCurrentPlanPath(lastPlanPath)
- }, [agentSubChats, activeSubChatIdForPlan, setCurrentPlanPath])
+ setCurrentPlanPath(lastPlanPath);
+ }, [agentSubChats, activeSubChatIdForPlan, setCurrentPlanPath]);
// Create or get Chat instance for a sub-chat
const getOrCreateChat = useCallback(
(subChatId: string): Chat | null => {
// Desktop uses worktreePath, web uses sandboxUrl
if (!chatWorkingDir || !agentChat) {
- return null
+ return null;
}
// Return existing chat if we have it
- const existing = agentChatStore.get(subChatId)
+ const existing = agentChatStore.get(subChatId);
if (existing) {
- return existing
+ return existing;
}
// Find sub-chat data
- const subChat = agentSubChats.find((sc) => sc.id === subChatId)
- const messages = (subChat?.messages as any[]) || []
+ const subChat = agentSubChats.find((sc) => sc.id === subChatId);
+ const messages = (subChat?.messages as any[]) || [];
// Get mode from store metadata (falls back to currentMode)
const subChatMeta = useAgentSubChatStore
.getState()
- .allSubChats.find((sc) => sc.id === subChatId)
- const subChatMode = subChatMeta?.mode || currentMode
+ .allSubChats.find((sc) => sc.id === subChatId);
+ const subChatMode = subChatMeta?.mode || currentMode;
// Create transport based on chat type (local worktree vs remote sandbox)
// Note: Extended thinking setting is read dynamically inside the transport
// projectPath: original project path for MCP config lookup (worktreePath is the cwd)
- const projectPath = (agentChat as any)?.project?.path as string | undefined
- const chatSandboxId = (agentChat as any)?.sandboxId || (agentChat as any)?.sandbox_id
- const chatSandboxUrl = chatSandboxId ? `https://3003-${chatSandboxId}.e2b.app` : null
- const isRemoteChat = !!(agentChat as any)?.isRemote || !!chatSandboxId
+ const projectPath = (agentChat as any)?.project?.path as
+ | string
+ | undefined;
+ const chatSandboxId =
+ (agentChat as any)?.sandboxId || (agentChat as any)?.sandbox_id;
+ const chatSandboxUrl = chatSandboxId
+ ? `https://3003-${chatSandboxId}.e2b.app`
+ : null;
+ const isRemoteChat = !!(agentChat as any)?.isRemote || !!chatSandboxId;
console.log("[getOrCreateChat] Transport selection", {
subChatId: subChatId.slice(-8),
@@ -5706,15 +6168,19 @@ Make sure to preserve all functionality from both branches when resolving confli
chatSandboxId,
chatSandboxUrl,
worktreePath: worktreePath ? "exists" : "none",
- })
+ });
- let transport: IPCChatTransport | RemoteChatTransport | null = null
+ let transport: IPCChatTransport | RemoteChatTransport | null = null;
if (isRemoteChat && chatSandboxUrl) {
// Remote sandbox chat: use HTTP SSE transport
- const subChatName = subChat?.name || "Chat"
- const modelString = MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"]
- console.log("[getOrCreateChat] Using RemoteChatTransport", { sandboxUrl: chatSandboxUrl, model: modelString })
+ const subChatName = subChat?.name || "Chat";
+ const modelString =
+ MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"];
+ console.log("[getOrCreateChat] Using RemoteChatTransport", {
+ sandboxUrl: chatSandboxUrl,
+ model: modelString,
+ });
transport = new RemoteChatTransport({
chatId,
subChatId,
@@ -5722,7 +6188,7 @@ Make sure to preserve all functionality from both branches when resolving confli
sandboxUrl: chatSandboxUrl,
mode: subChatMode,
model: modelString,
- })
+ });
} else if (worktreePath) {
// Local worktree chat: use IPC transport
transport = new IPCChatTransport({
@@ -5731,12 +6197,12 @@ Make sure to preserve all functionality from both branches when resolving confli
cwd: worktreePath,
projectPath,
mode: subChatMode,
- })
+ });
}
if (!transport) {
- console.error("[getOrCreateChat] No transport available")
- return null
+ console.error("[getOrCreateChat] No transport available");
+ return null;
}
const newChat = new Chat({
@@ -5745,76 +6211,78 @@ Make sure to preserve all functionality from both branches when resolving confli
transport,
onError: () => {
// Sync status to global store on error (allows queue to continue)
- useStreamingStatusStore.getState().setStatus(subChatId, "ready")
+ useStreamingStatusStore.getState().setStatus(subChatId, "ready");
},
// Clear loading when streaming completes (works even if component unmounted)
onFinish: () => {
- clearLoading(setLoadingSubChats, subChatId)
+ clearLoading(setLoadingSubChats, subChatId);
// Sync status to global store for queue processing (even when component unmounted)
- useStreamingStatusStore.getState().setStatus(subChatId, "ready")
+ useStreamingStatusStore.getState().setStatus(subChatId, "ready");
// Check if this was a manual abort (ESC/Ctrl+C) - skip sound if so
const wasManuallyAborted =
- agentChatStore.wasManuallyAborted(subChatId)
- agentChatStore.clearManuallyAborted(subChatId)
+ agentChatStore.wasManuallyAborted(subChatId);
+ agentChatStore.clearManuallyAborted(subChatId);
// Get CURRENT values at runtime (not stale closure values)
const currentActiveSubChatId =
- useAgentSubChatStore.getState().activeSubChatId
- const currentSelectedChatId = appStore.get(selectedAgentChatIdAtom)
+ useAgentSubChatStore.getState().activeSubChatId;
+ const currentSelectedChatId = appStore.get(selectedAgentChatIdAtom);
- const isViewingThisSubChat = currentActiveSubChatId === subChatId
- const isViewingThisChat = currentSelectedChatId === chatId
+ const isViewingThisSubChat = currentActiveSubChatId === subChatId;
+ const isViewingThisChat = currentSelectedChatId === chatId;
if (!isViewingThisSubChat) {
setSubChatUnseenChanges((prev: Set) => {
- const next = new Set(prev)
- next.add(subChatId)
- return next
- })
+ const next = new Set(prev);
+ next.add(subChatId);
+ return next;
+ });
}
// Also mark parent chat as unseen if user is not viewing it
if (!isViewingThisChat) {
setUnseenChanges((prev: Set) => {
- const next = new Set(prev)
- next.add(chatId)
- return next
- })
+ const next = new Set(prev);
+ next.add(chatId);
+ return next;
+ });
// Play completion sound only if NOT manually aborted and sound is enabled
if (!wasManuallyAborted) {
- const isSoundEnabled = appStore.get(soundNotificationsEnabledAtom)
+ const isSoundEnabled = appStore.get(
+ soundNotificationsEnabledAtom,
+ );
if (isSoundEnabled) {
try {
- const audio = new Audio("./sound.mp3")
- audio.volume = 1.0
- audio.play().catch(() => {})
+ const audio = new Audio("./sound.mp3");
+ audio.volume = 1.0;
+ audio.play().catch(() => {});
} catch {
// Ignore audio errors
}
}
// Show native notification (desktop app, when window not focused)
- notifyAgentComplete(agentChat?.name || "Agent")
+ notifyAgentComplete(agentChat?.name || "Agent");
}
}
// Refresh diff stats after agent finishes making changes
- fetchDiffStatsRef.current()
+ fetchDiffStatsRef.current();
// Note: sidebar timestamp update is handled via optimistic update in handleSend
// No need to refetch here as it would overwrite the optimistic update with stale data
},
- })
+ });
- agentChatStore.set(subChatId, newChat, chatId)
+ agentChatStore.set(subChatId, newChat, chatId);
// Store streamId at creation time to prevent resume during active streaming
// tRPC refetch would update stream_id in DB, but store stays stable
- agentChatStore.setStreamId(subChatId, subChat?.stream_id || null)
- forceUpdate({}) // Trigger re-render to use new chat
- return newChat
+ agentChatStore.setStreamId(subChatId, subChat?.stream_id || null);
+ forceUpdate({}); // Trigger re-render to use new chat
+ return newChat;
},
[
agentChat,
@@ -5827,38 +6295,38 @@ Make sure to preserve all functionality from both branches when resolving confli
setUnseenChanges,
notifyAgentComplete,
],
- )
+ );
// Handle creating a new sub-chat
const handleCreateNewSubChat = useCallback(async () => {
- const store = useAgentSubChatStore.getState()
+ const store = useAgentSubChatStore.getState();
// New sub-chats use the user's default mode preference
- const newSubChatMode = defaultAgentMode
+ const newSubChatMode = defaultAgentMode;
// Check if this is a remote sandbox chat
- const isRemoteChat = !!(agentChat as any)?.isRemote
+ const isRemoteChat = !!(agentChat as any)?.isRemote;
- let newId: string
+ let newId: string;
if (isRemoteChat) {
// Sandbox mode: lazy creation (web app pattern)
// Sub-chat will be persisted on first message via RemoteChatTransport UPSERT
- newId = crypto.randomUUID()
+ newId = crypto.randomUUID();
} else {
// Local mode: create sub-chat in DB first to get the real ID
const newSubChat = await trpcClient.chats.createSubChat.mutate({
chatId,
name: "New Chat",
mode: newSubChatMode,
- })
- newId = newSubChat.id
- utils.agents.getAgentChat.invalidate({ chatId })
+ });
+ newId = newSubChat.id;
+ utils.agents.getAgentChat.invalidate({ chatId });
// Optimistic update: add new sub-chat to React Query cache immediately
// This is CRITICAL for workspace isolation - without this, the new sub-chat
// won't be in validSubChatIds and will be filtered out by tabsToRender
utils.agents.getAgentChat.setData({ chatId }, (old) => {
- if (!old) return old
+ if (!old) return old;
return {
...old,
subChats: [
@@ -5873,12 +6341,12 @@ Make sure to preserve all functionality from both branches when resolving confli
stream_id: null,
},
],
- }
- })
+ };
+ });
}
// Track this subchat as just created for typewriter effect
- setJustCreatedIds((prev) => new Set([...prev, newId]))
+ setJustCreatedIds((prev) => new Set([...prev, newId]));
// Add to allSubChats with placeholder name
store.addToAllSubChats({
@@ -5886,34 +6354,41 @@ Make sure to preserve all functionality from both branches when resolving confli
name: "New Chat",
created_at: new Date().toISOString(),
mode: newSubChatMode,
- })
+ });
// Set the mode atomFamily for the new sub-chat (so currentMode reads correct value)
- appStore.set(subChatModeAtomFamily(newId), newSubChatMode)
+ appStore.set(subChatModeAtomFamily(newId), newSubChatMode);
// Add to open tabs and set as active
- store.addToOpenSubChats(newId)
- store.setActiveSubChat(newId)
+ store.addToOpenSubChats(newId);
+ store.setActiveSubChat(newId);
// Create empty Chat instance for the new sub-chat
- const projectPath = (agentChat as any)?.project?.path as string | undefined
- const newSubChatSandboxId = (agentChat as any)?.sandboxId || (agentChat as any)?.sandbox_id
- const newSubChatSandboxUrl = newSubChatSandboxId ? `https://3003-${newSubChatSandboxId}.e2b.app` : null
- const isNewSubChatRemote = !!(agentChat as any)?.isRemote || !!newSubChatSandboxId
+ const projectPath = (agentChat as any)?.project?.path as string | undefined;
+ const newSubChatSandboxId =
+ (agentChat as any)?.sandboxId || (agentChat as any)?.sandbox_id;
+ const newSubChatSandboxUrl = newSubChatSandboxId
+ ? `https://3003-${newSubChatSandboxId}.e2b.app`
+ : null;
+ const isNewSubChatRemote =
+ !!(agentChat as any)?.isRemote || !!newSubChatSandboxId;
console.log("[createNewSubChat] Transport selection", {
newId: newId.slice(-8),
isNewSubChatRemote,
newSubChatSandboxId,
newSubChatSandboxUrl,
- })
+ });
- let newSubChatTransport: IPCChatTransport | RemoteChatTransport | null = null
+ let newSubChatTransport: IPCChatTransport | RemoteChatTransport | null =
+ null;
if (isNewSubChatRemote && newSubChatSandboxUrl) {
// Remote sandbox chat: use HTTP SSE transport
- const modelString = MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"]
- console.log("[createNewSubChat] Using RemoteChatTransport", { model: modelString })
+ const modelString = MODEL_ID_MAP[selectedModelId] || MODEL_ID_MAP["opus"];
+ console.log("[createNewSubChat] Using RemoteChatTransport", {
+ model: modelString,
+ });
newSubChatTransport = new RemoteChatTransport({
chatId,
subChatId: newId,
@@ -5921,7 +6396,7 @@ Make sure to preserve all functionality from both branches when resolving confli
sandboxUrl: newSubChatSandboxUrl,
mode: subChatMode,
model: modelString,
- })
+ });
} else if (worktreePath) {
// Local worktree chat: use IPC transport
newSubChatTransport = new IPCChatTransport({
@@ -5930,11 +6405,11 @@ Make sure to preserve all functionality from both branches when resolving confli
cwd: worktreePath,
projectPath,
mode: newSubChatMode,
- })
+ });
}
if (newSubChatTransport) {
- const transport = newSubChatTransport
+ const transport = newSubChatTransport;
const newChat = new Chat({
id: newId,
@@ -5942,71 +6417,73 @@ Make sure to preserve all functionality from both branches when resolving confli
transport,
onError: () => {
// Sync status to global store on error (allows queue to continue)
- useStreamingStatusStore.getState().setStatus(newId, "ready")
+ useStreamingStatusStore.getState().setStatus(newId, "ready");
},
// Clear loading when streaming completes
onFinish: () => {
- clearLoading(setLoadingSubChats, newId)
+ clearLoading(setLoadingSubChats, newId);
// Sync status to global store for queue processing (even when component unmounted)
- useStreamingStatusStore.getState().setStatus(newId, "ready")
+ useStreamingStatusStore.getState().setStatus(newId, "ready");
// Check if this was a manual abort (ESC/Ctrl+C) - skip sound if so
- const wasManuallyAborted = agentChatStore.wasManuallyAborted(newId)
- agentChatStore.clearManuallyAborted(newId)
+ const wasManuallyAborted = agentChatStore.wasManuallyAborted(newId);
+ agentChatStore.clearManuallyAborted(newId);
// Get CURRENT values at runtime (not stale closure values)
const currentActiveSubChatId =
- useAgentSubChatStore.getState().activeSubChatId
- const currentSelectedChatId = appStore.get(selectedAgentChatIdAtom)
+ useAgentSubChatStore.getState().activeSubChatId;
+ const currentSelectedChatId = appStore.get(selectedAgentChatIdAtom);
- const isViewingThisSubChat = currentActiveSubChatId === newId
- const isViewingThisChat = currentSelectedChatId === chatId
+ const isViewingThisSubChat = currentActiveSubChatId === newId;
+ const isViewingThisChat = currentSelectedChatId === chatId;
if (!isViewingThisSubChat) {
setSubChatUnseenChanges((prev: Set) => {
- const next = new Set(prev)
- next.add(newId)
- return next
- })
+ const next = new Set(prev);
+ next.add(newId);
+ return next;
+ });
}
// Also mark parent chat as unseen if user is not viewing it
if (!isViewingThisChat) {
setUnseenChanges((prev: Set) => {
- const next = new Set(prev)
- next.add(chatId)
- return next
- })
+ const next = new Set(prev);
+ next.add(chatId);
+ return next;
+ });
// Play completion sound only if NOT manually aborted and sound is enabled
if (!wasManuallyAborted) {
- const isSoundEnabled = appStore.get(soundNotificationsEnabledAtom)
+ const isSoundEnabled = appStore.get(
+ soundNotificationsEnabledAtom,
+ );
if (isSoundEnabled) {
try {
- const audio = new Audio("./sound.mp3")
- audio.volume = 1.0
- audio.play().catch(() => {})
+ const audio = new Audio("./sound.mp3");
+ audio.volume = 1.0;
+ audio.play().catch(() => {});
} catch {
// Ignore audio errors
}
}
// Show native notification (desktop app, when window not focused)
- notifyAgentComplete(agentChat?.name || "Agent")
+ notifyAgentComplete(agentChat?.name || "Agent");
}
}
// Refresh diff stats after agent finishes making changes
- fetchDiffStatsRef.current()
+ fetchDiffStatsRef.current();
// Note: sidebar timestamp update is handled via optimistic update in handleSend
// No need to refetch here as it would overwrite the optimistic update with stale data
},
- })
- agentChatStore.set(newId, newChat, chatId)
- agentChatStore.setStreamId(newId, null) // New chat has no active stream
- forceUpdate({}) // Trigger re-render
+ });
+ agentChatStore.set(newId, newChat, chatId);
+ agentChatStore.setStreamId(newId, null); // New chat has no active stream
+ forceUpdate({}); // Trigger re-render
}
}, [
worktreePath,
@@ -6019,64 +6496,73 @@ Make sure to preserve all functionality from both branches when resolving confli
notifyAgentComplete,
agentChat?.isRemote,
agentChat?.name,
- ])
+ ]);
// Keyboard shortcut: New sub-chat
// Web: Opt+Cmd+T (browser uses Cmd+T for new tab)
// Desktop: Cmd+T
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
- const isDesktop = isDesktopApp()
+ const isDesktop = isDesktopApp();
// Desktop: Cmd+T (without Alt)
if (isDesktop && e.metaKey && e.code === "KeyT" && !e.altKey) {
- e.preventDefault()
- handleCreateNewSubChat()
- return
+ e.preventDefault();
+ handleCreateNewSubChat();
+ return;
}
// Web: Opt+Cmd+T (with Alt)
if (e.altKey && e.metaKey && e.code === "KeyT") {
- e.preventDefault()
- handleCreateNewSubChat()
+ e.preventDefault();
+ handleCreateNewSubChat();
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [handleCreateNewSubChat])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [handleCreateNewSubChat]);
// NOTE: Desktop notifications for pending questions are now triggered directly
// in ipc-chat-transport.ts when the ask-user-question chunk arrives.
// This prevents duplicate notifications from multiple ChatView instances.
// Multi-select state for sub-chats (for Cmd+W bulk close)
- const selectedSubChatIds = useAtomValue(selectedSubChatIdsAtom)
- const isSubChatMultiSelectMode = useAtomValue(isSubChatMultiSelectModeAtom)
- const clearSubChatSelection = useSetAtom(clearSubChatSelectionAtom)
+ const selectedSubChatIds = useAtomValue(selectedSubChatIdsAtom);
+ const isSubChatMultiSelectMode = useAtomValue(isSubChatMultiSelectModeAtom);
+ const clearSubChatSelection = useSetAtom(clearSubChatSelectionAtom);
// Helper to add sub-chat to undo stack
- const addSubChatToUndoStack = useCallback((subChatId: string) => {
- const timeoutId = setTimeout(() => {
- setUndoStack((prev) => prev.filter(
- (item) => !(item.type === "subchat" && item.subChatId === subChatId)
- ))
- }, 10000)
-
- setUndoStack((prev) => [...prev, {
- type: "subchat",
- subChatId,
- chatId,
- timeoutId,
- }])
- }, [chatId, setUndoStack])
+ const addSubChatToUndoStack = useCallback(
+ (subChatId: string) => {
+ const timeoutId = setTimeout(() => {
+ setUndoStack((prev) =>
+ prev.filter(
+ (item) =>
+ !(item.type === "subchat" && item.subChatId === subChatId),
+ ),
+ );
+ }, 10000);
+
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ type: "subchat",
+ subChatId,
+ chatId,
+ timeoutId,
+ },
+ ]);
+ },
+ [chatId, setUndoStack],
+ );
// Keyboard shortcut: Close active sub-chat (or bulk close if multi-select mode)
// Web: Opt+Cmd+W (browser uses Cmd+W to close tab)
// Desktop: Cmd+W
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
- const isDesktop = isDesktopApp()
+ const isDesktop = isDesktopApp();
// Desktop: Cmd+W (without Alt)
const isDesktopShortcut =
@@ -6085,56 +6571,61 @@ Make sure to preserve all functionality from both branches when resolving confli
e.code === "KeyW" &&
!e.altKey &&
!e.shiftKey &&
- !e.ctrlKey
+ !e.ctrlKey;
// Web: Opt+Cmd+W (with Alt)
- const isWebShortcut = e.altKey && e.metaKey && e.code === "KeyW"
+ const isWebShortcut = e.altKey && e.metaKey && e.code === "KeyW";
if (isDesktopShortcut || isWebShortcut) {
- e.preventDefault()
+ e.preventDefault();
- const store = useAgentSubChatStore.getState()
+ const store = useAgentSubChatStore.getState();
// If multi-select mode, bulk close selected sub-chats
if (isSubChatMultiSelectMode && selectedSubChatIds.size > 0) {
- const idsToClose = Array.from(selectedSubChatIds)
+ const idsToClose = Array.from(selectedSubChatIds);
const remainingOpenIds = store.openSubChatIds.filter(
(id) => !idsToClose.includes(id),
- )
+ );
// Don't close all tabs via hotkey - user should use sidebar dialog for last tab
if (remainingOpenIds.length > 0) {
idsToClose.forEach((id) => {
- store.removeFromOpenSubChats(id)
- addSubChatToUndoStack(id)
- })
+ store.removeFromOpenSubChats(id);
+ addSubChatToUndoStack(id);
+ });
}
- clearSubChatSelection()
- return
+ clearSubChatSelection();
+ return;
}
// Otherwise close active sub-chat
- const activeId = store.activeSubChatId
- const openIds = store.openSubChatIds
+ const activeId = store.activeSubChatId;
+ const openIds = store.openSubChatIds;
// Only close if we have more than one tab open and there's an active tab
// removeFromOpenSubChats automatically switches to the last remaining tab
if (activeId && openIds.length > 1) {
- store.removeFromOpenSubChats(activeId)
- addSubChatToUndoStack(activeId)
+ store.removeFromOpenSubChats(activeId);
+ addSubChatToUndoStack(activeId);
}
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [isSubChatMultiSelectMode, selectedSubChatIds, clearSubChatSelection, addSubChatToUndoStack])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [
+ isSubChatMultiSelectMode,
+ selectedSubChatIds,
+ clearSubChatSelection,
+ addSubChatToUndoStack,
+ ]);
// Keyboard shortcut: Navigate between sub-chats
// Web: Opt+Cmd+[ and Opt+Cmd+] (browser uses Cmd+[ for back)
// Desktop: Cmd+[ and Cmd+]
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
- const isDesktop = isDesktopApp()
+ const isDesktop = isDesktopApp();
// Check for previous sub-chat shortcut ([ key)
const isPrevDesktop =
@@ -6143,41 +6634,41 @@ Make sure to preserve all functionality from both branches when resolving confli
e.code === "BracketLeft" &&
!e.altKey &&
!e.shiftKey &&
- !e.ctrlKey
- const isPrevWeb = e.altKey && e.metaKey && e.code === "BracketLeft"
+ !e.ctrlKey;
+ const isPrevWeb = e.altKey && e.metaKey && e.code === "BracketLeft";
if (isPrevDesktop || isPrevWeb) {
- e.preventDefault()
+ e.preventDefault();
- const store = useAgentSubChatStore.getState()
- const activeId = store.activeSubChatId
- const openIds = store.openSubChatIds
+ const store = useAgentSubChatStore.getState();
+ const activeId = store.activeSubChatId;
+ const openIds = store.openSubChatIds;
// Only navigate if we have multiple tabs
- if (openIds.length <= 1) return
+ if (openIds.length <= 1) return;
// If no active tab, select first one
if (!activeId) {
- store.setActiveSubChat(openIds[0])
- return
+ store.setActiveSubChat(openIds[0]);
+ return;
}
// Find current index
- const currentIndex = openIds.indexOf(activeId)
+ const currentIndex = openIds.indexOf(activeId);
if (currentIndex === -1) {
// Current tab not found, select first
- store.setActiveSubChat(openIds[0])
- return
+ store.setActiveSubChat(openIds[0]);
+ return;
}
// Navigate to previous tab (cycle to end if at start)
const nextIndex =
- currentIndex - 1 < 0 ? openIds.length - 1 : currentIndex - 1
- const nextId = openIds[nextIndex]
+ currentIndex - 1 < 0 ? openIds.length - 1 : currentIndex - 1;
+ const nextId = openIds[nextIndex];
if (nextId) {
- store.setActiveSubChat(nextId)
+ store.setActiveSubChat(nextId);
}
}
@@ -6188,47 +6679,47 @@ Make sure to preserve all functionality from both branches when resolving confli
e.code === "BracketRight" &&
!e.altKey &&
!e.shiftKey &&
- !e.ctrlKey
- const isNextWeb = e.altKey && e.metaKey && e.code === "BracketRight"
+ !e.ctrlKey;
+ const isNextWeb = e.altKey && e.metaKey && e.code === "BracketRight";
if (isNextDesktop || isNextWeb) {
- e.preventDefault()
+ e.preventDefault();
- const store = useAgentSubChatStore.getState()
- const activeId = store.activeSubChatId
- const openIds = store.openSubChatIds
+ const store = useAgentSubChatStore.getState();
+ const activeId = store.activeSubChatId;
+ const openIds = store.openSubChatIds;
// Only navigate if we have multiple tabs
- if (openIds.length <= 1) return
+ if (openIds.length <= 1) return;
// If no active tab, select first one
if (!activeId) {
- store.setActiveSubChat(openIds[0])
- return
+ store.setActiveSubChat(openIds[0]);
+ return;
}
// Find current index
- const currentIndex = openIds.indexOf(activeId)
+ const currentIndex = openIds.indexOf(activeId);
if (currentIndex === -1) {
// Current tab not found, select first
- store.setActiveSubChat(openIds[0])
- return
+ store.setActiveSubChat(openIds[0]);
+ return;
}
// Navigate to next tab (cycle to start if at end)
- const nextIndex = (currentIndex + 1) % openIds.length
- const nextId = openIds[nextIndex]
+ const nextIndex = (currentIndex + 1) % openIds.length;
+ const nextId = openIds[nextIndex];
if (nextId) {
- store.setActiveSubChat(nextId)
+ store.setActiveSubChat(nextId);
}
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown)
- return () => window.removeEventListener("keydown", handleKeyDown)
- }, [])
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, []);
// Keyboard shortcut: Cmd + D to toggle diff sidebar
useEffect(() => {
@@ -6241,18 +6732,17 @@ Make sure to preserve all functionality from both branches when resolving confli
!e.ctrlKey &&
e.code === "KeyD"
) {
- e.preventDefault()
- e.stopPropagation()
+ e.preventDefault();
+ e.stopPropagation();
// Toggle diff sidebar
- setIsDiffSidebarOpen(!isDiffSidebarOpen)
+ setIsDiffSidebarOpen(!isDiffSidebarOpen);
}
- }
-
- window.addEventListener("keydown", handleKeyDown, true)
- return () => window.removeEventListener("keydown", handleKeyDown, true)
- }, [isDiffSidebarOpen])
+ };
+ window.addEventListener("keydown", handleKeyDown, true);
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
+ }, [isDiffSidebarOpen]);
// Keyboard shortcut: Cmd + Shift + E to restore archived workspace
useEffect(() => {
@@ -6265,16 +6755,16 @@ Make sure to preserve all functionality from both branches when resolving confli
e.code === "KeyE"
) {
if (isArchived && !restoreWorkspaceMutation.isPending) {
- e.preventDefault()
- e.stopPropagation()
- handleRestoreWorkspace()
+ e.preventDefault();
+ e.stopPropagation();
+ handleRestoreWorkspace();
}
}
- }
+ };
- window.addEventListener("keydown", handleKeyDown, true)
- return () => window.removeEventListener("keydown", handleKeyDown, true)
- }, [isArchived, restoreWorkspaceMutation.isPending, handleRestoreWorkspace])
+ window.addEventListener("keydown", handleKeyDown, true);
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
+ }, [isArchived, restoreWorkspaceMutation.isPending, handleRestoreWorkspace]);
// Handle auto-rename for sub-chat and parent chat
// Receives subChatId as param to avoid stale closure issues
@@ -6282,8 +6772,8 @@ Make sure to preserve all functionality from both branches when resolving confli
(userMessage: string, subChatId: string) => {
// Check if this is the first sub-chat using agentSubChats directly
// to avoid race condition with store initialization
- const firstSubChatId = getFirstSubChatId(agentSubChats)
- const isFirst = firstSubChatId === subChatId
+ const firstSubChatId = getFirstSubChatId(agentSubChats);
+ const isFirst = firstSubChatId === subChatId;
autoRenameAgentChat({
subChatId,
@@ -6291,25 +6781,28 @@ Make sure to preserve all functionality from both branches when resolving confli
userMessage,
isFirstSubChat: isFirst,
generateName: async (msg) => {
- return generateSubChatNameMutation.mutateAsync({ userMessage: msg, ollamaModel: selectedOllamaModel })
+ return generateSubChatNameMutation.mutateAsync({
+ userMessage: msg,
+ ollamaModel: selectedOllamaModel,
+ });
},
renameSubChat: async (input) => {
- await renameSubChatMutation.mutateAsync(input)
+ await renameSubChatMutation.mutateAsync(input);
},
renameChat: async (input) => {
- await renameChatMutation.mutateAsync(input)
+ await renameChatMutation.mutateAsync(input);
},
updateSubChatName: (subChatIdToUpdate, name) => {
// Update local store
useAgentSubChatStore
.getState()
- .updateSubChatName(subChatIdToUpdate, name)
+ .updateSubChatName(subChatIdToUpdate, name);
// Also update query cache so init effect doesn't overwrite
utils.agents.getAgentChat.setData({ chatId }, (old) => {
- if (!old) return old
+ if (!old) return old;
const existsInCache = old.subChats.some(
(sc) => sc.id === subChatIdToUpdate,
- )
+ );
if (!existsInCache) {
// Sub-chat not in cache yet (DB save still in flight) - add it
return {
@@ -6327,15 +6820,15 @@ Make sure to preserve all functionality from both branches when resolving confli
chat_id: chatId,
},
],
- }
+ };
}
return {
...old,
subChats: old.subChats.map((sc) =>
sc.id === subChatIdToUpdate ? { ...sc, name } : sc,
),
- }
- })
+ };
+ });
},
updateChatName: (chatIdToUpdate, name) => {
// Optimistic update for sidebar (list query)
@@ -6343,22 +6836,22 @@ Make sure to preserve all functionality from both branches when resolving confli
utils.agents.getAgentChats.setData(
{ teamId: selectedTeamId },
(old) => {
- if (!old) return old
+ if (!old) return old;
return old.map((c) =>
c.id === chatIdToUpdate ? { ...c, name } : c,
- )
+ );
},
- )
+ );
// Optimistic update for header (single chat query)
utils.agents.getAgentChat.setData(
{ chatId: chatIdToUpdate },
(old) => {
- if (!old) return old
- return { ...old, name }
+ if (!old) return old;
+ return { ...old, name };
},
- )
+ );
},
- })
+ });
},
[
chatId,
@@ -6371,675 +6864,708 @@ Make sure to preserve all functionality from both branches when resolving confli
utils.agents.getAgentChats,
utils.agents.getAgentChat,
],
- )
+ );
// Get or create Chat instance for active sub-chat
const activeChat = useMemo(() => {
if (!activeSubChatId || !agentChat) {
- return null
+ return null;
}
- return getOrCreateChat(activeSubChatId)
- }, [activeSubChatId, agentChat, getOrCreateChat, chatId, chatWorkingDir])
+ return getOrCreateChat(activeSubChatId);
+ }, [activeSubChatId, agentChat, getOrCreateChat, chatId, chatWorkingDir]);
// Check if active sub-chat is the first one (for renaming parent chat)
// Use agentSubChats directly to avoid race condition with store initialization
const isFirstSubChatActive = useMemo(() => {
- if (!activeSubChatId) return false
- return getFirstSubChatId(agentSubChats) === activeSubChatId
- }, [activeSubChatId, agentSubChats])
+ if (!activeSubChatId) return false;
+ return getFirstSubChatId(agentSubChats) === activeSubChatId;
+ }, [activeSubChatId, agentSubChats]);
// Determine if chat header should be hidden
const shouldHideChatHeader =
subChatsSidebarMode === "sidebar" &&
isPreviewSidebarOpen &&
isDiffSidebarOpen &&
- !isMobileFullscreen
+ !isMobileFullscreen;
// No early return - let the UI render with loading state handled by activeChat check below
return (
-
- {/* File Search Dialog (Cmd+P) */}
- {worktreePath && (
-
- )}
-
- {/* Main content */}
-
- {/* Chat Panel */}
-
- {/* SubChatSelector header - absolute when sidebar open (desktop only), regular div otherwise */}
- {!shouldHideChatHeader && (
+
+ {/* File Search Dialog (Cmd+P) */}
+ {worktreePath && (
+
+ )}
+
+ {/* Main content */}
+
+ {/* Chat Panel */}
- {/* Gradient background - only when not absolute */}
- {(isMobileFullscreen || subChatsSidebarMode !== "sidebar") && (
-
- )}
-
-
- {/* Mobile header - simplified with chat name as trigger */}
- {isMobileFullscreen ? (
-
- ) : (
- <>
- {/* Header controls - desktop only */}
-
-
setIsDiffSidebarOpen(true) : undefined}
- canOpenDiff={canShowDiffButton}
- isDiffSidebarOpen={isDiffSidebarOpen}
- diffStats={diffStats}
- onOpenTerminal={() => setIsTerminalSidebarOpen(true)}
- canOpenTerminal={!!worktreePath}
- isTerminalOpen={isTerminalSidebarOpen}
- chatId={chatId}
- />
- {/* Open Locally button - desktop only, sandbox mode */}
- {showOpenLocally && (
-
-
-
- {isImporting ? (
-
- ) : (
-
- )}
- Fork Locally
-
-
-
- Continue this session on your local machine
-
-
- )}
- >
+ {/* SubChatSelector header - absolute when sidebar open (desktop only), regular div otherwise */}
+ {!shouldHideChatHeader && (
+
- {/* Open Preview Button - shows when preview is closed (desktop only, local mode only) */}
- {!isMobileFullscreen &&
- !isPreviewSidebarOpen &&
- sandboxId &&
- chatSourceMode === "local" &&
- (canOpenPreview ? (
-
-
- setIsPreviewSidebarOpen(true)}
- className="h-6 w-6 p-0 hover:bg-foreground/10 transition-colors text-foreground flex-shrink-0 rounded-md ml-2"
- aria-label="Open preview"
- >
-
-
-
- Open preview
-
- ) : (
-
-
-
-
-
-
-
- ))}
- {/* Overview/Terminal Button - shows when sidebar is closed and worktree/sandbox exists (desktop only) */}
- {!isMobileFullscreen &&
- (worktreePath || sandboxId) && (
- isUnifiedSidebarEnabled ? (
- // Details button for unified sidebar
- !isDetailsSidebarOpen && (
+ >
+ {/* Gradient background - only when not absolute */}
+ {(isMobileFullscreen ||
+ subChatsSidebarMode !== "sidebar") && (
+
+ )}
+
+
+ {/* Mobile header - simplified with chat name as trigger */}
+ {isMobileFullscreen ? (
+
+ ) : (
+ <>
+ {/* Header controls - desktop only */}
+
+
setIsDiffSidebarOpen(true)
+ : undefined
+ }
+ canOpenDiff={canShowDiffButton}
+ isDiffSidebarOpen={isDiffSidebarOpen}
+ diffStats={diffStats}
+ onOpenTerminal={() =>
+ setIsTerminalSidebarOpen(true)
+ }
+ canOpenTerminal={!!worktreePath}
+ isTerminalOpen={isTerminalSidebarOpen}
+ chatId={chatId}
+ />
+ {/* */}
+ {/* Open Locally button - desktop only, sandbox mode */}
+ {showOpenLocally && (
+
+
+
+ {isImporting ? (
+
+ ) : (
+
+ )}
+ Fork Locally
+
+
+
+ Continue this session on your local machine
+
+
+ )}
+ >
+ )}
+
+ {/* Open Preview Button - shows when preview is closed (desktop only, local mode only) */}
+ {!isMobileFullscreen &&
+ !isPreviewSidebarOpen &&
+ sandboxId &&
+ chatSourceMode === "local" &&
+ (canOpenPreview ? (
setIsDetailsSidebarOpen(true)}
+ onClick={() => setIsPreviewSidebarOpen(true)}
className="h-6 w-6 p-0 hover:bg-foreground/10 transition-colors text-foreground flex-shrink-0 rounded-md ml-2"
- aria-label="View details"
+ aria-label="Open preview"
>
-
- View details
- {toggleDetailsHotkey && {toggleDetailsHotkey} }
-
+ Open preview
- )
- ) : (
- // Terminal button for legacy sidebars
- !isTerminalSidebarOpen && (
-
-
+ ) : (
+
+
setIsTerminalSidebarOpen(true)}
- className="h-6 w-6 p-0 hover:bg-foreground/10 transition-colors text-foreground flex-shrink-0 rounded-md ml-2"
- aria-label="Open terminal"
+ disabled
+ className="h-6 w-6 p-0 text-muted-foreground flex-shrink-0 rounded-md cursor-not-allowed pointer-events-none"
+ aria-label="Preview not available"
>
-
+
-
-
- Open terminal
- {toggleTerminalHotkey && {toggleTerminalHotkey} }
-
-
- )
- )
- )}
- {/* Restore Button - shows when viewing archived workspace (desktop only) */}
- {!isMobileFullscreen && isArchived && (
-
-
-
-
- Restore
-
-
-
- Restore workspace
- ⇧⌘E
-
-
- )}
-
-
- )}
-
- {/* Chat Content - Keep-alive: render all open tabs, hide inactive with CSS */}
- {tabsToRender.length > 0 && agentChat ? (
-
- {/* Loading gate: prevent getOrCreateChat() from caching empty messages before data is ready */}
- {isLocalChatLoading ? (
-
-
-
- ) : (
- tabsToRender.map(subChatId => {
- const chat = getOrCreateChat(subChatId)
- const isActive = subChatId === activeSubChatId
- const isFirstSubChat = getFirstSubChatId(agentSubChats) === subChatId
-
- // Defense in depth: double-check workspace ownership
- // Use agentSubChats (server data) as primary source, fall back to allSubChats for optimistic updates
- // This fixes the race condition where allSubChats is empty after setChatId but before setAllSubChats
- const belongsToWorkspace = agentSubChats.some(sc => sc.id === subChatId) ||
- allSubChats.some(sc => sc.id === subChatId)
-
- if (!chat || !belongsToWorkspace) return null
-
- return (
-
-
-
- )
- })
- )}
-
- ) : (
- <>
- {/* Empty chat area - no loading indicator */}
-
-
- {/* Disabled input while loading */}
-
-
-
-
-
- Plan, @ for context, / for commands
-
-
-
- {/* Mode selector placeholder */}
-
-
- Agent
-
-
-
- {/* Model selector placeholder */}
-
-
-
- {hasCustomClaudeConfig ? (
- "Custom Model"
- ) : (
- <>
- Sonnet{" "}
-
- 4.5
-
- >
- )}
-
-
-
-
-
- {/* Attach button placeholder */}
+
+
+ ))}
+ {/* Overview/Terminal Button - shows when sidebar is closed and worktree/sandbox exists (desktop only) */}
+ {!isMobileFullscreen &&
+ (worktreePath || sandboxId) &&
+ (isUnifiedSidebarEnabled
+ ? // Details button for unified sidebar
+ !isDetailsSidebarOpen && (
+
+
+ setIsDetailsSidebarOpen(true)}
+ className="h-6 w-6 p-0 hover:bg-foreground/10 transition-colors text-foreground flex-shrink-0 rounded-md ml-2"
+ aria-label="View details"
+ >
+
+
+
+
+ View details
+ {toggleDetailsHotkey && (
+ {toggleDetailsHotkey}
+ )}
+
+
+ )
+ : // Terminal button for legacy sidebars
+ !isTerminalSidebarOpen && (
+
+
+ setIsTerminalSidebarOpen(true)}
+ className="h-6 w-6 p-0 hover:bg-foreground/10 transition-colors text-foreground flex-shrink-0 rounded-md ml-2"
+ aria-label="Open terminal"
+ >
+
+
+
+
+ Open terminal
+ {toggleTerminalHotkey && (
+ {toggleTerminalHotkey}
+ )}
+
+
+ ))}
+ {/* Restore Button - shows when viewing archived workspace (desktop only) */}
+ {!isMobileFullscreen && isArchived && (
+
+
-
+
+ Restore
+
+
+ Restore workspace
+ ⇧⌘E
+
+
+ )}
+
+
+ )}
- {/* Send button */}
-
+ {/* Chat Content - Keep-alive: render all open tabs, hide inactive with CSS */}
+ {tabsToRender.length > 0 && agentChat ? (
+
+ {/* Loading gate: prevent getOrCreateChat() from caching empty messages before data is ready */}
+ {isLocalChatLoading ? (
+
+
+
+ ) : (
+ tabsToRender.map((subChatId) => {
+ const chat = getOrCreateChat(subChatId);
+ const isActive = subChatId === activeSubChatId;
+ const isFirstSubChat =
+ getFirstSubChatId(agentSubChats) === subChatId;
+
+ // Defense in depth: double-check workspace ownership
+ // Use agentSubChats (server data) as primary source, fall back to allSubChats for optimistic updates
+ // This fixes the race condition where allSubChats is empty after setChatId but before setAllSubChats
+ const belongsToWorkspace =
+ agentSubChats.some((sc) => sc.id === subChatId) ||
+ allSubChats.some((sc) => sc.id === subChatId);
+
+ if (!chat || !belongsToWorkspace) return null;
+
+ return (
+
+
-
-
-
+ );
+ })
+ )}
-
- >
- )}
-
+ ) : (
+ <>
+ {/* Empty chat area - no loading indicator */}
+
+
+ {/* Disabled input while loading */}
+
+
+
+
+
+ Plan, @ for context, / for commands
+
+
+
+ {/* Mode selector placeholder */}
+
+
+ Agent
+
+
+
+ {/* Model selector placeholder */}
+
+
+
+ {hasCustomClaudeConfig ? (
+ "Custom Model"
+ ) : (
+ <>
+ Sonnet{" "}
+
+ 4.5
+
+ >
+ )}
+
+
+
+
+
+ {/* Attach button placeholder */}
+
+
+
+
+ {/* Send button */}
+
+
+
+
+
+
+
+ >
+ )}
+
- {/* Plan Sidebar - shows plan files on the right (leftmost right sidebar) */}
- {/* Only show when we have an active sub-chat with a plan */}
- {!isMobileFullscreen && activeSubChatIdForPlan && (
-
setIsPlanSidebarOpen(false)}
- widthAtom={agentsPlanSidebarWidthAtom}
- minWidth={400}
- maxWidth={800}
- side="right"
- animationDuration={0}
- initialWidth={0}
- exitWidth={0}
- showResizeTooltip={true}
- className="bg-tl-background border-l"
- style={{ borderLeftWidth: "0.5px" }}
- >
- setIsPlanSidebarOpen(false)}
- onBuildPlan={handleApprovePlanFromSidebar}
- refetchTrigger={planEditRefetchTrigger}
- mode={currentMode}
- />
-
- )}
+ {/* Plan Sidebar - shows plan files on the right (leftmost right sidebar) */}
+ {/* Only show when we have an active sub-chat with a plan */}
+ {!isMobileFullscreen && activeSubChatIdForPlan && (
+
setIsPlanSidebarOpen(false)}
+ widthAtom={agentsPlanSidebarWidthAtom}
+ minWidth={400}
+ maxWidth={800}
+ side="right"
+ animationDuration={0}
+ initialWidth={0}
+ exitWidth={0}
+ showResizeTooltip={true}
+ className="bg-tl-background border-l"
+ style={{ borderLeftWidth: "0.5px" }}
+ >
+ setIsPlanSidebarOpen(false)}
+ onBuildPlan={handleApprovePlanFromSidebar}
+ refetchTrigger={planEditRefetchTrigger}
+ mode={currentMode}
+ />
+
+ )}
- {/* Diff View - hidden on mobile fullscreen and when diff is not available */}
- {/* Supports three display modes: side-peek (sidebar), center-peek (dialog), full-page */}
- {/* Wrapped in DiffStateProvider to isolate diff state and prevent ChatView re-renders */}
- {canOpenDiff && !isMobileFullscreen && (
-
-
-
- )}
+ {/* Diff View - hidden on mobile fullscreen and when diff is not available */}
+ {/* Supports three display modes: side-peek (sidebar), center-peek (dialog), full-page */}
+ {/* Wrapped in DiffStateProvider to isolate diff state and prevent ChatView re-renders */}
+ {canOpenDiff && !isMobileFullscreen && (
+
+
+
+ )}
- {/* Preview Sidebar - hidden on mobile fullscreen and when preview is not available */}
- {canOpenPreview && !isMobileFullscreen && (
-
setIsPreviewSidebarOpen(false)}
- widthAtom={agentsPreviewSidebarWidthAtom}
- minWidth={350}
- side="right"
- animationDuration={0}
- initialWidth={0}
- exitWidth={0}
- showResizeTooltip={true}
- className="bg-tl-background border-l"
- style={{ borderLeftWidth: "0.5px" }}
- >
- {isQuickSetup ? (
-
- {/* Header with close button */}
-
- setIsPreviewSidebarOpen(false)}
- >
-
-
-
- {/* Content */}
-
-
-
-
-
-
-
+ {/* Preview Sidebar - hidden on mobile fullscreen and when preview is not available */}
+ {canOpenPreview && !isMobileFullscreen && (
+
setIsPreviewSidebarOpen(false)}
+ widthAtom={agentsPreviewSidebarWidthAtom}
+ minWidth={350}
+ side="right"
+ animationDuration={0}
+ initialWidth={0}
+ exitWidth={0}
+ showResizeTooltip={true}
+ className="bg-tl-background border-l"
+ style={{ borderLeftWidth: "0.5px" }}
+ >
+ {isQuickSetup ? (
+
+ {/* Header with close button */}
+
+ setIsPreviewSidebarOpen(false)}
+ >
+
+
+
+ {/* Content */}
+
+
+
+
+
+
+
+
+
+ Preview not available
+
+
+ Set up this repository to enable live preview
+
+
-
- Preview not available
-
-
- Set up this repository to enable live preview
-
-
-
- ) : (
-
setIsPreviewSidebarOpen(false)}
+ />
+ )}
+
+ )}
+
+ {/* File Viewer - opens when a file is clicked */}
+ {!isMobileFullscreen &&
+ fileViewerPath &&
+ worktreePath &&
+ fileViewerDisplayMode === "side-peek" && (
+ setFileViewerPath(null)}
+ widthAtom={fileViewerSidebarWidthAtom}
+ minWidth={350}
+ maxWidth={900}
+ side="right"
+ animationDuration={0}
+ initialWidth={0}
+ exitWidth={0}
+ showResizeTooltip={true}
+ className="bg-tl-background border-l"
+ style={{ borderLeftWidth: "0.5px" }}
+ >
+ setFileViewerPath(null)}
+ />
+
+ )}
+ {fileViewerPath &&
+ worktreePath &&
+ fileViewerDisplayMode === "center-peek" && (
+ setFileViewerPath(null)}
+ >
+ setFileViewerPath(null)}
+ />
+
+ )}
+ {fileViewerPath &&
+ worktreePath &&
+ fileViewerDisplayMode === "full-page" && (
+ setFileViewerPath(null)}
+ >
+ setFileViewerPath(null)}
+ />
+
+ )}
+
+ {/* Terminal Sidebar - shows when worktree exists (desktop only) */}
+ {worktreePath && (
+ setIsPreviewSidebarOpen(false)}
+ cwd={worktreePath}
+ workspaceId={chatId}
/>
)}
-
- )}
- {/* File Viewer - opens when a file is clicked */}
- {!isMobileFullscreen && fileViewerPath && worktreePath && fileViewerDisplayMode === "side-peek" && (
- setFileViewerPath(null)}
- widthAtom={fileViewerSidebarWidthAtom}
- minWidth={350}
- maxWidth={900}
- side="right"
- animationDuration={0}
- initialWidth={0}
- exitWidth={0}
- showResizeTooltip={true}
- className="bg-tl-background border-l"
- style={{ borderLeftWidth: "0.5px" }}
- >
- setFileViewerPath(null)}
- />
-
- )}
- {fileViewerPath && worktreePath && fileViewerDisplayMode === "center-peek" && (
- setFileViewerPath(null)}
- >
- setFileViewerPath(null)}
- />
-
- )}
- {fileViewerPath && worktreePath && fileViewerDisplayMode === "full-page" && (
- setFileViewerPath(null)}
- >
- setFileViewerPath(null)}
+ {/* Open Locally Dialog - for importing sandbox chats to local */}
+ setOpenLocallyDialogOpen(false)}
+ remoteChat={remoteAgentChat ?? null}
+ matchingProjects={openLocallyMatchingProjects}
+ allProjects={projects ?? []}
+ remoteSubChatId={activeSubChatId}
/>
-
- )}
- {/* Terminal Sidebar - shows when worktree exists (desktop only) */}
- {worktreePath && (
-
- )}
-
- {/* Open Locally Dialog - for importing sandbox chats to local */}
- setOpenLocallyDialogOpen(false)}
- remoteChat={remoteAgentChat ?? null}
- matchingProjects={openLocallyMatchingProjects}
- allProjects={projects ?? []}
- remoteSubChatId={activeSubChatId}
- />
-
- {/* Unified Details Sidebar - combines all right sidebars into one (rightmost) */}
- {/* Show for both local (worktreePath) and remote (sandboxId) chats */}
- {isUnifiedSidebarEnabled && !isMobileFullscreen && (worktreePath || sandboxId) && (
- setIsTerminalSidebarOpen(true)}
- onExpandPlan={() => setIsPlanSidebarOpen(true)}
- onExpandDiff={() => setIsDiffSidebarOpen(true)}
- onFileSelect={(filePath) => {
- // Set the selected file path
- setSelectedFilePath(filePath)
- // Set filtered files to just this file
- setFilteredDiffFiles([filePath])
- // Open the diff sidebar
- setIsDiffSidebarOpen(true)
- }}
- remoteInfo={remoteInfo}
- isRemoteChat={!!remoteInfo}
- />
- )}
-
+ {/* Unified Details Sidebar - combines all right sidebars into one (rightmost) */}
+ {/* Show for both local (worktreePath) and remote (sandboxId) chats */}
+ {isUnifiedSidebarEnabled &&
+ !isMobileFullscreen &&
+ (worktreePath || sandboxId) && (
+ setIsTerminalSidebarOpen(true)}
+ onExpandPlan={() => setIsPlanSidebarOpen(true)}
+ onExpandDiff={() => setIsDiffSidebarOpen(true)}
+ onFileSelect={(filePath) => {
+ // Set the selected file path
+ setSelectedFilePath(filePath);
+ // Set filtered files to just this file
+ setFilteredDiffFiles([filePath]);
+ // Open the diff sidebar
+ setIsDiffSidebarOpen(true);
+ }}
+ remoteInfo={remoteInfo}
+ isRemoteChat={!!remoteInfo}
+ />
+ )}
+
- {/* Terminal Bottom Panel — renders below the main row when displayMode is "bottom" */}
- {terminalDisplayMode === "bottom" && worktreePath && !isMobileFullscreen && (
-
setIsTerminalSidebarOpen(false)}
- heightAtom={terminalBottomHeightAtom}
- minHeight={150}
- maxHeight={500}
- showResizeTooltip={true}
- closeHotkey={toggleTerminalHotkey ?? undefined}
- className="bg-background border-t"
- style={{ borderTopWidth: "0.5px" }}
- >
- setIsTerminalSidebarOpen(false)}
- />
-
- )}
-
-
+ {/* Terminal Bottom Panel — renders below the main row when displayMode is "bottom" */}
+ {terminalDisplayMode === "bottom" &&
+ worktreePath &&
+ !isMobileFullscreen && (
+
setIsTerminalSidebarOpen(false)}
+ heightAtom={terminalBottomHeightAtom}
+ minHeight={150}
+ maxHeight={500}
+ showResizeTooltip={true}
+ closeHotkey={toggleTerminalHotkey ?? undefined}
+ className="bg-background border-t"
+ style={{ borderTopWidth: "0.5px" }}
+ >
+ setIsTerminalSidebarOpen(false)}
+ />
+
+ )}
+
+
- )
+ );
}
diff --git a/src/renderer/features/agents/main/chat-input-area.tsx b/src/renderer/features/agents/main/chat-input-area.tsx
index 8e98ede18..2671865c4 100644
--- a/src/renderer/features/agents/main/chat-input-area.tsx
+++ b/src/renderer/features/agents/main/chat-input-area.tsx
@@ -1,9 +1,8 @@
"use client"
import { useAtom, useAtomValue, useSetAtom } from "jotai"
-import { ChevronDown, Loader2, RefreshCw, Zap } from "lucide-react"
+import { BrainIcon, ChevronDown, Loader2, NotebookIcon, RefreshCw, RouteIcon, Zap } from "lucide-react"
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
-import { createPortal } from "react-dom"
import { Button } from "../../../components/ui/button"
import {
@@ -86,6 +85,13 @@ import {
} from "../../../lib/hooks/use-voice-recording"
import { getResolvedHotkey } from "../../../lib/hotkeys"
import { customHotkeysAtom } from "../../../lib/atoms"
+import { ProfileSelector, useEffectiveProfile, useEffectiveModel } from "../ui/profile-selector"
+import {
+ modelProfilesAtom,
+ lastUsedProfileIdAtom,
+ selectedProfileModelIdAtom,
+ type ModelMapping,
+} from "../../../lib/atoms"
// Hook to get available models (including offline models if Ollama is available and debug enabled)
function useAvailableModels() {
@@ -178,6 +184,9 @@ export interface ChatInputAreaProps {
onInputContentChange?: (hasContent: boolean) => void
// Callback to send message with question answer (Enter sends immediately, not to queue)
onSubmitWithQuestionAnswer?: () => void
+ // Per-workspace model profile (from chat data)
+ chatModelProfileId?: string | null
+ chatSelectedModelId?: string | null
}
/**
@@ -373,12 +382,95 @@ export const ChatInputArea = memo(function ChatInputArea({
firstQueueItemId,
onInputContentChange,
onSubmitWithQuestionAnswer,
+ chatModelProfileId,
+ chatSelectedModelId,
}: ChatInputAreaProps) {
// Local state - changes here don't re-render parent
const [hasContent, setHasContent] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const [isDragOver, setIsDragOver] = useState(false)
+ // Get effective profile and model from per-workspace settings
+ const effectiveProfile = useEffectiveProfile(chatModelProfileId)
+ const effectiveModel = useEffectiveModel(effectiveProfile, chatSelectedModelId)
+
+ // Derive available models from profile config (like settings do)
+ const profileModels = useMemo((): ModelMapping[] => {
+ if (!effectiveProfile?.config) return []
+ const models: ModelMapping[] = []
+ const config = effectiveProfile.config
+
+ // Add main model
+ if (config.model) {
+ models.push({
+ id: "main",
+ displayName: "Main",
+ modelId: config.model,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Opus model
+ if (config.defaultOpusModel) {
+ models.push({
+ id: "opus",
+ displayName: "Opus",
+ modelId: config.defaultOpusModel,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Sonnet model
+ if (config.defaultSonnetModel) {
+ models.push({
+ id: "sonnet",
+ displayName: "Sonnet",
+ modelId: config.defaultSonnetModel,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Haiku model
+ if (config.defaultHaikuModel) {
+ models.push({
+ id: "haiku",
+ displayName: "Haiku",
+ modelId: config.defaultHaikuModel,
+ supportsThinking: false,
+ })
+ }
+
+ // Add Subagent model
+ if (config.subagentModel) {
+ models.push({
+ id: "subagent",
+ displayName: "Subagent",
+ modelId: config.subagentModel,
+ supportsThinking: false,
+ })
+ }
+
+ return models
+ }, [effectiveProfile?.config])
+
+ // Selected model within profile - use global atom so ipc-chat-transport can read it
+ const [selectedProfileModelId, setSelectedProfileModelId] = useAtom(selectedProfileModelIdAtom)
+
+ // Get selected model from profile models
+ const selectedProfileModel = useMemo((): ModelMapping | null => {
+ if (profileModels.length === 0) return null
+ if (selectedProfileModelId) {
+ const found = profileModels.find((m) => m.id === selectedProfileModelId)
+ if (found) return found
+ }
+ return profileModels[0] ?? null
+ }, [profileModels, selectedProfileModelId])
+
+ // Reset selected model when profile changes
+ useEffect(() => {
+ setSelectedProfileModelId(null)
+ }, [effectiveProfile?.id])
+
// Mention dropdown state
const [showMentionDropdown, setShowMentionDropdown] = useState(false)
const [mentionSearchText, setMentionSearchText] = useState("")
@@ -395,15 +487,7 @@ export const ChatInputArea = memo(function ChatInputArea({
const [slashSearchText, setSlashSearchText] = useState("")
const [slashPosition, setSlashPosition] = useState({ top: 0, left: 0 })
- // Mode dropdown state
- const [modeDropdownOpen, setModeDropdownOpen] = useState(false)
- const [modeTooltip, setModeTooltip] = useState<{
- visible: boolean
- position: { top: number; left: number }
- mode: "agent" | "plan"
- } | null>(null)
- const tooltipTimeoutRef = useRef
| null>(null)
- const hasShownTooltipRef = useRef(false)
+
// Model dropdown state
const [isModelDropdownOpen, setIsModelDropdownOpen] = useState(false)
@@ -442,6 +526,9 @@ export const ChatInputArea = memo(function ChatInputArea({
// Extended thinking (reasoning) toggle
const [thinkingEnabled, setThinkingEnabled] = useAtom(extendedThinkingEnabledAtom)
+ // Check if selected model supports thinking
+ const selectedModelSupportsThinking = selectedModel?.supportsThinking ?? true
+
// MCP status - from getAllMcpConfig query (provides global/local grouping)
const setSettingsOpen = useSetAtom(agentsSettingsDialogOpenAtom)
const setSettingsTab = useSetAtom(agentsSettingsDialogActiveTabAtom)
@@ -941,7 +1028,6 @@ export const ChatInputArea = memo(function ChatInputArea({
// Process other files - for text files, read content and add as file mention
for (const file of otherFiles) {
// Get file path using Electron's webUtils API (more reliable than file.path)
- // @ts-expect-error - Electron's webUtils API
const filePath: string | undefined = window.webUtils?.getPathForFile?.(file) || (file as File & { path?: string }).path
let mentionId: string
@@ -1141,175 +1227,15 @@ export const ChatInputArea = memo(function ChatInputArea({
- {/* Mode toggle (Agent/Plan) */}
-
{
- setModeDropdownOpen(open)
- if (!open) {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- hasShownTooltipRef.current = false
- }
- }}
- >
-
-
- {subChatMode === "plan" ? (
-
- ) : (
-
- )}
- {subChatMode === "plan" ? "Plan" : "Agent"}
-
-
-
- e.preventDefault()}
- >
- {
- // Clear tooltip before closing dropdown (onMouseLeave won't fire)
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- updateMode("agent")
- setModeDropdownOpen(false)
- }}
- className="justify-between gap-2"
- onMouseEnter={(e) => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- const rect = e.currentTarget.getBoundingClientRect()
- const showTooltip = () => {
- setModeTooltip({
- visible: true,
- position: {
- top: rect.top,
- left: rect.right + 8,
- },
- mode: "agent",
- })
- hasShownTooltipRef.current = true
- tooltipTimeoutRef.current = null
- }
- if (hasShownTooltipRef.current) {
- showTooltip()
- } else {
- tooltipTimeoutRef.current = setTimeout(
- showTooltip,
- 1000,
- )
- }
- }}
- onMouseLeave={() => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- }}
- >
-
- {subChatMode !== "plan" && (
-
- )}
-
- {
- // Clear tooltip before closing dropdown (onMouseLeave won't fire)
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- updateMode("plan")
- setModeDropdownOpen(false)
- }}
- className="justify-between gap-2"
- onMouseEnter={(e) => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- const rect = e.currentTarget.getBoundingClientRect()
- const showTooltip = () => {
- setModeTooltip({
- visible: true,
- position: {
- top: rect.top,
- left: rect.right + 8,
- },
- mode: "plan",
- })
- hasShownTooltipRef.current = true
- tooltipTimeoutRef.current = null
- }
- if (hasShownTooltipRef.current) {
- showTooltip()
- } else {
- tooltipTimeoutRef.current = setTimeout(
- showTooltip,
- 1000,
- )
- }
- }}
- onMouseLeave={() => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- }}
- >
-
- {subChatMode === "plan" && (
-
- )}
-
-
- {modeTooltip?.visible &&
- createPortal(
-
-
-
- {modeTooltip.mode === "agent"
- ? "Apply changes directly without a plan"
- : "Create a plan before making changes"}
-
-
-
,
- document.body,
- )}
-
- {/* Model selector - shows Ollama models when offline, Claude models when online */}
+ {/* Profile selector - per-workspace model profile */}
+
+
+ {/* Model selector - shows Ollama models when offline, profile models, or Claude models */}
{availableModels.isOffline && availableModels.hasOllama ? (
// Offline mode: show Ollama model selector
- ) : (
- // Online mode: show Claude model selector
+ ) : profileModels.length > 0 ? (
+ // Profile mode: show models from selected profile config
{
- if (!hasCustomClaudeConfig) {
- setIsModelDropdownOpen(open)
- }
- }}
+ open={isModelDropdownOpen}
+ onOpenChange={setIsModelDropdownOpen}
>
- {hasCustomClaudeConfig ? (
- "Custom Model"
- ) : (
- <>
- {selectedModel?.name}{" "}
- 4.5
- >
- )}
+ {selectedProfileModel?.displayName || "Select model"}{" "}
+
+ ({selectedProfileModel?.modelId || ""})
+
+
+ {profileModels.map((model) => {
+ const isSelected = model.id === selectedProfileModel?.id
+ return (
+ setSelectedProfileModelId(model.id)}
+ className="gap-2 justify-between"
+ >
+
+
+
+ {model.displayName}{" "}
+
+ ({model.modelId})
+
+
+
+ {isSelected && (
+
+ )}
+
+ )
+ })}
+
+
+ ) : (
+ // Fallback: show Claude model selector (no profile or empty profile)
+
+
+
+
+ {selectedModel?.name}
+
+
+
{availableModels.models.map((model) => {
const isSelected = selectedModel?.id === model.id
@@ -1403,10 +1356,7 @@ export const ChatInputArea = memo(function ChatInputArea({
>
-
- {model.name}{" "}
- 4.5
-
+ {model.name}
{isSelected && (
@@ -1414,25 +1364,49 @@ export const ChatInputArea = memo(function ChatInputArea({
)
})}
-
- e.stopPropagation()}
- >
-
-
- Thinking
-
-
-
)}
+ {/* Thinking toggle - simple toggle button */}
+
{
+ if (selectedModelSupportsThinking) {
+ setThinkingEnabled(!thinkingEnabled)
+ }
+ }}
+ disabled={!selectedModelSupportsThinking}
+ className={cn(
+ "flex items-center gap-1.5 px-2 py-1 text-sm transition-colors rounded-md outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70",
+ !selectedModelSupportsThinking
+ ? "opacity-40 cursor-not-allowed text-muted-foreground"
+ : thinkingEnabled
+ ? "text-[#D4846C] hover:text-[#E8A08C] hover:bg-[#D4846C]/10"
+ : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
+ )}
+ >
+
+ {thinkingEnabled && (
+ Thinking
+ )}
+
+
+ {/* Mode toggle (Agent/Plan) - simple toggle button */}
+
+
+ {subChatMode === "plan" && (
+ Plan
+ )}
+
+
diff --git a/src/renderer/features/agents/main/new-chat-form.tsx b/src/renderer/features/agents/main/new-chat-form.tsx
index 4d9dc25b1..21ef03396 100644
--- a/src/renderer/features/agents/main/new-chat-form.tsx
+++ b/src/renderer/features/agents/main/new-chat-form.tsx
@@ -2,25 +2,23 @@
import { useVirtualizer } from "@tanstack/react-virtual"
import { useAtom, useAtomValue, useSetAtom } from "jotai"
-import { AlignJustify, Plus, Zap } from "lucide-react"
+import { AlignJustify, BrainIcon, Plus, RouteIcon, Zap } from "lucide-react"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
-import { createPortal } from "react-dom"
import { Button } from "../../../components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
+ DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../../../components/ui/dropdown-menu"
import {
- AgentIcon,
AttachIcon,
BranchIcon,
CheckIcon,
ClaudeCodeIcon,
CursorIcon,
IconChevronDown,
- PlanIcon,
SearchIcon,
} from "../../../components/ui/icons"
import {
@@ -59,6 +57,12 @@ import {
selectedOllamaModelAtom,
customHotkeysAtom,
chatSourceModeAtom,
+ extendedThinkingEnabledAtom,
+ modelProfilesAtom,
+ lastUsedProfileIdAtom,
+ selectedProfileModelIdAtom,
+ type ModelProfile,
+ type ModelMapping,
} from "../../../lib/atoms"
// Desktop uses real tRPC
import { toast } from "sonner"
@@ -111,6 +115,7 @@ import {
type DraftProject,
} from "../lib/drafts"
import { CLAUDE_MODELS } from "../lib/models"
+import { Settings } from "lucide-react"
// import type { PlanType } from "@/lib/config/subscription-plans"
type PlanType = string
@@ -229,12 +234,119 @@ export function NewChatForm({
const toggleMode = useCallback(() => {
setAgentMode(getNextMode)
}, [])
+ // Extended thinking toggle
+ const [thinkingEnabled, setThinkingEnabled] = useAtom(extendedThinkingEnabledAtom)
const [workMode, setWorkMode] = useAtom(lastSelectedWorkModeAtom)
const debugMode = useAtomValue(agentsDebugModeAtom)
const customClaudeConfig = useAtomValue(customClaudeConfigAtom)
const normalizedCustomClaudeConfig =
normalizeCustomClaudeConfig(customClaudeConfig)
const hasCustomClaudeConfig = Boolean(normalizedCustomClaudeConfig)
+
+ // Model profile state for new chats
+ const profiles = useAtomValue(modelProfilesAtom)
+ const [lastUsedProfileId, setLastUsedProfileId] = useAtom(lastUsedProfileIdAtom)
+
+ // Filter out offline profiles from display
+ const displayProfiles = profiles.filter((p) => !p.isOffline)
+
+ // Get effective profile (lastUsed or first available)
+ const effectiveProfile = useMemo(() => {
+ if (lastUsedProfileId) {
+ const found = profiles.find((p) => p.id === lastUsedProfileId)
+ if (found) return found
+ }
+ return displayProfiles[0] ?? null
+ }, [profiles, displayProfiles, lastUsedProfileId])
+
+ // Selected model within profile - use global atom so ipc-chat-transport can read it
+ const [selectedProfileModelId, setSelectedProfileModelId] = useAtom(selectedProfileModelIdAtom)
+
+ // Get effective model from profile
+ const effectiveProfileModel = useMemo((): ModelMapping | null => {
+ if (!effectiveProfile?.models || effectiveProfile.models.length === 0) {
+ return null
+ }
+ if (selectedProfileModelId) {
+ const found = effectiveProfile.models.find((m) => m.id === selectedProfileModelId)
+ if (found) return found
+ }
+ return effectiveProfile.models[0] ?? null
+ }, [effectiveProfile, selectedProfileModelId])
+
+ // Derive available models from profile config (like settings do)
+ const profileModels = useMemo((): ModelMapping[] => {
+ if (!effectiveProfile?.config) return []
+ const models: ModelMapping[] = []
+ const config = effectiveProfile.config
+
+ // Add main model
+ if (config.model) {
+ models.push({
+ id: "main",
+ displayName: "Main",
+ modelId: config.model,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Opus model
+ if (config.defaultOpusModel) {
+ models.push({
+ id: "opus",
+ displayName: "Opus",
+ modelId: config.defaultOpusModel,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Sonnet model
+ if (config.defaultSonnetModel) {
+ models.push({
+ id: "sonnet",
+ displayName: "Sonnet",
+ modelId: config.defaultSonnetModel,
+ supportsThinking: true,
+ })
+ }
+
+ // Add Haiku model
+ if (config.defaultHaikuModel) {
+ models.push({
+ id: "haiku",
+ displayName: "Haiku",
+ modelId: config.defaultHaikuModel,
+ supportsThinking: false,
+ })
+ }
+
+ // Add Subagent model
+ if (config.subagentModel) {
+ models.push({
+ id: "subagent",
+ displayName: "Subagent",
+ modelId: config.subagentModel,
+ supportsThinking: false,
+ })
+ }
+
+ return models
+ }, [effectiveProfile?.config])
+
+ // Get selected model from profile models
+ const selectedProfileModel = useMemo((): ModelMapping | null => {
+ if (profileModels.length === 0) return null
+ if (selectedProfileModelId) {
+ const found = profileModels.find((m) => m.id === selectedProfileModelId)
+ if (found) return found
+ }
+ return profileModels[0] ?? null
+ }, [profileModels, selectedProfileModelId])
+
+ // Reset selected model when profile changes
+ useEffect(() => {
+ setSelectedProfileModelId(null)
+ }, [effectiveProfile?.id])
const setSettingsDialogOpen = useSetAtom(agentsSettingsDialogOpenAtom)
const setSettingsActiveTab = useSetAtom(agentsSettingsDialogActiveTabAtom)
const setJustCreatedIds = useSetAtom(justCreatedIdsAtom)
@@ -389,15 +501,6 @@ export function NewChatForm({
const [slashSearchText, setSlashSearchText] = useState("")
const [slashPosition, setSlashPosition] = useState({ top: 0, left: 0 })
- // Mode tooltip state (floating tooltip like canvas)
- const [modeTooltip, setModeTooltip] = useState<{
- visible: boolean
- position: { top: number; left: number }
- mode: "agent" | "plan"
- } | null>(null)
- const tooltipTimeoutRef = useRef | null>(null)
- const hasShownTooltipRef = useRef(false)
- const [modeDropdownOpen, setModeDropdownOpen] = useState(false)
const [isModelDropdownOpen, setIsModelDropdownOpen] = useState(false)
// Voice input state
@@ -1410,6 +1513,8 @@ export function NewChatForm({
[validatedProject?.path, handleAddAttachments, trpcUtils],
)
+ const selectedModelSupportsThinking = selectedModel?.supportsThinking ?? false
+
// Context items for images, files, and pasted text files
const contextItems =
images.length > 0 || files.length > 0 || pastedTexts.length > 0 ? (
@@ -1566,174 +1671,49 @@ export function NewChatForm({
- {/* Mode toggle (Agent/Plan) */}
-
{
- setModeDropdownOpen(open)
- if (!open) {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- hasShownTooltipRef.current = false
- }
- }}
- >
-
- {agentMode === "plan" ? (
-
- ) : (
-
- )}
- {agentMode === "plan" ? "Plan" : "Agent"}
-
-
- e.preventDefault()}
- >
- {
- // Clear tooltip before closing dropdown (onMouseLeave won't fire)
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- setAgentMode("agent")
- setModeDropdownOpen(false)
- }}
- className="justify-between gap-2"
- onMouseEnter={(e) => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- const rect =
- e.currentTarget.getBoundingClientRect()
- const showTooltip = () => {
- setModeTooltip({
- visible: true,
- position: {
- top: rect.top,
- left: rect.right + 8,
- },
- mode: "agent",
- })
- hasShownTooltipRef.current = true
- tooltipTimeoutRef.current = null
- }
- if (hasShownTooltipRef.current) {
- showTooltip()
- } else {
- tooltipTimeoutRef.current = setTimeout(
- showTooltip,
- 1000,
- )
- }
- }}
- onMouseLeave={() => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- }}
- >
-
- {agentMode !== "plan" && (
-
- )}
-
- {
- // Clear tooltip before closing dropdown (onMouseLeave won't fire)
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- setAgentMode("plan")
- setModeDropdownOpen(false)
- }}
- className="justify-between gap-2"
- onMouseEnter={(e) => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- const rect = e.currentTarget.getBoundingClientRect()
- const showTooltip = () => {
- setModeTooltip({
- visible: true,
- position: {
- top: rect.top,
- left: rect.right + 8,
- },
- mode: "plan",
- })
- hasShownTooltipRef.current = true
- tooltipTimeoutRef.current = null
- }
- if (hasShownTooltipRef.current) {
- showTooltip()
- } else {
- tooltipTimeoutRef.current = setTimeout(
- showTooltip,
- 1000,
- )
- }
- }}
- onMouseLeave={() => {
- if (tooltipTimeoutRef.current) {
- clearTimeout(tooltipTimeoutRef.current)
- tooltipTimeoutRef.current = null
- }
- setModeTooltip(null)
- }}
- >
-
- {agentMode === "plan" && (
-
- )}
-
-
- {modeTooltip?.visible &&
- createPortal(
- 0 && (
+
+
+
+
+ {effectiveProfile?.name || "Default"}
+
+
+
+
+
+ {displayProfiles.map((profile) => {
+ const isSelected = profile.id === effectiveProfile?.id
+ return (
+ setLastUsedProfileId(profile.id)}
+ className="gap-2 justify-between"
+ >
+ {profile.name}
+ {isSelected && }
+
+ )
+ })}
+
+ {
+ setSettingsActiveTab("models")
+ setSettingsDialogOpen(true)
}}
+ className="gap-2"
>
-
-
- {modeTooltip.mode === "agent"
- ? "Apply changes directly without a plan"
- : "Create a plan before making changes"}
-
-
- ,
- document.body,
- )}
-
+
+
Manage Profiles...
+
+
+
+ )}
- {/* Model selector - shows Ollama models when offline, Claude models when online */}
+ {/* Model selector - shows profile models, Ollama models when offline, or Claude models */}
{availableModels.isOffline && availableModels.hasOllama ? (
// Offline mode: show Ollama model selector
{currentOllamaModel || "Select model"}
@@ -1776,8 +1756,54 @@ export function NewChatForm({
})}
+ ) : profileModels.length > 0 ? (
+ // Profile mode: show models from selected profile config
+
+
+
+
+
+ {selectedProfileModel?.displayName || "Select model"}{" "}
+
+ ({selectedProfileModel?.modelId || ""})
+
+
+
+
+
+
+ {profileModels.map((model) => {
+ const isSelected = model.id === selectedProfileModel?.id
+ return (
+ setSelectedProfileModelId(model.id)}
+ className="gap-2 justify-between"
+ >
+
+
+
+ {model.displayName}{" "}
+
+ ({model.modelId})
+
+
+
+ {isSelected && (
+
+ )}
+
+ )
+ })}
+
+
) : (
- // Online mode: show Claude model selector
+ // Fallback: show Claude model selector (no profile or empty profile)
{
@@ -1790,10 +1816,10 @@ export function NewChatForm({
@@ -1803,7 +1829,7 @@ export function NewChatForm({
) : (
<>
{selectedModel?.name}{" "}
- 4.5
+ {/* 4.5 */}
>
)}
@@ -1826,7 +1852,6 @@ export function NewChatForm({
{model.name}{" "}
- 4.5
{isSelected && (
@@ -1838,6 +1863,45 @@ export function NewChatForm({
)}
+
+ {/* Thinking toggle - simple toggle button */}
+ {
+ if (selectedModelSupportsThinking) {
+ setThinkingEnabled(!thinkingEnabled)
+ }
+ }}
+ disabled={!selectedModelSupportsThinking}
+ className={cn(
+ "flex items-center gap-1.5 px-2 py-1 text-sm transition-colors rounded-md outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70",
+ !selectedModelSupportsThinking
+ ? "opacity-40 cursor-not-allowed text-muted-foreground"
+ : thinkingEnabled
+ ? "text-[#D4846C] hover:text-[#E8A08C] hover:bg-[#D4846C]/10"
+ : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
+ )}
+ >
+
+ {thinkingEnabled && (
+ Thinking
+ )}
+
+
+ {/* Mode toggle (Agent/Plan) - simple toggle button */}
+
+
+ {agentMode === "plan" && (
+ Plan
+ )}
+
diff --git a/src/renderer/features/agents/ui/agents-content.tsx b/src/renderer/features/agents/ui/agents-content.tsx
index 1c7a8d7e1..31861b15b 100644
--- a/src/renderer/features/agents/ui/agents-content.tsx
+++ b/src/renderer/features/agents/ui/agents-content.tsx
@@ -32,6 +32,7 @@ import {
betaKanbanEnabledAtom,
betaAutomationsEnabledAtom,
chatSourceModeAtom,
+ isRemoteModeAtom,
} from "../../../lib/atoms"
import { NewChatForm } from "../main/new-chat-form"
import { KanbanView } from "../../kanban"
@@ -166,20 +167,33 @@ export function AgentsContent() {
}
}, [activeSubChatName])
+ // Check if running in remote mode (web browser)
+ const isRemoteMode = useAtomValue(isRemoteModeAtom)
+
// Fetch teams for header
const { data: teams } = api.teams.getUserTeams.useQuery(undefined, {
enabled: !!selectedTeamId,
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ refetchInterval: isRemoteMode ? 5000 : false,
})
const selectedTeam = teams?.find((t: any) => t.id === selectedTeamId) as any
// Fetch agent chats for keyboard navigation and mobile view
const { data: agentChats } = api.agents.getAgentChats.useQuery(
{ teamId: selectedTeamId! },
- { enabled: !!selectedTeamId },
+ {
+ enabled: !!selectedTeamId,
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ refetchInterval: isRemoteMode ? 5000 : false,
+ },
)
// Fetch all projects for git info (like sidebar does)
- const { data: projects } = trpc.projects.list.useQuery()
+ const { data: projects } = trpc.projects.list.useQuery(
+ undefined, // no input
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ isRemoteMode ? { refetchInterval: 5000 } : undefined,
+ )
// Create map for quick project lookup by id
const projectsMap = useMemo(() => {
diff --git a/src/renderer/features/agents/ui/profile-selector.tsx b/src/renderer/features/agents/ui/profile-selector.tsx
new file mode 100644
index 000000000..be6784b2f
--- /dev/null
+++ b/src/renderer/features/agents/ui/profile-selector.tsx
@@ -0,0 +1,189 @@
+import { useAtom, useAtomValue, useSetAtom } from "jotai"
+import { ChevronDown, Settings } from "lucide-react"
+import { useCallback } from "react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "../../../components/ui/dropdown-menu"
+import { CheckIcon } from "../../../components/ui/icons"
+import {
+ agentsSettingsDialogActiveTabAtom,
+ agentsSettingsDialogOpenAtom,
+ lastUsedProfileIdAtom,
+ modelProfilesAtom,
+ type ModelProfile,
+} from "../../../lib/atoms"
+import { trpc } from "../../../lib/trpc"
+import { cn } from "../../../lib/utils"
+import { isRemoteMode } from "../../../lib/remote-transport"
+
+interface ProfileSelectorProps {
+ chatId: string
+ currentProfileId: string | null
+ disabled?: boolean
+ className?: string
+}
+
+/**
+ * Dropdown selector for choosing a model profile per-chat.
+ * Updates the chat's modelProfileId in the database and syncs lastUsedProfileIdAtom.
+ */
+export function ProfileSelector({
+ chatId,
+ currentProfileId,
+ disabled = false,
+ className,
+}: ProfileSelectorProps) {
+ const profiles = useAtomValue(modelProfilesAtom)
+ const [lastUsedProfileId, setLastUsedProfileId] = useAtom(lastUsedProfileIdAtom)
+ const setSettingsOpen = useSetAtom(agentsSettingsDialogOpenAtom)
+ const setSettingsTab = useSetAtom(agentsSettingsDialogActiveTabAtom)
+
+ const utils = trpc.useUtils()
+ const updateProfileMutation = trpc.chats.updateModelProfile.useMutation({
+ onSuccess: () => {
+ utils.chats.get.invalidate({ id: chatId })
+ },
+ })
+
+ // Filter out offline profiles from display (they're auto-selected when offline)
+ const displayProfiles = profiles.filter((p) => !p.isOffline)
+
+ // Get effective profile ID (chat's profile or fallback to lastUsed)
+ const effectiveProfileId = currentProfileId ?? lastUsedProfileId
+
+ // Find current profile
+ const currentProfile = profiles.find((p) => p.id === effectiveProfileId)
+
+ const handleProfileChange = useCallback(
+ (profileId: string) => {
+ // Update the chat's profile in database
+ updateProfileMutation.mutate({ id: chatId, modelProfileId: profileId })
+
+ // Update lastUsedProfileId so new chats inherit this
+ setLastUsedProfileId(profileId)
+ },
+ [chatId, updateProfileMutation, setLastUsedProfileId],
+ )
+
+ const handleOpenSettings = useCallback(() => {
+ // In web mode, settings dialog is not available
+ if (isRemoteMode()) {
+ console.log("[ProfileSelector] Settings not available in web mode")
+ return
+ }
+ setSettingsTab("models")
+ setSettingsOpen(true)
+ }, [setSettingsTab, setSettingsOpen])
+
+ // For web app or when no custom profiles: show "Default" option
+ // This ensures the selector is always visible in remote/web mode
+ const showDefaultOption = displayProfiles.length === 0 || isRemoteMode()
+
+ // In web mode, hide the "Manage Profiles" option since settings dialog is desktop-only
+ const showManageOption = !isRemoteMode()
+
+ return (
+
+
+
+
+ {currentProfile?.name || "Default"}
+
+
+
+
+
+ {/* Show "Default" option for web app or when no custom profiles */}
+ {showDefaultOption && (
+ {
+ // Clear the profile to use default
+ updateProfileMutation.mutate({ id: chatId, modelProfileId: null })
+ setLastUsedProfileId("")
+ }}
+ className="gap-2 justify-between"
+ >
+ Default
+ {!effectiveProfileId && }
+
+ )}
+ {displayProfiles.map((profile) => {
+ const isSelected = profile.id === effectiveProfileId
+ return (
+ handleProfileChange(profile.id)}
+ className="gap-2 justify-between"
+ >
+ {profile.name}
+ {isSelected && }
+
+ )
+ })}
+ {showDefaultOption && displayProfiles.length > 0 && }
+ {showManageOption && (
+
+
+ Manage Profiles...
+
+ )}
+
+
+ )
+}
+
+/**
+ * Hook to get the effective profile for a chat.
+ * Returns the profile from chat.modelProfileId or falls back to lastUsedProfileId.
+ */
+export function useEffectiveProfile(
+ chatModelProfileId: string | null | undefined,
+): ModelProfile | null {
+ const profiles = useAtomValue(modelProfilesAtom)
+ const lastUsedProfileId = useAtomValue(lastUsedProfileIdAtom)
+
+ const effectiveProfileId = chatModelProfileId ?? lastUsedProfileId
+
+ if (!effectiveProfileId) {
+ // Return first non-offline profile as default
+ return profiles.find((p) => !p.isOffline) ?? null
+ }
+
+ return profiles.find((p) => p.id === effectiveProfileId) ?? null
+}
+
+/**
+ * Hook to get the effective model for a chat within a profile.
+ * Returns the model from chat.selectedModelId or falls back to first model in profile.
+ */
+export function useEffectiveModel(
+ profile: ModelProfile | null,
+ chatSelectedModelId: string | null | undefined,
+) {
+ if (!profile || !profile.models || profile.models.length === 0) {
+ return null
+ }
+
+ // Try to find the selected model
+ if (chatSelectedModelId) {
+ const selectedModel = profile.models.find((m) => m.id === chatSelectedModelId)
+ if (selectedModel) {
+ return selectedModel
+ }
+ }
+
+ // Fallback to first model
+ return profile.models[0] ?? null
+}
diff --git a/src/renderer/features/layout/agents-layout.tsx b/src/renderer/features/layout/agents-layout.tsx
index 7d651cf16..cdd21fa33 100644
--- a/src/renderer/features/layout/agents-layout.tsx
+++ b/src/renderer/features/layout/agents-layout.tsx
@@ -18,6 +18,7 @@ import { trpc } from "../../lib/trpc"
import { useAgentsHotkeys } from "../agents/lib/agents-hotkeys-manager"
import { toggleSearchAtom } from "../agents/search"
import { ClaudeLoginModal } from "../../components/dialogs/claude-login-modal"
+import { RemoteAccessDialog } from "../../components/dialogs/remote-access-dialog"
import { TooltipProvider } from "../../components/ui/tooltip"
import { ResizableSidebar } from "../../components/ui/resizable-sidebar"
import { AgentsSidebar } from "../sidebar/agents-sidebar"
@@ -241,6 +242,7 @@ export function AgentsLayout() {
{/* Global queue processor - handles message queues for all sub-chats */}
+
{/* Windows Title Bar (only shown on Windows with frameless window) */}
diff --git a/src/renderer/features/onboarding/api-key-onboarding-page.tsx b/src/renderer/features/onboarding/api-key-onboarding-page.tsx
index 8bb0f18ac..e3315b840 100644
--- a/src/renderer/features/onboarding/api-key-onboarding-page.tsx
+++ b/src/renderer/features/onboarding/api-key-onboarding-page.tsx
@@ -12,8 +12,11 @@ import {
apiKeyOnboardingCompletedAtom,
billingMethodAtom,
customClaudeConfigAtom,
+ modelProfilesAtom,
+ activeProfileIdAtom,
type CustomClaudeConfig,
} from "../../lib/atoms"
+import { trpc } from "../../lib/trpc"
import { cn } from "../../lib/utils"
// Check if the key looks like a valid Anthropic API key
@@ -24,6 +27,8 @@ const isValidApiKey = (key: string) => {
export function ApiKeyOnboardingPage() {
const [storedConfig, setStoredConfig] = useAtom(customClaudeConfigAtom)
+ const [profiles, setProfiles] = useAtom(modelProfilesAtom)
+ const setActiveProfileId = useSetAtom(activeProfileIdAtom)
const billingMethod = useAtomValue(billingMethodAtom)
const setBillingMethod = useSetAtom(billingMethodAtom)
const setApiKeyOnboardingCompleted = useSetAtom(apiKeyOnboardingCompletedAtom)
@@ -34,11 +39,20 @@ export function ApiKeyOnboardingPage() {
const defaultModel = "claude-sonnet-4-20250514"
const defaultBaseUrl = "https://api.anthropic.com"
+ // Query for existing CLI config (to pre-fill form with detected proxy)
+ const { data: cliConfig } = trpc.claudeCode.hasExistingCliConfig.useQuery()
+
const [apiKey, setApiKey] = useState(storedConfig.token)
const [model, setModel] = useState(storedConfig.model || "")
const [token, setToken] = useState(storedConfig.token)
const [baseUrl, setBaseUrl] = useState(storedConfig.baseUrl || "")
+ // Optional model configuration fields
+ const [defaultOpusModel, setDefaultOpusModel] = useState(storedConfig.defaultOpusModel || "")
+ const [defaultSonnetModel, setDefaultSonnetModel] = useState(storedConfig.defaultSonnetModel || "")
+ const [defaultHaikuModel, setDefaultHaikuModel] = useState(storedConfig.defaultHaikuModel || "")
+ const [subagentModel, setSubagentModel] = useState(storedConfig.subagentModel || "")
const [isSubmitting, setIsSubmitting] = useState(false)
+ const [hasAppliedCliConfig, setHasAppliedCliConfig] = useState(false)
// Sync from stored config on mount
useEffect(() => {
@@ -48,8 +62,32 @@ export function ApiKeyOnboardingPage() {
}
if (storedConfig.model) setModel(storedConfig.model)
if (storedConfig.baseUrl) setBaseUrl(storedConfig.baseUrl)
+ if (storedConfig.defaultOpusModel) setDefaultOpusModel(storedConfig.defaultOpusModel)
+ if (storedConfig.defaultSonnetModel) setDefaultSonnetModel(storedConfig.defaultSonnetModel)
+ if (storedConfig.defaultHaikuModel) setDefaultHaikuModel(storedConfig.defaultHaikuModel)
+ if (storedConfig.subagentModel) setSubagentModel(storedConfig.subagentModel)
}, [])
+ // Pre-fill form with detected CLI config (proxy settings) if available
+ useEffect(() => {
+ // Only proceed if we have config and haven't applied it yet
+ if (cliConfig && !hasAppliedCliConfig && isCustomModel) {
+ setHasAppliedCliConfig(true)
+
+ // Pre-fill with detected values if the current state is empty
+ if (cliConfig.apiKey && token === "") {
+ setToken(cliConfig.apiKey)
+ setApiKey(cliConfig.apiKey)
+ }
+ if (cliConfig.baseUrl && baseUrl === "") setBaseUrl(cliConfig.baseUrl)
+ if (cliConfig.model && model === "") setModel(cliConfig.model)
+ if (cliConfig.defaultOpusModel && defaultOpusModel === "") setDefaultOpusModel(cliConfig.defaultOpusModel)
+ if (cliConfig.defaultSonnetModel && defaultSonnetModel === "") setDefaultSonnetModel(cliConfig.defaultSonnetModel)
+ if (cliConfig.defaultHaikuModel && defaultHaikuModel === "") setDefaultHaikuModel(cliConfig.defaultHaikuModel)
+ if (cliConfig.subagentModel && subagentModel === "") setSubagentModel(cliConfig.subagentModel)
+ }
+ }, [cliConfig, hasAppliedCliConfig, isCustomModel, token, baseUrl, model, defaultOpusModel, defaultSonnetModel, defaultHaikuModel, subagentModel])
+
const handleBack = () => {
setBillingMethod(null)
}
@@ -71,13 +109,14 @@ export function ApiKeyOnboardingPage() {
setIsSubmitting(false)
}
- // Submit for custom model mode (all three fields)
- const submitCustomModel = () => {
+ // Submit for custom model mode (all fields)
+ const submitCustomModel = async () => {
const trimmedModel = model.trim()
const trimmedToken = token.trim()
const trimmedBaseUrl = baseUrl.trim()
- if (!trimmedModel || !trimmedToken || !trimmedBaseUrl) return
+ // Only model and baseUrl are required - token is optional for proxy configs
+ if (!trimmedModel || !trimmedBaseUrl) return
setIsSubmitting(true)
@@ -85,8 +124,62 @@ export function ApiKeyOnboardingPage() {
model: trimmedModel,
token: trimmedToken,
baseUrl: trimmedBaseUrl,
+ defaultOpusModel: defaultOpusModel.trim() || undefined,
+ defaultSonnetModel: defaultSonnetModel.trim() || undefined,
+ defaultHaikuModel: defaultHaikuModel.trim() || undefined,
+ subagentModel: subagentModel.trim() || undefined,
}
+
+ // Build ModelMapping array from config (unique, non-empty model IDs)
+ const modelMappings: Array<{ id: string; displayName: string; modelId: string; supportsThinking: boolean }> = []
+
+ const addModel = (id: string, displayName: string, modelId: string) => {
+ if (modelId) {
+ modelMappings.push({ id, displayName, modelId, supportsThinking: false })
+ }
+ }
+
+ // Add default model
+ addModel("default", "Default", trimmedModel)
+
+ // Add optional models
+ if (defaultOpusModel.trim()) addModel("opus", "Opus", defaultOpusModel.trim())
+ if (defaultSonnetModel.trim()) addModel("sonnet", "Sonnet", defaultSonnetModel.trim())
+ if (defaultHaikuModel.trim()) addModel("haiku", "Haiku", defaultHaikuModel.trim())
+ if (subagentModel.trim()) addModel("subagent", "Subagent", subagentModel.trim())
+
+ // Save to legacy config atom
setStoredConfig(config)
+
+ // Create profile data for database and localStorage
+ const profileData = {
+ name: "Default",
+ config,
+ models: modelMappings,
+ isOffline: false,
+ }
+
+ // Save to database (works in both desktop and remote mode)
+ try {
+ await trpc.modelProfiles.create.mutate(profileData)
+ console.log("[ApiKeyOnboarding] Created Default profile in database")
+ } catch (error) {
+ console.error("[ApiKeyOnboarding] Failed to save profile to database:", error)
+ }
+
+ // Also create a profile in modelProfilesAtom so it shows in settings immediately
+ const newProfileId = crypto.randomUUID()
+ const newProfile = {
+ id: newProfileId,
+ ...profileData,
+ }
+
+ // Add new profile (keep existing profiles like offline profile)
+ setProfiles([...profiles, newProfile])
+
+ // Set as active profile
+ setActiveProfileId(newProfileId)
+
setApiKeyOnboardingCompleted(true)
setIsSubmitting(false)
@@ -108,8 +201,9 @@ export function ApiKeyOnboardingPage() {
}
}
+ // Token is optional if using a proxy (baseUrl) - proxy may handle auth
const canSubmitCustomModel = Boolean(
- model.trim() && token.trim() && baseUrl.trim()
+ model.trim() && baseUrl.trim() // Only model and baseUrl are required
)
// Simple API key input mode
@@ -188,22 +282,22 @@ export function ApiKeyOnboardingPage() {
// Custom model mode with all fields
return (
-
+
{/* Draggable title bar area */}
{/* Back button - fixed in top left corner below traffic lights */}
-
+
{/* Header with dual icons */}
@@ -226,23 +320,21 @@ export function ApiKeyOnboardingPage() {
{/* Form Fields */}
- {/* Model Name */}
+ {/* Base URL */}
-
Model name
+
Base URL *
setModel(e.target.value)}
- placeholder="claude-sonnet-4-20250514"
+ value={baseUrl}
+ onChange={(e) => setBaseUrl(e.target.value)}
+ placeholder="https://api.anthropic.com"
className="w-full"
/>
-
- Model identifier for API requests
-
+
API endpoint URL
{/* API Token */}
- API token
+ API Token (Optional for proxies)
- {/* Base URL */}
+ {/* Default Model */}
-
Base URL
+
Default Model *
setBaseUrl(e.target.value)}
- placeholder="https://api.anthropic.com"
+ value={model}
+ onChange={(e) => setModel(e.target.value)}
+ placeholder="claude-sonnet-4-5-thinking"
className="w-full"
/>
-
API endpoint URL
+
+ Model identifier for API requests
+
+
+
+ {/* Optional Model Configuration Section */}
+
+
+ Additional Model Configuration (Optional)
+
+
+
+ {/* 2-column grid for optional model fields */}
+
diff --git a/src/renderer/features/onboarding/cli-config-detected-page.tsx b/src/renderer/features/onboarding/cli-config-detected-page.tsx
new file mode 100644
index 000000000..0d7fed58c
--- /dev/null
+++ b/src/renderer/features/onboarding/cli-config-detected-page.tsx
@@ -0,0 +1,207 @@
+"use client";
+
+import { useSetAtom } from "jotai";
+import { useState } from "react";
+import { ChevronLeft, Check, ExternalLink } from "lucide-react";
+
+import { ClaudeCodeIcon, KeyFilledIcon } from "../../components/ui/icons";
+import {
+ billingMethodAtom,
+ cliConfigDetectedShownAtom,
+} from "../../lib/atoms";
+import { trpc } from "../../lib/trpc";
+import { cn } from "../../lib/utils";
+
+export function CliConfigDetectedPage() {
+ const setBillingMethod = useSetAtom(billingMethodAtom);
+ const setCliConfigDetectedShown = useSetAtom(cliConfigDetectedShownAtom);
+ const [isContinuing, setIsContinuing] = useState(false);
+ const [showOAuthFlow, setShowOAuthFlow] = useState(false);
+
+ // Check for existing CLI config
+ const { data: cliConfig, isLoading } =
+ trpc.claudeCode.hasExistingCliConfig.useQuery();
+
+ const handleUseExistingConfig = () => {
+ setIsContinuing(true);
+ // Mark CLI config detected as shown so we don't show this page again
+ setCliConfigDetectedShown(true);
+ // Navigate to custom-model form (api-key-onboarding-page in "custom-model" mode)
+ // This allows users to review/edit the pre-filled values before continuing
+ setBillingMethod("custom-model");
+ // Note: apiKeyOnboardingCompleted is NOT set here - user needs to submit the form
+ };
+
+ const handleUseOAuth = () => {
+ setShowOAuthFlow(true);
+ setBillingMethod("claude-subscription");
+ };
+
+ const handleBack = () => {
+ setBillingMethod(null);
+ };
+
+ if (isLoading) {
+ return (
+
+
+ Checking configuration...
+
+
+ );
+ }
+
+ // If user chose OAuth, show loading while redirecting
+ if (showOAuthFlow) {
+ return (
+
+
+
+
+
+ Connecting to Claude...
+
+
+ Opening browser for authentication
+
+
+
+
+ );
+ }
+
+ const hasApiKey = cliConfig?.hasApiKey;
+ const hasBaseUrl = cliConfig?.baseUrl !== null;
+
+ return (
+
+ {/* Draggable title bar area */}
+
+
+ {/* Back button - fixed in top left corner below traffic lights */}
+
+
+
+
+
+ {/* Header */}
+
+
+
+
+ Claude Configuration Detected
+
+
+ We found an existing Claude CLI configuration in your shell
+ environment.
+
+
+
+
+ {/* Configuration Status */}
+
+
+
+
+ API Key: {" "}
+ {hasApiKey ? (
+
+ Configured
+
+ ) : (
+ Not set
+ )}
+
+
+
+
+
+
+ API Proxy: {" "}
+ {hasBaseUrl ? (
+
+ {cliConfig?.baseUrl}
+
+ ) : (
+
+ Default (api.anthropic.com)
+
+ )}
+
+
+
+
+
+ Your ANTHROPIC_API_KEY
+ {hasBaseUrl && " and "}
+ {hasBaseUrl && (
+ ANTHROPIC_BASE_URL
+ )}{" "}
+ environment variables were detected.
+
+
+
+
+ {/* Primary Action - Use Existing Config */}
+ {/* Enable if we have API key OR proxy URL (proxies may handle auth) */}
+
+ {isContinuing ? (
+ <>Setting up...>
+ ) : (
+ <>
+
+ Use Existing Configuration
+ >
+ )}
+
+
+ {/* Secondary Action - OAuth */}
+
+
+
+ Continue with OAuth Instead
+
+
+ Use your Claude Pro/Max subscription with OAuth authentication
+
+
+
+
+ );
+}
diff --git a/src/renderer/features/onboarding/index.ts b/src/renderer/features/onboarding/index.ts
index 9cb588fd2..9bfac611e 100644
--- a/src/renderer/features/onboarding/index.ts
+++ b/src/renderer/features/onboarding/index.ts
@@ -1,4 +1,5 @@
-export { AnthropicOnboardingPage } from "./anthropic-onboarding-page"
-export { ApiKeyOnboardingPage } from "./api-key-onboarding-page"
-export { BillingMethodPage } from "./billing-method-page"
-export { SelectRepoPage } from "./select-repo-page"
+export { AnthropicOnboardingPage } from "./anthropic-onboarding-page";
+export { ApiKeyOnboardingPage } from "./api-key-onboarding-page";
+export { BillingMethodPage } from "./billing-method-page";
+export { CliConfigDetectedPage } from "./cli-config-detected-page";
+export { SelectRepoPage } from "./select-repo-page";
diff --git a/src/renderer/features/sidebar/agents-sidebar.tsx b/src/renderer/features/sidebar/agents-sidebar.tsx
index fd0f7245a..b1d0328d5 100644
--- a/src/renderer/features/sidebar/agents-sidebar.tsx
+++ b/src/renderer/features/sidebar/agents-sidebar.tsx
@@ -20,6 +20,7 @@ import {
clearAgentChatSelectionAtom,
selectedAgentChatsCountAtom,
isDesktopAtom,
+ isRemoteModeAtom,
isFullscreenAtom,
showOfflineModeFeaturesAtom,
chatSourceModeAtom,
@@ -28,6 +29,7 @@ import {
showWorkspaceIconAtom,
betaKanbanEnabledAtom,
betaAutomationsEnabledAtom,
+ remoteAccessDialogOpenAtom,
} from "../../lib/atoms"
import {
useRemoteChats,
@@ -39,7 +41,7 @@ import {
useRenameRemoteChat,
} from "../../lib/hooks/use-remote-chats"
import { ArchivePopover } from "../agents/ui/archive-popover"
-import { ChevronDown, MoreHorizontal, Columns3, ArrowUpRight } from "lucide-react"
+import { ChevronDown, MoreHorizontal, Columns3, ArrowUpRight, Globe } from "lucide-react"
import { useQuery } from "@tanstack/react-query"
import { remoteTrpc } from "../../lib/remote-trpc"
// import { useRouter } from "next/navigation" // Desktop doesn't use next/navigation
@@ -136,6 +138,8 @@ import { Checkbox } from "../../components/ui/checkbox"
import { useHaptic } from "./hooks/use-haptic"
import { TypewriterText } from "../../components/ui/typewriter-text"
import { exportChat, copyChat, type ExportFormat } from "../agents/lib/export-chat"
+import { modelProfilesAtom, lastUsedProfileIdAtom } from "../../lib/atoms"
+import { CheckIcon } from "../../components/ui/icons"
// Feedback URL: uses env variable for hosted version, falls back to public Discord for open source
const FEEDBACK_URL =
@@ -176,6 +180,66 @@ const GitHubAvatar = React.memo(function GitHubAvatar({
)
})
+// Model Profile submenu for context menu
+const ModelProfileSubmenu = React.memo(function ModelProfileSubmenu({
+ chatId,
+ currentProfileId,
+}: {
+ chatId: string
+ currentProfileId: string | null | undefined
+}) {
+ const profiles = useAtomValue(modelProfilesAtom)
+ const [lastUsedProfileId, setLastUsedProfileId] = useAtom(lastUsedProfileIdAtom)
+ const utils = trpc.useUtils()
+
+ // Use chat's profile or fall back to lastUsedProfileId
+ const effectiveProfileId = currentProfileId ?? lastUsedProfileId
+
+ const updateProfileMutation = trpc.chats.updateModelProfile.useMutation({
+ onSuccess: () => {
+ utils.chats.get.invalidate({ id: chatId })
+ utils.chats.list.invalidate()
+ },
+ })
+
+ // Filter out offline profiles
+ const displayProfiles = profiles.filter((p) => !p.isOffline)
+
+ const handleProfileChange = useCallback(
+ (profileId: string) => {
+ updateProfileMutation.mutate({ id: chatId, modelProfileId: profileId })
+ setLastUsedProfileId(profileId)
+ },
+ [chatId, updateProfileMutation, setLastUsedProfileId],
+ )
+
+ if (displayProfiles.length === 0) {
+ return (
+
+ No profiles configured
+
+ )
+ }
+
+ return (
+ <>
+ {displayProfiles.map((profile) => {
+ const isSelected = profile.id === effectiveProfileId
+ return (
+
handleProfileChange(profile.id)}
+ className="justify-between"
+ >
+ {profile.name}
+ {isSelected && }
+
+ )
+ })}
+ >
+ )
+})
+
// Component to render chat icon with loading status
const ChatIcon = React.memo(function ChatIcon({
isSelected,
@@ -763,6 +827,15 @@ const AgentChatItem = React.memo(function AgentChatItem({
Open in new window
)}
+ {/* Model Profile submenu - only show for local chats */}
+ {!isRemote && (
+
+ Model Profile
+
+
+
+
+ )}
onArchive(chatId)} className="justify-between">
Archive workspace
@@ -1066,6 +1139,26 @@ const ArchiveButton = memo(forwardRef
+
+ setRemoteAccessDialogOpen(true)}
+ className="flex items-center justify-center h-7 w-7 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.97] outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70"
+ >
+
+
+
+ Remote Access
+
+ )
+})
+
const KanbanButton = memo(function KanbanButton() {
const kanbanEnabled = useAtomValue(betaKanbanEnabledAtom)
const setSelectedChatId = useSetAtom(selectedAgentChatIdAtom)
@@ -1671,6 +1764,7 @@ export function AgentsSidebar({
// Global desktop/fullscreen state from atoms (initialized in AgentsLayout)
const isDesktop = useAtomValue(isDesktopAtom)
+ const isRemoteMode = useAtomValue(isRemoteModeAtom)
const isFullscreen = useAtomValue(isFullscreenAtom)
// Multi-select state
@@ -1779,7 +1873,18 @@ export function AgentsSidebar({
}, [])
// Fetch all local chats (no project filter)
- const { data: localChats } = trpc.chats.list.useQuery({})
+ const { data: localChats, isLoading: isChatsLoading, error: chatsError } = trpc.chats.list.useQuery(
+ {}, // empty input
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ isRemoteMode ? { refetchInterval: 5000 } : {},
+ )
+
+ // Debug: Log chat data in remote mode
+ useEffect(() => {
+ if (isRemoteMode) {
+ console.log('[AgentsSidebar] Remote mode - localChats:', localChats, 'isLoading:', isChatsLoading, 'error:', chatsError)
+ }
+ }, [isRemoteMode, localChats, isChatsLoading, chatsError])
// Fetch user's teams (same as web) - always enabled to allow merged list
const { data: teams, isLoading: isTeamsLoading, isError: isTeamsError } = useUserTeams(true)
@@ -1921,7 +2026,11 @@ export function AgentsSidebar({
)
// Fetch all projects for git info
- const { data: projects } = trpc.projects.list.useQuery()
+ const { data: projects } = trpc.projects.list.useQuery(
+ undefined, // no input
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ isRemoteMode ? { refetchInterval: 5000 } : undefined,
+ )
// Auto-import hook for "Open Locally" functionality
const { getMatchingProjects, autoImport, isImporting } = useAutoImport()
@@ -1933,7 +2042,11 @@ export function AgentsSidebar({
}, [projects])
// Fetch all archived chats (to get count)
- const { data: archivedChats } = trpc.chats.listArchived.useQuery({})
+ const { data: archivedChats } = trpc.chats.listArchived.useQuery(
+ undefined, // no input
+ // Poll every 5 seconds in remote mode to sync with desktop changes
+ isRemoteMode ? { refetchInterval: 5000 } : {},
+ )
const archivedChatsCount = archivedChats?.length ?? 0
// Get utils outside of callbacks - hooks must be called at top level
@@ -3398,22 +3511,24 @@ export function AgentsSidebar({
>
- {/* Settings Button */}
-
-
- {
- setSettingsActiveTab("preferences")
- setSettingsDialogOpen(true)
- }}
- className="flex items-center justify-center h-7 w-7 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.97] outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70"
- >
-
-
-
- Settings{settingsHotkey && <> {settingsHotkey} >}
-
+ {/* Settings Button - hide in remote mode */}
+ {!isRemoteMode && (
+
+
+ {
+ setSettingsActiveTab("preferences")
+ setSettingsDialogOpen(true)
+ }}
+ className="flex items-center justify-center h-7 w-7 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.97] outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70"
+ >
+
+
+
+ Settings{settingsHotkey && <> {settingsHotkey} >}
+
+ )}
{/* Help Button - isolated component to prevent sidebar re-renders */}
@@ -3421,6 +3536,9 @@ export function AgentsSidebar({
{/* Kanban View Button - isolated component */}
+ {/* Remote Access Button - hide in remote mode */}
+ {!isRemoteMode &&
}
+
{/* Archive Button - isolated component to prevent sidebar re-renders */}
diff --git a/src/renderer/lib/atoms/index.ts b/src/renderer/lib/atoms/index.ts
index 178d5645d..887f81fae 100644
--- a/src/renderer/lib/atoms/index.ts
+++ b/src/renderer/lib/atoms/index.ts
@@ -204,18 +204,50 @@ export const agentsSettingsDialogOpenAtom = atom(
}
)
+// Model profile types (defined here to avoid circular dependencies)
+export type ModelMapping = {
+ id: string // Internal reference ID (e.g., "opus", "sonnet", "haiku")
+ displayName: string // User-facing name (e.g., "Opus", "Sonnet 3.5")
+ modelId: string // Actual model ID sent to API (e.g., "glm-4.7", "claude-3-opus-20240229")
+ supportsThinking?: boolean // Whether this model supports extended thinking
+}
+
export type CustomClaudeConfig = {
model: string
- token: string
+ token?: string
baseUrl: string
+ // Additional model configuration (optional)
+ defaultOpusModel?: string // ANTHROPIC_DEFAULT_OPUS_MODEL
+ defaultSonnetModel?: string // ANTHROPIC_DEFAULT_SONNET_MODEL
+ defaultHaikuModel?: string // ANTHROPIC_DEFAULT_HAIKU_MODEL
+ subagentModel?: string // CLAUDE_CODE_SUBAGENT_MODEL
}
-// Model profile system - support multiple configs
export type ModelProfile = {
id: string
name: string
config: CustomClaudeConfig
- isOffline?: boolean // Mark as offline/Ollama profile
+ models: ModelMapping[]
+ isOffline?: boolean
+}
+
+export const OFFLINE_PROFILE: ModelProfile = {
+ id: 'offline-ollama',
+ name: 'Offline (Ollama)',
+ isOffline: true,
+ config: {
+ model: 'qwen2.5-coder:7b',
+ token: 'ollama',
+ baseUrl: 'http://localhost:11434',
+ },
+ models: [
+ {
+ id: 'default',
+ displayName: 'Default',
+ modelId: 'qwen2.5-coder:7b',
+ supportsThinking: false,
+ },
+ ],
}
// Selected Ollama model for offline mode
@@ -236,20 +268,16 @@ export const getOfflineProfile = (modelName?: string | null): ModelProfile => ({
token: 'ollama',
baseUrl: 'http://localhost:11434',
},
+ models: [
+ {
+ id: 'default',
+ displayName: 'Default',
+ modelId: modelName || 'qwen2.5-coder:7b',
+ supportsThinking: false,
+ },
+ ],
})
-// Predefined offline profile for Ollama (legacy, uses default model)
-export const OFFLINE_PROFILE: ModelProfile = {
- id: 'offline-ollama',
- name: 'Offline (Ollama)',
- isOffline: true,
- config: {
- model: 'qwen2.5-coder:7b',
- token: 'ollama',
- baseUrl: 'http://localhost:11434',
- },
-}
-
// Legacy single config (deprecated, kept for backwards compatibility)
export const customClaudeConfigAtom = atomWithStorage
(
"agents:claude-custom-config",
@@ -262,6 +290,8 @@ export const customClaudeConfigAtom = atomWithStorage(
{ getOnInit: true },
)
+// Migration block removed as per user request (legacy config no longer auto-migrates to "Migrated Config")
+
// OpenAI API key for voice transcription (for users without paid subscription)
export const openaiApiKeyAtom = atomWithStorage(
"agents:openai-api-key",
@@ -270,17 +300,84 @@ export const openaiApiKeyAtom = atomWithStorage(
{ getOnInit: true },
)
-// New: Model profiles storage
-export const modelProfilesAtom = atomWithStorage(
+// New: Model profiles storage (localStorage-based for desktop)
+// Renamed to localStorageModelProfilesAtom - use modelProfilesAtom from model-profiles-sync.ts instead
+export const localStorageModelProfilesAtom = atomWithStorage(
"agents:model-profiles",
[OFFLINE_PROFILE], // Start with offline profile
undefined,
{ getOnInit: true },
)
+// Re-export the database-synced model profiles atom
+// This atom automatically handles:
+// - Desktop mode: Uses localStorage, syncs to database
+// - Web mode: Loads from database via tRPC
+export { modelProfilesAtom, useSyncModelProfiles, activeConfigAtom, normalizeCustomClaudeConfig } from "./model-profiles-sync"
+
+// Migration: add models array to existing profiles that don't have it
+if (typeof window !== "undefined") {
+ const profilesKey = "agents:model-profiles"
+ const stored = localStorage.getItem(profilesKey)
+ if (stored) {
+ try {
+ const profiles = JSON.parse(stored) as ModelProfile[]
+ let needsMigration = false
+ const migrated = profiles.map(profile => {
+ if (!profile.models || profile.models.length === 0) {
+ needsMigration = true
+ return {
+ ...profile,
+ models: [
+ {
+ id: 'default',
+ displayName: 'Default',
+ modelId: profile.config.model || 'claude-3-opus',
+ supportsThinking: true,
+ },
+ ],
+ }
+ }
+ return profile
+ })
+ if (needsMigration) {
+ localStorage.setItem(profilesKey, JSON.stringify(migrated))
+ console.log("[atoms] Migrated profiles to include models array")
+ }
+ } catch (e) {
+ console.error("[atoms] Failed to migrate profiles:", e)
+ }
+ }
+}
+
// Active profile ID (null = use Claude Code default)
-export const activeProfileIdAtom = atomWithStorage(
- "agents:active-profile-id",
+// Renamed from activeProfileIdAtom - now serves as default for new chats
+export const lastUsedProfileIdAtom = atomWithStorage(
+ "agents:last-used-profile-id",
+ null,
+ undefined,
+ { getOnInit: true },
+)
+
+// Migration: rename old key to new key
+if (typeof window !== "undefined") {
+ const oldKey = "agents:active-profile-id"
+ const newKey = "agents:last-used-profile-id"
+ const oldValue = localStorage.getItem(oldKey)
+ if (oldValue !== null && localStorage.getItem(newKey) === null) {
+ localStorage.setItem(newKey, oldValue)
+ localStorage.removeItem(oldKey)
+ console.log("[atoms] Migrated activeProfileId to lastUsedProfileId")
+ }
+}
+
+// Backwards compatibility alias
+export const activeProfileIdAtom = lastUsedProfileIdAtom
+
+// Selected model ID within the current profile (e.g., "main", "opus", "sonnet", "haiku", "subagent")
+// Used by ipc-chat-transport to determine which model from the profile to use
+export const selectedProfileModelIdAtom = atomWithStorage(
+ "agents:selected-profile-model-id",
null,
undefined,
{ getOnInit: true },
@@ -313,55 +410,8 @@ export const showOfflineModeFeaturesAtom = atomWithStorage(
// Network status (updated from main process)
export const networkOnlineAtom = atom(true)
-export function normalizeCustomClaudeConfig(
- config: CustomClaudeConfig,
-): CustomClaudeConfig | undefined {
- const model = config.model.trim()
- const token = config.token.trim()
- const baseUrl = config.baseUrl.trim()
-
- if (!model || !token || !baseUrl) return undefined
-
- return { model, token, baseUrl }
-}
-
-// Get active config (considering network status and auto-fallback)
-export const activeConfigAtom = atom((get) => {
- const activeProfileId = get(activeProfileIdAtom)
- const profiles = get(modelProfilesAtom)
- const legacyConfig = get(customClaudeConfigAtom)
- const networkOnline = get(networkOnlineAtom)
- const autoOffline = get(autoOfflineModeAtom)
-
- // If auto-offline enabled and no internet, use offline profile
- if (!networkOnline && autoOffline) {
- const offlineProfile = profiles.find(p => p.isOffline)
- if (offlineProfile) {
- return offlineProfile.config
- }
- }
-
- // If specific profile is selected, use it
- if (activeProfileId) {
- const profile = profiles.find(p => p.id === activeProfileId)
- if (profile) {
- return profile.config
- }
- }
-
- // Fallback to legacy config if set
- const normalized = normalizeCustomClaudeConfig(legacyConfig)
- if (normalized) {
- return normalized
- }
-
- // No custom config
- return undefined
-})
-
// Preferences - Extended Thinking
// When enabled, Claude will use extended thinking for deeper reasoning (128K tokens)
-// Note: Extended thinking disables response streaming
export const extendedThinkingEnabledAtom = atomWithStorage(
"preferences:extended-thinking-enabled",
false,
@@ -712,6 +762,17 @@ export const updateInfoAtom = atom(null)
// Whether app is running in Electron desktop environment
export const isDesktopAtom = atom(false)
+// Whether app is running in remote mode (web browser, not desktop)
+// In remote mode, certain desktop-only features should be hidden
+export const isRemoteModeAtom = atom(() => {
+ // Check if running in remote mode (no desktopApi or has remote auth token)
+ if (typeof window === "undefined") return false
+ // If we have a stored remote auth PIN, we're in remote mode
+ if (sessionStorage.getItem("remote_ws_auth")) return true
+ // If desktopApi is missing, we're in remote mode
+ return !window.desktopApi
+})
+
// Fullscreen state - null means not initialized yet
// null = not yet loaded, false = not fullscreen, true = fullscreen
export const isFullscreenAtom = atom(null)
@@ -753,6 +814,15 @@ export const apiKeyOnboardingCompletedAtom = atomWithStorage(
{ getOnInit: true },
)
+// Whether user has seen the CLI config detected page
+// This tracks if the user was shown the "Configuration Detected" screen
+export const cliConfigDetectedShownAtom = atomWithStorage(
+ "onboarding:cli-config-detected-shown",
+ false,
+ undefined,
+ { getOnInit: true },
+)
+
// ============================================
// SESSION INFO ATOMS (MCP, Plugins, Tools)
// ============================================
@@ -846,3 +916,9 @@ export const mcpApprovalDialogOpenAtom = atom(false)
// Pending MCP approvals to show in the dialog
export const pendingMcpApprovalsAtom = atom([])
+
+// ============================================
+// REMOTE ACCESS ATOMS
+// ============================================
+
+export * from "./remote-access"
diff --git a/src/renderer/lib/atoms/model-profiles-sync.ts b/src/renderer/lib/atoms/model-profiles-sync.ts
new file mode 100644
index 000000000..c4ab1dc67
--- /dev/null
+++ b/src/renderer/lib/atoms/model-profiles-sync.ts
@@ -0,0 +1,223 @@
+import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"
+import { useEffect, useState } from "react"
+import { trpc } from "../../lib/trpc"
+import {
+ localStorageModelProfilesAtom,
+ activeProfileIdAtom,
+ customClaudeConfigAtom,
+ networkOnlineAtom,
+ autoOfflineModeAtom,
+} from "./index"
+import { OFFLINE_PROFILE, type ModelProfile, type CustomClaudeConfig } from "./index"
+import { isRemoteMode } from "../remote-transport"
+
+// Helper to normalize custom config (moved from index.ts to break circular dependency)
+export function normalizeCustomClaudeConfig(
+ config: CustomClaudeConfig,
+): CustomClaudeConfig | undefined {
+ const model = config.model.trim()
+ const token = config.token.trim()
+ const baseUrl = config.baseUrl.trim()
+
+ if (!model || !token || !baseUrl) return undefined
+
+ return { model, token, baseUrl }
+}
+
+/**
+ * Database-synced model profiles atom
+ * - Desktop mode: Uses localStorage (fast, offline-capable) and syncs to database
+ * - Web mode: Loads from database via tRPC
+ */
+
+// Internal atom to track if database sync has been done (desktop only)
+const hasSyncedToDatabaseAtom = atom(false)
+
+// Atom to hold database-loaded profiles (for web mode or after sync)
+const databaseProfilesAtom = atom(null)
+
+// Helper to convert database profile to ModelProfile
+function fromDBProfile(dbProfile: any): ModelProfile {
+ return {
+ id: dbProfile.id,
+ name: dbProfile.name,
+ config: typeof dbProfile.config === 'string' ? JSON.parse(dbProfile.config) : dbProfile.config,
+ models: typeof dbProfile.models === 'string' ? JSON.parse(dbProfile.models) : dbProfile.models,
+ isOffline: dbProfile.isOffline || false,
+ }
+}
+
+// Helper to convert ModelProfile to database format
+// Note: The tRPC router expects objects, Drizzle will handle serialization to JSON for TEXT columns
+function toDBProfile(profile: ModelProfile): Omit {
+ return {
+ name: profile.name,
+ config: profile.config,
+ models: profile.models || [],
+ isOffline: profile.isOffline || false,
+ }
+}
+
+/**
+ * Hook to sync model profiles between localStorage and database
+ * - Desktop: Syncs localStorage profiles to database on app start
+ * - Web: Loads profiles from database and stores in atom
+ */
+export function useSyncModelProfiles() {
+ const localStorageProfiles = useAtomValue(localStorageModelProfilesAtom)
+ const setDatabaseProfiles = useSetAtom(databaseProfilesAtom)
+ const [hasSynced, setHasSynced] = useAtom(hasSyncedToDatabaseAtom)
+ const isRemote = isRemoteMode()
+
+ const { data: dbProfiles, isLoading: isLoadingDB } = trpc.modelProfiles.list.useQuery(
+ undefined,
+ {
+ enabled: isRemote || !hasSynced, // Always load if remote, or if desktop hasn't synced yet
+ staleTime: Infinity, // Don't refetch automatically
+ }
+ )
+
+ const createMutation = trpc.modelProfiles.create.useMutation()
+ const updateMutation = trpc.modelProfiles.update.useMutation()
+ const deleteMutation = trpc.modelProfiles.delete.useMutation()
+
+ // Sync desktop localStorage profiles to database (one-time)
+ useEffect(() => {
+ if (!isRemote && !hasSynced && !isLoadingDB && dbProfiles) {
+ const syncToDatabase = async () => {
+ try {
+ // Get profiles from localStorage, excluding offline profile
+ const localProfiles = localStorageProfiles.filter(p => !p.isOffline)
+
+ // For each local profile, check if it exists in database
+ for (const localProfile of localProfiles) {
+ const existsInDB = dbProfiles.find((db: any) => db.name === localProfile.name)
+
+ if (!existsInDB) {
+ // Create new profile in database
+ const dbProfile = toDBProfile(localProfile)
+ await createMutation.mutateAsync(dbProfile)
+ console.log("[ModelProfilesSync] Created profile in DB:", localProfile.name)
+ }
+ // Note: We don't sync back from database to localStorage to avoid conflicts
+ // In the future, we can add proper conflict resolution with timestamps
+ }
+
+ setHasSynced(true)
+ console.log("[ModelProfilesSync] Sync completed")
+ } catch (error) {
+ console.error("[ModelProfilesSync] Failed to sync:", error)
+ }
+ }
+
+ syncToDatabase()
+ }
+ }, [isRemote, hasSynced, isLoadingDB, dbProfiles, localStorageProfiles, createMutation])
+
+ // In web mode, use database profiles directly
+ useEffect(() => {
+ if (isRemote && !isLoadingDB && dbProfiles) {
+ // Add offline profile to database profiles
+ const profiles = [
+ OFFLINE_PROFILE,
+ ...dbProfiles.map(fromDBProfile),
+ ]
+ setDatabaseProfiles(profiles)
+ }
+ }, [isRemote, isLoadingDB, dbProfiles, setDatabaseProfiles])
+
+ return {
+ isLoading: isLoadingDB,
+ dbProfiles,
+ createProfile: createMutation.mutateAsync,
+ updateProfile: updateMutation.mutateAsync,
+ deleteProfile: deleteMutation.mutateAsync,
+ }
+}
+
+/**
+ * Combined model profiles atom
+ * - Desktop: Returns localStorage profiles, writes to localStorage
+ * - Web: Returns database profiles, writes are not supported (web mode is read-only)
+ */
+export const modelProfilesAtom = atom(
+ (get) => {
+ const isRemote = isRemoteMode()
+ const localStorageProfiles = get(localStorageModelProfilesAtom)
+ const databaseProfiles = get(databaseProfilesAtom)
+
+ if (isRemote) {
+ // In web mode, use database profiles (with offline profile added)
+ return databaseProfiles || [OFFLINE_PROFILE]
+ }
+
+ // In desktop mode, use localStorage profiles
+ return localStorageProfiles
+ },
+ (get, set, newValue) => {
+ const isRemote = isRemoteMode()
+
+ if (isRemote) {
+ // In web mode, writes are not supported (read-only)
+ // In the future, we could add tRPC mutations to update database
+ console.warn("[modelProfilesAtom] Cannot modify profiles in web mode")
+ return
+ }
+
+ // In desktop mode, write to localStorage
+ set(localStorageModelProfilesAtom, newValue)
+ }
+)
+
+/**
+ * Get active config (considering network status and auto-fallback)
+ * - If auto-offline enabled and no internet, use offline profile
+ * - If specific profile is selected, use it
+ * - Fallback to legacy config if set
+ * - Returns undefined if no custom config found
+ */
+export const activeConfigAtom = atom((get) => {
+ const activeProfileId = get(activeProfileIdAtom)
+ const profiles = get(modelProfilesAtom)
+ const legacyConfig = get(customClaudeConfigAtom)
+ const networkOnline = get(networkOnlineAtom)
+ const autoOffline = get(autoOfflineModeAtom)
+
+ console.log('[activeConfigAtom] Debug:', {
+ activeProfileId,
+ profilesCount: profiles.length,
+ legacyConfigModel: legacyConfig.model,
+ legacyConfigBaseUrl: legacyConfig.baseUrl,
+ })
+
+ // If auto-offline enabled and no internet, use offline profile
+ if (!networkOnline && autoOffline) {
+ const offlineProfile = profiles.find(p => p.isOffline)
+ if (offlineProfile) {
+ console.log('[activeConfigAtom] Using offline profile')
+ return offlineProfile.config
+ }
+ }
+
+ // If specific profile is selected, use it
+ if (activeProfileId) {
+ const profile = profiles.find(p => p.id === activeProfileId)
+ if (profile) {
+ console.log('[activeConfigAtom] Using profile:', profile.name, 'with model:', profile.config.model)
+ return profile.config
+ } else {
+ console.log('[activeConfigAtom] Profile ID set but not found in profiles')
+ }
+ }
+
+ // Fallback to legacy config if set
+ const normalized = normalizeCustomClaudeConfig(legacyConfig)
+ if (normalized) {
+ console.log('[activeConfigAtom] Using normalized legacy config')
+ return normalized
+ }
+
+ console.log('[activeConfigAtom] No custom config found, returning undefined')
+ // No custom config
+ return undefined
+})
diff --git a/src/renderer/lib/atoms/remote-access.ts b/src/renderer/lib/atoms/remote-access.ts
new file mode 100644
index 000000000..e685fa147
--- /dev/null
+++ b/src/renderer/lib/atoms/remote-access.ts
@@ -0,0 +1,18 @@
+// src/renderer/lib/atoms/remote-access.ts
+
+import { atom } from "jotai"
+
+export type RemoteAccessStatus =
+ | { status: "disabled" }
+ | { status: "downloading"; progress: number }
+ | { status: "starting" }
+ | { status: "active"; url: string; pin: string; clients: number }
+ | { status: "error"; message: string }
+
+export const remoteAccessStatusAtom = atom({ status: "disabled" })
+
+export const remoteAccessDialogOpenAtom = atom(false)
+
+// Connection state for remote mode (web client)
+export type ConnectionState = "connecting" | "connected" | "disconnected"
+export const remoteConnectionStateAtom = atom("connected")
diff --git a/src/renderer/lib/hooks/use-remote-connection.ts b/src/renderer/lib/hooks/use-remote-connection.ts
new file mode 100644
index 000000000..f2fb710d3
--- /dev/null
+++ b/src/renderer/lib/hooks/use-remote-connection.ts
@@ -0,0 +1,29 @@
+// src/renderer/lib/hooks/use-remote-connection.ts
+
+import { useEffect } from "react"
+import { useSetAtom } from "jotai"
+import { remoteConnectionStateAtom } from "../atoms/remote-access"
+import { onConnectionStateChange, isRemoteMode } from "../remote-transport"
+
+/**
+ * Hook to track remote connection state
+ * Only active in remote mode (web browser)
+ */
+export function useRemoteConnection(): void {
+ const setConnectionState = useSetAtom(remoteConnectionStateAtom)
+
+ useEffect(() => {
+ // Only track connection in remote mode
+ if (!isRemoteMode()) return
+
+ // Subscribe to connection state changes
+ const unsubscribe = onConnectionStateChange((state) => {
+ console.log("[useRemoteConnection] State changed:", state)
+ setConnectionState(state)
+ })
+
+ return () => {
+ unsubscribe()
+ }
+ }, [setConnectionState])
+}
diff --git a/src/renderer/lib/remote-transport/get-link.ts b/src/renderer/lib/remote-transport/get-link.ts
new file mode 100644
index 000000000..2b07a9629
--- /dev/null
+++ b/src/renderer/lib/remote-transport/get-link.ts
@@ -0,0 +1,62 @@
+// src/renderer/lib/remote-transport/get-link.ts
+// Conditional tRPC link - returns wsLink for browser, ipcLink for Electron
+
+import { wsLink } from "./ws-link"
+import superjson from "superjson"
+
+// Cache the link to avoid re-creation
+let cachedLink: any = null
+let ipcLinkModule: any = null
+
+// Dynamically import ipcLink only in Electron
+// This avoids importing the module in browser where it would throw
+async function loadIpcLink() {
+ if (ipcLinkModule) return ipcLinkModule
+
+ // Check if we're in Electron (electronTRPC global exists)
+ // @ts-ignore
+ if (typeof window !== "undefined" && window.electronTRPC) {
+ try {
+ // Only import if electronTRPC exists
+ const { ipcLink } = await import("trpc-electron/renderer")
+ ipcLinkModule = ipcLink
+ } catch (error) {
+ // If import fails (e.g., in web bundle), fall back to null
+ console.warn("[get-link] Failed to load ipcLink, using wsLink fallback:", error)
+ }
+ }
+
+ return ipcLinkModule
+}
+
+/**
+ * Get the appropriate tRPC link based on environment
+ * - Browser (remote mode): WebSocket link
+ * - Electron: IPC link
+ *
+ * Note: Uses dynamic import to avoid loading trpc-electron in browser
+ */
+export async function getLink() {
+ if (cachedLink) return cachedLink
+
+ // Check if remote mode (no desktopApi means browser/remote)
+ // @ts-ignore
+ const isRemote = typeof window !== "undefined" && !window.desktopApi
+
+ if (isRemote) {
+ // Browser mode - always use wsLink
+ cachedLink = wsLink()
+ } else {
+ // Electron mode - try to dynamically import ipcLink
+ const ipc = await loadIpcLink()
+ if (ipc) {
+ cachedLink = ipc({ transformer: superjson })
+ } else {
+ // Fallback to wsLink if ipcLink failed to load
+ console.warn("[get-link] ipcLink not available, using wsLink fallback")
+ cachedLink = wsLink()
+ }
+ }
+
+ return cachedLink
+}
diff --git a/src/renderer/lib/remote-transport/index.ts b/src/renderer/lib/remote-transport/index.ts
new file mode 100644
index 000000000..4fdcf8f0d
--- /dev/null
+++ b/src/renderer/lib/remote-transport/index.ts
@@ -0,0 +1,378 @@
+// src/renderer/lib/remote-transport/index.ts
+// WebSocket Transport Adapter for Remote Access
+// Replaces IPC calls with WebSocket when running in browser
+
+import superjson from "superjson"
+
+type WSMessageType = "auth" | "trpc" | "api" | "subscribe" | "unsubscribe"
+
+interface WSRequest {
+ id: string
+ type: WSMessageType
+ method?: string
+ params?: unknown
+ pin?: string
+ channel?: string
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "result" | "error" | "subscription"
+ data?: unknown
+ error?: string
+ channel?: string
+}
+
+type PendingRequest = {
+ resolve: (data: unknown) => void
+ reject: (error: Error) => void
+}
+
+let ws: WebSocket | null = null
+let isAuthenticated = false
+let connectionPromise: Promise | null = null
+const pendingRequests = new Map()
+const subscriptionHandlers = new Map void>>()
+let requestId = 0
+
+// Connection state listeners
+type ConnectionState = "connecting" | "connected" | "disconnected"
+const connectionStateListeners = new Set<(state: ConnectionState) => void>()
+let currentConnectionState: ConnectionState = "connecting"
+
+/**
+ * Notify listeners of connection state change
+ */
+function notifyConnectionStateChange(state: ConnectionState) {
+ currentConnectionState = state
+ for (const listener of connectionStateListeners) {
+ try {
+ listener(state)
+ } catch (err) {
+ console.error("[RemoteTransport] Error in connection state listener:", err)
+ }
+ }
+}
+
+/**
+ * Subscribe to connection state changes
+ * Returns unsubscribe function
+ */
+export function onConnectionStateChange(callback: (state: ConnectionState) => void): () => void {
+ connectionStateListeners.add(callback)
+ // Immediately call with current state
+ try {
+ callback(currentConnectionState)
+ } catch (err) {
+ console.error("[RemoteTransport] Error in initial connection state callback:", err)
+ }
+ return () => {
+ connectionStateListeners.delete(callback)
+ }
+}
+
+/**
+ * Get current connection state
+ */
+export function getConnectionState(): ConnectionState {
+ return currentConnectionState
+}
+
+/**
+ * Check if we're running in remote browser mode
+ * Robust check that works even after desktopApi is mocked
+ */
+export function isRemoteMode(): boolean {
+ if (typeof window === "undefined") return false;
+
+ // If we have a stored PIN, we are definitely in remote mode
+ if (sessionStorage.getItem("remote_ws_auth")) return true;
+
+ // Otherwise check if desktopApi is missing (initial load)
+ return !window.desktopApi;
+}
+
+/**
+ * Get stored PIN from sessionStorage
+ */
+function getStoredPin(): string | null {
+ return sessionStorage.getItem("remote_ws_auth")
+}
+
+/**
+ * Connect to WebSocket server and authenticate
+ */
+export function connect(): Promise {
+ if (connectionPromise) return connectionPromise
+ if (ws?.readyState === WebSocket.OPEN && isAuthenticated) {
+ return Promise.resolve()
+ }
+
+ // Notify we're connecting
+ notifyConnectionStateChange("connecting")
+
+ connectionPromise = new Promise((resolve, reject) => {
+ const pin = getStoredPin()
+ if (!pin) {
+ // Redirect to login page
+ notifyConnectionStateChange("disconnected")
+ window.location.href = "/login"
+ reject(new Error("No PIN stored"))
+ return
+ }
+
+ const protocol = location.protocol === "https:" ? "wss:" : "ws:"
+ ws = new WebSocket(`${protocol}//${location.host}`)
+
+ ws.onopen = () => {
+ // Authenticate with stored PIN
+ const authRequest: WSRequest = {
+ id: `auth-${Date.now()}`,
+ type: "auth",
+ pin,
+ }
+ ws!.send(JSON.stringify(authRequest))
+ }
+
+ ws.onmessage = (event) => {
+ const message: WSResponse = JSON.parse(event.data)
+
+ // Handle auth response
+ if (message.type === "auth_success") {
+ console.log("[RemoteTransport] Authentication successful")
+ isAuthenticated = true
+ notifyConnectionStateChange("connected")
+ connectionPromise = null
+ resolve()
+ return
+ }
+
+ if (message.type === "auth_failed") {
+ console.error("[RemoteTransport] Authentication failed")
+ isAuthenticated = false
+ notifyConnectionStateChange("disconnected")
+ sessionStorage.removeItem("remote_ws_auth")
+ window.location.href = "/login"
+ reject(new Error("Authentication failed"))
+ return
+ }
+
+ if (message.type === "auth_required") {
+ console.log("[RemoteTransport] Auth required, waiting for success/fail...")
+ // Don't redirect immediately, give it a chance to process the auth we just sent
+ return
+ }
+
+ // Handle subscription messages
+ if (message.type === "subscription" && message.channel) {
+ const handlers = subscriptionHandlers.get(message.channel)
+ if (handlers) {
+ handlers.forEach((handler) => handler(message.data))
+ }
+ return
+ }
+
+ // Handle request responses
+ if (message.id) {
+ const pending = pendingRequests.get(message.id)
+ if (pending) {
+ pendingRequests.delete(message.id)
+ if (message.type === "error") {
+ pending.reject(new Error(message.error || "Unknown error"))
+ } else {
+ // Deserialize superjson data
+ const data = message.data
+ if (data && typeof data === "object" && "json" in data) {
+ pending.resolve(superjson.deserialize(data as any))
+ } else {
+ pending.resolve(data)
+ }
+ }
+ }
+ }
+ }
+
+ ws.onerror = (error) => {
+ console.error("[RemoteTransport] WebSocket error:", error)
+ notifyConnectionStateChange("disconnected")
+ connectionPromise = null
+ reject(new Error("WebSocket connection failed"))
+ }
+
+ ws.onclose = () => {
+ console.log("[RemoteTransport] WebSocket closed")
+ ws = null
+ isAuthenticated = false
+ connectionPromise = null
+ notifyConnectionStateChange("disconnected")
+ }
+ })
+
+ return connectionPromise
+}
+
+/**
+ * Send a request and wait for response
+ */
+async function sendRequest(type: WSMessageType, method?: string, params?: unknown): Promise {
+ await connect()
+
+ return new Promise((resolve, reject) => {
+ const id = `req-${++requestId}-${Date.now()}`
+ const request: WSRequest = { id, type, method, params }
+
+ pendingRequests.set(id, { resolve, reject })
+
+ // Timeout after 30 seconds
+ setTimeout(() => {
+ if (pendingRequests.has(id)) {
+ pendingRequests.delete(id)
+ reject(new Error("Request timeout"))
+ }
+ }, 30000)
+
+ ws!.send(JSON.stringify(request))
+ })
+}
+
+/**
+ * Call a tRPC procedure
+ */
+export async function trpcCall(method: string, params?: unknown): Promise {
+ return sendRequest("trpc", method, params)
+}
+
+/**
+ * Subscribe to a channel
+ */
+export async function subscribe(channel: string, handler: (data: unknown) => void): Promise<() => void> {
+ await connect()
+
+ // Register handler
+ if (!subscriptionHandlers.has(channel)) {
+ subscriptionHandlers.set(channel, new Set())
+ // Send subscribe request
+ await sendRequest("subscribe", undefined, undefined)
+ const id = `sub-${++requestId}`
+ const request: WSRequest = { id, type: "subscribe", channel }
+ ws!.send(JSON.stringify(request))
+ }
+ subscriptionHandlers.get(channel)!.add(handler)
+
+ // Return unsubscribe function
+ return () => {
+ const handlers = subscriptionHandlers.get(channel)
+ if (handlers) {
+ handlers.delete(handler)
+ if (handlers.size === 0) {
+ subscriptionHandlers.delete(channel)
+ // Send unsubscribe request
+ if (ws?.readyState === WebSocket.OPEN) {
+ const id = `unsub-${++requestId}`
+ const request: WSRequest = { id, type: "unsubscribe", channel }
+ ws.send(JSON.stringify(request))
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Disconnect from server
+ */
+export function disconnect(): void {
+ if (ws) {
+ ws.close()
+ ws = null
+ }
+ isAuthenticated = false
+ pendingRequests.clear()
+ subscriptionHandlers.clear()
+}
+
+/**
+ * Create a remote desktopApi that bridges to WebSocket
+ */
+export function createRemoteDesktopApi(): Partial {
+ return {
+ platform: "darwin" as NodeJS.Platform, // Will be overridden by server
+ arch: "arm64",
+
+ // These will use tRPC calls over WebSocket
+ getVersion: async () => {
+ const result = await trpcCall("external.getAppVersion")
+ return result as string
+ },
+
+ isPackaged: async () => true,
+
+ // Window controls - no-op in browser
+ windowMinimize: async () => {},
+ windowMaximize: async () => {},
+ windowClose: async () => { window.close() },
+ windowIsMaximized: async () => false,
+ windowToggleFullscreen: async () => {},
+ windowIsFullscreen: async () => false,
+ setTrafficLightVisibility: async () => {},
+
+ // Clipboard
+ clipboardWrite: async (text: string) => {
+ await navigator.clipboard.writeText(text)
+ },
+ clipboardRead: async () => {
+ return navigator.clipboard.readText()
+ },
+
+ // Open external links
+ openExternal: async (url: string) => {
+ window.open(url, "_blank")
+ },
+
+ // Remote access - return disabled status (we're already remote)
+ getRemoteAccessStatus: async () => ({ status: "disabled" as const }),
+ enableRemoteAccess: async () => ({ status: "disabled" as const }),
+ disableRemoteAccess: async () => ({ status: "disabled" as const }),
+ onRemoteAccessStatusChange: () => () => {},
+
+ // Notifications
+ showNotification: async (options: { title: string; body: string }) => {
+ if ("Notification" in window && Notification.permission === "granted") {
+ new Notification(options.title, { body: options.body })
+ }
+ },
+
+ // Auth - no-op in remote mode
+ getUser: async () => null,
+ isAuthenticated: async () => false,
+ logout: async () => {},
+ startAuthFlow: async () => {},
+ submitAuthCode: async () => {},
+ getAuthToken: async () => null,
+
+ // Events - return no-op unsubscribe functions
+ onFullscreenChange: () => () => {},
+ onFocusChange: () => () => {},
+ onUpdateChecking: () => () => {},
+ onUpdateAvailable: () => () => {},
+ onUpdateNotAvailable: () => () => {},
+ onUpdateProgress: () => () => {},
+ onUpdateDownloaded: () => () => {},
+ onUpdateError: () => () => {},
+ onUpdateManualCheck: () => () => {},
+ onAuthSuccess: () => () => {},
+ onAuthError: () => () => {},
+ onShortcutNewAgent: () => () => {},
+ onFileChanged: () => () => {},
+ onGitStatusChanged: () => () => {},
+
+ // Git Watcher
+ subscribeToGitWatcher: async () => {},
+ unsubscribeFromGitWatcher: async () => {},
+
+ // Storage
+ getAppDataPath: async () => "/tmp/1code-remote",
+
+ // System
+ setAnalyticsOptOut: async () => {},
+ }
+}
diff --git a/src/renderer/lib/remote-transport/init.ts b/src/renderer/lib/remote-transport/init.ts
new file mode 100644
index 000000000..905bc0403
--- /dev/null
+++ b/src/renderer/lib/remote-transport/init.ts
@@ -0,0 +1,56 @@
+// src/renderer/lib/remote-transport/init.ts
+// Initialize remote transport when running in browser
+
+import { isRemoteMode, connect, createRemoteDesktopApi } from "./index"
+
+/**
+ * Initialize remote mode if running in browser without Electron
+ */
+export async function initRemoteTransport(): Promise {
+ if (!isRemoteMode()) {
+ console.log("[RemoteTransport] Running in Electron, skipping remote init")
+ return false
+ }
+
+ console.log("[RemoteTransport] Running in remote browser mode")
+
+ // Check if we have stored auth
+ const pin = sessionStorage.getItem("remote_ws_auth")
+ if (!pin) {
+ // Redirect to login if not on login page
+ if (!window.location.pathname.includes("/login") && window.location.pathname !== "/") {
+ window.location.href = "/login"
+ }
+ return false
+ }
+
+ try {
+ // Connect to WebSocket
+ await connect()
+
+ // Create and expose remote desktopApi
+ const remoteApi = createRemoteDesktopApi()
+ ;(window as any).desktopApi = remoteApi
+
+ // Also expose webUtils stub
+ ;(window as any).webUtils = {
+ getPathForFile: () => "",
+ }
+
+ console.log("[RemoteTransport] Remote transport initialized")
+ return true
+ } catch (error) {
+ console.error("[RemoteTransport] Failed to initialize:", error)
+ // Clear auth and redirect to login
+ sessionStorage.removeItem("remote_ws_auth")
+ window.location.href = "/login"
+ return false
+ }
+}
+
+/**
+ * Check if remote mode is active
+ */
+export function isRemoteModeActive(): boolean {
+ return isRemoteMode() && sessionStorage.getItem("remote_ws_auth") !== null
+}
diff --git a/src/renderer/lib/remote-transport/trpc-client.ts b/src/renderer/lib/remote-transport/trpc-client.ts
new file mode 100644
index 000000000..dfba497e1
--- /dev/null
+++ b/src/renderer/lib/remote-transport/trpc-client.ts
@@ -0,0 +1,39 @@
+// src/renderer/lib/remote-transport/trpc-client.ts
+// tRPC Client for Remote Access via WebSocket
+
+import { trpcCall } from "./index"
+
+type TRPCProxy = {
+ [key: string]: TRPCProxy & {
+ query: (input?: unknown) => Promise
+ mutate: (input?: unknown) => Promise
+ }
+}
+
+/**
+ * Create a proxy that intercepts tRPC calls and sends them via WebSocket
+ */
+function createTRPCProxy(path: string[] = []): TRPCProxy {
+ return new Proxy(() => {}, {
+ get(_, prop: string) {
+ if (prop === "query" || prop === "mutate") {
+ return async (input?: unknown) => {
+ const method = path.join(".")
+ return trpcCall(method, input)
+ }
+ }
+ return createTRPCProxy([...path, prop])
+ },
+ apply(_, __, args) {
+ // Handle direct calls like trpc.projects.list()
+ const method = path.join(".")
+ return trpcCall(method, args[0])
+ },
+ }) as TRPCProxy
+}
+
+/**
+ * Remote tRPC client
+ * Usage: remoteTrpc.projects.list.query() or remoteTrpc.chats.create.mutate({ ... })
+ */
+export const remoteTrpcClient = createTRPCProxy()
diff --git a/src/renderer/lib/remote-transport/ws-link.ts b/src/renderer/lib/remote-transport/ws-link.ts
new file mode 100644
index 000000000..ff53dce20
--- /dev/null
+++ b/src/renderer/lib/remote-transport/ws-link.ts
@@ -0,0 +1,281 @@
+// src/renderer/lib/remote-transport/ws-link.ts
+// WebSocket tRPC Link for Remote Access
+
+import { TRPCLink } from "@trpc/client"
+import { observable } from "@trpc/server/observable"
+import type { AppRouter } from "../../../main/lib/trpc/routers"
+import superjson from "superjson"
+
+type WSMessageType = "auth" | "trpc" | "api" | "subscribe" | "unsubscribe"
+
+interface WSRequest {
+ id: string
+ type: WSMessageType
+ method?: string
+ params?: unknown
+ pin?: string
+ channel?: string
+}
+
+interface WSResponse {
+ id: string | null
+ type: "auth_required" | "auth_success" | "auth_failed" | "result" | "error" | "subscription"
+ data?: unknown
+ error?: string
+ channel?: string
+}
+
+type PendingRequest = {
+ resolve: (data: unknown) => void
+ reject: (error: Error) => void
+}
+
+type SubscriptionHandler = {
+ onData: (data: unknown) => void
+ onError: (error: Error) => void
+ onComplete: () => void
+}
+
+let ws: WebSocket | null = null
+let isAuthenticated = false
+let connectionPromise: Promise | null = null
+const pendingRequests = new Map()
+const subscriptionHandlers = new Map()
+let requestId = 0
+
+/**
+ * Get stored PIN from sessionStorage
+ */
+function getStoredPin(): string | null {
+ return sessionStorage.getItem("remote_ws_auth")
+}
+
+/**
+ * Connect to WebSocket server and authenticate
+ */
+function connect(): Promise {
+ if (connectionPromise) return connectionPromise
+ if (ws?.readyState === WebSocket.OPEN && isAuthenticated) {
+ return Promise.resolve()
+ }
+
+ connectionPromise = new Promise((resolve, reject) => {
+ const pin = getStoredPin()
+ if (!pin) {
+ window.location.href = "/login"
+ reject(new Error("No PIN stored"))
+ return
+ }
+
+ const protocol = location.protocol === "https:" ? "wss:" : "ws:"
+ ws = new WebSocket(`${protocol}//${location.host}`)
+
+ ws.onopen = () => {
+ const authRequest: WSRequest = {
+ id: `auth-${Date.now()}`,
+ type: "auth",
+ pin,
+ }
+ ws!.send(JSON.stringify(authRequest))
+ }
+
+ ws.onmessage = (event) => {
+ const message: WSResponse = JSON.parse(event.data)
+
+ // Handle auth response
+ if (message.type === "auth_success") {
+ console.log("[ws-link] Authentication successful")
+ isAuthenticated = true
+ connectionPromise = null
+ resolve()
+ return
+ }
+
+ if (message.type === "auth_failed") {
+ console.error("[ws-link] Authentication failed")
+ isAuthenticated = false
+ sessionStorage.removeItem("remote_ws_auth")
+ window.location.href = "/login"
+ reject(new Error("Authentication failed"))
+ return
+ }
+
+ if (message.type === "auth_required") {
+ console.log("[ws-link] Auth required, waiting for success/fail...")
+ return
+ }
+
+ // Handle subscription messages
+ if (message.id) {
+ const subHandler = subscriptionHandlers.get(message.id)
+ if (subHandler) {
+ if (message.type === "subscription" && message.data !== undefined) {
+ console.log("[ws-link] Subscription data:", message.id, message.data)
+ subHandler.onData(message.data)
+ } else if (message.type === "error") {
+ console.error("[ws-link] Subscription error:", message.id, message.error)
+ subHandler.onError(new Error(message.error || "Subscription error"))
+ } else if (message.type === "result") {
+ // Check if this is the actual completion or just the initial subscription confirmation
+ const resultData = message.data as { completed?: boolean; subscribed?: boolean } | undefined
+ if (resultData?.completed) {
+ console.log("[ws-link] Subscription complete:", message.id)
+ subHandler.onComplete()
+ subscriptionHandlers.delete(message.id)
+ } else if (resultData?.subscribed) {
+ // Initial subscription confirmation - keep the handler active
+ console.log("[ws-link] Subscription confirmed:", message.id)
+ }
+ }
+ return
+ }
+
+ // Handle normal query/mutation responses
+ const pending = pendingRequests.get(message.id)
+ if (pending) {
+ pendingRequests.delete(message.id)
+ if (message.type === "error") {
+ pending.reject(new Error(message.error || "Unknown error"))
+ } else {
+ const data = message.data
+ if (data && typeof data === "object" && "json" in data) {
+ pending.resolve(superjson.deserialize(data as any))
+ } else {
+ pending.resolve(data)
+ }
+ }
+ }
+ }
+ }
+
+ ws.onerror = () => {
+ connectionPromise = null
+ reject(new Error("WebSocket connection failed"))
+ }
+
+ ws.onclose = () => {
+ ws = null
+ isAuthenticated = false
+ connectionPromise = null
+ // Clear all pending requests
+ for (const [id, pending] of pendingRequests) {
+ pending.reject(new Error("Connection closed"))
+ }
+ pendingRequests.clear()
+ // Clear all subscriptions
+ for (const handler of subscriptionHandlers.values()) {
+ handler.onComplete()
+ }
+ subscriptionHandlers.clear()
+ }
+ })
+
+ return connectionPromise
+}
+
+/**
+ * Send a tRPC request via WebSocket (for queries/mutations)
+ */
+async function sendTRPCRequest(method: string, params?: unknown): Promise {
+ await connect()
+
+ return new Promise((resolve, reject) => {
+ const id = `trpc-${++requestId}-${Date.now()}`
+ const request: WSRequest = { id, type: "trpc", method, params }
+
+ pendingRequests.set(id, { resolve, reject })
+
+ setTimeout(() => {
+ if (pendingRequests.has(id)) {
+ pendingRequests.delete(id)
+ reject(new Error("Request timeout"))
+ }
+ }, 30000)
+
+ ws!.send(JSON.stringify(request))
+ })
+}
+
+/**
+ * Create a WebSocket-based tRPC link
+ */
+export function wsLink(): TRPCLink {
+ return () => {
+ return ({ op }) => {
+ return observable((observer) => {
+ const { path, input, type } = op
+
+ // Convert path to method name (e.g., "projects.list")
+ const method = path
+
+ console.log("[ws-link] tRPC call:", { type, path: method })
+
+ // Handle subscriptions differently from queries/mutations
+ if (type === "subscription") {
+ console.log("[ws-link] Starting subscription:", method)
+
+ const id = `sub-${++requestId}-${Date.now()}`
+
+ // Set up subscription handler
+ const handler: SubscriptionHandler = {
+ onData: (data) => {
+ console.log("[ws-link] Subscription data received:", method, data)
+ observer.next({
+ result: {
+ type: "data",
+ data: data,
+ },
+ })
+ },
+ onError: (error) => {
+ console.error("[ws-link] Subscription error:", method, error)
+ observer.error(error)
+ },
+ onComplete: () => {
+ console.log("[ws-link] Subscription complete:", method)
+ observer.complete()
+ },
+ }
+
+ subscriptionHandlers.set(id, handler)
+
+ // Wait for connection, then send subscription request
+ connect().then(() => {
+ const request: WSRequest = { id, type: "trpc", method, params: input }
+ ws!.send(JSON.stringify(request))
+ console.log("[ws-link] Subscription request sent:", id, method)
+ }).catch((error) => {
+ console.error("[ws-link] Failed to connect for subscription:", error)
+ subscriptionHandlers.delete(id)
+ observer.error(error)
+ })
+
+ // Return cleanup function
+ return () => {
+ console.log("[ws-link] Unsubscribing:", method, id)
+ subscriptionHandlers.delete(id)
+ }
+ }
+
+ // Regular query/mutation
+ sendTRPCRequest(method, input)
+ .then((result) => {
+ observer.next({
+ result: {
+ type: "data",
+ data: result,
+ },
+ })
+ observer.complete()
+ })
+ .catch((error) => {
+ observer.error(error)
+ })
+
+ return () => {
+ // Cleanup for query/mutation (none needed)
+ }
+ })
+ }
+ }
+}
diff --git a/src/renderer/lib/tray-events.ts b/src/renderer/lib/tray-events.ts
new file mode 100644
index 000000000..03d32aedd
--- /dev/null
+++ b/src/renderer/lib/tray-events.ts
@@ -0,0 +1,37 @@
+import { useEffect } from "react"
+import { useSetAtom } from "jotai"
+import { desktopViewAtom } from "../features/agents/atoms"
+
+/**
+ * Hook to listen for tray events and handle them
+ * These events are dispatched from the main process via executeJavaScript
+ */
+export function useTrayEvents() {
+ const setDesktopView = useSetAtom(desktopViewAtom)
+
+ useEffect(() => {
+ // Handle workspace selection from tray
+ const handleWorkspaceOpen = (event: CustomEvent) => {
+ const { projectId } = event.detail
+ console.log("[Tray Events] Workspace opened:", projectId)
+
+ // The project is already set in localStorage by the tray handler
+ // Window will reload automatically
+ }
+
+ // Handle settings open from tray
+ const handleSettingsOpen = () => {
+ console.log("[Tray Events] Opening settings via atom")
+ // Set desktopView to 'settings' which opens the settings dialog
+ setDesktopView("settings")
+ }
+
+ window.addEventListener("tray:open-workspace", handleWorkspaceOpen as EventListener)
+ window.addEventListener("tray:open-settings", handleSettingsOpen)
+
+ return () => {
+ window.removeEventListener("tray:open-workspace", handleWorkspaceOpen as EventListener)
+ window.removeEventListener("tray:open-settings", handleSettingsOpen)
+ }
+ }, [setDesktopView])
+}
diff --git a/src/renderer/lib/trpc.ts b/src/renderer/lib/trpc.ts
index 684d64c03..36b79c224 100644
--- a/src/renderer/lib/trpc.ts
+++ b/src/renderer/lib/trpc.ts
@@ -1,17 +1,96 @@
import { createTRPCReact } from "@trpc/react-query"
import { createTRPCProxyClient } from "@trpc/client"
-import { ipcLink } from "trpc-electron/renderer"
import type { AppRouter } from "../../main/lib/trpc/routers"
import superjson from "superjson"
+import { wsLink } from "./remote-transport/ws-link"
+
+/**
+ * Check if we're in remote/browser mode (no electronTRPC global)
+ * Must be called at runtime, not at module load time
+ */
+function checkIsRemoteMode(): boolean {
+ // @ts-ignore
+ return typeof window !== "undefined" && !window.electronTRPC
+}
/**
* React hooks for tRPC
*/
export const trpc = createTRPCReact()
+// Vanilla client - initialized lazily
+let _trpcClient: ReturnType> | null = null
+let _clientPromise: Promise>> | null = null
+let _initStarted = false
+
+/**
+ * Initialize the vanilla client
+ */
+async function initVanillaClient(): Promise>> {
+ if (_trpcClient) return _trpcClient
+ if (_clientPromise) return _clientPromise
+
+ _clientPromise = (async () => {
+ const isRemote = checkIsRemoteMode()
+ console.log("[trpc] Initializing vanilla client, isRemote:", isRemote)
+
+ let link: any
+ if (isRemote) {
+ link = wsLink()
+ } else {
+ const { ipcLink } = await import("trpc-electron/renderer")
+ link = ipcLink({ transformer: superjson })
+ }
+
+ _trpcClient = createTRPCProxyClient({ links: [link] })
+ console.log("[trpc] Vanilla client initialized")
+ return _trpcClient
+ })()
+
+ return _clientPromise
+}
+
+// Start initialization immediately (will run after module loads)
+if (typeof window !== "undefined") {
+ // Use setTimeout to ensure this runs after preload has set up electronTRPC
+ setTimeout(() => {
+ if (!_initStarted) {
+ _initStarted = true
+ initVanillaClient().catch(console.error)
+ }
+ }, 0)
+}
+
+/**
+ * Get vanilla tRPC client (async to handle dynamic link loading)
+ */
+export async function getTrpcClient() {
+ if (_trpcClient) return _trpcClient
+ if (!_initStarted) {
+ _initStarted = true
+ return initVanillaClient()
+ }
+ return _clientPromise!
+}
+
/**
* Vanilla client for use outside React components (stores, utilities)
+ * Returns a proxy that delegates to the real client once initialized
*/
-export const trpcClient = createTRPCProxyClient({
- links: [ipcLink({ transformer: superjson })],
+export const trpcClient = new Proxy({} as ReturnType>, {
+ get(_target, prop) {
+ if (_trpcClient) {
+ return (_trpcClient as any)[prop]
+ }
+ // Return a proxy that waits for initialization
+ return new Proxy({} as any, {
+ get(_t, subProp) {
+ // This handles trpcClient.git.query(...) pattern
+ return async (...args: any[]) => {
+ const client = await getTrpcClient()
+ return (client as any)[prop][subProp](...args)
+ }
+ },
+ })
+ },
})