From de138ee69b5a5cc7e7d72ba1cd6fb9e50082d98a Mon Sep 17 00:00:00 2001 From: divyeshio <79130336+divyeshio@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:25:00 +0530 Subject: [PATCH 1/6] refactor: remove unused command creation functions from flat.ts --- registry/commandly/utils/flat.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/registry/commandly/utils/flat.ts b/registry/commandly/utils/flat.ts index f2b24c0..2e4ed6a 100644 --- a/registry/commandly/utils/flat.ts +++ b/registry/commandly/utils/flat.ts @@ -93,16 +93,6 @@ export const exportToStructuredJSON = (tool: Tool) => { }; }; -export const createNewCommand = (parentKey?: string): Command => { - const name = randomCommandName(); - return { - key: slugify(name), - parentCommandKey: parentKey, - name, - isDefault: false, - sortOrder: 1, - }; -}; export const createNewParameter = (isGlobal: boolean, commandKey?: string): Parameter => { return { @@ -116,16 +106,6 @@ export const createNewParameter = (isGlobal: boolean, commandKey?: string): Para }; }; -export const randomCommandName = () => { - const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - let result = ""; - const charactersLength = characters.length; - for (let i = 0; i < 7; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; -}; - const isEmpty = (value: object | null | undefined): boolean => { if (value == null) return true; if (Array.isArray(value)) return value.length === 0; From d51155d829d602e1eb9442e121eb29be36c37718 Mon Sep 17 00:00:00 2001 From: divyeshio <79130336+divyeshio@users.noreply.github.com> Date: Wed, 15 Apr 2026 22:34:59 +0530 Subject: [PATCH 2/6] feat: enhance parameter list with context selection and pending changes tracking - Added support for context selection in the ParameterList component, allowing multi-select functionality with Ctrl/Cmd and Shift keys. - Integrated pending changes tracking to highlight added, updated, and removed parameters. - Updated UI to reflect changes with visual indicators for parameter states. - Refactored related components to accommodate new features and improve maintainability. feat: update preview tabs to handle original tool state - Modified PreviewTabs to include originalTool for better comparison and state management. - Adjusted JsonOutput component to accept originalTool for enhanced functionality. refactor: improve prompt generation logic for better context handling - Refactored generatePrompt function to support multiple selected commands and parameters. - Enhanced context block generation to focus on relevant commands and parameters. feat: implement tool editing and application utilities - Created utility functions for applying JSON merge patches and managing tool edits. - Added functions for reading tool definitions and finalizing edits with user approval. feat: add progress and spinner components for better loading indicators - Introduced Progress and Spinner components to enhance user experience during loading states. fix: update CSS for dark mode compatibility - Adjusted CSS to ensure proper styling for dark mode, improving readability. refactor: enhance AI key management with encryption - Implemented IndexedDB for secure storage of AI keys, utilizing encryption for sensitive data. - Updated useAIKeys hook to handle encrypted key storage and retrieval. --- bun.lock | 197 +- package.json | 29 +- public/specification/flat.json | 70 +- public/specification/nested.json | 65 +- public/tools.json | 91 +- .../__tests__/tool-renderer.test.tsx | 30 + registry/commandly/json-output.tsx | 122 +- registry/commandly/tool-renderer.tsx | 6 +- registry/commandly/types/flat.ts | 60 + registry/commandly/types/nested.ts | 43 + scripts/generate-tools-json.ts | 2 +- .../ai-elements/chain-of-thought.tsx | 222 +++ src/components/ai-elements/context.tsx | 409 +++++ src/components/ai-elements/prompt-input.tsx | 1463 +++++++++++++++ src/components/ai-elements/reasoning.tsx | 221 +++ src/components/tool-card.tsx | 9 +- src/components/tool-editor/ai-chat.tsx | 1586 +++++++++++------ .../tool-editor/api-key-settings.tsx | 144 ++ src/components/tool-editor/command-tree.tsx | 67 +- .../tool-editor/dialogs/command-dialog.tsx | 2 +- src/components/tool-editor/model-picker.tsx | 334 ++++ src/components/tool-editor/parameter-list.tsx | 135 +- src/components/tool-editor/preview-tabs.tsx | 8 +- src/components/tool-editor/prompt.ts | 75 +- .../tool-editor/tool-editor.context.tsx | 39 +- src/components/tool-editor/tool-editor.tsx | 35 +- src/components/tool-editor/tools.ts | 141 ++ src/components/ui/progress.tsx | 31 + src/components/ui/spinner.tsx | 10 + src/index.css | 8 +- src/lib/ai-keys.ts | 98 +- 31 files changed, 5018 insertions(+), 734 deletions(-) create mode 100644 src/components/ai-elements/chain-of-thought.tsx create mode 100644 src/components/ai-elements/context.tsx create mode 100644 src/components/ai-elements/prompt-input.tsx create mode 100644 src/components/ai-elements/reasoning.tsx create mode 100644 src/components/tool-editor/api-key-settings.tsx create mode 100644 src/components/tool-editor/model-picker.tsx create mode 100644 src/components/tool-editor/tools.ts create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/spinner.tsx diff --git a/bun.lock b/bun.lock index 1e0e21c..7034417 100644 --- a/bun.lock +++ b/bun.lock @@ -5,8 +5,8 @@ "": { "name": "commandly", "dependencies": { - "@ai-sdk/anthropic": "^3.0.68", - "@ai-sdk/google": "^3.0.60", + "@ai-sdk/anthropic": "^3.0.69", + "@ai-sdk/google": "^3.0.62", "@ai-sdk/groq": "^3.0.35", "@ai-sdk/mistral": "^3.0.30", "@ai-sdk/openai": "^3.0.52", @@ -24,11 +24,11 @@ "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", "@tanstack/react-pacer": "^0.21.1", - "@tanstack/react-query": "^5.96.2", - "@tanstack/react-router": "1.168.10", + "@tanstack/react-query": "^5.99.0", + "@tanstack/react-router": "1.168.18", "@tanstack/react-router-with-query": "1.130.17", - "@tanstack/react-start": "^1.167.16", - "ai": "^6.0.154", + "@tanstack/react-start": "^1.167.32", + "ai": "^6.0.158", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -46,29 +46,30 @@ "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.2", "tailwindcss-animate": "^1.0.7", + "tokenlens": "^1.3.1", "tw-animate-css": "^1.4.0", "use-stick-to-bottom": "^1.1.3", "zod": "^4.3.6", }, "devDependencies": { "@mdx-js/rollup": "^3.1.1", - "@tanstack/react-query-devtools": "^5.96.2", - "@tanstack/react-router-devtools": "1.166.11", - "@tanstack/router-plugin": "1.167.12", + "@tanstack/react-query-devtools": "^5.99.0", + "@tanstack/react-router-devtools": "1.166.13", + "@tanstack/router-plugin": "1.167.18", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/bun": "^1.3.11", + "@types/bun": "^1.3.12", "@types/jest": "^30.0.0", "@types/json-schema": "^7.0.15", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript/native-preview": "^7.0.0-dev.20260408.1", + "@typescript/native-preview": "^7.0.0-dev.20260412.1", "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "4.1.2", + "@vitest/coverage-v8": "4.1.4", "ajv": "^8.18.0", "jsdom": "^29.0.2", - "oxfmt": "^0.43.0", + "oxfmt": "^0.44.0", "oxlint": "^1.59.0", "oxlint-tsgolint": "^0.20.0", "rehype-pretty-code": "^0.14.3", @@ -78,7 +79,7 @@ "typescript": "^6.0.2", "typescript-json-schema": "^0.67.1", "vite": "^8.0.8", - "vitest": "^4.1.3", + "vitest": "^4.1.4", "vitest-browser-react": "^2.2.0", }, }, @@ -86,11 +87,11 @@ "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.68", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BAd+fmgYoJMmGw0/uV+jRlXX60PyGxelA6Clp4cK/NI0dsyv9jOOwzQmKNaz2nwb+Jz7HqI7I70KK4XtU5EcXQ=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.94", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-uDDwLZhCkvC89crVS3S90D5L7AcVN8WriGuYVNYgVAaVcvy3Mthy3R9ICfzG75BObhz6pm2FWnhxDfNRK+t69Q=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.95", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ=="], - "@ai-sdk/google": ["@ai-sdk/google@3.0.60", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ye/hG0LeO24VmjLbfgkFZV8V8k/l4nVBODutpJQkFPyUiGOCbFtFUTgxSeC7+njrk5+HhgyHrzJay4zmhwMH+w=="], + "@ai-sdk/google": ["@ai-sdk/google@3.0.62", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cC9HAjR5WZxjqGyEJrJqFTlVqyPE9UOFmmGdf5dINaimgfPmzqXYN1qTYEJ+1knbyTVsNMub0KAF5SOqqtO8IQ=="], "@ai-sdk/groq": ["@ai-sdk/groq@3.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LXoPwSKaqXst9LyLN2J7gK8n7RldQLbP2zsnBYxXcOsXKrtceksqtbsmGXujvab2TM9FisquAw/ZG2hTbD5vnQ=="], @@ -110,9 +111,9 @@ "@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=="], - "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.8", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-OISPR9c2uPo23rUdvfEQiLPjoMLOpEeLNnP5iGkxr6tDDxJd3NjD+6fxY0mdaMbIPUjFGL4HFOJqLvow5q4aqQ=="], + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.10", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww=="], - "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.0.8", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-erMO6FgtM02dC24NGm0xufMzWz5OF0wXKR7BpvGD973bq/GbmR8/DbxNZbj0YevQ5hlToJaWSVK/G9/NDgGEVw=="], + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.0.9", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg=="], "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], @@ -198,13 +199,13 @@ "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="], - "@csstools/css-calc": ["@csstools/css-calc@3.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ=="], + "@csstools/css-calc": ["@csstools/css-calc@3.2.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w=="], - "@csstools/css-color-parser": ["@csstools/css-color-parser@4.0.2", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw=="], + "@csstools/css-color-parser": ["@csstools/css-color-parser@4.1.0", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.2.0" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ=="], "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], - "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.2", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA=="], + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.3", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg=="], "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], @@ -372,43 +373,43 @@ "@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], - "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.43.0", "", { "os": "android", "cpu": "arm" }, "sha512-CgU2s+/9hHZgo0IxVxrbMPrMj+tJ6VM3mD7Mr/4oiz4FNTISLoCvRmB5nk4wAAle045RtRjd86m673jwPyb1OQ=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.44.0", "", { "os": "android", "cpu": "arm" }, "sha512-5UvghMd9SA/yvKTWCAxMAPXS1d2i054UeOf4iFjZjfayTwCINcC3oaSXjtbZfCaEpxgJod7XiOjTtby5yEv/BQ=="], - "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.43.0", "", { "os": "android", "cpu": "arm64" }, "sha512-T9OfRwjA/EdYxAqbvR7TtqLv5nIrwPXuCtTwOHtS7aR9uXyn74ZYgzgTo6/ZwvTq9DY4W+DsV09hB2EXgn9EbA=="], + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.44.0", "", { "os": "android", "cpu": "arm64" }, "sha512-IVudM1BWfvrYO++Khtzr8q9n5Rxu7msUvoFMqzGJVdX7HfUXUDHwaH2zHZNB58svx2J56pmCUzophyaPFkcG/A=="], - "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.43.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-o3i49ZUSJWANzXMAAVY1wnqb65hn4JVzwlRQ5qfcwhRzIA8lGVaud31Q3by5ALHPrksp5QEaKCQF9aAS3TXpZA=="], + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.44.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eWCLAIKAHfx88EqEP1Ga2yz7qVcqDU5lemn4xck+07bH182hDdprOHjbogyk0In1Djys3T0/pO2JepFnRJ41Mg=="], - "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.43.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-vWECzzCFkb0kK6jaHjbtC5sC3adiNWtqawFCxhpvsWlzVeKmv5bNvkB4nux+o4JKWTpHCM57NDK/MeXt44txmA=="], + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.44.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-eHTBznHLM49++dwz07MblQ2cOXyIgeedmE3Wgy4ptUESj38/qYZyRi1MPwC9olQJWssMeY6WI3UZ7YmU5ggvyQ=="], - "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.43.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-rgz8JpkKiI/umOf7fl9gwKyQasC8bs5SYHy6g7e4SunfLBY3+8ATcD5caIg8KLGEtKFm5ujKaH8EfjcmnhzTLg=="], + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.44.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jLMmbj0u0Ft43QpkUVr/0v1ZfQCGWAvU+WznEHcN3wZC/q6ox7XeSJtk9P36CCpiDSUf3sGnzbIuG1KdEMEDJQ=="], - "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-nWYnF3vIFzT4OM1qL/HSf1Yuj96aBuKWSaObXHSWliwAk2rcj7AWd6Lf7jowEBQMo4wCZVnueIGw/7C4u0KTBQ=="], + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-n+A/u/ByK1qV8FVGOwyaSpw5NPNl0qlZfgTBqHeGIqr8Qzq1tyWZ4lAaxPoe5mZqE3w88vn3+jZtMxriHPE7tg=="], - "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-sFg+NWJbLfupYTF4WELHAPSnLPOn1jiDZ33Z1jfDnTaA+cC3iB35x0FMMZTFdFOz3icRIArncwCcemJFGXu6TQ=="], + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-5eax+FkxyCqAi3Rw0mrZFr7+KTt/XweFsbALR+B5ljWBLBl8nHe4ADrUnb1gLEfQCJLl+Ca5FIVD4xEt95AwIw=="], - "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-MelWqv68tX6wZEILDrTc9yewiGXe7im62+5x0bNXlCYFOZdA+VnYiJfAihbROsZ5fm90p9C3haFrqjj43XnlAA=="], + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-58l8JaHxSGOmOMOG2CIrNsnkRJAj0YcHQCmvNACniOa/vd1iRHhlPajczegzS5jwMENlqgreyiTR9iNlke8qCw=="], - "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ROaWfYh+6BSJ1Arwy5ujijTlwnZetxDxzBpDc1oBR4d7rfrPBqzeyjd5WOudowzQUgyavl2wEpzn1hw3jWcqLA=="], + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AlObQIXyVRZ96LbtVljtFq0JqH5B92NU+BQeDFrXWBUWlCKAM0wF5GLfIhCLT5kQ3Sl+U0YjRJ7Alqj5hGQaCg=="], - "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.43.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-PJRs/uNxmFipJJ8+SyKHh7Y7VZIKQicqrrBzvfyM5CtKi8D7yZKTwUOZV3ffxmiC2e7l1SDJpkBEOyue5NAFsg=="], + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.44.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-YcFE8/q/BbrCiIiM5piwbkA6GwJc5QqhMQp2yDrqQ2fuVkZ7CInb1aIijZ/k8EXc72qXMSwKpVlBv1w/MsGO/A=="], - "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-j6biGAgzIhj+EtHXlbNumvwG7XqOIdiU4KgIWRXAEj/iUbHKukKW8eXa4MIwpQwW1YkxovduKtzEAPnjlnAhVQ=="], + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-eOdzs6RqkRzuqNHUX5C8ISN5xfGh4xDww8OEd9YAmc3OWN8oAe5bmlIqQ+rrHLpv58/0BuU48bxkhnIGjA/ATQ=="], - "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-RYWxAcslKxvy7yri24Xm9cmD0RiANaiEPs007EFG6l9h1ChM69Q5SOzACaCoz4Z9dEplnhhneeBaTWMEdpgIbA=="], + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-YBgNTxntD/QvlFUfgvh8bEdwOhXiquX8gaofZJAwYa/Xp1S1DQrFVZEeck7GFktr24DztsSp8N8WtWCBwxs0Hw=="], - "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.43.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-DT6Q8zfQQy3jxpezAsBACEHNUUixKSYTwdXeXojNHe4DQOoxjPdjr3Szu6BRNjxLykZM/xMNmp9ElOIyDppwtw=="], + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.44.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-GLIh1R6WHWshl/i4QQDNgj0WtT25aRO4HNUWEoitxiywyRdhTFmFEYT2rXlcl9U6/26vhmOqG5cRlMLG3ocaIA=="], - "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-R8Yk7iYcuZORXmCfFZClqbDxRZgZ9/HEidUuBNdoX8Ptx07cMePnMVJ/woB84lFIDjh2ROHVaOP40Ds3rBXFqg=="], + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-gZOpgTlOsLcLfAF9qgpTr7FIIFSKnQN3hDf/0JvQ4CIwMY7h+eilNjxq/CorqvYcEOu+LRt1W4ZS7KccEHLOdA=="], - "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-F2YYqyvnQNvi320RWZNAvsaWEHwmW3k4OwNJ1hZxRKXupY63expbBaNp6jAgvYs7y/g546vuQnGHQuCBhslhLQ=="], + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1CyS9JTB+pCUFYFI6pkQGGZaT/AY5gnhHVrQQLhFba6idP9AzVYm1xbdWfywoldTYvjxQJV6x4SuduCIfP3W+A=="], - "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.43.0", "", { "os": "none", "cpu": "arm64" }, "sha512-OE6TdietLXV3F6c7pNIhx/9YC1/2YFwjU9DPc/fbjxIX19hNIaP1rS0cFjCGJlGX+cVJwIKWe8Mos+LdQ1yAJw=="], + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.44.0", "", { "os": "none", "cpu": "arm64" }, "sha512-bmEv70Ak6jLr1xotCbF5TxIKjsmQaiX+jFRtnGtfA03tJPf6VG3cKh96S21boAt3JZc+Vjx8PYcDuLj39vM2Pw=="], - "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.43.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-0nWK6a7pGkbdoypfVicmV9k/N1FwjPZENoqhlTU+5HhZnAhpIO3za30nEE33u6l6tuy9OVfpdXUqxUgZ+4lbZw=="], + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.44.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yWzB+oCpSnP/dmw85eFLAT5o35Ve5pkGS2uF/UCISpIwDqf1xa7OpmtomiqY/Vzg8VyvMbuf6vroF2khF/+1Vg=="], - "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.43.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-9aokTR4Ft+tRdvgN/pKzSkVy2ksc4/dCpDm9L/xFrbIw0yhLtASLbvoG/5WOTUh/BRPPnfGTsWznEqv0dlOmhA=="], + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.44.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-TcWpo18xEIE3AmIG2kpr3kz5IEhQgnx0lazl2+8L+3eTopOAUevQcmlr4nhguImNWz0OMeOZrYZOhJNCf16nlQ=="], - "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.43.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4bPgdQux2ZLWn3bf2TTXXMHcJB4lenmuxrLqygPmvCJ104Yqzj1UctxSRzR31TiJ4MLaG22RK8dUsVpJtrCz5g=="], + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.44.0", "", { "os": "win32", "cpu": "x64" }, "sha512-oj8aLkPJZppIM4CMQNsyir9ybM1Xw/CfGPTSsTnzpVGyljgfbdP0EVUlURiGM0BDrmw5psQ6ArmGCcUY/yABaQ=="], "@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.20.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KKQcIHZHMxqpHUA1VXIbOG6chNCFkUWbQy6M+AFVtPKkA/3xAeJkJ3njoV66bfzwPHRcWQO+kcj5XqtbkjakoA=="], @@ -728,49 +729,51 @@ "@tanstack/pacer": ["@tanstack/pacer@0.20.1", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.3", "@tanstack/store": "^0.9.3" } }, "sha512-ZNQ1bIL6eUXVKdic0tiImvBVkWrg/IoSK6VIacTrO3d3HAGnd70qFJNJagR/YOJIOw4EKGWnodwpYZkN1pWuVQ=="], - "@tanstack/query-core": ["@tanstack/query-core@5.96.2", "", {}, "sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA=="], + "@tanstack/query-core": ["@tanstack/query-core@5.99.0", "", {}, "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ=="], - "@tanstack/query-devtools": ["@tanstack/query-devtools@5.96.2", "", {}, "sha512-vBTB1Qhbm3nHSbEUtQwks/EdcAtFfEapr1WyBW4w2ExYKuXVi3jIxUIHf5MlSltiHuL7zNyUuanqT/7sI2sb6g=="], + "@tanstack/query-devtools": ["@tanstack/query-devtools@5.99.0", "", {}, "sha512-m4ufXaJ8FjWXw7xDtyzE/6fkZAyQFg9WrbMrUpt8ZecRJx58jiFOZ2lxZMphZdIpAnIeto/S8stbwLKLusyckQ=="], "@tanstack/react-pacer": ["@tanstack/react-pacer@0.21.1", "", { "dependencies": { "@tanstack/pacer": "0.20.1", "@tanstack/react-store": "^0.9.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-BIBWmxSi+RHuB4Sh8SPyh3i3PVK8GR3yAyxOqXjERyww1cfvKdu2veUfJuTTiR4os1lfdctcS4HAibt8KTmMiw=="], - "@tanstack/react-query": ["@tanstack/react-query@5.96.2", "", { "dependencies": { "@tanstack/query-core": "5.96.2" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA=="], + "@tanstack/react-query": ["@tanstack/react-query@5.99.0", "", { "dependencies": { "@tanstack/query-core": "5.99.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw=="], - "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.96.2", "", { "dependencies": { "@tanstack/query-devtools": "5.96.2" }, "peerDependencies": { "@tanstack/react-query": "^5.96.2", "react": "^18 || ^19" } }, "sha512-nTFKLGuTOFvmFRvcyZ3ArWC/DnMNPoBh6h/2yD6rsf7TCTJCQt+oUWOp2uKPTIuEPtF/vN9Kw5tl5mD1Kbposw=="], + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.99.0", "", { "dependencies": { "@tanstack/query-devtools": "5.99.0" }, "peerDependencies": { "@tanstack/react-query": "^5.99.0", "react": "^18 || ^19" } }, "sha512-CqqX7LCU9yOfCY/vBURSx2YSD83ryfX+QkfkaKionTfg1s2Hdm572Ro99gW3QPoJjzvsj1HM4pnN4nbDy3MXKA=="], - "@tanstack/react-router": ["@tanstack/react-router@1.168.10", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.9", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA=="], + "@tanstack/react-router": ["@tanstack/react-router@1.168.18", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.14", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-RmBptS3/qtkGhvG/u41JWOgxz1FIWybBz7iBTgLUIoFkqOj6NE4XlhUOsP2fabxACtbZdJnpvCWcJFWpWGIngw=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.11", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.1" }, "peerDependencies": { "@tanstack/react-router": "^1.168.2", "@tanstack/router-core": "^1.168.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-WYR3q4Xui5yPT/5PXtQh8i03iUA7q8dONBjWpV3nsGdM8Cs1FxpfhLstW0wZO1dOvSyElscwTRCJ6nO5N8r3Lg=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.13", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.3" }, "peerDependencies": { "@tanstack/react-router": "^1.168.15", "@tanstack/router-core": "^1.168.11", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-6yKRFFJrEEOiGp5RAAuGCYsl81M4XAhJmLcu9PKj+HZle4A3dsP60lwHoqQYWHMK9nKKFkdXR+D8qxzxqtQbEA=="], "@tanstack/react-router-with-query": ["@tanstack/react-router-with-query@1.130.17", "", { "peerDependencies": { "@tanstack/react-query": ">=5.49.2", "@tanstack/react-router": ">=1.43.2", "@tanstack/router-core": ">=1.114.7", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-TNaSocW20KuPwUojEm130DLWTr9M5hsSzxiu4QqS2jNCnrGLuDrwMHyP+6fq13lG3YuU4u9O1qajxfJIGomZCg=="], - "@tanstack/react-start": ["@tanstack/react-start@1.167.16", "", { "dependencies": { "@tanstack/react-router": "1.168.10", "@tanstack/react-start-client": "1.166.25", "@tanstack/react-start-server": "1.166.25", "@tanstack/router-utils": "^1.161.6", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-plugin-core": "1.167.17", "@tanstack/start-server-core": "1.167.9", "pathe": "^2.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-vHIhn+FTWfAVhRus1BZEaBZPhnYL+StDuMlShslIBPEGGTCRt11BxNUfV/iDpr7zbxw36Snj7zGfI7DwfjjlDQ=="], + "@tanstack/react-start": ["@tanstack/react-start@1.167.32", "", { "dependencies": { "@tanstack/react-router": "1.168.18", "@tanstack/react-start-client": "1.166.35", "@tanstack/react-start-rsc": "0.0.12", "@tanstack/react-start-server": "1.166.36", "@tanstack/router-utils": "^1.161.6", "@tanstack/start-client-core": "1.167.16", "@tanstack/start-plugin-core": "1.167.29", "@tanstack/start-server-core": "1.167.18", "pathe": "^2.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-y/f6ALbDKslMwb6O/Epzt8cl2C+oBWEcjiWtfNIziIaAX6vyCnRSgwRtZ6lNGTmEOzRZK3dSnAHkZQdYBtEiJg=="], - "@tanstack/react-start-client": ["@tanstack/react-start-client@1.166.25", "", { "dependencies": { "@tanstack/react-router": "1.168.10", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-FvD279zzneUtsfhaTv2c29qhE1Z3wHy3dt3cCjn9LzWZehOgn5Ij78s0YpmQaQ8lSF3YL7CySE3pDk9XHE6YeA=="], + "@tanstack/react-start-client": ["@tanstack/react-start-client@1.166.35", "", { "dependencies": { "@tanstack/react-router": "1.168.18", "@tanstack/router-core": "1.168.14", "@tanstack/start-client-core": "1.167.16" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-FFlYKUMLeFK53eh258RtmK/sT3ZJW4/LAHhjElGYZEhl6PeUSheZ8J+xw1Fn5t02nYixuj4CyCuLpzq/2XUVgQ=="], - "@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.25", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-router": "1.168.10", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-server-core": "1.167.9" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-bPLADxlplvcnAcnZvBjJl2MzgUnB85d7Mu5aEkYoOFxhz0WiG6mZp7BDadIJuCd33NYMirsd3XrjfCHNzrMTyg=="], + "@tanstack/react-start-rsc": ["@tanstack/react-start-rsc@0.0.12", "", { "dependencies": { "@tanstack/react-router": "1.168.18", "@tanstack/react-start-server": "1.166.36", "@tanstack/router-core": "1.168.14", "@tanstack/router-utils": "1.161.6", "@tanstack/start-client-core": "1.167.16", "@tanstack/start-fn-stubs": "1.161.6", "@tanstack/start-plugin-core": "1.167.29", "@tanstack/start-server-core": "1.167.18", "@tanstack/start-storage-context": "1.166.28", "pathe": "^2.0.3" }, "peerDependencies": { "@vitejs/plugin-rsc": ">=0.5.20", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@vitejs/plugin-rsc"] }, "sha512-P2fmYmDBeXyO+9rxUdgvWfa5YD+3RFm+G4z6msEUM0F9QL0RdK8OJW70pkTO4Q1BGf5R9PgGoeQ+yRWMXyCQ/A=="], + + "@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.36", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-router": "1.168.18", "@tanstack/router-core": "1.168.14", "@tanstack/start-client-core": "1.167.16", "@tanstack/start-server-core": "1.167.18" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-auebdZYZE7Hnmg0HEUZfgBbhTyJ8cYJq0nUGVVrCpArtvR07V16QDEB0AAeH80TKnBY1Kfb4S9uk7nC5Do8Cmw=="], "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "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-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], - "@tanstack/router-core": ["@tanstack/router-core@1.168.9", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-18oeEwEDyXOIuO1VBP9ACaK7tYHZUjynGDCoUh/5c/BNhia9vCJCp9O0LfhZXOorDc/PmLSgvmweFhVmIxF10g=="], + "@tanstack/router-core": ["@tanstack/router-core@1.168.14", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.0", "seroval-plugins": "^1.5.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-UhCJtjNrd5wcTmhgB2HyUP0+Rj1M7BD4dS11YsF9x6VC2KH/eqxzs/vK+nN5f+cOhPOLZdmLkWMW+WGmacZ8HA=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.167.1", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.168.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-ECMM47J4KmifUvJguGituSiBpfN8SyCUEoxQks5RY09hpIBfR2eswCv2e6cJimjkKwBQXOVTPkTUk/yRvER+9w=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.167.3", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.168.11", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-fJ1VMhyQgnoashTrP763c2HRc9kofgF61L7Jb3F6eTHAmCKtGVx8BRtiFt37sr3U0P0jmaaiiSPGP6nT5JtVNg=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.166.24", "", { "dependencies": { "@tanstack/router-core": "1.168.9", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-vdaGKwuH+r+DPe6R1mjk+TDDmDH6NTG7QqwxHqGEvOH4aGf9sPjhmRKNJZqQr8cPIbfp6u5lXyZ1TeDcSNMVEA=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.166.29", "", { "dependencies": { "@tanstack/router-core": "1.168.14", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-X/9/4z4tcPyiQfm1kGm9vzEpJboNbfpg/p+QoI5KyaWtqZgF00nyq5dUQKXwacwZBEgHCzUaWCM9etRFCNnXrg=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.12", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.168.9", "@tanstack/router-generator": "1.166.24", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.168.10", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"], "bin": { "intent": "bin/intent.js" } }, "sha512-StEHcctCuFI5taSjO+lhR/yQ+EK63BdyYa+ne6FoNQPB3MMrOUrz2ZVnbqILRLkh2b+p2EfBKt65sgAKdKygPQ=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.18", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.168.14", "@tanstack/router-generator": "1.166.29", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.168.18", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"], "bin": { "intent": "bin/intent.js" } }, "sha512-LkQYEv9rXWSXJ9BKVmaZz27lZij5UDBJscGY3HHK+IenFlakqqiozKBZKlSMl8/WUGZ2JTAecBzAAOCRE9Vm9Q=="], "@tanstack/router-utils": ["@tanstack/router-utils@1.161.6", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw=="], - "@tanstack/start-client-core": ["@tanstack/start-client-core@1.167.9", "", { "dependencies": { "@tanstack/router-core": "1.168.9", "@tanstack/start-fn-stubs": "1.161.6", "@tanstack/start-storage-context": "1.166.23", "seroval": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-2ETQO/bxiZGsoTdPxZb7xR8YqCy5l4kv/QPkwIXuvx/A4BjufngXfgISjXUicXsFRIBZeiFnBzp9A38UMsS2iA=="], + "@tanstack/start-client-core": ["@tanstack/start-client-core@1.167.16", "", { "dependencies": { "@tanstack/router-core": "1.168.14", "@tanstack/start-fn-stubs": "1.161.6", "@tanstack/start-storage-context": "1.166.28", "seroval": "^1.5.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-ftQ+f5SB2gLqU24QeofmbkfXiSSPS/VlLRdNC6DmshUIrDp8SQFb8gP/7hbkORs7jx4YULGA4z2xQrz/RmC+Kg=="], "@tanstack/start-fn-stubs": ["@tanstack/start-fn-stubs@1.161.6", "", {}, "sha512-Y6QSlGiLga8cHfvxGGaonXIlt2bIUTVdH6AMjmpMp7+ANNCp+N96GQbjjhLye3JkaxDfP68x5iZA8NK4imgRig=="], - "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.167.17", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@rolldown/pluginutils": "1.0.0-beta.40", "@tanstack/router-core": "1.168.9", "@tanstack/router-generator": "1.166.24", "@tanstack/router-plugin": "1.167.12", "@tanstack/router-utils": "1.161.6", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-server-core": "1.167.9", "cheerio": "^1.0.0", "exsolve": "^1.0.7", "pathe": "^2.0.3", "picomatch": "^4.0.3", "source-map": "^0.7.6", "srvx": "^0.11.9", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=7.0.0" } }, "sha512-OkorpOobGOEDVr72QUmkzKjbawKC05CSz+1B3OObB/AxBIIw+lLLhTXbV45QkX2LZA7dcRvPJYZGOH1pkFqA1g=="], + "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.167.29", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@rolldown/pluginutils": "1.0.0-beta.40", "@tanstack/router-core": "1.168.14", "@tanstack/router-generator": "1.166.29", "@tanstack/router-plugin": "1.167.18", "@tanstack/router-utils": "1.161.6", "@tanstack/start-client-core": "1.167.16", "@tanstack/start-server-core": "1.167.18", "cheerio": "^1.0.0", "exsolve": "^1.0.7", "pathe": "^2.0.3", "picomatch": "^4.0.3", "seroval": "^1.5.0", "source-map": "^0.7.6", "srvx": "^0.11.9", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=7.0.0" } }, "sha512-thH2Gg3N3oFxUo9K0FBDLUj93Z50SGZtjn78PIHgx9RV5fYjqeMrwTgNRG4x1M92RiCWT5SXWJccHJrxLTA+RQ=="], - "@tanstack/start-server-core": ["@tanstack/start-server-core@1.167.9", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-storage-context": "1.166.23", "h3-v2": "npm:h3@2.0.1-rc.16", "seroval": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-vKkslQIihoDDVumF73VXT7PVFmN7Nea0nKhZx7gMbc0m09yPQYYR1dn86/dz14k6/7cDkJ+qKXa09rlVlN/i9Q=="], + "@tanstack/start-server-core": ["@tanstack/start-server-core@1.167.18", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/router-core": "1.168.14", "@tanstack/start-client-core": "1.167.16", "@tanstack/start-storage-context": "1.166.28", "h3-v2": "npm:h3@2.0.1-rc.20", "seroval": "^1.5.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-JiV0Eqyj5SfEjGZrm/u3nBICeLZRQyeYNI7ine4PjTh0TVL7IklfN5Xfka+p80a/DuUvpHWqKvE4s3tdFZyc1A=="], - "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.23", "", { "dependencies": { "@tanstack/router-core": "1.168.9" } }, "sha512-3vEdiYRMx+r+Q7Xqxj3YmADPIpMm7fkKxDa8ITwodGXiw+SBJCGkpBXGUWjOXyXkIyqGHKM5UrReTcVUTkmaug=="], + "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.28", "", { "dependencies": { "@tanstack/router-core": "1.168.14" } }, "sha512-CUQMd6YtJ7hejKXDqT1R4N5gQ9PYyxUCv/ERNJ6/c/8ohNuhMPlOGSFVvqy2BLYNTFSj9GWjahMazeQpQomPgw=="], "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="], @@ -784,6 +787,14 @@ "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + "@tokenlens/core": ["@tokenlens/core@1.3.0", "", {}, "sha512-d8YNHNC+q10bVpi95fELJwJyPVf1HfvBEI18eFQxRSZTdByXrP+f/ZtlhSzkx0Jl0aEmYVeBA5tPeeYRioLViQ=="], + + "@tokenlens/fetch": ["@tokenlens/fetch@1.3.0", "", { "dependencies": { "@tokenlens/core": "1.3.0" } }, "sha512-RONDRmETYly9xO8XMKblmrZjKSwCva4s5ebJwQNfNlChZoA5kplPoCgnWceHnn1J1iRjLVlrCNB43ichfmGBKQ=="], + + "@tokenlens/helpers": ["@tokenlens/helpers@1.3.1", "", { "dependencies": { "@tokenlens/core": "1.3.0", "@tokenlens/fetch": "1.3.0" } }, "sha512-t6yL8N6ES8337E6eVSeH4hCKnPdWkZRFpupy9w5E66Q9IeqQ9IO7XQ6gh12JKjvWiRHuyyJ8MBP5I549Cr41EQ=="], + + "@tokenlens/models": ["@tokenlens/models@1.3.0", "", { "dependencies": { "@tokenlens/core": "1.3.0" } }, "sha512-9mx7ZGeewW4ndXAiD7AT1bbCk4OpJeortbjHHyNkgap+pMPPn1chY6R5zqe1ggXIUzZ2l8VOAKfPqOvpcrisJw=="], + "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="], "@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="], @@ -798,7 +809,7 @@ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], @@ -912,21 +923,21 @@ "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260408.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260408.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260408.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260408.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260408.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260408.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260408.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260408.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-N0MZLEUnAoP/aRVk7MY119LDsESkbtEwIw+YeXi/jjx2XCqf7ni3GxIVsUYtf/troyuSedq3V/OUrkoCh5A9gA=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260412.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260412.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260412.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260412.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260412.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260412.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260412.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260412.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-tDw3XZt2BkjAlt/MJmnFGmbe9lgKmc5wezmrMoBIEvJcqz+/KVpVBVvjbkZoaiABnJmuG3G3b6IUFrEveTw6UQ=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260408.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YcPczNLfPDB13eUBYHkTOkL7HyWqqqEhho4eSxhAvigZuxvtHQ1uyILIvLVAwipEVzhJ8QciKmLdLucpfi4XyA=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260412.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sSkFG+hjtRWffg6FddF3dEkk7N3TRMEqfiUpixwcWhXgyocMdPw8wutTvQRBxQdgxeL9y01M2SO8A8YPPiEgVg=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260408.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cHqkDg53xxxz21MThLBf4vx1kyIpRPEYNdEiQlvu9O35Tth49+aub6F+/YEMd9MG4TYZmxh1bEjkjErTUIElpA=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260412.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2BTeaLkrHEEDg0D9snigddy01qTY+wgx+W+GpXAfx36PPvW4xWuGXNVWfSaB8bqAC9C8NeLnT/C9/G/rJ5v2w=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260408.1", "", { "os": "linux", "cpu": "arm" }, "sha512-w26Gv9yq9LIYIhxjkQC+i0wBPDdQdX+H06ZhyVRL5grKWTIsk9Xwjp9mDRB/dGlXBKcvnM25JH16OyAA0rFH3A=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260412.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wDLekbfsfmKMWORg7CTnEnpKj8oXpU/6AEBrtVN9CEUCiQAe6yH878nZHhJNzWQXHtrtFf3lY49Yplqmdxja3w=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260408.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-iHG0FEXq/QFsn+qlTPllxdcbvfQ9aRYggy4lc1z0+f11Nyk4YDNCSiR8WW7pbnOTx/VreGbbXhlpuJXTidqL8g=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260412.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-JAdsG6MlVV1hoAUKPy8zxAL7xLeNxz8JgCbLCJVqW8EyH29R9FD4cFTqr7CSIRTNUEDzDTrgnXUyoRtDe1gr+w=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260408.1", "", { "os": "linux", "cpu": "x64" }, "sha512-hMcUlUIzYbvbdq6j/B4RPL+kZR917NGnE9AgPZ7dJ92yamH/7LGT1Mnlc6McUx31yqTFBFHdTc7Cfx+ynua7Iw=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260412.1", "", { "os": "linux", "cpu": "x64" }, "sha512-gYgppiQIqid3jZ7D8THh4k3Q+4bwidrQH6SL9Xgbk1qfP6/jwv8twuPqDOfZ+cK2OD55lQHp15fOh2lMNAC40Q=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260408.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-avJWIEKSx4rdBLZD1FOOTuxTU51dQfYb3jZvZMaXD4thJjq+6eSwfzu2elwL36AZDlnaxggGjB5nBxp0t54iOA=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260412.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-TOh7rH5H3jisHJqRXJSjmUGMzcbNBocS/hufhXPQIv+g3pdG5IKZoSnv3SV62I5d12FFDSS5KQon5MQPnOKAHg=="], - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260408.1", "", { "os": "win32", "cpu": "x64" }, "sha512-gpvEHkF/WoxkA3711c4uWNCZO9WAuwrq49COdNwxgOTzYHnMc1yCj8CpkCUJwU0f/Ydwp2s6/efn6gTMvtckPg=="], + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260412.1", "", { "os": "win32", "cpu": "x64" }, "sha512-u+70wL89wspN1wKoX6FVNUATRGCG3BpleByP3H/UqOZvlwuMm8N7Gy8hEbM0U8bDyAuyP/daUfTBVkqXjjv9mA=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], @@ -936,21 +947,21 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="], - "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.2", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.2", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.2", "vitest": "4.1.2" }, "optionalPeers": ["@vitest/browser"] }, "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg=="], + "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.4", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.4", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.4", "vitest": "4.1.4" }, "optionalPeers": ["@vitest/browser"] }, "sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w=="], - "@vitest/expect": ["@vitest/expect@4.1.3", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.3", "@vitest/utils": "4.1.3", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ=="], + "@vitest/expect": ["@vitest/expect@4.1.4", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.4", "@vitest/utils": "4.1.4", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww=="], - "@vitest/mocker": ["@vitest/mocker@4.1.3", "", { "dependencies": { "@vitest/spy": "4.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw=="], + "@vitest/mocker": ["@vitest/mocker@4.1.4", "", { "dependencies": { "@vitest/spy": "4.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg=="], - "@vitest/pretty-format": ["@vitest/pretty-format@4.1.3", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg=="], + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.4", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A=="], - "@vitest/runner": ["@vitest/runner@4.1.3", "", { "dependencies": { "@vitest/utils": "4.1.3", "pathe": "^2.0.3" } }, "sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA=="], + "@vitest/runner": ["@vitest/runner@4.1.4", "", { "dependencies": { "@vitest/utils": "4.1.4", "pathe": "^2.0.3" } }, "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ=="], - "@vitest/snapshot": ["@vitest/snapshot@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "@vitest/utils": "4.1.3", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ=="], + "@vitest/snapshot": ["@vitest/snapshot@4.1.4", "", { "dependencies": { "@vitest/pretty-format": "4.1.4", "@vitest/utils": "4.1.4", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw=="], - "@vitest/spy": ["@vitest/spy@4.1.3", "", {}, "sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw=="], + "@vitest/spy": ["@vitest/spy@4.1.4", "", {}, "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ=="], - "@vitest/utils": ["@vitest/utils@4.1.2", "", { "dependencies": { "@vitest/pretty-format": "4.1.2", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ=="], + "@vitest/utils": ["@vitest/utils@4.1.4", "", { "dependencies": { "@vitest/pretty-format": "4.1.4", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -962,7 +973,7 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ai": ["ai@6.0.154", "", { "dependencies": { "@ai-sdk/gateway": "3.0.94", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HfKJKCTJsDZxqrIUDSVnBQ7DpQlx5WI4ExqtLd7Bl70epLmvkpc/HYMzU1hP9W+g9VEAcvZo4fbMqc3v5D+9gQ=="], + "ai": ["ai@6.0.158", "", { "dependencies": { "@ai-sdk/gateway": "3.0.95", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gLTp1UXFtMqKUi3XHs33K7UFglbvojkxF/aq337TxnLGOhHIW9+GyP2jwW4hYX87f1es+wId3VQoPRRu9zEStQ=="], "ajv": ["ajv@8.18.0", "", { "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-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], @@ -998,7 +1009,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A=="], "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], @@ -1008,13 +1019,13 @@ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], - "brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="], + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], - "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], @@ -1092,7 +1103,7 @@ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "cookie-es": ["cookie-es@2.0.1", "", {}, "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA=="], + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], @@ -1242,7 +1253,7 @@ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], - "dotenv": ["dotenv@17.4.1", "", {}, "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], "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=="], @@ -1250,7 +1261,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.334", "", {}, "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog=="], + "electron-to-chromium": ["electron-to-chromium@1.5.335", "", {}, "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q=="], "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], @@ -1388,7 +1399,7 @@ "graphql": ["graphql@16.13.2", "", {}, "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig=="], - "h3-v2": ["h3@2.0.1-rc.16", "", { "dependencies": { "rou3": "^0.8.0", "srvx": "^0.11.9" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-h+pjvyujdo9way8qj6FUbhaQcHlR8FEq65EhTX9ViT5pK8aLj68uFl4hBkF+hsTJAH+H1END2Yv6hTIsabGfag=="], + "h3-v2": ["h3@2.0.1-rc.20", "", { "dependencies": { "rou3": "^0.8.1", "srvx": "^0.11.13" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg=="], "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], @@ -1806,7 +1817,7 @@ "outvariant": ["outvariant@1.4.3", "", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="], - "oxfmt": ["oxfmt@0.43.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.43.0", "@oxfmt/binding-android-arm64": "0.43.0", "@oxfmt/binding-darwin-arm64": "0.43.0", "@oxfmt/binding-darwin-x64": "0.43.0", "@oxfmt/binding-freebsd-x64": "0.43.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.43.0", "@oxfmt/binding-linux-arm-musleabihf": "0.43.0", "@oxfmt/binding-linux-arm64-gnu": "0.43.0", "@oxfmt/binding-linux-arm64-musl": "0.43.0", "@oxfmt/binding-linux-ppc64-gnu": "0.43.0", "@oxfmt/binding-linux-riscv64-gnu": "0.43.0", "@oxfmt/binding-linux-riscv64-musl": "0.43.0", "@oxfmt/binding-linux-s390x-gnu": "0.43.0", "@oxfmt/binding-linux-x64-gnu": "0.43.0", "@oxfmt/binding-linux-x64-musl": "0.43.0", "@oxfmt/binding-openharmony-arm64": "0.43.0", "@oxfmt/binding-win32-arm64-msvc": "0.43.0", "@oxfmt/binding-win32-ia32-msvc": "0.43.0", "@oxfmt/binding-win32-x64-msvc": "0.43.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-KTYNG5ISfHSdmeZ25Xzb3qgz9EmQvkaGAxgBY/p38+ZiAet3uZeu7FnMwcSQJg152Qwl0wnYAxDc+Z/H6cvrwA=="], + "oxfmt": ["oxfmt@0.44.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.44.0", "@oxfmt/binding-android-arm64": "0.44.0", "@oxfmt/binding-darwin-arm64": "0.44.0", "@oxfmt/binding-darwin-x64": "0.44.0", "@oxfmt/binding-freebsd-x64": "0.44.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.44.0", "@oxfmt/binding-linux-arm-musleabihf": "0.44.0", "@oxfmt/binding-linux-arm64-gnu": "0.44.0", "@oxfmt/binding-linux-arm64-musl": "0.44.0", "@oxfmt/binding-linux-ppc64-gnu": "0.44.0", "@oxfmt/binding-linux-riscv64-gnu": "0.44.0", "@oxfmt/binding-linux-riscv64-musl": "0.44.0", "@oxfmt/binding-linux-s390x-gnu": "0.44.0", "@oxfmt/binding-linux-x64-gnu": "0.44.0", "@oxfmt/binding-linux-x64-musl": "0.44.0", "@oxfmt/binding-openharmony-arm64": "0.44.0", "@oxfmt/binding-win32-arm64-msvc": "0.44.0", "@oxfmt/binding-win32-ia32-msvc": "0.44.0", "@oxfmt/binding-win32-x64-msvc": "0.44.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-lnncqvHewyRvaqdrnntVIrZV2tEddz8lbvPsQzG/zlkfvgZkwy0HP1p/2u1aCDToeg1jb9zBpbJdfkV73Itw+w=="], "oxlint": ["oxlint@1.59.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.59.0", "@oxlint/binding-android-arm64": "1.59.0", "@oxlint/binding-darwin-arm64": "1.59.0", "@oxlint/binding-darwin-x64": "1.59.0", "@oxlint/binding-freebsd-x64": "1.59.0", "@oxlint/binding-linux-arm-gnueabihf": "1.59.0", "@oxlint/binding-linux-arm-musleabihf": "1.59.0", "@oxlint/binding-linux-arm64-gnu": "1.59.0", "@oxlint/binding-linux-arm64-musl": "1.59.0", "@oxlint/binding-linux-ppc64-gnu": "1.59.0", "@oxlint/binding-linux-riscv64-gnu": "1.59.0", "@oxlint/binding-linux-riscv64-musl": "1.59.0", "@oxlint/binding-linux-s390x-gnu": "1.59.0", "@oxlint/binding-linux-x64-gnu": "1.59.0", "@oxlint/binding-linux-x64-musl": "1.59.0", "@oxlint/binding-openharmony-arm64": "1.59.0", "@oxlint/binding-win32-arm64-msvc": "1.59.0", "@oxlint/binding-win32-ia32-msvc": "1.59.0", "@oxlint/binding-win32-x64-msvc": "1.59.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-0xBLeGGjP4vD9pygRo8iuOkOzEU1MqOnfiOl7KYezL/QvWL8NUg6n03zXc7ZVqltiOpUxBk2zgHI3PnRIEdAvw=="], @@ -1864,7 +1875,7 @@ "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + "prettier": ["prettier@3.8.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q=="], "pretty-format": ["pretty-format@30.3.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ=="], @@ -2104,6 +2115,8 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tokenlens": ["tokenlens@1.3.1", "", { "dependencies": { "@tokenlens/core": "1.3.0", "@tokenlens/fetch": "1.3.0", "@tokenlens/helpers": "1.3.1", "@tokenlens/models": "1.3.0" } }, "sha512-7oxmsS5PNCX3z+b+z07hL5vCzlgHKkCGrEQjQmWl5l+v5cUrtL7S1cuST4XThaL1XyjbTX8J5hfP0cjDJRkaLA=="], + "tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], @@ -2194,7 +2207,7 @@ "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], - "vitest": ["vitest@4.1.3", "", { "dependencies": { "@vitest/expect": "4.1.3", "@vitest/mocker": "4.1.3", "@vitest/pretty-format": "4.1.3", "@vitest/runner": "4.1.3", "@vitest/snapshot": "4.1.3", "@vitest/spy": "4.1.3", "@vitest/utils": "4.1.3", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.3", "@vitest/browser-preview": "4.1.3", "@vitest/browser-webdriverio": "4.1.3", "@vitest/coverage-istanbul": "4.1.3", "@vitest/coverage-v8": "4.1.3", "@vitest/ui": "4.1.3", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw=="], + "vitest": ["vitest@4.1.4", "", { "dependencies": { "@vitest/expect": "4.1.4", "@vitest/mocker": "4.1.4", "@vitest/pretty-format": "4.1.4", "@vitest/runner": "4.1.4", "@vitest/snapshot": "4.1.4", "@vitest/spy": "4.1.4", "@vitest/utils": "4.1.4", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.4", "@vitest/browser-preview": "4.1.4", "@vitest/browser-webdriverio": "4.1.4", "@vitest/coverage-istanbul": "4.1.4", "@vitest/coverage-v8": "4.1.4", "@vitest/ui": "4.1.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg=="], "vitest-browser-react": ["vitest-browser-react@2.2.0", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "vitest": "^4.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oY3KM6305kwJMa6nHo92vVtkOsih7mjEf12dLKuphaF+9ywWPEc+qanIBd394SZ6m5LadVEaG6dicvvizOzmjA=="], @@ -2336,14 +2349,6 @@ "@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@vitest/expect/@vitest/utils": ["@vitest/utils@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw=="], - - "@vitest/runner/@vitest/utils": ["@vitest/utils@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw=="], - - "@vitest/snapshot/@vitest/utils": ["@vitest/utils@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw=="], - - "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@4.1.2", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA=="], - "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "cheerio/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], @@ -2354,6 +2359,8 @@ "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "cmdk/@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=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], @@ -2430,8 +2437,6 @@ "typescript-json-schema/typescript": ["typescript@5.5.4", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q=="], - "vitest/@vitest/utils": ["@vitest/utils@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw=="], - "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], diff --git a/package.json b/package.json index a897852..75d3ec6 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "tools:build": "bun run ./scripts/generate-tools-json.ts" }, "dependencies": { - "@ai-sdk/anthropic": "^3.0.68", - "@ai-sdk/google": "^3.0.60", + "@ai-sdk/anthropic": "^3.0.69", + "@ai-sdk/google": "^3.0.62", "@ai-sdk/groq": "^3.0.35", "@ai-sdk/mistral": "^3.0.30", "@ai-sdk/openai": "^3.0.52", @@ -41,11 +41,11 @@ "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", "@tanstack/react-pacer": "^0.21.1", - "@tanstack/react-query": "^5.96.2", - "@tanstack/react-router": "1.168.10", + "@tanstack/react-query": "^5.99.0", + "@tanstack/react-router": "1.168.18", "@tanstack/react-router-with-query": "1.130.17", - "@tanstack/react-start": "^1.167.16", - "ai": "^6.0.154", + "@tanstack/react-start": "^1.167.32", + "ai": "^6.0.158", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -63,29 +63,30 @@ "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.2", "tailwindcss-animate": "^1.0.7", + "tokenlens": "^1.3.1", "tw-animate-css": "^1.4.0", "use-stick-to-bottom": "^1.1.3", "zod": "^4.3.6" }, "devDependencies": { "@mdx-js/rollup": "^3.1.1", - "@tanstack/react-query-devtools": "^5.96.2", - "@tanstack/react-router-devtools": "1.166.11", - "@tanstack/router-plugin": "1.167.12", + "@tanstack/react-query-devtools": "^5.99.0", + "@tanstack/react-router-devtools": "1.166.13", + "@tanstack/router-plugin": "1.167.18", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/bun": "^1.3.11", + "@types/bun": "^1.3.12", "@types/jest": "^30.0.0", "@types/json-schema": "^7.0.15", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript/native-preview": "^7.0.0-dev.20260408.1", + "@typescript/native-preview": "^7.0.0-dev.20260412.1", "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "4.1.2", + "@vitest/coverage-v8": "4.1.4", "ajv": "^8.18.0", "jsdom": "^29.0.2", - "oxfmt": "^0.43.0", + "oxfmt": "^0.44.0", "oxlint": "^1.59.0", "oxlint-tsgolint": "^0.20.0", "rehype-pretty-code": "^0.14.3", @@ -95,7 +96,7 @@ "typescript": "^6.0.2", "typescript-json-schema": "^0.67.1", "vite": "^8.0.8", - "vitest": "^4.1.3", + "vitest": "^4.1.4", "vitest-browser-react": "^2.2.0" } } diff --git a/public/specification/flat.json b/public/specification/flat.json index bc38b91..e0e2751 100644 --- a/public/specification/flat.json +++ b/public/specification/flat.json @@ -2,33 +2,40 @@ "type": "object", "properties": { "name": { + "description": "Unique machine-readable identifier for the tool (e.g. \"httpx\").", "type": "string" }, "displayName": { + "description": "Human-readable display name for the tool (e.g. \"HTTPx\").", "type": "string" }, "info": { + "description": "General information about the tool such as description, version, and URL.", "$ref": "#/definitions/ToolInfo" }, "commands": { + "description": "List of all commands and subcommands defined for this tool.", "type": "array", "items": { "$ref": "#/definitions/Command" } }, "parameters": { + "description": "Flat list of all parameters across all commands and global scope.", "type": "array", "items": { "$ref": "#/definitions/Parameter" } }, "exclusionGroups": { + "description": "Groups of parameters with mutual exclusion or required-one-of constraints.", "type": "array", "items": { "$ref": "#/definitions/ExclusionGroup" } }, "metadata": { + "description": "Arbitrary metadata attached to the tool.", "$ref": "#/definitions/ToolMetadata" } }, @@ -43,12 +50,15 @@ "type": "object", "properties": { "description": { + "description": "A brief human-readable description of what the tool does.", "type": "string" }, "version": { + "description": "The version string of the tool (e.g. \"1.0.0\").", "type": "string" }, "url": { + "description": "The homepage or documentation URL for the tool.", "type": "string" } } @@ -57,24 +67,31 @@ "type": "object", "properties": { "key": { + "description": "Unique identifier for this command within the tool.", "type": "string" }, "parentCommandKey": { + "description": "Key of the parent command; used to represent subcommand nesting.", "type": "string" }, "name": { + "description": "Human-readable display name of the command.", "type": "string" }, "description": { + "description": "Brief description of what this command does.", "type": "string" }, "interactive": { + "description": "Whether this command opens an interactive session or prompt.", "type": "boolean" }, "isDefault": { + "description": "Whether this is the default command when no subcommand is specified.", "type": "boolean" }, "sortOrder": { + "description": "Display sort position relative to sibling commands.", "type": "number" } }, @@ -87,66 +104,86 @@ "type": "object", "properties": { "key": { + "description": "Unique identifier for this parameter within the tool.", "type": "string" }, "name": { + "description": "Human-readable display name of the parameter.", "type": "string" }, "commandKey": { + "description": "Key of the command this parameter belongs to; omit for global parameters.", "type": "string" }, "description": { + "description": "Brief description of what this parameter does or accepts.", "type": "string" }, "group": { + "description": "Optional grouping label for organising related parameters in the UI.", "type": "string" }, "metadata": { + "description": "Additional metadata such as tags.", "$ref": "#/definitions/ParameterMetadata" }, "parameterType": { - "$ref": "#/definitions/ParameterType" + "$ref": "#/definitions/ParameterType", + "description": "Whether this is a boolean flag, a key-value option, or a positional argument." }, "dataType": { - "$ref": "#/definitions/ParameterDataType" + "$ref": "#/definitions/ParameterDataType", + "description": "The data type of the parameter's value." }, "isRequired": { + "description": "Whether the user must provide this parameter.", "type": "boolean" }, "isRepeatable": { + "description": "Whether this parameter can be specified multiple times.", "type": "boolean" }, "isGlobal": { + "description": "Whether this parameter applies to all commands rather than a single command.", "type": "boolean" }, "shortFlag": { + "description": "The single-character short flag (e.g. \"-v\").", "type": "string" }, "longFlag": { + "description": "The long-form flag or option name (e.g. \"--verbose\").", "type": "string" }, "position": { + "description": "Zero-based position index for positional arguments.", "type": "number" }, "sortOrder": { + "description": "Display sort position relative to sibling parameters.", "type": "number" }, "arraySeparator": { + "description": "Separator character used when the option accepts multiple values in one argument (e.g. \",\").", "type": "string" }, "keyValueSeparator": { + "description": "Separator between key and value for key=value style options (e.g. \"=\").", "type": "string" }, "enum": { + "description": "Allowed enum choices when dataType is \"Enum\".", "$ref": "#/definitions/ParameterEnumValues" }, "validations": { + "description": "Validation rules applied to this parameter's value.", "type": "array", "items": { "$ref": "#/definitions/ParameterValidation" } }, "dependencies": { + "description": "Dependencies on other parameters (requires or conflicts-with relationships).", "type": "array", "items": { "$ref": "#/definitions/ParameterDependency" @@ -164,6 +201,7 @@ "type": "object", "properties": { "tags": { + "description": "Arbitrary tags for categorising or filtering parameters.", "type": "array", "items": { "type": "string" @@ -192,15 +230,18 @@ "type": "object", "properties": { "values": { + "description": "The list of allowed enum choices.", "type": "array", "items": { "$ref": "#/definitions/ParameterEnumValue" } }, "allowMultiple": { + "description": "Whether the user can select multiple values at once.", "type": "boolean" }, "separator": { + "description": "Separator character used when joining multiple selected values.", "type": "string" } }, @@ -212,18 +253,23 @@ "type": "object", "properties": { "value": { + "description": "The raw value passed to the CLI for this choice.", "type": "string" }, "displayName": { + "description": "Human-readable label shown to the user for this enum choice.", "type": "string" }, "description": { + "description": "Description of what this enum value does or represents.", "type": "string" }, "isDefault": { + "description": "Whether this is the default selection when no value is provided.", "type": "boolean" }, "sortOrder": { + "description": "Display sort position relative to sibling enum values.", "type": "number" } }, @@ -236,15 +282,19 @@ "type": "object", "properties": { "key": { + "description": "Unique identifier for this validation rule.", "type": "string" }, "validationType": { - "$ref": "#/definitions/ParameterValidationType" + "$ref": "#/definitions/ParameterValidationType", + "description": "The type of validation to apply." }, "validationValue": { + "description": "The value to validate against (e.g. the max length number, or a regex pattern).", "type": "string" }, "errorMessage": { + "description": "The error message to display when validation fails.", "type": "string" } }, @@ -269,18 +319,23 @@ "type": "object", "properties": { "key": { + "description": "Unique identifier for this dependency rule.", "type": "string" }, "parameterKey": { + "description": "Key of the parameter that owns this dependency.", "type": "string" }, "dependsOnParameterKey": { + "description": "Key of the parameter this dependency references.", "type": "string" }, "dependencyType": { - "$ref": "#/definitions/ParameterDependencyType" + "$ref": "#/definitions/ParameterDependencyType", + "description": "Whether this parameter requires or conflicts with the referenced parameter." }, "conditionValue": { + "description": "Optional value that the referenced parameter must have for this dependency to apply.", "type": "string" } }, @@ -302,18 +357,23 @@ "type": "object", "properties": { "key": { + "description": "Unique identifier for this exclusion group.", "type": "string" }, "commandKey": { + "description": "Key of the command this exclusion group belongs to; omit for global groups.", "type": "string" }, "name": { + "description": "Human-readable name for this exclusion group.", "type": "string" }, "exclusionType": { - "$ref": "#/definitions/ExclusionType" + "$ref": "#/definitions/ExclusionType", + "description": "Whether parameters in this group are mutually exclusive or one is required." }, "parameterKeys": { + "description": "Keys of the parameters that participate in this exclusion group.", "type": "array", "items": { "type": "string" diff --git a/public/specification/nested.json b/public/specification/nested.json index b7dd801..2de1a07 100644 --- a/public/specification/nested.json +++ b/public/specification/nested.json @@ -2,33 +2,41 @@ "type": "object", "properties": { "$schema": { + "description": "Optional JSON schema URI for validation.", "type": "string" }, "name": { + "description": "Unique machine-readable identifier for the tool (e.g. \"httpx\").", "type": "string" }, "displayName": { + "description": "Human-readable display name for the tool (e.g. \"HTTPx\").", "type": "string" }, "info": { + "description": "General information about the tool such as description, version, and URL.", "$ref": "#/definitions/ToolInfo" }, "url": { + "description": "The homepage or documentation URL for the tool.", "type": "string" }, "globalParameters": { + "description": "Parameters that apply to all commands globally.", "type": "array", "items": { "$ref": "#/definitions/NestedParameter" } }, "commands": { + "description": "Hierarchical list of commands and their nested subcommands.", "type": "array", "items": { "$ref": "#/definitions/NestedCommand" } }, "exclusionGroups": { + "description": "Groups of parameters with mutual exclusion or required-one-of constraints.", "anyOf": [ { "type": "array", @@ -42,6 +50,7 @@ ] }, "metadata": { + "description": "Arbitrary metadata attached to the tool.", "$ref": "#/definitions/ToolMetadata" } }, @@ -56,12 +65,15 @@ "type": "object", "properties": { "description": { + "description": "A brief human-readable description of what the tool does.", "type": "string" }, "version": { + "description": "The version string of the tool (e.g. \"1.0.0\").", "type": "string" }, "url": { + "description": "The homepage or documentation URL for the tool.", "type": "string" } } @@ -70,60 +82,78 @@ "type": "object", "properties": { "name": { + "description": "Human-readable display name of the parameter.", "type": "string" }, "description": { + "description": "Brief description of what this parameter does or accepts.", "type": "string" }, "group": { + "description": "Optional grouping label for organising related parameters in the UI.", "type": "string" }, "parameterType": { - "$ref": "#/definitions/ParameterType" + "$ref": "#/definitions/ParameterType", + "description": "Whether this is a boolean flag, a key-value option, or a positional argument." }, "dataType": { - "$ref": "#/definitions/ParameterDataType" + "$ref": "#/definitions/ParameterDataType", + "description": "The data type of the parameter's value." }, "metadata": { + "description": "Additional metadata such as tags.", "$ref": "#/definitions/ParameterMetadata" }, "isRequired": { + "description": "Whether the user must provide this parameter.", "type": "boolean" }, "isRepeatable": { + "description": "Whether this parameter can be specified multiple times.", "type": "boolean" }, "isGlobal": { + "description": "Whether this parameter applies to all commands rather than a single command.", "type": "boolean" }, "shortFlag": { + "description": "The single-character short flag (e.g. \"-v\").", "type": "string" }, "longFlag": { + "description": "The long-form flag or option name (e.g. \"--verbose\").", "type": "string" }, "position": { + "description": "Zero-based position index for positional arguments.", "type": "number" }, "sortOrder": { + "description": "Display sort position relative to sibling parameters.", "type": "number" }, "arraySeparator": { + "description": "Separator character used when the option accepts multiple values in one argument (e.g. \",\").", "type": "string" }, "keyValueSeparator": { + "description": "Separator between key and value for key=value style options (e.g. \"=\").", "type": "string" }, "enum": { + "description": "Allowed enum choices when dataType is \"Enum\".", "$ref": "#/definitions/ParameterEnumValues" }, "validations": { + "description": "Validation rules applied to this parameter's value.", "type": "array", "items": { "$ref": "#/definitions/NestedParameterValidation" } }, "dependencies": { + "description": "Dependencies on other parameters (requires or conflicts-with relationships).", "type": "array", "items": { "$ref": "#/definitions/NestedParameterDependency" @@ -157,6 +187,7 @@ "type": "object", "properties": { "tags": { + "description": "Arbitrary tags for categorising or filtering parameters.", "type": "array", "items": { "type": "string" @@ -168,15 +199,18 @@ "type": "object", "properties": { "values": { + "description": "The list of allowed enum choices.", "type": "array", "items": { "$ref": "#/definitions/ParameterEnumValue" } }, "allowMultiple": { + "description": "Whether the user can select multiple values at once.", "type": "boolean" }, "separator": { + "description": "Separator character used when joining multiple selected values.", "type": "string" } }, @@ -188,18 +222,23 @@ "type": "object", "properties": { "value": { + "description": "The raw value passed to the CLI for this choice.", "type": "string" }, "displayName": { + "description": "Human-readable label shown to the user for this enum choice.", "type": "string" }, "description": { + "description": "Description of what this enum value does or represents.", "type": "string" }, "isDefault": { + "description": "Whether this is the default selection when no value is provided.", "type": "boolean" }, "sortOrder": { + "description": "Display sort position relative to sibling enum values.", "type": "number" } }, @@ -212,12 +251,15 @@ "type": "object", "properties": { "validationType": { - "$ref": "#/definitions/ParameterValidationType" + "$ref": "#/definitions/ParameterValidationType", + "description": "The type of validation to apply." }, "validationValue": { + "description": "The value to validate against (e.g. max length number or regex pattern).", "type": "string" }, "errorMessage": { + "description": "The error message to display when validation fails.", "type": "string" } }, @@ -241,12 +283,15 @@ "type": "object", "properties": { "dependsOnParameter": { + "description": "Name of the parameter this dependency references.", "type": "string" }, "dependencyType": { - "$ref": "#/definitions/ParameterDependencyType" + "$ref": "#/definitions/ParameterDependencyType", + "description": "Whether this parameter requires or conflicts with the referenced parameter." }, "conditionValue": { + "description": "Optional value that the referenced parameter must have for this dependency to apply.", "type": "string" } }, @@ -266,27 +311,34 @@ "type": "object", "properties": { "name": { + "description": "Human-readable display name of the command.", "type": "string" }, "description": { + "description": "Brief description of what this command does.", "type": "string" }, "interactive": { + "description": "Whether this command opens an interactive session or prompt.", "type": "boolean" }, "isDefault": { + "description": "Whether this is the default command when no subcommand is specified.", "type": "boolean" }, "sortOrder": { + "description": "Display sort position relative to sibling commands.", "type": "number" }, "parameters": { + "description": "Parameters that belong directly to this command.", "type": "array", "items": { "$ref": "#/definitions/NestedParameter" } }, "subcommands": { + "description": "Nested subcommands of this command.", "type": "array", "items": { "$ref": "#/definitions/NestedCommand" @@ -305,12 +357,15 @@ "type": "object", "properties": { "name": { + "description": "Human-readable name for this exclusion group.", "type": "string" }, "exclusionType": { - "$ref": "#/definitions/ExclusionType" + "$ref": "#/definitions/ExclusionType", + "description": "Whether parameters in this group are mutually exclusive or one is required." }, "parameters": { + "description": "Names of the parameters that participate in this exclusion group.", "type": "array", "items": { "type": "string" diff --git a/public/tools.json b/public/tools.json index 164d488..3cdb231 100644 --- a/public/tools.json +++ b/public/tools.json @@ -2,66 +2,131 @@ { "name": "asnmap", "displayName": "ASNMap", - "description": "Go CLI and Library for quickly mapping organization network ranges using ASN information." + "description": "Go CLI and Library for quickly mapping organization network ranges using ASN information.", + "info": { + "description": "Go CLI and Library for quickly mapping organization network ranges using ASN information.", + "version": "1.1.1", + "url": "https://github.com/projectdiscovery/asnmap" + } }, { "name": "cdncheck", "displayName": "CDNCheck", - "description": "cdncheck is a tool for identifying the technology associated with dns / ip network addresses." + "description": "cdncheck is a tool for identifying the technology associated with dns / ip network addresses.", + "info": { + "description": "cdncheck is a tool for identifying the technology associated with dns / ip network addresses.", + "version": "1.2.27", + "url": "https://github.com/projectdiscovery/cdncheck" + } }, { "name": "curl", "displayName": "Curl", - "description": "curl is a command line tool and library for transferring data with URLs." + "description": "curl is a command line tool and library for transferring data with URLs.", + "info": { + "description": "curl is a command line tool and library for transferring data with URLs.", + "version": "8.19.0", + "url": "https://curl.se/" + } }, { "name": "dnsx", "displayName": "DNSX", - "description": "A fast and multi-purpose DNS toolkit designed for running DNS queries." + "description": "A fast and multi-purpose DNS toolkit designed for running DNS queries.", + "info": { + "description": "A fast and multi-purpose DNS toolkit designed for running DNS queries.", + "version": "1.2.3", + "url": "https://github.com/projectdiscovery/dnsx" + } }, { "name": "gospider", "displayName": "GoSpider", - "description": "Fast web spider written in Go." + "description": "Fast web spider written in Go.", + "info": { + "description": "Fast web spider written in Go.", + "version": "1.1.6", + "url": "https://github.com/jaeles-project/gospider" + } }, { "name": "httpx", "displayName": "Httpx", - "description": "Httpx is a fast and multi-purpose HTTP toolkit that allows running multiple probes using the retryablehttp library." + "description": "Httpx is a fast and multi-purpose HTTP toolkit that allows running multiple probes using the retryablehttp library.", + "info": { + "description": "Httpx is a fast and multi-purpose HTTP toolkit that allows running multiple probes using the retryablehttp library.", + "version": "1.9.0", + "url": "https://github.com/projectdiscovery/httpx" + } }, { "name": "katana", "displayName": "Katana", - "description": "Katana is a fast crawler focused on execution in automation pipelines offering both headless and non-headless crawling." + "description": "Katana is a fast crawler focused on execution in automation pipelines offering both headless and non-headless crawling.", + "info": { + "description": "Katana is a fast crawler focused on execution in automation pipelines offering both headless and non-headless crawling.", + "version": "1.5.0", + "url": "https://github.com/projectdiscovery/katana" + } }, { "name": "naabu", "displayName": "Naabu", - "description": "Fast port scanner for discovering open ports on hosts." + "description": "Fast port scanner for discovering open ports on hosts.", + "info": { + "description": "Fast port scanner for discovering open ports on hosts.", + "version": "2.5.0", + "url": "https://github.com/projectdiscovery/naabu" + } }, { "name": "nuclei", "displayName": "Nuclei", - "description": "Nuclei is a fast, template based vulnerability scanner focusing on extensive configurability, massive extensibility and ease of use." + "description": "Nuclei is a fast, template based vulnerability scanner focusing on extensive configurability, massive extensibility and ease of use.", + "info": { + "description": "Nuclei is a fast, template based vulnerability scanner focusing on extensive configurability, massive extensibility and ease of use.", + "version": "3.7.1", + "url": "https://github.com/projectdiscovery/nuclei" + } }, { "name": "shuffledns", "displayName": "ShuffleDNS", - "description": "MassDNS wrapper written in go to enumerate valid subdomains using active bruteforce as well as resolve subdomains with wildcard filtering and easy input-output support." + "description": "MassDNS wrapper written in go to enumerate valid subdomains using active bruteforce as well as resolve subdomains with wildcard filtering and easy input-output support.", + "info": { + "description": "MassDNS wrapper written in go to enumerate valid subdomains using active bruteforce as well as resolve subdomains with wildcard filtering and easy input-output support.", + "version": "1.2.1", + "url": "https://github.com/projectdiscovery/shuffledns" + } }, { "name": "subfinder", "displayName": "Subfinder", - "description": "Subfinder is a subdomain discovery tool that discovers subdomains for websites by using passive online sources." + "description": "Subfinder is a subdomain discovery tool that discovers subdomains for websites by using passive online sources.", + "info": { + "description": "Subfinder is a subdomain discovery tool that discovers subdomains for websites by using passive online sources.", + "version": "2.13.0", + "url": "https://github.com/projectdiscovery/subfinder" + } }, { "name": "urlfinder", "displayName": "URLFinder", - "description": "A streamlined tool for discovering associated URLs." + "description": "A streamlined tool for discovering associated URLs.", + "info": { + "description": "A streamlined tool for discovering associated URLs.", + "version": "0.0.3", + "url": "https://github.com/projectdiscovery/urlfinder" + } }, { "name": "yt-dlp", "displayName": "yt-dlp", - "description": "yt-dlp is a command-line program to download videos from YouTube and other sites." + "description": "yt-dlp is a command-line program to download videos from YouTube and other sites.", + "info": { + "description": "yt-dlp is a command-line program to download videos from YouTube and other sites.", + "version": "2026.03.17", + "url": "https://github.com/yt-dlp/yt-dlp" + } } ] \ No newline at end of file diff --git a/registry/commandly/__tests__/tool-renderer.test.tsx b/registry/commandly/__tests__/tool-renderer.test.tsx index 9cb222e..55fdbdb 100644 --- a/registry/commandly/__tests__/tool-renderer.test.tsx +++ b/registry/commandly/__tests__/tool-renderer.test.tsx @@ -165,6 +165,36 @@ describe("ToolRenderer", () => { expect(inputs).toHaveLength(2); }); + it("renders an allowMultiple Enum parameter without crashing when value is an array (repeatable-to-non-repeatable transition)", () => { + const param = { + ...createNewParameter(false, "my-tool"), + key: "format", + name: "Format", + parameterType: "Option" as const, + dataType: "Enum" as const, + isRepeatable: false, + enum: { + values: [ + { value: "json", displayName: "JSON" }, + { value: "xml", displayName: "XML" }, + ], + allowMultiple: true, + separator: ",", + }, + }; + expect(() => + render( + {}} + />, + ), + ).not.toThrow(); + expect(screen.getByText("Format")).toBeInTheDocument(); + }); + it("custom catalog entry takes precedence over built-in", () => { const param = { ...createNewParameter(false, "my-tool"), diff --git a/registry/commandly/json-output.tsx b/registry/commandly/json-output.tsx index dbed8a2..e260f92 100644 --- a/registry/commandly/json-output.tsx +++ b/registry/commandly/json-output.tsx @@ -1,6 +1,7 @@ import { Tool } from "@/components/commandly/types/flat"; import { exportToStructuredJSON } from "@/components/commandly/utils/flat"; import { convertToNestedStructure } from "@/components/commandly/utils/nested"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { @@ -14,7 +15,7 @@ import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { CheckIcon, ChevronsUpDownIcon, CopyIcon, Edit2Icon, XIcon } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; const jsonOptions = [ @@ -22,17 +23,53 @@ const jsonOptions = [ { value: "flat", label: "Flat" }, ]; +type DiffLine = { type: "same" | "added" | "removed"; text: string }; + +function diffLines(before: string, after: string): DiffLine[] { + const a = before.split("\n"); + const b = after.split("\n"); + const m = a.length; + const n = b.length; + const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); + for (let i = 1; i <= m; i++) + for (let j = 1; j <= n; j++) + dp[i][j] = + a[i - 1] === b[j - 1] + ? dp[i - 1][j - 1] + 1 + : Math.max(dp[i - 1][j], dp[i][j - 1]); + const result: DiffLine[] = []; + let i = m, + j = n; + while (i > 0 || j > 0) { + if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) { + result.unshift({ type: "same", text: a[i - 1] }); + i--; + j--; + } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) { + result.unshift({ type: "added", text: b[j - 1] }); + j--; + } else { + result.unshift({ type: "removed", text: a[i - 1] }); + i--; + } + } + return result; +} + interface JsonTypeComponentProps { tool: Tool; + originalTool?: Tool; onApply?: (tool: Tool) => void; } -export function JsonOutput({ tool, onApply }: JsonTypeComponentProps) { +export function JsonOutput({ tool, originalTool, onApply }: JsonTypeComponentProps) { const [open, setOpen] = useState(false); const [jsonString, setJsonString] = useState(); + const [originalJsonString, setOriginalJsonString] = useState(); const [jsonType, setJsonType] = useState<"nested" | "flat">("flat"); const [isEditing, setIsEditing] = useState(false); const [editValue, setEditValue] = useState(""); + const [showDiff, setShowDiff] = useState(true); useEffect(() => { const config = @@ -40,6 +77,30 @@ export function JsonOutput({ tool, onApply }: JsonTypeComponentProps) { setJsonString(JSON.stringify(config, null, 2)); }, [jsonType, tool]); + useEffect(() => { + if (!originalTool) { + setOriginalJsonString(undefined); + return; + } + const config = + jsonType === "flat" + ? exportToStructuredJSON(originalTool) + : convertToNestedStructure(originalTool); + setOriginalJsonString(JSON.stringify(config, null, 2)); + }, [jsonType, originalTool]); + + const diff = useMemo(() => { + if (!originalJsonString || !jsonString || originalJsonString === jsonString) return null; + return diffLines(originalJsonString, jsonString); + }, [originalJsonString, jsonString]); + + const diffStats = useMemo(() => { + if (!diff) return null; + const added = diff.filter((l) => l.type === "added").length; + const removed = diff.filter((l) => l.type === "removed").length; + return { added, removed }; + }, [diff]); + const handleEditToggle = () => { setEditValue(jsonString ?? ""); setIsEditing(true); @@ -137,6 +198,34 @@ export function JsonOutput({ tool, onApply }: JsonTypeComponentProps) { + {diffStats && !isEditing && ( +
+ {diffStats.added > 0 && ( + + +{diffStats.added} added + + )} + {diffStats.removed > 0 && ( + + -{diffStats.removed} removed + + )} + +
+ )} {isEditing ? (
+ ) : diff && showDiff ? ( + +
+              {diff.map((line, idx) => (
+                
+ + {line.type === "added" ? "+ " : line.type === "removed" ? "- " : " "} + + {line.text} +
+ ))} +
+ + +
) : ( ); } + diff --git a/registry/commandly/tool-renderer.tsx b/registry/commandly/tool-renderer.tsx index 7633d30..b88f54a 100644 --- a/registry/commandly/tool-renderer.tsx +++ b/registry/commandly/tool-renderer.tsx @@ -129,7 +129,11 @@ function OptionEnumInput({ parameter, value, onUpdate }: ParameterRenderContext) ); if (parameter.enum?.allowMultiple) { - const selected = value ? (value as string).split(separator).filter(Boolean) : []; + const selected = Array.isArray(value) + ? (value as string[]).filter(Boolean) + : value + ? (value as string).split(separator).filter(Boolean) + : []; return (
{label} diff --git a/registry/commandly/types/flat.ts b/registry/commandly/types/flat.ts index 7d31e20..c74a2c6 100644 --- a/registry/commandly/types/flat.ts +++ b/registry/commandly/types/flat.ts @@ -1,30 +1,48 @@ export interface ToolInfo { + /** A brief human-readable description of what the tool does. */ description?: string; + /** The version string of the tool (e.g. "1.0.0"). */ version?: string; + /** The homepage or documentation URL for the tool. */ url?: string; } export interface Command { + /** Unique identifier for this command within the tool. */ key: string; + /** Key of the parent command; used to represent subcommand nesting. */ parentCommandKey?: string; + /** Human-readable display name of the command. */ name: string; + /** Brief description of what this command does. */ description?: string; + /** Whether this command opens an interactive session or prompt. */ interactive?: boolean; + /** Whether this is the default command when no subcommand is specified. */ isDefault?: boolean; + /** Display sort position relative to sibling commands. */ sortOrder?: number; } export interface ParameterEnumValue { + /** The raw value passed to the CLI for this choice. */ value: string; + /** Human-readable label shown to the user for this enum choice. */ displayName: string; + /** Description of what this enum value does or represents. */ description?: string; + /** Whether this is the default selection when no value is provided. */ isDefault?: boolean; + /** Display sort position relative to sibling enum values. */ sortOrder?: number; } export interface ParameterEnumValues { + /** The list of allowed enum choices. */ values: ParameterEnumValue[]; + /** Whether the user can select multiple values at once. */ allowMultiple?: boolean; + /** Separator character used when joining multiple selected values. */ separator?: string; } @@ -36,19 +54,28 @@ export type ParameterValidationType = | "regex"; export interface ParameterValidation { + /** Unique identifier for this validation rule. */ key: string; + /** The type of validation to apply. */ validationType: ParameterValidationType; + /** The value to validate against (e.g. the max length number, or a regex pattern). */ validationValue: string; + /** The error message to display when validation fails. */ errorMessage: string; } export type ParameterDependencyType = "requires" | "conflicts_with"; export interface ParameterDependency { + /** Unique identifier for this dependency rule. */ key: string; + /** Key of the parameter that owns this dependency. */ parameterKey: string; + /** Key of the parameter this dependency references. */ dependsOnParameterKey: string; + /** Whether this parameter requires or conflicts with the referenced parameter. */ dependencyType: ParameterDependencyType; + /** Optional value that the referenced parameter must have for this dependency to apply. */ conditionValue?: string; } @@ -59,6 +86,7 @@ export interface ToolMetadata {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface ParameterMetadata { + /** Arbitrary tags for categorising or filtering parameters. */ tags?: string[]; } @@ -67,44 +95,76 @@ export type ParameterType = "Flag" | "Option" | "Argument"; export type ParameterDataType = "String" | "Number" | "Boolean" | "Enum"; export interface Parameter { + /** Unique identifier for this parameter within the tool. */ key: string; + /** Human-readable display name of the parameter. */ name: string; + /** Key of the command this parameter belongs to; omit for global parameters. */ commandKey?: string; + /** Brief description of what this parameter does or accepts. */ description?: string; + /** Optional grouping label for organising related parameters in the UI. */ group?: string; + /** Additional metadata such as tags. */ metadata?: ParameterMetadata; + /** Whether this is a boolean flag, a key-value option, or a positional argument. */ parameterType: ParameterType; + /** The data type of the parameter's value. */ dataType: ParameterDataType; + /** Whether the user must provide this parameter. */ isRequired?: boolean; + /** Whether this parameter can be specified multiple times. */ isRepeatable?: boolean; + /** Whether this parameter applies to all commands rather than a single command. */ isGlobal?: boolean; + /** The single-character short flag (e.g. "-v"). */ shortFlag?: string; + /** The long-form flag or option name (e.g. "--verbose"). */ longFlag?: string; + /** Zero-based position index for positional arguments. */ position?: number; + /** Display sort position relative to sibling parameters. */ sortOrder?: number; + /** Separator character used when the option accepts multiple values in one argument (e.g. ","). */ arraySeparator?: string; + /** Separator between key and value for key=value style options (e.g. "="). */ keyValueSeparator?: string; + /** Allowed enum choices when dataType is "Enum". */ enum?: ParameterEnumValues; + /** Validation rules applied to this parameter's value. */ validations?: ParameterValidation[]; + /** Dependencies on other parameters (requires or conflicts-with relationships). */ dependencies?: ParameterDependency[]; } export type ExclusionType = "mutual_exclusive" | "required_one_of"; export interface ExclusionGroup { + /** Unique identifier for this exclusion group. */ key?: string; + /** Key of the command this exclusion group belongs to; omit for global groups. */ commandKey?: string; + /** Human-readable name for this exclusion group. */ name: string; + /** Whether parameters in this group are mutually exclusive or one is required. */ exclusionType: ExclusionType; + /** Keys of the parameters that participate in this exclusion group. */ parameterKeys: string[]; } export interface Tool { + /** Unique machine-readable identifier for the tool (e.g. "httpx"). */ name: string; + /** Human-readable display name for the tool (e.g. "HTTPx"). */ displayName: string; + /** General information about the tool such as description, version, and URL. */ info?: ToolInfo; + /** List of all commands and subcommands defined for this tool. */ commands: Command[]; + /** Flat list of all parameters across all commands and global scope. */ parameters: Parameter[]; + /** Groups of parameters with mutual exclusion or required-one-of constraints. */ exclusionGroups?: ExclusionGroup[]; + /** Arbitrary metadata attached to the tool. */ metadata?: ToolMetadata; } diff --git a/registry/commandly/types/nested.ts b/registry/commandly/types/nested.ts index 8606997..ede55d3 100644 --- a/registry/commandly/types/nested.ts +++ b/registry/commandly/types/nested.ts @@ -11,62 +11,105 @@ import type { } from "@/components/commandly/types/flat"; export interface NestedParameterValidation { + /** The type of validation to apply. */ validationType: ParameterValidationType; + /** The value to validate against (e.g. max length number or regex pattern). */ validationValue: string; + /** The error message to display when validation fails. */ errorMessage: string; } export interface NestedParameterDependency { + /** Name of the parameter this dependency references. */ dependsOnParameter: string; + /** Whether this parameter requires or conflicts with the referenced parameter. */ dependencyType: ParameterDependencyType; + /** Optional value that the referenced parameter must have for this dependency to apply. */ conditionValue?: string; } export interface NestedParameter { + /** Human-readable display name of the parameter. */ name: string; + /** Brief description of what this parameter does or accepts. */ description?: string; + /** Optional grouping label for organising related parameters in the UI. */ group?: string; + /** Whether this is a boolean flag, a key-value option, or a positional argument. */ parameterType: ParameterType; + /** The data type of the parameter's value. */ dataType: ParameterDataType; + /** Additional metadata such as tags. */ metadata?: ParameterMetadata; + /** Whether the user must provide this parameter. */ isRequired?: boolean; + /** Whether this parameter can be specified multiple times. */ isRepeatable?: boolean; + /** Whether this parameter applies to all commands rather than a single command. */ isGlobal?: boolean; + /** The single-character short flag (e.g. "-v"). */ shortFlag?: string; + /** The long-form flag or option name (e.g. "--verbose"). */ longFlag?: string; + /** Zero-based position index for positional arguments. */ position?: number; + /** Display sort position relative to sibling parameters. */ sortOrder?: number; + /** Separator character used when the option accepts multiple values in one argument (e.g. ","). */ arraySeparator?: string; + /** Separator between key and value for key=value style options (e.g. "="). */ keyValueSeparator?: string; + /** Allowed enum choices when dataType is "Enum". */ enum?: ParameterEnumValues; + /** Validation rules applied to this parameter's value. */ validations?: NestedParameterValidation[]; + /** Dependencies on other parameters (requires or conflicts-with relationships). */ dependencies?: NestedParameterDependency[]; } export interface NestedCommand { + /** Human-readable display name of the command. */ name: string; + /** Brief description of what this command does. */ description?: string; + /** Whether this command opens an interactive session or prompt. */ interactive?: boolean; + /** Whether this is the default command when no subcommand is specified. */ isDefault: boolean; + /** Display sort position relative to sibling commands. */ sortOrder: number; + /** Parameters that belong directly to this command. */ parameters: NestedParameter[]; + /** Nested subcommands of this command. */ subcommands: NestedCommand[]; } export interface NestedExclusionGroup { + /** Human-readable name for this exclusion group. */ name: string; + /** Whether parameters in this group are mutually exclusive or one is required. */ exclusionType: ExclusionType; + /** Names of the parameters that participate in this exclusion group. */ parameters: string[]; } export interface NestedTool { + /** Optional JSON schema URI for validation. */ $schema?: string; + /** Unique machine-readable identifier for the tool (e.g. "httpx"). */ name: string; + /** Human-readable display name for the tool (e.g. "HTTPx"). */ displayName: string; + /** General information about the tool such as description, version, and URL. */ info?: ToolInfo; + /** The homepage or documentation URL for the tool. */ url?: string; + /** Parameters that apply to all commands globally. */ globalParameters: NestedParameter[]; + /** Hierarchical list of commands and their nested subcommands. */ commands: NestedCommand[]; + /** Groups of parameters with mutual exclusion or required-one-of constraints. */ exclusionGroups?: NestedExclusionGroup[] | null; + /** Arbitrary metadata attached to the tool. */ metadata?: ToolMetadata; } diff --git a/scripts/generate-tools-json.ts b/scripts/generate-tools-json.ts index 2dd8cea..f18091d 100644 --- a/scripts/generate-tools-json.ts +++ b/scripts/generate-tools-json.ts @@ -13,7 +13,7 @@ const tools = files.sort().map((file) => { name: tool.name, displayName: tool.displayName || tool.name, description: tool.info?.description, - url: tool.info?.url, + info: tool.info }; }); diff --git a/src/components/ai-elements/chain-of-thought.tsx b/src/components/ai-elements/chain-of-thought.tsx new file mode 100644 index 0000000..914aa3e --- /dev/null +++ b/src/components/ai-elements/chain-of-thought.tsx @@ -0,0 +1,222 @@ +"use client"; + +import { useControllableState } from "@radix-ui/react-use-controllable-state"; +import { Badge } from "@/components/ui/badge"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { cn } from "@/lib/utils"; +import type { LucideIcon } from "lucide-react"; +import { BrainIcon, ChevronDownIcon, DotIcon } from "lucide-react"; +import type { ComponentProps, ReactNode } from "react"; +import { createContext, memo, useContext, useMemo } from "react"; + +interface ChainOfThoughtContextValue { + isOpen: boolean; + setIsOpen: (open: boolean) => void; +} + +const ChainOfThoughtContext = createContext( + null +); + +const useChainOfThought = () => { + const context = useContext(ChainOfThoughtContext); + if (!context) { + throw new Error( + "ChainOfThought components must be used within ChainOfThought" + ); + } + return context; +}; + +export type ChainOfThoughtProps = ComponentProps<"div"> & { + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; +}; + +export const ChainOfThought = memo( + ({ + className, + open, + defaultOpen = false, + onOpenChange, + children, + ...props + }: ChainOfThoughtProps) => { + const [isOpen, setIsOpen] = useControllableState({ + defaultProp: defaultOpen, + onChange: onOpenChange, + prop: open, + }); + + const chainOfThoughtContext = useMemo( + () => ({ isOpen, setIsOpen }), + [isOpen, setIsOpen] + ); + + return ( + +
+ {children} +
+
+ ); + } +); + +export type ChainOfThoughtHeaderProps = ComponentProps< + typeof CollapsibleTrigger +>; + +export const ChainOfThoughtHeader = memo( + ({ className, children, ...props }: ChainOfThoughtHeaderProps) => { + const { isOpen, setIsOpen } = useChainOfThought(); + + return ( + + + + + {children ?? "Chain of Thought"} + + + + + ); + } +); + +export type ChainOfThoughtStepProps = ComponentProps<"div"> & { + icon?: LucideIcon; + label: ReactNode; + description?: ReactNode; + status?: "complete" | "active" | "pending"; +}; + +const stepStatusStyles = { + active: "text-foreground", + complete: "text-muted-foreground", + pending: "text-muted-foreground/50", +}; + +export const ChainOfThoughtStep = memo( + ({ + className, + icon: Icon = DotIcon, + label, + description, + status = "complete", + children, + ...props + }: ChainOfThoughtStepProps) => ( +
+
+ +
+
+
+
{label}
+ {description && ( +
{description}
+ )} + {children} +
+
+ ) +); + +export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">; + +export const ChainOfThoughtSearchResults = memo( + ({ className, ...props }: ChainOfThoughtSearchResultsProps) => ( +
+ ) +); + +export type ChainOfThoughtSearchResultProps = ComponentProps; + +export const ChainOfThoughtSearchResult = memo( + ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => ( + + {children} + + ) +); + +export type ChainOfThoughtContentProps = ComponentProps< + typeof CollapsibleContent +>; + +export const ChainOfThoughtContent = memo( + ({ className, children, ...props }: ChainOfThoughtContentProps) => { + const { isOpen } = useChainOfThought(); + + return ( + + + {children} + + + ); + } +); + +export type ChainOfThoughtImageProps = ComponentProps<"div"> & { + caption?: string; +}; + +export const ChainOfThoughtImage = memo( + ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => ( +
+
+ {children} +
+ {caption &&

{caption}

} +
+ ) +); + +ChainOfThought.displayName = "ChainOfThought"; +ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader"; +ChainOfThoughtStep.displayName = "ChainOfThoughtStep"; +ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults"; +ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult"; +ChainOfThoughtContent.displayName = "ChainOfThoughtContent"; +ChainOfThoughtImage.displayName = "ChainOfThoughtImage"; diff --git a/src/components/ai-elements/context.tsx b/src/components/ai-elements/context.tsx new file mode 100644 index 0000000..1327616 --- /dev/null +++ b/src/components/ai-elements/context.tsx @@ -0,0 +1,409 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { Progress } from "@/components/ui/progress"; +import { cn } from "@/lib/utils"; +import type { LanguageModelUsage } from "ai"; +import type { ComponentProps } from "react"; +import { createContext, useContext, useMemo } from "react"; +import { getUsage } from "tokenlens"; + +const PERCENT_MAX = 100; +const ICON_RADIUS = 10; +const ICON_VIEWBOX = 24; +const ICON_CENTER = 12; +const ICON_STROKE_WIDTH = 2; + +type ModelId = string; + +interface ContextSchema { + usedTokens: number; + maxTokens: number; + usage?: LanguageModelUsage; + modelId?: ModelId; +} + +const ContextContext = createContext(null); + +const useContextValue = () => { + const context = useContext(ContextContext); + + if (!context) { + throw new Error("Context components must be used within Context"); + } + + return context; +}; + +export type ContextProps = ComponentProps & ContextSchema; + +export const Context = ({ + usedTokens, + maxTokens, + usage, + modelId, + ...props +}: ContextProps) => { + const contextValue = useMemo( + () => ({ maxTokens, modelId, usage, usedTokens }), + [maxTokens, modelId, usage, usedTokens] + ); + + return ( + + + + ); +}; + +const ContextIcon = () => { + const { usedTokens, maxTokens } = useContextValue(); + const circumference = 2 * Math.PI * ICON_RADIUS; + const usedPercent = usedTokens / maxTokens; + const dashOffset = circumference * (1 - usedPercent); + + return ( + + + + + ); +}; + +export type ContextTriggerProps = ComponentProps; + +export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => { + const { usedTokens, maxTokens } = useContextValue(); + const usedPercent = usedTokens / maxTokens; + const renderedPercent = new Intl.NumberFormat("en-US", { + maximumFractionDigits: 1, + style: "percent", + }).format(usedPercent); + + return ( + + {children ?? ( + + )} + + ); +}; + +export type ContextContentProps = ComponentProps; + +export const ContextContent = ({ + className, + ...props +}: ContextContentProps) => ( + +); + +export type ContextContentHeaderProps = ComponentProps<"div">; + +export const ContextContentHeader = ({ + children, + className, + ...props +}: ContextContentHeaderProps) => { + const { usedTokens, maxTokens } = useContextValue(); + const usedPercent = usedTokens / maxTokens; + const displayPct = new Intl.NumberFormat("en-US", { + maximumFractionDigits: 1, + style: "percent", + }).format(usedPercent); + const used = new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(usedTokens); + const total = new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(maxTokens); + + return ( +
+ {children ?? ( + <> +
+

{displayPct}

+

+ {used} / {total} +

+
+
+ +
+ + )} +
+ ); +}; + +export type ContextContentBodyProps = ComponentProps<"div">; + +export const ContextContentBody = ({ + children, + className, + ...props +}: ContextContentBodyProps) => ( +
+ {children} +
+); + +export type ContextContentFooterProps = ComponentProps<"div">; + +export const ContextContentFooter = ({ + children, + className, + ...props +}: ContextContentFooterProps) => { + const { modelId, usage } = useContextValue(); + const costUSD = modelId + ? getUsage({ + modelId, + usage: { + input: usage?.inputTokens ?? 0, + output: usage?.outputTokens ?? 0, + }, + }).costUSD?.totalUSD + : undefined; + const totalCost = new Intl.NumberFormat("en-US", { + currency: "USD", + style: "currency", + }).format(costUSD ?? 0); + + return ( +
+ {children ?? ( + <> + Total cost + {totalCost} + + )} +
+ ); +}; + +const TokensWithCost = ({ + tokens, + costText, +}: { + tokens?: number; + costText?: string; +}) => ( + + {tokens === undefined + ? "—" + : new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(tokens)} + {costText ? ( + • {costText} + ) : null} + +); + +export type ContextInputUsageProps = ComponentProps<"div">; + +export const ContextInputUsage = ({ + className, + children, + ...props +}: ContextInputUsageProps) => { + const { usage, modelId } = useContextValue(); + const inputTokens = usage?.inputTokens ?? 0; + + if (children) { + return children; + } + + if (!inputTokens) { + return null; + } + + const inputCost = modelId + ? getUsage({ + modelId, + usage: { input: inputTokens, output: 0 }, + }).costUSD?.totalUSD + : undefined; + const inputCostText = new Intl.NumberFormat("en-US", { + currency: "USD", + style: "currency", + }).format(inputCost ?? 0); + + return ( +
+ Input + +
+ ); +}; + +export type ContextOutputUsageProps = ComponentProps<"div">; + +export const ContextOutputUsage = ({ + className, + children, + ...props +}: ContextOutputUsageProps) => { + const { usage, modelId } = useContextValue(); + const outputTokens = usage?.outputTokens ?? 0; + + if (children) { + return children; + } + + if (!outputTokens) { + return null; + } + + const outputCost = modelId + ? getUsage({ + modelId, + usage: { input: 0, output: outputTokens }, + }).costUSD?.totalUSD + : undefined; + const outputCostText = new Intl.NumberFormat("en-US", { + currency: "USD", + style: "currency", + }).format(outputCost ?? 0); + + return ( +
+ Output + +
+ ); +}; + +export type ContextReasoningUsageProps = ComponentProps<"div">; + +export const ContextReasoningUsage = ({ + className, + children, + ...props +}: ContextReasoningUsageProps) => { + const { usage, modelId } = useContextValue(); + const reasoningTokens = usage?.reasoningTokens ?? 0; + + if (children) { + return children; + } + + if (!reasoningTokens) { + return null; + } + + const reasoningCost = modelId + ? getUsage({ + modelId, + usage: { reasoningTokens }, + }).costUSD?.totalUSD + : undefined; + const reasoningCostText = new Intl.NumberFormat("en-US", { + currency: "USD", + style: "currency", + }).format(reasoningCost ?? 0); + + return ( +
+ Reasoning + +
+ ); +}; + +export type ContextCacheUsageProps = ComponentProps<"div">; + +export const ContextCacheUsage = ({ + className, + children, + ...props +}: ContextCacheUsageProps) => { + const { usage, modelId } = useContextValue(); + const cacheTokens = usage?.cachedInputTokens ?? 0; + + if (children) { + return children; + } + + if (!cacheTokens) { + return null; + } + + const cacheCost = modelId + ? getUsage({ + modelId, + usage: { cacheReads: cacheTokens, input: 0, output: 0 }, + }).costUSD?.totalUSD + : undefined; + const cacheCostText = new Intl.NumberFormat("en-US", { + currency: "USD", + style: "currency", + }).format(cacheCost ?? 0); + + return ( +
+ Cache + +
+ ); +}; diff --git a/src/components/ai-elements/prompt-input.tsx b/src/components/ai-elements/prompt-input.tsx new file mode 100644 index 0000000..512bec6 --- /dev/null +++ b/src/components/ai-elements/prompt-input.tsx @@ -0,0 +1,1463 @@ +"use client"; + +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from "@/components/ui/command"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { + InputGroup, + InputGroupAddon, + InputGroupButton, + InputGroupTextarea, +} from "@/components/ui/input-group"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Spinner } from "@/components/ui/spinner"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import type { ChatStatus, FileUIPart, SourceDocumentUIPart } from "ai"; +import { + ArrowUpIcon, + ImageIcon, + MonitorIcon, + PlusIcon, + SquareIcon, + XIcon, +} from "lucide-react"; +import { nanoid } from "nanoid"; +import type { + ChangeEvent, + ChangeEventHandler, + ClipboardEventHandler, + ComponentProps, + FormEvent, + FormEventHandler, + HTMLAttributes, + KeyboardEventHandler, + PropsWithChildren, + ReactNode, + RefObject, +} from "react"; +import { + Children, + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; + +// ============================================================================ +// Helpers +// ============================================================================ + +const convertBlobUrlToDataUrl = async (url: string): Promise => { + try { + const response = await fetch(url); + const blob = await response.blob(); + // FileReader uses callback-based API, wrapping in Promise is necessary + // oxlint-disable-next-line eslint-plugin-promise(avoid-new) + return new Promise((resolve) => { + const reader = new FileReader(); + // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener) + reader.onloadend = () => resolve(reader.result as string); + // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener) + reader.onerror = () => resolve(null); + reader.readAsDataURL(blob); + }); + } catch { + return null; + } +}; + +const captureScreenshot = async (): Promise => { + if ( + typeof navigator === "undefined" || + !navigator.mediaDevices?.getDisplayMedia + ) { + return null; + } + + let stream: MediaStream | null = null; + const video = document.createElement("video"); + video.muted = true; + video.playsInline = true; + + try { + stream = await navigator.mediaDevices.getDisplayMedia({ + audio: false, + video: true, + }); + + video.srcObject = stream; + + // Video element uses callback-based API, wrapping in Promise is necessary + // oxlint-disable-next-line eslint-plugin-promise(avoid-new) + await new Promise((resolve, reject) => { + // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener) + video.onloadedmetadata = () => resolve(); + // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener) + video.onerror = () => reject(new Error("Failed to load screen stream")); + }); + + await video.play(); + + const width = video.videoWidth; + const height = video.videoHeight; + if (!width || !height) { + return null; + } + + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + const context = canvas.getContext("2d"); + if (!context) { + return null; + } + + context.drawImage(video, 0, 0, width, height); + // canvas.toBlob uses callback-based API, wrapping in Promise is necessary + // oxlint-disable-next-line eslint-plugin-promise(avoid-new) + const blob = await new Promise((resolve) => { + canvas.toBlob(resolve, "image/png"); + }); + if (!blob) { + return null; + } + + const timestamp = new Date() + .toISOString() + .replaceAll(/[:.]/g, "-") + .replace("T", "_") + .replace("Z", ""); + + return new File([blob], `screenshot-${timestamp}.png`, { + lastModified: Date.now(), + type: "image/png", + }); + } finally { + if (stream) { + for (const track of stream.getTracks()) { + track.stop(); + } + } + video.pause(); + video.srcObject = null; + } +}; + +// ============================================================================ +// Provider Context & Types +// ============================================================================ + +export interface AttachmentsContext { + files: (FileUIPart & { id: string })[]; + add: (files: File[] | FileList) => void; + remove: (id: string) => void; + clear: () => void; + openFileDialog: () => void; + fileInputRef: RefObject; +} + +export interface TextInputContext { + value: string; + setInput: (v: string) => void; + clear: () => void; +} + +export interface PromptInputControllerProps { + textInput: TextInputContext; + attachments: AttachmentsContext; + /** INTERNAL: Allows PromptInput to register its file textInput + "open" callback */ + __registerFileInput: ( + ref: RefObject, + open: () => void + ) => void; +} + +const PromptInputController = createContext( + null +); +const ProviderAttachmentsContext = createContext( + null +); + +export const usePromptInputController = () => { + const ctx = useContext(PromptInputController); + if (!ctx) { + throw new Error( + "Wrap your component inside to use usePromptInputController()." + ); + } + return ctx; +}; + +// Optional variants (do NOT throw). Useful for dual-mode components. +const useOptionalPromptInputController = () => + useContext(PromptInputController); + +export const useProviderAttachments = () => { + const ctx = useContext(ProviderAttachmentsContext); + if (!ctx) { + throw new Error( + "Wrap your component inside to use useProviderAttachments()." + ); + } + return ctx; +}; + +const useOptionalProviderAttachments = () => + useContext(ProviderAttachmentsContext); + +export type PromptInputProviderProps = PropsWithChildren<{ + initialInput?: string; +}>; + +/** + * Optional global provider that lifts PromptInput state outside of PromptInput. + * If you don't use it, PromptInput stays fully self-managed. + */ +export const PromptInputProvider = ({ + initialInput: initialTextInput = "", + children, +}: PromptInputProviderProps) => { + // ----- textInput state + const [textInput, setTextInput] = useState(initialTextInput); + const clearInput = useCallback(() => setTextInput(""), []); + + // ----- attachments state (global when wrapped) + const [attachmentFiles, setAttachmentFiles] = useState< + (FileUIPart & { id: string })[] + >([]); + const fileInputRef = useRef(null); + // oxlint-disable-next-line eslint(no-empty-function) + const openRef = useRef<() => void>(() => {}); + + const add = useCallback((files: File[] | FileList) => { + const incoming = [...files]; + if (incoming.length === 0) { + return; + } + + setAttachmentFiles((prev) => [ + ...prev, + ...incoming.map((file) => ({ + filename: file.name, + id: nanoid(), + mediaType: file.type, + type: "file" as const, + url: URL.createObjectURL(file), + })), + ]); + }, []); + + const remove = useCallback((id: string) => { + setAttachmentFiles((prev) => { + const found = prev.find((f) => f.id === id); + if (found?.url) { + URL.revokeObjectURL(found.url); + } + return prev.filter((f) => f.id !== id); + }); + }, []); + + const clear = useCallback(() => { + setAttachmentFiles((prev) => { + for (const f of prev) { + if (f.url) { + URL.revokeObjectURL(f.url); + } + } + return []; + }); + }, []); + + // Keep a ref to attachments for cleanup on unmount (avoids stale closure) + const attachmentsRef = useRef(attachmentFiles); + + useEffect(() => { + attachmentsRef.current = attachmentFiles; + }, [attachmentFiles]); + + // Cleanup blob URLs on unmount to prevent memory leaks + useEffect( + () => () => { + for (const f of attachmentsRef.current) { + if (f.url) { + URL.revokeObjectURL(f.url); + } + } + }, + [] + ); + + const openFileDialog = useCallback(() => { + openRef.current?.(); + }, []); + + const attachments = useMemo( + () => ({ + add, + clear, + fileInputRef, + files: attachmentFiles, + openFileDialog, + remove, + }), + [attachmentFiles, add, remove, clear, openFileDialog] + ); + + const __registerFileInput = useCallback( + (ref: RefObject, open: () => void) => { + fileInputRef.current = ref.current; + openRef.current = open; + }, + [] + ); + + const controller = useMemo( + () => ({ + __registerFileInput, + attachments, + textInput: { + clear: clearInput, + setInput: setTextInput, + value: textInput, + }, + }), + [textInput, clearInput, attachments, __registerFileInput] + ); + + return ( + + + {children} + + + ); +}; + +// ============================================================================ +// Component Context & Hooks +// ============================================================================ + +const LocalAttachmentsContext = createContext(null); + +export const usePromptInputAttachments = () => { + // Prefer local context (inside PromptInput) as it has validation, fall back to provider + const provider = useOptionalProviderAttachments(); + const local = useContext(LocalAttachmentsContext); + const context = local ?? provider; + if (!context) { + throw new Error( + "usePromptInputAttachments must be used within a PromptInput or PromptInputProvider" + ); + } + return context; +}; + +// ============================================================================ +// Referenced Sources (Local to PromptInput) +// ============================================================================ + +export interface ReferencedSourcesContext { + sources: (SourceDocumentUIPart & { id: string })[]; + add: (sources: SourceDocumentUIPart[] | SourceDocumentUIPart) => void; + remove: (id: string) => void; + clear: () => void; +} + +export const LocalReferencedSourcesContext = + createContext(null); + +export const usePromptInputReferencedSources = () => { + const ctx = useContext(LocalReferencedSourcesContext); + if (!ctx) { + throw new Error( + "usePromptInputReferencedSources must be used within a LocalReferencedSourcesContext.Provider" + ); + } + return ctx; +}; + +export type PromptInputActionAddAttachmentsProps = ComponentProps< + typeof DropdownMenuItem +> & { + label?: string; +}; + +export const PromptInputActionAddAttachments = ({ + label = "Add photos or files", + ...props +}: PromptInputActionAddAttachmentsProps) => { + const attachments = usePromptInputAttachments(); + + const handleSelect = useCallback( + (e: Event) => { + e.preventDefault(); + attachments.openFileDialog(); + }, + [attachments] + ); + + return ( + + {label} + + ); +}; + +export type PromptInputActionAddScreenshotProps = ComponentProps< + typeof DropdownMenuItem +> & { + label?: string; +}; + +export const PromptInputActionAddScreenshot = ({ + label = "Take screenshot", + onSelect, + ...props +}: PromptInputActionAddScreenshotProps) => { + const attachments = usePromptInputAttachments(); + + const handleSelect = useCallback( + async (event: Event) => { + onSelect?.(event); + if (event.defaultPrevented) { + return; + } + + try { + const screenshot = await captureScreenshot(); + if (screenshot) { + attachments.add([screenshot]); + } + } catch (error) { + if ( + error instanceof DOMException && + (error.name === "NotAllowedError" || error.name === "AbortError") + ) { + return; + } + throw error; + } + }, + [onSelect, attachments] + ); + + return ( + + + {label} + + ); +}; + +export interface PromptInputMessage { + text: string; + files: FileUIPart[]; +} + +export type PromptInputProps = Omit< + HTMLAttributes, + "onSubmit" | "onError" +> & { + // e.g., "image/*" or leave undefined for any + accept?: string; + multiple?: boolean; + // When true, accepts drops anywhere on document. Default false (opt-in). + globalDrop?: boolean; + // Render a hidden input with given name and keep it in sync for native form posts. Default false. + syncHiddenInput?: boolean; + // Minimal constraints + maxFiles?: number; + // bytes + maxFileSize?: number; + onError?: (err: { + code: "max_files" | "max_file_size" | "accept"; + message: string; + }) => void; + onSubmit: ( + message: PromptInputMessage, + event: FormEvent + ) => void | Promise; +}; + +export const PromptInput = ({ + className, + accept, + multiple, + globalDrop, + syncHiddenInput, + maxFiles, + maxFileSize, + onError, + onSubmit, + children, + ...props +}: PromptInputProps) => { + // Try to use a provider controller if present + const controller = useOptionalPromptInputController(); + const usingProvider = !!controller; + + // Refs + const inputRef = useRef(null); + const formRef = useRef(null); + + // ----- Local attachments (only used when no provider) + const [items, setItems] = useState<(FileUIPart & { id: string })[]>([]); + const files = usingProvider ? controller.attachments.files : items; + + // ----- Local referenced sources (always local to PromptInput) + const [referencedSources, setReferencedSources] = useState< + (SourceDocumentUIPart & { id: string })[] + >([]); + + // Keep a ref to files for cleanup on unmount (avoids stale closure) + const filesRef = useRef(files); + + useEffect(() => { + filesRef.current = files; + }, [files]); + + const openFileDialogLocal = useCallback(() => { + inputRef.current?.click(); + }, []); + + const matchesAccept = useCallback( + (f: File) => { + if (!accept || accept.trim() === "") { + return true; + } + + const patterns = accept + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + + return patterns.some((pattern) => { + if (pattern.endsWith("/*")) { + // e.g: image/* -> image/ + const prefix = pattern.slice(0, -1); + return f.type.startsWith(prefix); + } + return f.type === pattern; + }); + }, + [accept] + ); + + const addLocal = useCallback( + (fileList: File[] | FileList) => { + const incoming = [...fileList]; + const accepted = incoming.filter((f) => matchesAccept(f)); + if (incoming.length && accepted.length === 0) { + onError?.({ + code: "accept", + message: "No files match the accepted types.", + }); + return; + } + const withinSize = (f: File) => + maxFileSize ? f.size <= maxFileSize : true; + const sized = accepted.filter(withinSize); + if (accepted.length > 0 && sized.length === 0) { + onError?.({ + code: "max_file_size", + message: "All files exceed the maximum size.", + }); + return; + } + + setItems((prev) => { + const capacity = + typeof maxFiles === "number" + ? Math.max(0, maxFiles - prev.length) + : undefined; + const capped = + typeof capacity === "number" ? sized.slice(0, capacity) : sized; + if (typeof capacity === "number" && sized.length > capacity) { + onError?.({ + code: "max_files", + message: "Too many files. Some were not added.", + }); + } + const next: (FileUIPart & { id: string })[] = []; + for (const file of capped) { + next.push({ + filename: file.name, + id: nanoid(), + mediaType: file.type, + type: "file", + url: URL.createObjectURL(file), + }); + } + return [...prev, ...next]; + }); + }, + [matchesAccept, maxFiles, maxFileSize, onError] + ); + + const removeLocal = useCallback( + (id: string) => + setItems((prev) => { + const found = prev.find((file) => file.id === id); + if (found?.url) { + URL.revokeObjectURL(found.url); + } + return prev.filter((file) => file.id !== id); + }), + [] + ); + + // Wrapper that validates files before calling provider's add + const addWithProviderValidation = useCallback( + (fileList: File[] | FileList) => { + const incoming = [...fileList]; + const accepted = incoming.filter((f) => matchesAccept(f)); + if (incoming.length && accepted.length === 0) { + onError?.({ + code: "accept", + message: "No files match the accepted types.", + }); + return; + } + const withinSize = (f: File) => + maxFileSize ? f.size <= maxFileSize : true; + const sized = accepted.filter(withinSize); + if (accepted.length > 0 && sized.length === 0) { + onError?.({ + code: "max_file_size", + message: "All files exceed the maximum size.", + }); + return; + } + + const currentCount = files.length; + const capacity = + typeof maxFiles === "number" + ? Math.max(0, maxFiles - currentCount) + : undefined; + const capped = + typeof capacity === "number" ? sized.slice(0, capacity) : sized; + if (typeof capacity === "number" && sized.length > capacity) { + onError?.({ + code: "max_files", + message: "Too many files. Some were not added.", + }); + } + + if (capped.length > 0) { + controller?.attachments.add(capped); + } + }, + [matchesAccept, maxFileSize, maxFiles, onError, files.length, controller] + ); + + const clearAttachments = useCallback( + () => + usingProvider + ? controller?.attachments.clear() + : setItems((prev) => { + for (const file of prev) { + if (file.url) { + URL.revokeObjectURL(file.url); + } + } + return []; + }), + [usingProvider, controller] + ); + + const clearReferencedSources = useCallback( + () => setReferencedSources([]), + [] + ); + + const add = usingProvider ? addWithProviderValidation : addLocal; + const remove = usingProvider ? controller.attachments.remove : removeLocal; + const openFileDialog = usingProvider + ? controller.attachments.openFileDialog + : openFileDialogLocal; + + const clear = useCallback(() => { + clearAttachments(); + clearReferencedSources(); + }, [clearAttachments, clearReferencedSources]); + + // Let provider know about our hidden file input so external menus can call openFileDialog() + useEffect(() => { + if (!usingProvider) { + return; + } + controller.__registerFileInput(inputRef, () => inputRef.current?.click()); + }, [usingProvider, controller]); + + // Note: File input cannot be programmatically set for security reasons + // The syncHiddenInput prop is no longer functional + useEffect(() => { + if (syncHiddenInput && inputRef.current && files.length === 0) { + inputRef.current.value = ""; + } + }, [files, syncHiddenInput]); + + // Attach drop handlers on nearest form and document (opt-in) + useEffect(() => { + const form = formRef.current; + if (!form) { + return; + } + if (globalDrop) { + // when global drop is on, let the document-level handler own drops + return; + } + + const onDragOver = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + }; + const onDrop = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { + add(e.dataTransfer.files); + } + }; + form.addEventListener("dragover", onDragOver); + form.addEventListener("drop", onDrop); + return () => { + form.removeEventListener("dragover", onDragOver); + form.removeEventListener("drop", onDrop); + }; + }, [add, globalDrop]); + + useEffect(() => { + if (!globalDrop) { + return; + } + + const onDragOver = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + }; + const onDrop = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { + add(e.dataTransfer.files); + } + }; + document.addEventListener("dragover", onDragOver); + document.addEventListener("drop", onDrop); + return () => { + document.removeEventListener("dragover", onDragOver); + document.removeEventListener("drop", onDrop); + }; + }, [add, globalDrop]); + + useEffect( + () => () => { + if (!usingProvider) { + for (const f of filesRef.current) { + if (f.url) { + URL.revokeObjectURL(f.url); + } + } + } + }, + [usingProvider] + ); + + const handleChange: ChangeEventHandler = useCallback( + (event) => { + if (event.currentTarget.files) { + add(event.currentTarget.files); + } + // Reset input value to allow selecting files that were previously removed + event.currentTarget.value = ""; + }, + [add] + ); + + const attachmentsCtx = useMemo( + () => ({ + add, + clear: clearAttachments, + fileInputRef: inputRef, + files: files.map((item) => ({ ...item, id: item.id })), + openFileDialog, + remove, + }), + [files, add, remove, clearAttachments, openFileDialog] + ); + + const refsCtx = useMemo( + () => ({ + add: (incoming: SourceDocumentUIPart[] | SourceDocumentUIPart) => { + const array = Array.isArray(incoming) ? incoming : [incoming]; + setReferencedSources((prev) => [ + ...prev, + ...array.map((s) => ({ ...s, id: nanoid() })), + ]); + }, + clear: clearReferencedSources, + remove: (id: string) => { + setReferencedSources((prev) => prev.filter((s) => s.id !== id)); + }, + sources: referencedSources, + }), + [referencedSources, clearReferencedSources] + ); + + const handleSubmit: FormEventHandler = useCallback( + async (event) => { + event.preventDefault(); + + const form = event.currentTarget; + const text = usingProvider + ? controller.textInput.value + : (() => { + const formData = new FormData(form); + return (formData.get("message") as string) || ""; + })(); + + // Reset form immediately after capturing text to avoid race condition + // where user input during async blob conversion would be lost + if (!usingProvider) { + form.reset(); + } + + try { + // Convert blob URLs to data URLs asynchronously + const convertedFiles: FileUIPart[] = await Promise.all( + files.map(async ({ id: _id, ...item }) => { + if (item.url?.startsWith("blob:")) { + const dataUrl = await convertBlobUrlToDataUrl(item.url); + // If conversion failed, keep the original blob URL + return { + ...item, + url: dataUrl ?? item.url, + }; + } + return item; + }) + ); + + const result = onSubmit({ files: convertedFiles, text }, event); + + // Handle both sync and async onSubmit + if (result instanceof Promise) { + try { + await result; + clear(); + if (usingProvider) { + controller.textInput.clear(); + } + } catch { + // Don't clear on error - user may want to retry + } + } else { + // Sync function completed without throwing, clear inputs + clear(); + if (usingProvider) { + controller.textInput.clear(); + } + } + } catch { + // Don't clear on error - user may want to retry + } + }, + [usingProvider, controller, files, onSubmit, clear] + ); + + // Render with or without local provider + const inner = ( + <> + +
+ {children} +
+ + ); + + const withReferencedSources = ( + + {inner} + + ); + + // Always provide LocalAttachmentsContext so children get validated add function + return ( + + {withReferencedSources} + + ); +}; + +export type PromptInputBodyProps = HTMLAttributes; + +export const PromptInputBody = ({ + className, + ...props +}: PromptInputBodyProps) => ( +
+); + +export type PromptInputTextareaProps = ComponentProps< + typeof InputGroupTextarea +>; + +export const PromptInputTextarea = ({ + onChange, + onKeyDown, + className, + placeholder = "What would you like to know?", + ...props +}: PromptInputTextareaProps) => { + const controller = useOptionalPromptInputController(); + const attachments = usePromptInputAttachments(); + const [isComposing, setIsComposing] = useState(false); + + const handleKeyDown: KeyboardEventHandler = useCallback( + (e) => { + // Call the external onKeyDown handler first + onKeyDown?.(e); + + // If the external handler prevented default, don't run internal logic + if (e.defaultPrevented) { + return; + } + + if (e.key === "Enter") { + if (isComposing || e.nativeEvent.isComposing) { + return; + } + if (e.shiftKey) { + return; + } + e.preventDefault(); + + // Check if the submit button is disabled before submitting + const { form } = e.currentTarget; + const submitButton = form?.querySelector( + 'button[type="submit"]' + ) as HTMLButtonElement | null; + if (submitButton?.disabled) { + return; + } + + form?.requestSubmit(); + } + + // Remove last attachment when Backspace is pressed and textarea is empty + if ( + e.key === "Backspace" && + e.currentTarget.value === "" && + attachments.files.length > 0 + ) { + e.preventDefault(); + const lastAttachment = attachments.files.at(-1); + if (lastAttachment) { + attachments.remove(lastAttachment.id); + } + } + }, + [onKeyDown, isComposing, attachments] + ); + + const handlePaste: ClipboardEventHandler = useCallback( + (event) => { + const items = event.clipboardData?.items; + + if (!items) { + return; + } + + const files: File[] = []; + + for (const item of items) { + if (item.kind === "file") { + const file = item.getAsFile(); + if (file) { + files.push(file); + } + } + } + + if (files.length > 0) { + event.preventDefault(); + attachments.add(files); + } + }, + [attachments] + ); + + const handleCompositionEnd = useCallback(() => setIsComposing(false), []); + const handleCompositionStart = useCallback(() => setIsComposing(true), []); + + const controlledProps = controller + ? { + onChange: (e: ChangeEvent) => { + controller.textInput.setInput(e.currentTarget.value); + onChange?.(e); + }, + value: controller.textInput.value, + } + : { + onChange, + }; + + return ( + + ); +}; + +export type PromptInputHeaderProps = Omit< + ComponentProps, + "align" +>; + +export const PromptInputHeader = ({ + className, + ...props +}: PromptInputHeaderProps) => ( + +); + +export type PromptInputFooterProps = Omit< + ComponentProps, + "align" +>; + +export const PromptInputFooter = ({ + className, + ...props +}: PromptInputFooterProps) => ( + +); + +export type PromptInputToolsProps = HTMLAttributes; + +export const PromptInputTools = ({ + className, + ...props +}: PromptInputToolsProps) => ( +
+); + +export type PromptInputButtonTooltip = + | string + | { + content: ReactNode; + shortcut?: string; + side?: ComponentProps["side"]; + }; + +export type PromptInputButtonProps = ComponentProps & { + tooltip?: PromptInputButtonTooltip; +}; + +export const PromptInputButton = ({ + variant = "ghost", + className, + size, + tooltip, + ...props +}: PromptInputButtonProps) => { + const newSize = + size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm"); + + const button = ( + + ); + + if (!tooltip) { + return button; + } + + const tooltipContent = + typeof tooltip === "string" ? tooltip : tooltip.content; + const shortcut = typeof tooltip === "string" ? undefined : tooltip.shortcut; + const side = typeof tooltip === "string" ? "top" : (tooltip.side ?? "top"); + + return ( + + {button} + + {tooltipContent} + {shortcut && ( + {shortcut} + )} + + + ); +}; + +export type PromptInputActionMenuProps = ComponentProps; +export const PromptInputActionMenu = (props: PromptInputActionMenuProps) => ( + +); + +export type PromptInputActionMenuTriggerProps = PromptInputButtonProps; + +export const PromptInputActionMenuTrigger = ({ + className, + children, + ...props +}: PromptInputActionMenuTriggerProps) => ( + + + {children ?? } + + +); + +export type PromptInputActionMenuContentProps = ComponentProps< + typeof DropdownMenuContent +>; +export const PromptInputActionMenuContent = ({ + className, + ...props +}: PromptInputActionMenuContentProps) => ( + +); + +export type PromptInputActionMenuItemProps = ComponentProps< + typeof DropdownMenuItem +>; +export const PromptInputActionMenuItem = ({ + className, + ...props +}: PromptInputActionMenuItemProps) => ( + +); + +// Note: Actions that perform side-effects (like opening a file dialog) +// are provided in opt-in modules (e.g., prompt-input-attachments). + +export type PromptInputSubmitProps = ComponentProps & { + status?: ChatStatus; + onStop?: () => void; +}; + +export const PromptInputSubmit = ({ + className, + variant = "default", + size = "icon-sm", + status, + onStop, + onClick, + children, + ...props +}: PromptInputSubmitProps) => { + const isGenerating = status === "submitted" || status === "streaming"; + + let Icon = ; + + if (status === "submitted") { + Icon = ; + } else if (status === "streaming") { + Icon = ; + } else if (status === "error") { + Icon = ; + } + + const handleClick = useCallback( + (e: React.MouseEvent) => { + if (isGenerating && onStop) { + e.preventDefault(); + onStop(); + return; + } + onClick?.(e); + }, + [isGenerating, onStop, onClick] + ); + + return ( + + {children ?? Icon} + + ); +}; + +export type PromptInputSelectProps = ComponentProps; + +export const PromptInputSelect = (props: PromptInputSelectProps) => ( + keys.setKey(e.target.value)} - className="h-8 text-xs" - placeholder={g.keyPlaceholder} - /> -
- - keys.setSaved(c === "indeterminate" ? false : c) - } - /> - -
-
-
- ); - })} - -
-
- - Tavily (Web Search) - {chat.tavilyKeys.isSaved && ( - - )} -
- chat.tavilyKeys.setKey(e.target.value)} - className="h-8 text-xs" - placeholder="tvly-..." - /> -
- - chat.tavilyKeys.setSaved(c === "indeterminate" ? false : c) - } - /> - -
-
-
- - +
@@ -896,7 +1447,26 @@ export function AIChatPanel({ Describe what you want or ask to modify the tool

- + {chat.recentSessions.length > 0 && ( +
+

+ Recent sessions +

+
+ {chat.recentSessions.map((session) => ( + + ))} +
+
+ )} + {PROMPT_PILLS.map((pill) => ( chat.resendFromIndex(i - 1, chat.messages[i - 1]?.content ?? "")} + pendingApproval={chat.pendingApproval} + onConfirmPatch={chat.confirmPatch} + onRejectPatch={chat.rejectPatch} /> )) )} @@ -931,127 +1504,108 @@ export function AIChatPanel({ -
-
-