From 36a9e1ddbe543f4f6a0b99b28a6c0a0abd630123 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Nov 2025 22:10:27 +0100 Subject: [PATCH 01/27] add e2e tests --- .github/copilot-instructions.md | 22 +- .github/workflows/check.yml | 13 + apps/internal-storybook/package.json | 2 + .../tests/check-deps.e2e.test.ts | 55 ++ .../tests/mcp-endpoint.e2e.test.ts | 562 ++++++++++++++++++ apps/internal-storybook/tsconfig.json | 12 +- apps/internal-storybook/vitest.config.ts | 8 + package.json | 1 + pnpm-lock.yaml | 480 +++++++++++---- pnpm-workspace.yaml | 26 +- turbo.json | 4 + vitest.config.ts | 2 +- 12 files changed, 1068 insertions(+), 119 deletions(-) create mode 100644 apps/internal-storybook/tests/check-deps.e2e.test.ts create mode 100644 apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts create mode 100644 apps/internal-storybook/vitest.config.ts diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ed33bbc4..75c26c32 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -89,9 +89,25 @@ The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic: **Testing:** -- Only `packages/mcp` has tests (Vitest with coverage) -- Run `pnpm test run --coverage` in mcp package -- Prefer TDD when adding new tools +- **Unit tests**: `packages/mcp` has unit tests (Vitest with coverage) + - Run `pnpm test run --coverage` in mcp package + - Prefer TDD when adding new tools +- **E2E tests**: `apps/internal-storybook/tests` contains E2E tests for the addon + - Run `pnpm test:e2e` at root or in internal-storybook + - Tests verify MCP endpoint works with latest Storybook prereleases + - Uses inline snapshots for response validation + - **When to update E2E tests**: + - Adding or modifying MCP tools (update tool discovery snapshots) + - Changing MCP protocol implementation (update session init tests) + - Modifying tool responses or schemas (update tool-specific tests) + - Adding new toolsets or changing toolset behavior (update filtering tests) + - **Running tests**: + - `pnpm test:e2e` - run all E2E tests + - `pnpm vitest run -u` - update snapshots when responses change + - Tests start Storybook server automatically, wait for MCP endpoint, then run + - **Test structure**: + - `mcp-endpoint.e2e.test.ts` - MCP protocol and tool tests + - `check-deps.e2e.test.ts` - Storybook version validation **Type checking:** diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5a093921..c39ff679 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -75,6 +75,19 @@ jobs: files: | test-report.junit.xml + test-e2e: + name: E2E Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Setup Node.js and Install Dependencies + uses: ./.github/actions/setup-node-and-install + + - name: Run E2E tests + run: pnpm turbo run test:e2e + typecheck: name: Type check runs-on: ubuntu-latest diff --git a/apps/internal-storybook/package.json b/apps/internal-storybook/package.json index 9df701c5..935f836e 100644 --- a/apps/internal-storybook/package.json +++ b/apps/internal-storybook/package.json @@ -6,6 +6,7 @@ "scripts": { "build-storybook": "storybook build", "storybook": "storybook dev --port 6006 --loglevel debug --no-open", + "test": "cd ../.. && pnpm vitest --project=e2e", "typecheck": "tsc" }, "devDependencies": { @@ -18,6 +19,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "storybook": "catalog:", + "tinyexec": "^1.0.2", "vite": "catalog:" } } diff --git a/apps/internal-storybook/tests/check-deps.e2e.test.ts b/apps/internal-storybook/tests/check-deps.e2e.test.ts new file mode 100644 index 00000000..7264e9fd --- /dev/null +++ b/apps/internal-storybook/tests/check-deps.e2e.test.ts @@ -0,0 +1,55 @@ +import { describe, it } from 'vitest'; +import { x } from 'tinyexec'; + +const PACKAGES_TO_CHECK = [ + '@storybook/addon-docs', + '@storybook/react-vite', + 'storybook', +]; + +describe('Storybook Dependencies', () => { + it('should be using latest versions from registry', async () => { + const outdated: Array<{ pkg: string; current: string; latest: string }> = + []; + + for (const pkg of PACKAGES_TO_CHECK) { + // Get local version + const listResult = await x('pnpm', ['list', pkg, '--json'], { + nodeOptions: { cwd: process.cwd() }, + }); + const listData = JSON.parse(listResult.stdout); + const currentVersion = + listData[0]?.devDependencies?.[pkg]?.version || + listData[0]?.dependencies?.[pkg]?.version; + + if (!currentVersion) { + continue; + } + + // Get registry version for @next tag + const viewResult = await x('pnpm', ['view', `${pkg}@next`, 'version'], { + nodeOptions: { cwd: process.cwd() }, + }); + const latestVersion = viewResult.stdout.trim(); + + // Compare versions + if (currentVersion !== latestVersion) { + outdated.push({ pkg, current: currentVersion, latest: latestVersion }); + } + } + + // If there are outdated packages, fail the test with instructions + if (outdated.length > 0) { + const outdatedList = outdated + .map(({ pkg, current, latest }) => ` - ${pkg}: ${current} → ${latest}`) + .join('\n'); + + const currentVersion = outdated[0]!.current.replace(/\./g, '\\.'); + const latestVersion = outdated[0]!.latest; + + throw new Error( + `Storybook dependencies are outdated. Update the catalog in pnpm-workspace.yaml:\n\n sed -i '' 's/${currentVersion}/${latestVersion}/g' pnpm-workspace.yaml && pnpm install\n\nOutdated packages:\n${outdatedList}`, + ); + } + }); +}); diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts new file mode 100644 index 00000000..ac7236af --- /dev/null +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -0,0 +1,562 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { x } from 'tinyexec'; + +const STORYBOOK_DIR = new URL('..', import.meta.url).pathname; + +const MCP_ENDPOINT = 'http://localhost:6006/mcp'; +const STARTUP_TIMEOUT = 15_000; + +let storybookProcess: ReturnType | null = null; + +/** + * Helper to create MCP protocol requests + */ +function createMCPRequestBody( + method: string, + params: any = {}, + id: number = 1, +) { + return { + jsonrpc: '2.0', + id, + method, + params, + }; +} + +/** + * Helper to make MCP requests + */ +async function mcpRequest(method: string, params: any = {}, id: number = 1) { + const response = await fetch(MCP_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(createMCPRequestBody(method, params, id)), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // MCP responses come as SSE (Server-Sent Events) format + const text = await response.text(); + // Remove "data: " prefix if present + const jsonText = text.replace(/^data: /, '').trim(); + return JSON.parse(jsonText); +} + +/** + * Wait for MCP endpoint to be ready by polling it directly + */ +async function waitForMcpEndpoint( + maxAttempts = 90, + interval = 500, +): Promise { + const { promise, resolve, reject } = Promise.withResolvers(); + let attempts = 0; + + const intervalId = setInterval(async () => { + attempts++; + try { + const response = await fetch(MCP_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(createMCPRequestBody('tools/list')), + }); + if (response.ok) { + console.log('✓ MCP endpoint is ready'); + clearInterval(intervalId); + resolve(); + return; + } + } catch (error) { + // Server not ready yet + } + + if (attempts >= maxAttempts) { + clearInterval(intervalId); + reject( + new Error('MCP endpoint failed to start within the timeout period'), + ); + } + }, interval); + + return promise; +} + +describe('MCP Endpoint E2E Tests', () => { + beforeAll(async () => { + console.log('Starting Storybook dev server...'); + + // Start Storybook process from the internal-storybook directory + storybookProcess = x('pnpm', ['storybook'], { + nodeOptions: { + cwd: STORYBOOK_DIR, + }, + }); + + // Wait for MCP endpoint to be ready + await waitForMcpEndpoint(); + }, STARTUP_TIMEOUT); + + afterAll(async () => { + if (storybookProcess) { + console.log('Shutting down Storybook...'); + storybookProcess.kill('SIGTERM'); + storybookProcess = null; + } + }); + + describe('Session Initialization', () => { + it('should successfully initialize an MCP session', async () => { + const response = await mcpRequest('initialize', { + protocolVersion: '2025-06-18', + capabilities: {}, + clientInfo: { + name: 'e2e-test-client', + version: '1.0.0', + }, + }); + + expect(response).toMatchObject({ + jsonrpc: '2.0', + id: 1, + result: { + protocolVersion: '2025-06-18', + capabilities: { + tools: { listChanged: true }, + }, + serverInfo: { + name: '@storybook/addon-mcp', + description: expect.stringContaining('agents'), + }, + }, + }); + + expect(response.result.serverInfo.version).toBeDefined(); + }); + + it('should return error for invalid protocol version', async () => { + const response = await mcpRequest('initialize', { + protocolVersion: '1.0.0', // Invalid version + capabilities: {}, + clientInfo: { + name: 'test', + version: '1.0.0', + }, + }); + + expect(response.error).toMatchInlineSnapshot(` + { + "code": 0, + "message": "MCP error -32602: Invalid protocol version format", + } + `); + }); + }); + + describe('Tools Discovery', () => { + it('should list available tools', async () => { + const response = await mcpRequest('tools/list'); + + expect(response.result).toHaveProperty('tools'); + // Dev and docs tools should be present + expect(response.result.tools).toHaveLength(4); + + expect(response.result.tools).toMatchInlineSnapshot(` + [ + { + "description": "Get the URL for one or more stories.", + "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "stories": { + "items": { + "properties": { + "absoluteStoryPath": { + "type": "string", + }, + "explicitStoryName": { + "type": "string", + }, + "exportName": { + "type": "string", + }, + }, + "required": [ + "exportName", + "absoluteStoryPath", + ], + "type": "object", + }, + "type": "array", + }, + }, + "required": [ + "stories", + ], + "type": "object", + }, + "name": "get-story-urls", + "title": "Get stories' URLs", + }, + { + "description": "Instructions on how to do UI component development. + + ALWAYS call this tool before doing any UI/frontend/React/component development, including but not + limited to adding or updating new components, pages, screens or layouts.", + "inputSchema": { + "properties": {}, + "type": "object", + }, + "name": "get-ui-building-instructions", + "title": "UI Component Building Instructions", + }, + { + "description": "List all available UI components from the component library", + "inputSchema": { + "properties": {}, + "type": "object", + }, + "name": "list-all-components", + "title": "List All Components", + }, + { + "description": "Get detailed documentation for a specific UI component", + "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "componentId": { + "type": "string", + }, + }, + "required": [ + "componentId", + ], + "type": "object", + }, + "name": "get-component-documentation", + "title": "Get Documentation for Component", + }, + ] + `); + }); + }); + + describe('Tool: get-story-urls', () => { + it('should return story URLs for valid stories', async () => { + const cwd = process.cwd(); + const storyPath = cwd.endsWith('/apps/internal-storybook') + ? `${cwd}/stories/components/Button.stories.ts` + : `${cwd}/apps/internal-storybook/stories/components/Button.stories.ts`; + + const response = await mcpRequest('tools/call', { + name: 'get-story-urls', + arguments: { + stories: [ + { + exportName: 'Primary', + absoluteStoryPath: storyPath, + }, + ], + }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "http://localhost:6006/?path=/story/example-button--primary", + "type": "text", + }, + ], + } + `); + }); + + it('should return error message for non-existent story', async () => { + const response = await mcpRequest('tools/call', { + name: 'get-story-urls', + arguments: { + stories: [ + { + exportName: 'NonExistent', + absoluteStoryPath: `${process.cwd()}/stories/components/NonExistent.stories.ts`, + }, + ], + }, + }); + + // The tool returns error messages as regular content, not isError + expect(response.result).toHaveProperty('content'); + expect(response.result.content).toHaveLength(1); + expect(response.result.content[0].text).toContain('No story found'); + expect(response.result.content[0].text).toContain('NonExistent'); + }); + }); + describe('Tool: get-ui-building-instructions', () => { + it('should return UI building instructions', async () => { + const response = await mcpRequest('tools/call', { + name: 'get-ui-building-instructions', + arguments: {}, + }); + + expect(response.result).toHaveProperty('content'); + expect(response.result.content[0]).toHaveProperty('type', 'text'); + + const text = response.result.content[0].text; + expect(text).toContain('stories'); + expect(text.length).toBeGreaterThan(100); + }); + }); + + describe('Tool: list-all-components', () => { + it('should list all components from manifest', async () => { + const response = await mcpRequest('tools/call', { + name: 'list-all-components', + arguments: {}, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": " + + example-button + Button + + A customizable button component for user interactions. + + + + header + Header + + + page + Page + + + other-ui-card + Card + + Card component with title, image, content, and action button + + + ", + "type": "text", + }, + ], + } + `); + }); + }); + + describe('Tool: get-component-documentation', () => { + it('should return documentation for a specific component', async () => { + // First, get the list to find a valid component ID + const listResponse = await mcpRequest('tools/call', { + name: 'list-all-components', + arguments: {}, + }); + + const listText = listResponse.result.content[0].text; + const idMatch = listText.match(/([^<]+)<\/id>/); + expect(idMatch).toBeTruthy(); + + const componentId = idMatch![1]; + + // Now get documentation for that component + const response = await mcpRequest('tools/call', { + name: 'get-component-documentation', + arguments: { + componentId, + }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": " + example-button + Button + + Primary UI component for user interaction + + + Primary + + import { Button } from "@my-org/my-component-library"; + + const Primary = () => ; + + + + Secondary + + import { Button } from "@my-org/my-component-library"; + + const Secondary = () => ; + + + + Large + + import { Button } from "@my-org/my-component-library"; + + const Large = () => ; + + + + Small + + import { Button } from "@my-org/my-component-library"; + + const Small = () => ; + + + + + primary + + Is this the principal call to action on the page? + + boolean + false + false + + + backgroundColor + + What background color to use + + string + false + + + size + + How large should the button be? + + 'small' | 'medium' | 'large' + false + 'medium' + + + label + + Button contents + + string + true + + + onClick + + Optional click handler + + () => void + false + + + ", + "type": "text", + }, + ], + } + `); + }); + + it('should return error for non-existent component', async () => { + const response = await mcpRequest('tools/call', { + name: 'get-component-documentation', + arguments: { + componentId: 'non-existent-component-id', + }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "Component not found: "non-existent-component-id". Use the list-all-components tool to see available components.", + "type": "text", + }, + ], + "isError": true, + } + `); + }); + }); + + describe('Toolset Filtering', () => { + it('should respect X-MCP-Toolsets header for dev tools only', async () => { + const response = await fetch(MCP_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-MCP-Toolsets': 'dev', + }, + body: JSON.stringify(createMCPRequestBody('tools/list')), + }); + + const text = await response.text(); + const jsonText = text.replace(/^data: /, '').trim(); + const result = JSON.parse(jsonText); + + const toolNames = result.result.tools.map((tool: any) => tool.name); + + expect(toolNames).toMatchInlineSnapshot(` + [ + "get-story-urls", + "get-ui-building-instructions", + ] + `); + }); + + it('should respect X-MCP-Toolsets header for docs tools only', async () => { + const response = await fetch(MCP_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-MCP-Toolsets': 'docs', + }, + body: JSON.stringify(createMCPRequestBody('tools/list')), + }); + + const text = await response.text(); + const jsonText = text.replace(/^data: /, '').trim(); + const result = JSON.parse(jsonText); + + const toolNames = result.result.tools.map((tool: any) => tool.name); + + expect(toolNames).toMatchInlineSnapshot(` + [ + "list-all-components", + "get-component-documentation", + ] + `); + }); + }); + + describe('HTTP Methods', () => { + it('should return HTML when Accept header is text/html', async () => { + const response = await fetch(MCP_ENDPOINT, { + method: 'GET', + headers: { + Accept: 'text/html', + }, + }); + + expect(response.ok).toBe(true); + expect(response.headers.get('content-type')).toContain('text/html'); + + const html = await response.text(); + expect(html.toLowerCase()).toContain(''); + }); + }); +}); diff --git a/apps/internal-storybook/tsconfig.json b/apps/internal-storybook/tsconfig.json index 72eb9f7b..8d0fc06c 100644 --- a/apps/internal-storybook/tsconfig.json +++ b/apps/internal-storybook/tsconfig.json @@ -1,7 +1,17 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "jsx": "react" + "jsx": "react", + "lib": [ + "es2024", + "ESNext.Array", + "ESNext.Collection", + "ESNext.Iterator", + "ESNext.Promise", + "DOM", + "DOM.AsyncIterable", + "DOM.Iterable" + ] }, "include": ["stories/**/*", "**/*.ts"] } diff --git a/apps/internal-storybook/vitest.config.ts b/apps/internal-storybook/vitest.config.ts new file mode 100644 index 00000000..b4290009 --- /dev/null +++ b/apps/internal-storybook/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + testTimeout: 60_000, + hookTimeout: 60_000, + }, +}); diff --git a/package.json b/package.json index 95de7e66..95508862 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "test": "vitest", "test:run": "vitest run", "test:ci": "vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml", + "test:e2e": "turbo run test:e2e", "typecheck": "tsc", "turbo": "turbo", "turbo:test": "turbo run test", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a09e592a..11e3f42d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,14 +7,14 @@ settings: catalogs: default: '@storybook/addon-a11y': - specifier: 10.1.0-alpha.10 - version: 10.1.0-alpha.10 + specifier: 10.1.0-alpha.11 + version: 10.1.0-alpha.11 '@storybook/addon-docs': - specifier: 10.1.0-alpha.10 - version: 10.1.0-alpha.10 + specifier: 10.1.0-alpha.11 + version: 10.1.0-alpha.11 '@storybook/react-vite': - specifier: 10.1.0-alpha.10 - version: 10.1.0-alpha.10 + specifier: 10.1.0-alpha.11 + version: 10.1.0-alpha.11 '@tmcp/adapter-valibot': specifier: ^0.1.4 version: 0.1.4 @@ -25,8 +25,8 @@ catalogs: specifier: ^0.4.1 version: 0.4.1 storybook: - specifier: 10.1.0-alpha.10 - version: 10.1.0-alpha.10 + specifier: 10.1.0-alpha.11 + version: 10.1.0-alpha.11 tmcp: specifier: ^1.16.0 version: 1.16.0 @@ -140,13 +140,13 @@ importers: devDependencies: '@storybook/addon-docs': specifier: 'catalog:' - version: 10.1.0-alpha.10(@types/react@18.3.26)(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) + version: 10.1.0-alpha.11(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@storybook/addon-mcp': specifier: workspace:* version: link:../../packages/addon-mcp '@storybook/react-vite': specifier: 'catalog:' - version: 10.1.0-alpha.10(esbuild@0.25.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) + version: 10.1.0-alpha.11(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@types/react': specifier: ^18.2.65 version: 18.3.26 @@ -164,7 +164,10 @@ importers: version: 18.3.1(react@18.3.1) storybook: specifier: 'catalog:' - version: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + version: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + tinyexec: + specifier: ^1.0.2 + version: 1.0.2 vite: specifier: 'catalog:' version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) @@ -191,13 +194,13 @@ importers: version: 1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@storybook/addon-a11y': specifier: 'catalog:' - version: 10.1.0-alpha.10(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) + version: 10.1.0-alpha.11(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) '@storybook/mcp': specifier: workspace:* version: link:../packages/mcp '@storybook/react-vite': specifier: 'catalog:' - version: 10.1.0-alpha.10(esbuild@0.25.11)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) + version: 10.1.0-alpha.11(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@tsconfig/node-ts': specifier: ^23.6.1 version: 23.6.2 @@ -260,10 +263,10 @@ importers: version: 5.83.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) storybook: specifier: 'catalog:' - version: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + version: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) storybook-addon-test-codegen: specifier: ^3.0.0 - version: 3.0.0(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) + version: 3.0.0(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) tinyexec: specifier: ^1.0.1 version: 1.0.2 @@ -343,7 +346,7 @@ importers: devDependencies: storybook: specifier: 'catalog:' - version: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + version: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) ts-dedent: specifier: ^2.2.0 version: 2.2.0 @@ -616,156 +619,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.11': resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.11': resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.11': resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.11': resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.11': resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.11': resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.11': resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.11': resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.11': resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.11': resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.11': resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.11': resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.11': resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.11': resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.11': resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.11': resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.11': resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.11': resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.11': resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.11': resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.11': resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.11': resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.11': resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.11': resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.11': resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2064,28 +2223,28 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@storybook/addon-a11y@10.1.0-alpha.10': - resolution: {integrity: sha512-lbAbm6mC1HeUuiwxJ5jsJ1c+c0ktwsEBfTgEJYu9ifMs8jH3uAkWezYPi50MqystZcFdbCjgMU823/pZ4FOVYg==} + '@storybook/addon-a11y@10.1.0-alpha.11': + resolution: {integrity: sha512-0bU9rjgVzP7vg+VuU8QFYI8u5BeAxewR0D/gyXDPgexjFzoySApV2jYZrdQjPNbECcn6XN+XMEXPorcXLLZj4Q==} peerDependencies: - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 - '@storybook/addon-docs@10.1.0-alpha.10': - resolution: {integrity: sha512-2GriGpjtBsP1U/oT36ACH5yZXx+eUODLBu5j0U/AF6MreeBJE3iGdija0EKb1VFUchCnCjpg7Wp8WDOGCpjBEQ==} + '@storybook/addon-docs@10.1.0-alpha.11': + resolution: {integrity: sha512-3MWDEQrtjEmYo0DGiFhZSspxjeI87pr3g7XxGKph7nhTYQlNteeARQT7QGBturZz02PYqX/9GMG/huwiPSdT9A==} peerDependencies: - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 - '@storybook/builder-vite@10.1.0-alpha.10': - resolution: {integrity: sha512-Sb/Kwucehq0I9e5sZb+ZchP4iGUa6LQ0RIi+uXBiyvmnYA6OYKZCnGsCG8VQARwiWcSDmeHd71ncnyMBwJSEDA==} + '@storybook/builder-vite@10.1.0-alpha.11': + resolution: {integrity: sha512-WKlU1SgvaOk05bR+FIsotpngLAnUoRzSvvWnGqH/sfK8WFB81Kr2vRlmWLl4ZEN7SfZPS6aUSFe3GEVBu4wK6Q==} peerDependencies: - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/csf-plugin@10.1.0-alpha.10': - resolution: {integrity: sha512-DLFzOaUwu6gPFZ3cMxRdhIOUROq2LSKXuDioXlTTS23lQu5F4SPZM8TeWXQ6Oy5bg1LUhxsGWohTO7jmsThLRA==} + '@storybook/csf-plugin@10.1.0-alpha.11': + resolution: {integrity: sha512-dypLNxcc0LFa5erY6A1Zhy/2RBQ7wTJqpACtscn78usOwpNJyp+nxp0HleQTkd9u24fuc4hoZ2FaGoX2kBWuFw==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 vite: '*' webpack: '*' peerDependenciesMeta: @@ -2101,34 +2260,33 @@ packages: '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - '@storybook/icons@1.6.0': - resolution: {integrity: sha512-hcFZIjW8yQz8O8//2WTIXylm5Xsgc+lW9ISLgUk1xGmptIJQRdlhVIXCpSyLrQaaRiyhQRaVg7l3BD9S216BHw==} - engines: {node: '>=14.0.0'} + '@storybook/icons@2.0.1': + resolution: {integrity: sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@storybook/react-dom-shim@10.1.0-alpha.10': - resolution: {integrity: sha512-YFzH9pPweA/RHsyrR9C2bHUKZzlGJTXNO/NQCr2k/3SzmPFcYkprwQjtLx40INd6xD/rcpOCFrVoFs7yNyVfaw==} + '@storybook/react-dom-shim@10.1.0-alpha.11': + resolution: {integrity: sha512-NB6FW2JDqCqMfCtY2I8Xb3ZIQgE0vah9yRuFtkmL/3H467FeqeOFBp/h930oUEbxvNVDAra2Pkqj3+O2+fZVDg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 - '@storybook/react-vite@10.1.0-alpha.10': - resolution: {integrity: sha512-v2ZqLpkbsYlIyV4+AIBnYEoXwVROo2xsEdpQgVThvmx0vo2PrN1iUdalRQT5Dji/iLc/uhAWh7bA42pWE6C61Q==} + '@storybook/react-vite@10.1.0-alpha.11': + resolution: {integrity: sha512-2dmRKShaGT/twXqh6PR+orCi1kepCWI8UkWgFh6x87rEe0KOJZQbEREpRJjnwSisZIcCcE+X0IEyqv+gHCYsTQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/react@10.1.0-alpha.10': - resolution: {integrity: sha512-mQRIjWvjyejoEXZY8QDYa9MU/Kxae5ppK1wq582OANxXJUcHon19l01tSnRoA8r6AlV2UWDHHT3TGIEz5O0KfQ==} + '@storybook/react@10.1.0-alpha.11': + resolution: {integrity: sha512-zGEOhMUQ48Mpv+p0fJgvOU4MEH5TnAmDIwEdAZ5e2jFzdNtEGOa/O60+sPsIsA3dnzBJlkZtAxHADfVw5j2z6A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.1.0-alpha.10 + storybook: ^10.1.0-alpha.11 typescript: '>= 4.9.x' peerDependenciesMeta: typescript: @@ -2287,6 +2445,9 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -3150,6 +3311,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -4660,8 +4826,8 @@ packages: peerDependencies: storybook: ^10.0.0 - storybook@10.1.0-alpha.10: - resolution: {integrity: sha512-GfYPOKC1uqe78MI/hcGkxHbUxi2UhigSmR5ayt6GhV6BgWes3gEQFBXMfRnhg+Cx15RAsbb1OvR/0hq6wvigZQ==} + storybook@10.1.0-alpha.11: + resolution: {integrity: sha512-4XWk3Mq1PYK3SEfG0s0Cp4ieLEQEDDba+FFPFGIY/k37KezBDf5pz/j1UqmX5PAUOalLzvLH4gqVLXf8lZO0bg==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -5637,81 +5803,159 @@ snapshots: '@esbuild/aix-ppc64@0.25.11': optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + '@esbuild/android-arm64@0.25.11': optional: true + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm@0.25.11': optional: true + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-x64@0.25.11': optional: true + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.25.11': optional: true + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.25.11': optional: true + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.25.11': optional: true + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.25.11': optional: true + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.25.11': optional: true + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm@0.25.11': optional: true + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-ia32@0.25.11': optional: true + '@esbuild/linux-ia32@0.25.12': + optional: true + '@esbuild/linux-loong64@0.25.11': optional: true + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.25.11': optional: true + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.25.11': optional: true + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.25.11': optional: true + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-s390x@0.25.11': optional: true + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-x64@0.25.11': optional: true + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.25.11': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.25.11': optional: true + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.25.11': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.25.11': optional: true + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.25.11': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.25.11': optional: true + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.25.11': optional: true + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-ia32@0.25.11': optional: true + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-x64@0.25.11': optional: true + '@esbuild/win32-x64@0.25.12': + optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -7433,21 +7677,21 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@storybook/addon-a11y@10.1.0-alpha.10(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': + '@storybook/addon-a11y@10.1.0-alpha.11(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.0 - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - '@storybook/addon-docs@10.1.0-alpha.10(@types/react@18.3.26)(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/addon-docs@10.1.0-alpha.11(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: '@mdx-js/react': 3.1.1(@types/react@18.3.26)(react@19.2.0) - '@storybook/csf-plugin': 10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) - '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/react-dom-shim': 10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) + '@storybook/csf-plugin': 10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/icons': 2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@storybook/react-dom-shim': 10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -7456,10 +7700,10 @@ snapshots: - vite - webpack - '@storybook/builder-vite@10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/builder-vite@10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + '@storybook/csf-plugin': 10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) ts-dedent: 2.2.0 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) transitivePeerDependencies: @@ -7467,10 +7711,10 @@ snapshots: - rollup - webpack - '@storybook/builder-vite@10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/builder-vite@10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + '@storybook/csf-plugin': 10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) ts-dedent: 2.2.0 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) transitivePeerDependencies: @@ -7478,69 +7722,69 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/csf-plugin@10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) unplugin: 2.3.10 optionalDependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 rollup: 4.52.5 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.11) + webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.12) - '@storybook/csf-plugin@10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/csf-plugin@10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) unplugin: 2.3.10 optionalDependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 rollup: 4.52.5 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.11) + webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.12) '@storybook/global@5.0.0': {} - '@storybook/icons@1.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@storybook/icons@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/icons@1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@storybook/icons@2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@storybook/react-dom-shim@10.1.0-alpha.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': + '@storybook/react-dom-shim@10.1.0-alpha.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - '@storybook/react-dom-shim@10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': + '@storybook/react-dom-shim@10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - '@storybook/react-dom-shim@10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': + '@storybook/react-dom-shim@10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - '@storybook/react-vite@10.1.0-alpha.10(esbuild@0.25.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/react-vite@10.1.0-alpha.11(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) '@rollup/pluginutils': 5.3.0(rollup@4.52.5) - '@storybook/builder-vite': 10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) - '@storybook/react': 10.1.0-alpha.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3) + '@storybook/builder-vite': 10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 10.1.0-alpha.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 react: 18.3.1 react-docgen: 8.0.2 react-dom: 18.3.1(react@18.3.1) resolve: 1.22.11 - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) tsconfig-paths: 4.2.0 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) transitivePeerDependencies: @@ -7550,19 +7794,19 @@ snapshots: - typescript - webpack - '@storybook/react-vite@10.1.0-alpha.10(esbuild@0.25.11)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11))': + '@storybook/react-vite@10.1.0-alpha.11(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) '@rollup/pluginutils': 5.3.0(rollup@4.52.5) - '@storybook/builder-vite': 10.1.0-alpha.10(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) - '@storybook/react': 10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3) + '@storybook/builder-vite': 10.1.0-alpha.11(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 react: 19.2.0 react-docgen: 8.0.2 react-dom: 19.2.0(react@19.2.0) resolve: 1.22.11 - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) tsconfig-paths: 4.2.0 vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) transitivePeerDependencies: @@ -7572,27 +7816,27 @@ snapshots: - typescript - webpack - '@storybook/react@10.1.0-alpha.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)': + '@storybook/react@10.1.0-alpha.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.1.0-alpha.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) + '@storybook/react-dom-shim': 10.1.0-alpha.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) react: 18.3.1 react-docgen: 8.0.2 react-dom: 18.3.1(react@18.3.1) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@storybook/react@10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)': + '@storybook/react@10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.1.0-alpha.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) + '@storybook/react-dom-shim': 10.1.0-alpha.11(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))) react: 19.2.0 react-docgen: 8.0.2 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7739,6 +7983,11 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/deep-eql@4.0.2': {} '@types/doctrine@0.0.9': {} @@ -7974,7 +8223,7 @@ snapshots: '@vitest/expect@3.2.4': dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 @@ -8706,6 +8955,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.11 '@esbuild/win32-x64': 0.25.11 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -10339,20 +10617,20 @@ snapshots: std-env@3.10.0: {} - storybook-addon-test-codegen@3.0.0(storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))): + storybook-addon-test-codegen@3.0.0(storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))): dependencies: - storybook: 10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + storybook: 10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)): + storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)): dependencies: '@storybook/global': 5.0.0 - '@storybook/icons': 1.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/icons': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 '@vitest/mocker': 3.2.4(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) '@vitest/spy': 3.2.4 - esbuild: 0.25.11 + esbuild: 0.25.12 recast: 0.23.11 semver: 7.7.3 use-sync-external-store: 1.6.0(react@18.3.1) @@ -10368,16 +10646,16 @@ snapshots: - utf-8-validate - vite - storybook@10.1.0-alpha.10(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)): + storybook@10.1.0-alpha.11(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)): dependencies: '@storybook/global': 5.0.0 - '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@storybook/icons': 2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 '@vitest/mocker': 3.2.4(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) '@vitest/spy': 3.2.4 - esbuild: 0.25.11 + esbuild: 0.25.12 recast: 0.23.11 semver: 7.7.3 use-sync-external-store: 1.6.0(react@19.2.0) @@ -10458,17 +10736,17 @@ snapshots: term-size@2.2.1: {} - terser-webpack-plugin@5.3.14(@swc/core@1.13.5)(esbuild@0.25.11)(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)): + terser-webpack-plugin@5.3.14(@swc/core@1.13.5)(esbuild@0.25.12)(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.0 - webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.11) + webpack: 5.102.1(@swc/core@1.13.5)(esbuild@0.25.12) optionalDependencies: '@swc/core': 1.13.5 - esbuild: 0.25.11 + esbuild: 0.25.12 optional: true terser@5.44.0: @@ -10846,7 +11124,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11): + webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -10870,7 +11148,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.13.5)(esbuild@0.25.11)(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.11)) + terser-webpack-plugin: 5.3.14(@swc/core@1.13.5)(esbuild@0.25.12)(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b5d30832..1e0b4abe 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,29 +6,29 @@ packages: - eval/evals/*/experiments/*/project catalog: - '@storybook/addon-a11y': 10.1.0-alpha.10 - '@storybook/addon-docs': 10.1.0-alpha.10 - '@storybook/addon-vitest': 10.1.0-alpha.10 - '@storybook/react-vite': 10.1.0-alpha.10 + '@storybook/addon-a11y': 10.1.0-alpha.11 + '@storybook/addon-docs': 10.1.0-alpha.11 + '@storybook/addon-vitest': 10.1.0-alpha.11 + '@storybook/react-vite': 10.1.0-alpha.11 '@tmcp/adapter-valibot': ^0.1.4 '@tmcp/transport-http': ^0.8.0 '@tmcp/transport-stdio': ^0.4.1 - eslint-plugin-storybook: 10.1.0-alpha.10 - storybook: 10.1.0-alpha.10 + eslint-plugin-storybook: 10.1.0-alpha.11 + storybook: 10.1.0-alpha.11 tmcp: ^1.16.0 tsdown: ^0.15.12 typescript: ^5.9.3 valibot: 1.1.0 - vitest: 4.0.6 vite: 7.2.2 + vitest: 4.0.6 catalogs: experiments: '@eslint/js': 9.39.1 - '@storybook/addon-a11y': 10.1.0-alpha.10 - '@storybook/addon-docs': 10.1.0-alpha.10 - '@storybook/addon-vitest': 10.1.0-alpha.10 - '@storybook/react-vite': 10.1.0-alpha.10 + '@storybook/addon-a11y': 10.1.0-alpha.11 + '@storybook/addon-docs': 10.1.0-alpha.11 + '@storybook/addon-vitest': 10.1.0-alpha.11 + '@storybook/react-vite': 10.1.0-alpha.11 '@types/node': 24.10.1 '@types/react': 19.2.6 '@types/react-dom': 19.2.3 @@ -37,11 +37,11 @@ catalogs: eslint: 9.39.1 eslint-plugin-react-hooks: 7.0.1 eslint-plugin-react-refresh: 0.4.24 - eslint-plugin-storybook: 10.1.0-alpha.10 + eslint-plugin-storybook: 10.1.0-alpha.11 globals: 16.5.0 react: 19.2.0 react-dom: 19.2.0 - storybook: 10.1.0-alpha.10 + storybook: 10.1.0-alpha.11 typescript: 5.9.3 typescript-eslint: 8.47.0 vite: 7.2.2 diff --git a/turbo.json b/turbo.json index fd8a7378..b5772a4b 100644 --- a/turbo.json +++ b/turbo.json @@ -25,6 +25,10 @@ "persistent": true, "interruptible": true }, + "test:e2e": { + "dependsOn": ["^build"], + "outputs": [] + }, "//#format": {}, "//#format:check": {}, "//#lint": {}, diff --git a/vitest.config.ts b/vitest.config.ts index 5dc3a7c2..1d75a23b 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -14,7 +14,7 @@ export default defineConfig({ }, ], test: { - projects: ['packages/*'], + projects: ['packages/*', 'apps/*'], coverage: { include: ['**/src/**/*.{ts,tsx}'], exclude: ['*.d.ts'], From 310f3a1600ace6f9dfd4a0b25cbff3dc057e1c86 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 08:43:47 +0100 Subject: [PATCH 02/27] improve e2e scripting --- .github/workflows/check.yml | 13 ------------- .../tests/mcp-endpoint.e2e.test.ts | 6 ------ apps/internal-storybook/vitest.config.ts | 5 +++-- package.json | 13 +++++++------ pnpm-lock.yaml | 6 ++++++ turbo.json | 14 +++++++++----- 6 files changed, 25 insertions(+), 32 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c39ff679..5a093921 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -75,19 +75,6 @@ jobs: files: | test-report.junit.xml - test-e2e: - name: E2E Tests - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Setup Node.js and Install Dependencies - uses: ./.github/actions/setup-node-and-install - - - name: Run E2E tests - run: pnpm turbo run test:e2e - typecheck: name: Type check runs-on: ubuntu-latest diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index ac7236af..7cd29822 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -2,7 +2,6 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { x } from 'tinyexec'; const STORYBOOK_DIR = new URL('..', import.meta.url).pathname; - const MCP_ENDPOINT = 'http://localhost:6006/mcp'; const STARTUP_TIMEOUT = 15_000; @@ -66,7 +65,6 @@ async function waitForMcpEndpoint( body: JSON.stringify(createMCPRequestBody('tools/list')), }); if (response.ok) { - console.log('✓ MCP endpoint is ready'); clearInterval(intervalId); resolve(); return; @@ -88,9 +86,6 @@ async function waitForMcpEndpoint( describe('MCP Endpoint E2E Tests', () => { beforeAll(async () => { - console.log('Starting Storybook dev server...'); - - // Start Storybook process from the internal-storybook directory storybookProcess = x('pnpm', ['storybook'], { nodeOptions: { cwd: STORYBOOK_DIR, @@ -103,7 +98,6 @@ describe('MCP Endpoint E2E Tests', () => { afterAll(async () => { if (storybookProcess) { - console.log('Shutting down Storybook...'); storybookProcess.kill('SIGTERM'); storybookProcess = null; } diff --git a/apps/internal-storybook/vitest.config.ts b/apps/internal-storybook/vitest.config.ts index b4290009..8be9d074 100644 --- a/apps/internal-storybook/vitest.config.ts +++ b/apps/internal-storybook/vitest.config.ts @@ -2,7 +2,8 @@ import { defineProject } from 'vitest/config'; export default defineProject({ test: { - testTimeout: 60_000, - hookTimeout: 60_000, + name: 'e2e', + testTimeout: 15_000, + hookTimeout: 15_000, }, }); diff --git a/package.json b/package.json index 95508862..bd97efc9 100644 --- a/package.json +++ b/package.json @@ -12,31 +12,32 @@ "build": "turbo run build", "build-storybook": "turbo run build-storybook", "changeset": "changeset", + "check": "turbo run build build-storybook format lint publint typecheck test:run --continue", + "check:watch": "turbo watch build build-storybook format lint publint typecheck test --continue --experimental-write-cache", "dev": "turbo run dev", "format": "prettier --write .", "format:check": "prettier --check .", "inspect": "pnpm run --filter @storybook/addon-mcp inspect", "lint": "oxlint --type-aware", "lint:ci": "oxlint --format github --type-aware", - "check": "turbo run build build-storybook format lint publint typecheck test:run --continue", - "check:watch": "turbo watch build build-storybook format lint publint typecheck test --continue --experimental-write-cache", "publint": "turbo run publint", "release": "turbo run build && pnpm changeset publish", "storybook": "turbo watch storybook", "test": "vitest", - "test:run": "vitest run", "test:ci": "vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml", - "test:e2e": "turbo run test:e2e", - "typecheck": "tsc", + "test:run": "vitest run", "turbo": "turbo", "turbo:test": "turbo run test", - "turbo:typecheck": "turbo run typecheck" + "turbo:typecheck": "turbo run typecheck", + "typecheck": "tsc" }, "devDependencies": { "@changesets/changelog-github": "^0.5.1", "@changesets/cli": "^2.29.6", "@codecov/rollup-plugin": "^1.9.1", "@modelcontextprotocol/inspector": "^0.17.2", + "@storybook/addon-mcp": "workspace:*", + "@storybook/mcp": "workspace:*", "@types/node": "20.19.0", "@vitest/coverage-v8": "4.0.6", "oxlint": "^1.25.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11e3f42d..08eea8b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,6 +96,12 @@ importers: '@modelcontextprotocol/inspector': specifier: ^0.17.2 version: 0.17.2(@swc/core@1.13.5)(@types/node@20.19.0)(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(typescript@5.9.3) + '@storybook/addon-mcp': + specifier: workspace:* + version: link:packages/addon-mcp + '@storybook/mcp': + specifier: workspace:* + version: link:packages/mcp '@types/node': specifier: 20.19.0 version: 20.19.0 diff --git a/turbo.json b/turbo.json index b5772a4b..f6f6fc27 100644 --- a/turbo.json +++ b/turbo.json @@ -25,16 +25,20 @@ "persistent": true, "interruptible": true }, - "test:e2e": { - "dependsOn": ["^build"], - "outputs": [] - }, "//#format": {}, "//#format:check": {}, "//#lint": {}, "//#lint:ci": {}, - "//#test:run": {}, + "//#test": { + "dependsOn": ["^build"], + "persistent": true, + "interruptible": true + }, + "//#test:run": { + "dependsOn": ["^build"] + }, "//#test:ci": { + "dependsOn": ["^build"], "outputs": ["coverage/**", "test-report.junit.xml"] }, "typecheck": {}, From c153b931e3aef5dd2acf061d7501356b736ed9d6 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 09:11:34 +0100 Subject: [PATCH 03/27] add tests for mcp index --- package.json | 1 + packages/mcp/src/index.test.ts | 236 +++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 51 ++++--- 3 files changed, 273 insertions(+), 15 deletions(-) create mode 100644 packages/mcp/src/index.test.ts diff --git a/package.json b/package.json index bd97efc9..f567070c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@changesets/cli": "^2.29.6", "@codecov/rollup-plugin": "^1.9.1", "@modelcontextprotocol/inspector": "^0.17.2", + "@modelcontextprotocol/sdk": "^1.22.0", "@storybook/addon-mcp": "workspace:*", "@storybook/mcp": "workspace:*", "@types/node": "20.19.0", diff --git a/packages/mcp/src/index.test.ts b/packages/mcp/src/index.test.ts new file mode 100644 index 00000000..9db594d1 --- /dev/null +++ b/packages/mcp/src/index.test.ts @@ -0,0 +1,236 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { createStorybookMcpHandler } from './index.ts'; +import smallManifestFixture from '../fixtures/small-manifest.fixture.json' with { type: 'json' }; + +describe('createStorybookMcpHandler', () => { + let client: Client; + let transport: StreamableHTTPClientTransport; + let fetchMock: ReturnType; + + /** + * Helper to setup client with a mock fetch that routes to our handler + */ + async function setupClient( + handler: Awaited>, + ) { + // Mock global fetch to route to our handler + fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { + const url = + typeof input === 'string' + ? input + : input instanceof URL + ? input.href + : input.url; + const request = new Request(url, init); + return await handler(request); + }); + (global as any).fetch = fetchMock; + + // Create client and transport + transport = new StreamableHTTPClientTransport( + new URL('http://localhost:3000/mcp'), + ); + client = new Client({ + name: 'test-client', + version: '1.0.0', + }); + + await client.connect(transport); + } + + afterEach(async () => { + if (client) { + await client.close(); + } + if (transport) { + await transport.close(); + } + }); + + it('should handle initialize and list tools', async () => { + const handler = await createStorybookMcpHandler(); + await setupClient(handler); + + const tools = await client.listTools(); + + expect(tools.tools).toHaveLength(2); + expect(tools.tools).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'list-all-components', + title: 'List All Components', + }), + expect.objectContaining({ + name: 'get-component-documentation', + title: 'Get Documentation for Component', + }), + ]), + ); + }); + + it('should call onSessionInitialize handler when provided', async () => { + const onSessionInitialize = vi.fn(); + const handler = await createStorybookMcpHandler({ onSessionInitialize }); + await setupClient(handler); + + // The handler should be called during connect + expect(onSessionInitialize).toHaveBeenCalledTimes(1); + expect(onSessionInitialize).toHaveBeenCalledWith( + expect.objectContaining({ + protocolVersion: expect.any(String), + capabilities: expect.any(Object), + clientInfo: expect.objectContaining({ + name: 'test-client', + version: '1.0.0', + }), + }), + ); + }); + + it('should use manifestProvider when calling list-all-components', async () => { + const manifestProvider = vi + .fn() + .mockResolvedValue(JSON.stringify(smallManifestFixture)); + + const handler = await createStorybookMcpHandler({ + source: 'https://example.com/manifest.json', + manifestProvider, + }); + await setupClient(handler); + + const result = await client.callTool({ + name: 'list-all-components', + arguments: {}, + }); + + expect(manifestProvider).toHaveBeenCalledWith( + 'https://example.com/manifest.json', + ); + expect(result.content).toHaveLength(1); + expect((result.content as any)[0]).toMatchObject({ + type: 'text', + text: expect.stringContaining(''), + }); + }); + + it('should call onListAllComponents handler when tool is invoked', async () => { + const onListAllComponents = vi.fn(); + const manifestProvider = vi + .fn() + .mockResolvedValue(JSON.stringify(smallManifestFixture)); + + const handler = await createStorybookMcpHandler({ + source: 'https://example.com/manifest.json', + manifestProvider, + onListAllComponents, + }); + await setupClient(handler); + + await client.callTool({ + name: 'list-all-components', + arguments: {}, + }); + + expect(onListAllComponents).toHaveBeenCalledTimes(1); + expect(onListAllComponents).toHaveBeenCalledWith({ + context: expect.objectContaining({ + source: 'https://example.com/manifest.json', + }), + manifest: smallManifestFixture, + }); + }); + + it('should call onGetComponentDocumentation handler when tool is invoked', async () => { + const onGetComponentDocumentation = vi.fn(); + const manifestProvider = vi + .fn() + .mockResolvedValue(JSON.stringify(smallManifestFixture)); + + const handler = await createStorybookMcpHandler({ + source: 'https://example.com/manifest.json', + manifestProvider, + onGetComponentDocumentation, + }); + await setupClient(handler); + + const result = await client.callTool({ + name: 'get-component-documentation', + arguments: { + componentId: 'button', + }, + }); + + expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); + expect(onGetComponentDocumentation).toHaveBeenCalledWith({ + context: expect.objectContaining({ + source: 'https://example.com/manifest.json', + }), + input: { componentId: 'button' }, + foundComponent: expect.objectContaining({ + id: 'button', + name: 'Button', + }), + }); + + expect(result.content).toHaveLength(1); + expect((result.content as any)[0]).toMatchObject({ + type: 'text', + text: expect.stringContaining('Button'), + }); + }); + + it('should handle errors gracefully when manifest is not available', async () => { + const handler = await createStorybookMcpHandler(); + await setupClient(handler); + + const result = await client.callTool({ + name: 'list-all-components', + arguments: {}, + }); + + expect(result.isError).toBe(true); + expect((result.content as any)[0]).toMatchObject({ + type: 'text', + text: expect.stringContaining('Error'), + }); + }); + + it('should handle non-existent component ID in get-component-documentation', async () => { + const onGetComponentDocumentation = vi.fn(); + const manifestProvider = vi + .fn() + .mockResolvedValue(JSON.stringify(smallManifestFixture)); + + const handler = await createStorybookMcpHandler({ + source: 'https://example.com/manifest.json', + manifestProvider, + onGetComponentDocumentation, + }); + await setupClient(handler); + + const result = await client.callTool({ + name: 'get-component-documentation', + arguments: { + componentId: 'non-existent', + }, + }); + + // Should still call the handler with foundComponent: undefined + expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); + expect(onGetComponentDocumentation).toHaveBeenCalledWith({ + context: expect.objectContaining({ + source: 'https://example.com/manifest.json', + }), + input: { componentId: 'non-existent' }, + foundComponent: undefined, + }); + + expect(result.content).toHaveLength(1); + expect((result.content as any)[0]).toMatchObject({ + type: 'text', + text: expect.stringContaining('Component not found'), + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08eea8b8..8c1501e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,6 +96,9 @@ importers: '@modelcontextprotocol/inspector': specifier: ^0.17.2 version: 0.17.2(@swc/core@1.13.5)(@types/node@20.19.0)(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(typescript@5.9.3) + '@modelcontextprotocol/sdk': + specifier: ^1.22.0 + version: 1.22.0 '@storybook/addon-mcp': specifier: workspace:* version: link:packages/addon-mcp @@ -1145,9 +1148,14 @@ packages: engines: {node: '>=22.7.5'} hasBin: true - '@modelcontextprotocol/sdk@1.20.1': - resolution: {integrity: sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA==} + '@modelcontextprotocol/sdk@1.22.0': + resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} @@ -2754,6 +2762,14 @@ packages: ajv: optional: true + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: @@ -6175,15 +6191,16 @@ snapshots: '@modelcontextprotocol/inspector-cli@0.17.2': dependencies: - '@modelcontextprotocol/sdk': 1.20.1 + '@modelcontextprotocol/sdk': 1.22.0 commander: 13.1.0 spawn-rx: 5.1.2 transitivePeerDependencies: + - '@cfworker/json-schema' - supports-color '@modelcontextprotocol/inspector-client@0.17.2(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)': dependencies: - '@modelcontextprotocol/sdk': 1.20.1 + '@modelcontextprotocol/sdk': 1.22.0 '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': 1.3.2(react@18.3.1) @@ -6209,13 +6226,14 @@ snapshots: tailwind-merge: 2.6.0 zod: 3.25.76 transitivePeerDependencies: + - '@cfworker/json-schema' - '@types/react' - '@types/react-dom' - supports-color '@modelcontextprotocol/inspector-server@0.17.2': dependencies: - '@modelcontextprotocol/sdk': 1.20.1 + '@modelcontextprotocol/sdk': 1.22.0 cors: 2.8.5 express: 5.1.0 shell-quote: 1.8.3 @@ -6223,6 +6241,7 @@ snapshots: ws: 8.18.3 zod: 3.25.76 transitivePeerDependencies: + - '@cfworker/json-schema' - bufferutil - supports-color - utf-8-validate @@ -6232,7 +6251,7 @@ snapshots: '@modelcontextprotocol/inspector-cli': 0.17.2 '@modelcontextprotocol/inspector-client': 0.17.2(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6) '@modelcontextprotocol/inspector-server': 0.17.2 - '@modelcontextprotocol/sdk': 1.20.1 + '@modelcontextprotocol/sdk': 1.22.0 concurrently: 9.2.1 node-fetch: 3.3.2 open: 10.2.0 @@ -6241,6 +6260,7 @@ snapshots: ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@20.19.0)(typescript@5.9.3) zod: 3.25.76 transitivePeerDependencies: + - '@cfworker/json-schema' - '@swc/core' - '@swc/wasm' - '@types/node' @@ -6251,9 +6271,10 @@ snapshots: - typescript - utf-8-validate - '@modelcontextprotocol/sdk@1.20.1': + '@modelcontextprotocol/sdk@1.22.0': dependencies: - ajv: 6.12.6 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 @@ -8420,6 +8441,10 @@ snapshots: ajv: 8.17.1 optional: true + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv-keywords@5.1.0(ajv@8.17.1): dependencies: ajv: 8.17.1 @@ -8439,7 +8464,6 @@ snapshots: fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - optional: true ansi-colors@4.1.3: {} @@ -9165,8 +9189,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.0: - optional: true + fast-uri@3.1.0: {} fastq@1.19.1: dependencies: @@ -9477,8 +9500,7 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: - optional: true + json-schema-traverse@1.0.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -10308,8 +10330,7 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: - optional: true + require-from-string@2.0.2: {} reshaped@3.8.8(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: From f3a467f1a26c1a1fa67868c4bca5209bcd41c3ac Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 09:36:12 +0100 Subject: [PATCH 04/27] add preset tests --- packages/addon-mcp/src/preset.test.ts | 131 ++++++++++++++++++++++++++ packages/addon-mcp/src/preset.ts | 9 -- vitest.config.ts | 8 ++ 3 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 packages/addon-mcp/src/preset.test.ts diff --git a/packages/addon-mcp/src/preset.test.ts b/packages/addon-mcp/src/preset.test.ts new file mode 100644 index 00000000..51d1ed92 --- /dev/null +++ b/packages/addon-mcp/src/preset.test.ts @@ -0,0 +1,131 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import type { Options } from 'storybook/internal/types'; +import { experimental_devServer } from './preset.ts'; + +describe('experimental_devServer', () => { + let mockApp: any; + let mockOptions: Options; + let mcpHandler: any; + + beforeEach(() => { + mockApp = { + use: vi.fn((path, handler) => { + mcpHandler = handler; + }), + }; + + mockOptions = { + presets: { + apply: vi.fn((key: string) => { + if (key === 'features') { + return Promise.resolve({ experimentalComponentsManifest: false }); + } + return Promise.resolve(undefined); + }), + }, + } as unknown as Options; + }); + + it('should register /mcp endpoint', async () => { + await (experimental_devServer as any)(mockApp, mockOptions); + + expect(mockApp.use).toHaveBeenCalledWith('/mcp', expect.any(Function)); + expect(mcpHandler).toBeDefined(); + }); + + it('should serve HTML for browser GET requests', async () => { + await (experimental_devServer as any)(mockApp, mockOptions); + + const mockReq = { + method: 'GET', + headers: { + accept: 'text/html', + }, + } as any; + + const mockRes = { + writeHead: vi.fn(), + end: vi.fn(), + } as any; + + await mcpHandler(mockReq, mockRes); + + expect(mockRes.writeHead).toHaveBeenCalledWith(200, { + 'Content-Type': 'text/html', + }); + expect(mockRes.end).toHaveBeenCalledWith(expect.stringContaining(' { + await (experimental_devServer as any)(mockApp, mockOptions); + + const initializeRequest = JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { + name: 'test-client', + version: '1.0.0', + }, + }, + }); + + const mockReq = { + method: 'POST', + headers: { + accept: 'text/html', + 'content-type': 'application/json', + }, + socket: {}, + url: '/', + [Symbol.asyncIterator]: async function* () { + yield Buffer.from(initializeRequest); + }, + } as any; + + const mockRes = { + writeHead: vi.fn(), + write: vi.fn(), + end: vi.fn(), + setHeader: vi.fn(), + statusCode: 0, + } as any; + + await mcpHandler(mockReq, mockRes); + + expect(mockRes.writeHead).not.toHaveBeenCalledWith(200, { + 'Content-Type': 'text/html', + }); + expect(mockRes.end).not.toHaveBeenCalledWith( + expect.stringContaining(' { + const result = await (experimental_devServer as any)(mockApp, mockOptions); + expect(result).toBe(mockApp); + }); + + it('should handle partial toolsets configuration', async () => { + const partialOptions = { + presets: { + apply: vi.fn((key: string) => { + if (key === 'features') { + return Promise.resolve({ experimentalComponentsManifest: false }); + } + return Promise.resolve(undefined); + }), + }, + toolsets: { + dev: false, + }, + } as unknown as Options; + + await (experimental_devServer as any)(mockApp, partialOptions); + + expect(mockApp.use).toHaveBeenCalledWith('/mcp', expect.any(Function)); + }); +}); diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index 0a02e7a2..9d81fca6 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -14,15 +14,6 @@ export const experimental_devServer: PresetProperty< toolsets: 'toolsets' in options ? options.toolsets : {}, }); - app!.post('/mcp', (req, res) => - mcpServerHandler({ - req, - res, - options, - addonOptions, - }), - ); - const shouldRedirect = await isManifestAvailable(options); app!.use('/mcp', (req, res) => { diff --git a/vitest.config.ts b/vitest.config.ts index 1d75a23b..a4ae014f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -12,6 +12,14 @@ export default defineConfig({ } }, }, + { + name: 'html-loader', + transform(code: string, id: string) { + if (id.endsWith('.html')) { + return { code: `export default ${JSON.stringify(code)};`, map: null }; + } + }, + }, ], test: { projects: ['packages/*', 'apps/*'], From d67a038b4bcb74b4c900b7d4bd50c87f8c7d4b5a Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 09:36:16 +0100 Subject: [PATCH 05/27] add telemetry tests --- packages/addon-mcp/src/telemetry.test.ts | 122 +++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 packages/addon-mcp/src/telemetry.test.ts diff --git a/packages/addon-mcp/src/telemetry.test.ts b/packages/addon-mcp/src/telemetry.test.ts new file mode 100644 index 00000000..8c79b7ba --- /dev/null +++ b/packages/addon-mcp/src/telemetry.test.ts @@ -0,0 +1,122 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import type { McpServer } from 'tmcp'; +import type { AddonContext } from './types.ts'; + +// Mock external dependencies only +vi.mock('storybook/internal/node-logger', () => ({ + logger: { + debug: vi.fn(), + }, +})); + +vi.mock('storybook/internal/telemetry', () => ({ + telemetry: vi.fn(), +})); + +import { collectTelemetry } from './telemetry.ts'; +import { logger } from 'storybook/internal/node-logger'; +import { telemetry } from 'storybook/internal/telemetry'; + +describe('collectTelemetry', () => { + let mockServer: McpServer; + + beforeEach(() => { + mockServer = { + ctx: { + sessionId: 'test-session-123', + }, + currentClientInfo: vi.fn().mockReturnValue({ + name: 'test-client', + version: '1.0.0', + }), + currentClientCapabilities: vi.fn().mockReturnValue({ + experimental: {}, + roots: { listChanged: true }, + }), + } as any; + }); + + it('should call telemetry with correct parameters', async () => { + vi.mocked(telemetry).mockResolvedValue(undefined); + + await collectTelemetry({ + event: 'test-event', + server: mockServer, + customField: 'custom-value', + }); + + expect(telemetry).toHaveBeenCalledWith('addon-mcp', { + event: 'test-event', + mcpSessionId: 'test-session-123', + clientInfo: { + name: 'test-client', + version: '1.0.0', + }, + clientCapabilities: { + experimental: {}, + roots: { listChanged: true }, + }, + customField: 'custom-value', + }); + }); + + it('should pass through additional payload fields', async () => { + vi.mocked(telemetry).mockResolvedValue(undefined); + + await collectTelemetry({ + event: 'tool-called', + server: mockServer, + toolName: 'list-all-components', + duration: 123, + success: true, + }); + + expect(telemetry).toHaveBeenCalledWith('addon-mcp', { + event: 'tool-called', + mcpSessionId: 'test-session-123', + clientInfo: expect.any(Object), + clientCapabilities: expect.any(Object), + toolName: 'list-all-components', + duration: 123, + success: true, + }); + }); + + it('should catch and log errors from telemetry', async () => { + const error = new Error('Telemetry failed'); + vi.mocked(telemetry).mockRejectedValue(error); + + await expect( + collectTelemetry({ + event: 'test-event', + server: mockServer, + }), + ).resolves.not.toThrow(); + + expect(logger.debug).toHaveBeenCalledWith( + 'Error collecting telemetry:', + error, + ); + }); + + it('should handle missing session ID gracefully', async () => { + vi.mocked(telemetry).mockResolvedValue(undefined); + + const serverWithoutSession = { + ...mockServer, + ctx: {}, + } as any; + + await collectTelemetry({ + event: 'test-event', + server: serverWithoutSession, + }); + + expect(telemetry).toHaveBeenCalledWith('addon-mcp', { + event: 'test-event', + mcpSessionId: undefined, + clientInfo: expect.any(Object), + clientCapabilities: expect.any(Object), + }); + }); +}); From c807fe7386f2c1eef401a399957db1b6952531b6 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:12:50 +0100 Subject: [PATCH 06/27] simplify tool test mocks --- .../src/tools/get-story-urls.test.ts | 30 ++++++++++--------- .../get-ui-building-instructions.test.ts | 25 +++++++++------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/addon-mcp/src/tools/get-story-urls.test.ts b/packages/addon-mcp/src/tools/get-story-urls.test.ts index f2d30021..2ef8ed14 100644 --- a/packages/addon-mcp/src/tools/get-story-urls.test.ts +++ b/packages/addon-mcp/src/tools/get-story-urls.test.ts @@ -8,17 +8,18 @@ import { import type { AddonContext } from '../types.ts'; import smallStoryIndexFixture from '../../fixtures/small-story-index.fixture.json' with { type: 'json' }; import * as fetchStoryIndex from '../utils/fetch-story-index.ts'; -import * as telemetry from '../telemetry.ts'; -// Mock CSF utilities vi.mock('storybook/internal/csf', () => ({ storyNameFromExport: (exportName: string) => exportName, })); +vi.mock('storybook/internal/telemetry', () => ({ + telemetry: vi.fn(), +})); + describe('getStoryUrlsTool', () => { let server: McpServer; let fetchStoryIndexSpy: any; - let collectTelemetrySpy: any; const testContext: AddonContext = { origin: 'http://localhost:6006', options: {} as any, @@ -63,10 +64,6 @@ describe('getStoryUrlsTool', () => { // Mock fetchStoryIndex to return the fixture fetchStoryIndexSpy = vi.spyOn(fetchStoryIndex, 'fetchStoryIndex'); fetchStoryIndexSpy.mockResolvedValue(smallStoryIndexFixture); - - // Mock collectTelemetry - collectTelemetrySpy = vi.spyOn(telemetry, 'collectTelemetry'); - collectTelemetrySpy.mockResolvedValue(undefined); }); it('should return story URL for a valid story', async () => { @@ -248,6 +245,8 @@ describe('getStoryUrlsTool', () => { }); it('should collect telemetry when enabled', async () => { + const { telemetry } = await import('storybook/internal/telemetry'); + const telemetryContext = { origin: 'http://localhost:6006', options: {} as any, @@ -276,13 +275,16 @@ describe('getStoryUrlsTool', () => { custom: telemetryContext, }); - expect(collectTelemetrySpy).toHaveBeenCalledWith({ - event: 'tool:getStoryUrls', - server, - toolset: 'dev', - inputStoryCount: 1, - outputStoryCount: 1, - }); + expect(telemetry).toHaveBeenCalledWith( + 'addon-mcp', + expect.objectContaining({ + event: 'tool:getStoryUrls', + mcpSessionId: 'test-session', + toolset: 'dev', + inputStoryCount: 1, + outputStoryCount: 1, + }), + ); }); it('should handle missing origin in context', async () => { diff --git a/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts b/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts index 2ca74dd5..3bbba0ee 100644 --- a/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts +++ b/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts @@ -6,12 +6,14 @@ import { GET_UI_BUILDING_INSTRUCTIONS_TOOL_NAME, } from './get-ui-building-instructions.ts'; import type { AddonContext } from '../types.ts'; -import * as telemetry from '../telemetry.ts'; import { GET_STORY_URLS_TOOL_NAME } from './get-story-urls.ts'; +vi.mock('storybook/internal/telemetry', () => ({ + telemetry: vi.fn(), +})); + describe('getUIBuildingInstructionsTool', () => { let server: McpServer; - let collectTelemetrySpy: any; beforeEach(async () => { const adapter = new ValibotJsonSchemaAdapter(); @@ -47,10 +49,6 @@ describe('getUIBuildingInstructionsTool', () => { ); await addGetUIBuildingInstructionsTool(server); - - // Mock collectTelemetry - collectTelemetrySpy = vi.spyOn(telemetry, 'collectTelemetry'); - collectTelemetrySpy.mockResolvedValue(undefined); }); it('should return UI building instructions with framework placeholders replaced', async () => { @@ -166,6 +164,8 @@ describe('getUIBuildingInstructionsTool', () => { }); it('should collect telemetry when enabled', async () => { + const { telemetry } = await import('storybook/internal/telemetry'); + const mockOptions = { presets: { apply: vi.fn().mockResolvedValue('@storybook/react-vite'), @@ -193,11 +193,14 @@ describe('getUIBuildingInstructionsTool', () => { custom: testContext, }); - expect(collectTelemetrySpy).toHaveBeenCalledWith({ - event: 'tool:getUIBuildingInstructions', - server, - toolset: 'dev', - }); + expect(telemetry).toHaveBeenCalledWith( + 'addon-mcp', + expect.objectContaining({ + event: 'tool:getUIBuildingInstructions', + mcpSessionId: 'test-session', + toolset: 'dev', + }), + ); }); it('should handle missing options in context', async () => { From 61baa0fff74e68e5dc1de7407186c4c2a6558aae Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:13:39 +0100 Subject: [PATCH 07/27] simplify mcp-handler tests, improve disableTelemetry handling --- packages/addon-mcp/src/mcp-handler.test.ts | 114 ++++++++++++--------- packages/addon-mcp/src/mcp-handler.ts | 16 +-- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/packages/addon-mcp/src/mcp-handler.test.ts b/packages/addon-mcp/src/mcp-handler.test.ts index 819eb97b..090f2710 100644 --- a/packages/addon-mcp/src/mcp-handler.test.ts +++ b/packages/addon-mcp/src/mcp-handler.test.ts @@ -1,19 +1,16 @@ // oxlint-disable typescript-eslint(unbound-method) -- I'm unsure how to fix this properly -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; import { incomingMessageToWebRequest, webResponseToServerResponse, - mcpServerHandler, getToolsets, } from './mcp-handler.ts'; import type { IncomingMessage, ServerResponse } from 'node:http'; import { PassThrough } from 'node:stream'; -// Mock dependencies -vi.mock('./telemetry.ts', { spy: true }); -vi.mock('./tools/get-story-urls.ts', { spy: true }); -vi.mock('./tools/get-ui-building-instructions.ts', { spy: true }); -vi.mock('@storybook/mcp', { spy: true }); +vi.mock('storybook/internal/telemetry', () => ({ + telemetry: vi.fn(), +})); // Test helpers to reduce boilerplate function createMockIncomingMessage(options: { @@ -207,6 +204,15 @@ describe('mcp-handler conversion utilities', () => { }); describe('mcpServerHandler', () => { + let mcpServerHandler: any; + + beforeEach(async () => { + // Reset modules and get fresh handler for each test to avoid state pollution + vi.resetModules(); + const handler = await import('./mcp-handler.ts'); + mcpServerHandler = handler.mcpServerHandler; + }); + function createMockOptions(overrides = {}) { return { port: 6006, @@ -277,13 +283,18 @@ describe('mcpServerHandler', () => { }); it('should respect disableTelemetry setting', async () => { - const { collectTelemetry } = await import('./telemetry.ts'); - vi.mocked(collectTelemetry).mockClear(); + const { telemetry } = await import('storybook/internal/telemetry'); + vi.mocked(telemetry).mockClear(); const mockOptions = createMockOptions({ port: 6007, presets: { - apply: vi.fn().mockResolvedValue({ disableTelemetry: true }), + apply: vi.fn(async (key: string) => { + if (key === 'core') { + return { disableTelemetry: true }; + } + return {}; + }), }, }); const mockReq = createMockIncomingMessage({ @@ -294,11 +305,6 @@ describe('mcpServerHandler', () => { }); const { response } = createMockServerResponse(); - // Reset module state by clearing transport - const handler = await import('./mcp-handler.ts'); - (handler as any).transport = undefined; - (handler as any).origin = undefined; - await mcpServerHandler({ req: mockReq, res: response, @@ -313,18 +319,14 @@ describe('mcpServerHandler', () => { // Verify handler completes successfully when telemetry is disabled expect(response.end).toHaveBeenCalled(); + + // Verify telemetry was NOT called when disabled + expect(telemetry).not.toHaveBeenCalled(); }); it('should register tools from @storybook/mcp when feature flag and generator are enabled', async () => { - // Force module reload to get fresh state - vi.resetModules(); - - const { mcpServerHandler: freshHandler } = await import('./mcp-handler.ts'); - const { addListAllComponentsTool, addGetComponentDocumentationTool } = - await import('@storybook/mcp'); - const applyMock = vi.fn(async (key: string, defaultValue?: any) => { - if (key === 'dev') { + if (key === 'core') { return { disableTelemetry: false }; } if (key === 'features') { @@ -340,44 +342,56 @@ describe('mcpServerHandler', () => { port: 6008, presets: { apply: applyMock }, }); - const mockReq = createMockIncomingMessage({ + + // First, initialize the MCP server + const initReq = createMockIncomingMessage({ method: 'POST', headers: { 'content-type': 'application/json', host: 'localhost:6008' }, body: createMCPInitializeRequest(), }); - const { response } = createMockServerResponse(); + const { response: initResponse } = createMockServerResponse(); - await freshHandler({ - req: mockReq, - res: response, + await mcpServerHandler({ + req: initReq, + res: initResponse, options: mockOptions as any, addonOptions: { toolsets: { dev: true, docs: true }, }, }); - // Verify component tools were registered - expect(addListAllComponentsTool).toHaveBeenCalledExactlyOnceWith( - expect.objectContaining({ - tool: expect.any(Function), - }), - expect.any(Function), - ); - expect(addGetComponentDocumentationTool).toHaveBeenCalledExactlyOnceWith( - expect.objectContaining({ - tool: expect.any(Function), - }), - expect.any(Function), - ); - - // Verify the 'enabled' callbacks matches the truthy addon options - const listToolEnabledCallback = vi.mocked(addListAllComponentsTool).mock - .calls[0]?.[1]; - const getToolEnabledCallback = vi.mocked(addGetComponentDocumentationTool) - .mock.calls[0]?.[1]; - - expect(listToolEnabledCallback?.()).toBe(true); - expect(getToolEnabledCallback?.()).toBe(true); + // Then, list tools to verify component manifest tools are registered + const listToolsReq = createMockIncomingMessage({ + method: 'POST', + headers: { 'content-type': 'application/json', host: 'localhost:6008' }, + body: { + jsonrpc: '2.0', + id: 2, + method: 'tools/list', + params: {}, + }, + }); + const { response: listResponse, getResponseData } = + createMockServerResponse(); + + await mcpServerHandler({ + req: listToolsReq, + res: listResponse, + options: mockOptions as any, + addonOptions: { + toolsets: { dev: true, docs: true }, + }, + }); + + // Parse the SSE response + const { body } = getResponseData(); + const responseText = body.replace(/^data: /, '').trim(); + const parsedResponse = JSON.parse(responseText); + + // Verify component manifest tools are included + const toolNames = parsedResponse.result.tools.map((t: any) => t.name); + expect(toolNames).toContain('list-all-components'); + expect(toolNames).toContain('get-component-documentation'); }); }); diff --git a/packages/addon-mcp/src/mcp-handler.ts b/packages/addon-mcp/src/mcp-handler.ts index 12c12719..049bda07 100644 --- a/packages/addon-mcp/src/mcp-handler.ts +++ b/packages/addon-mcp/src/mcp-handler.ts @@ -20,8 +20,12 @@ let transport: HttpTransport | undefined; let origin: string | undefined; // Promise that ensures single initialization, even with concurrent requests let initialize: Promise> | undefined; +let disableTelemetry: boolean | undefined; const initializeMCPServer = async (options: Options) => { + const core = await options.presets.apply('core', {}); + disableTelemetry = core?.disableTelemetry ?? false; + const server = new McpServer( { name: pkgJson.name, @@ -36,11 +40,11 @@ const initializeMCPServer = async (options: Options) => { }, ).withContext(); - server.on('initialize', async () => { - if (!options.disableTelemetry) { + if (!disableTelemetry) { + server.on('initialize', async () => { await collectTelemetry({ event: 'session:initialized', server }); - } - }); + }); + } // Register dev addon tools await addGetStoryUrlsTool(server); @@ -80,8 +84,6 @@ export const mcpServerHandler = async ({ options, addonOptions, }: McpServerHandlerParams) => { - const disableTelemetry = options.disableTelemetry ?? false; - // Initialize MCP server and transport on first request, with concurrency safety if (!initialize) { initialize = initializeMCPServer(options); @@ -95,7 +97,7 @@ export const mcpServerHandler = async ({ options, toolsets: getToolsets(webRequest, addonOptions), origin: origin!, - disableTelemetry, + disableTelemetry: disableTelemetry!, // Source URL for component manifest tools - points to the manifest endpoint source: `${origin}/manifests/components.json`, // Telemetry handlers for component manifest tools From e01da53633af1c0235ebfe51fe4215b9a04f77c3 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:34:50 +0100 Subject: [PATCH 08/27] add tests for manifest availability --- .../src/tools/is-manifest-available.test.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/addon-mcp/src/tools/is-manifest-available.test.ts diff --git a/packages/addon-mcp/src/tools/is-manifest-available.test.ts b/packages/addon-mcp/src/tools/is-manifest-available.test.ts new file mode 100644 index 00000000..40ae1fe5 --- /dev/null +++ b/packages/addon-mcp/src/tools/is-manifest-available.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect, vi } from 'vitest'; +import { isManifestAvailable } from './is-manifest-available.ts'; +import type { Options } from 'storybook/internal/types'; + +function createMockOptions({ + featureFlag = false, + hasGenerator = false, + hasFeaturesObject = true, +}: { + featureFlag?: boolean; + hasGenerator?: boolean; + hasFeaturesObject?: boolean; +} = {}): Options { + return { + presets: { + apply: vi.fn(async (key: string) => { + if (key === 'features') { + return hasFeaturesObject + ? { experimentalComponentsManifest: featureFlag } + : {}; + } + if (key === 'experimental_componentManifestGenerator') { + return hasGenerator ? vi.fn() : undefined; + } + return undefined; + }), + }, + } as unknown as Options; +} + +describe('isManifestAvailable', () => { + it.each([ + { + description: 'both feature flag and generator are present', + options: { featureFlag: true, hasGenerator: true }, + expected: true, + }, + { + description: 'feature flag is disabled', + options: { featureFlag: false, hasGenerator: true }, + expected: false, + }, + { + description: 'generator is not configured', + options: { featureFlag: true, hasGenerator: false }, + expected: false, + }, + { + description: 'both are missing', + options: { featureFlag: false, hasGenerator: false }, + expected: false, + }, + { + description: 'features object is missing the flag', + options: { hasGenerator: true, hasFeaturesObject: false }, + expected: false, + }, + ])( + 'should return $expected when $description', + async ({ options, expected }) => { + const mockOptions = createMockOptions(options); + const result = await isManifestAvailable(mockOptions); + + if (expected) { + expect(result).toBeTruthy(); + } else { + expect(result).toBeFalsy(); + } + }, + ); +}); From 1d4fbfd9d280544e382c286d146c31687643adf4 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:38:58 +0100 Subject: [PATCH 09/27] exclude evals from coverage --- vitest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vitest.config.ts b/vitest.config.ts index a4ae014f..65262ab5 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -25,7 +25,7 @@ export default defineConfig({ projects: ['packages/*', 'apps/*'], coverage: { include: ['**/src/**/*.{ts,tsx}'], - exclude: ['*.d.ts'], + exclude: ['*.d.ts', 'eval/templates/project', 'eval/evals'], reporter: ['text', 'lcov', 'html'], }, }, From 1cc6bbd1f9f2d67d7de0f26e5a4766dbccec79c2 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:49:51 +0100 Subject: [PATCH 10/27] cleanup --- .github/copilot-instructions.md | 9 +++++---- vitest.config.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 75c26c32..8105b5b6 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -89,11 +89,12 @@ The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic: **Testing:** -- **Unit tests**: `packages/mcp` has unit tests (Vitest with coverage) - - Run `pnpm test run --coverage` in mcp package +- **Unit tests**: Both `packages/mcp` and `packages/addon-mcp` have unit tests (Vitest with coverage) + - Run `pnpm test run --coverage` in individual package directories + - Run `pnpm test:run` at root to run all unit tests - Prefer TDD when adding new tools - **E2E tests**: `apps/internal-storybook/tests` contains E2E tests for the addon - - Run `pnpm test:e2e` at root or in internal-storybook + - Run `pnpm test` in `apps/internal-storybook` directory - Tests verify MCP endpoint works with latest Storybook prereleases - Uses inline snapshots for response validation - **When to update E2E tests**: @@ -102,7 +103,7 @@ The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic: - Modifying tool responses or schemas (update tool-specific tests) - Adding new toolsets or changing toolset behavior (update filtering tests) - **Running tests**: - - `pnpm test:e2e` - run all E2E tests + - `pnpm test` in apps/internal-storybook - run E2E tests - `pnpm vitest run -u` - update snapshots when responses change - Tests start Storybook server automatically, wait for MCP endpoint, then run - **Test structure**: diff --git a/vitest.config.ts b/vitest.config.ts index 65262ab5..a7d47325 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -25,7 +25,7 @@ export default defineConfig({ projects: ['packages/*', 'apps/*'], coverage: { include: ['**/src/**/*.{ts,tsx}'], - exclude: ['*.d.ts', 'eval/templates/project', 'eval/evals'], + exclude: ['*.d.ts', 'eval/templates/project/**', 'eval/evals/**'], reporter: ['text', 'lcov', 'html'], }, }, From 23daa6465fd810f647e33b125533a681caad757f Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 10:50:13 +0100 Subject: [PATCH 11/27] changeset --- .changeset/plain-hands-tap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/plain-hands-tap.md diff --git a/.changeset/plain-hands-tap.md b/.changeset/plain-hands-tap.md new file mode 100644 index 00000000..626b9f1b --- /dev/null +++ b/.changeset/plain-hands-tap.md @@ -0,0 +1,5 @@ +--- +'@storybook/addon-mcp': patch +--- + +improve handling of disableTelemetry option From ba473260f5673c60af7b4785370740ad70db2ce9 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 11:59:27 +0100 Subject: [PATCH 12/27] fix preset registering handlers instead of middlewares --- packages/addon-mcp/src/preset.test.ts | 29 ++++++++++++++------ packages/addon-mcp/src/preset.ts | 39 ++++++++++++++++----------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/packages/addon-mcp/src/preset.test.ts b/packages/addon-mcp/src/preset.test.ts index 51d1ed92..1b355f1e 100644 --- a/packages/addon-mcp/src/preset.test.ts +++ b/packages/addon-mcp/src/preset.test.ts @@ -9,9 +9,10 @@ describe('experimental_devServer', () => { beforeEach(() => { mockApp = { - use: vi.fn((path, handler) => { + post: vi.fn((path, handler) => { mcpHandler = handler; }), + get: vi.fn(), }; mockOptions = { @@ -26,18 +27,28 @@ describe('experimental_devServer', () => { } as unknown as Options; }); - it('should register /mcp endpoint', async () => { + it('should register /mcp POST endpoint', async () => { await (experimental_devServer as any)(mockApp, mockOptions); - expect(mockApp.use).toHaveBeenCalledWith('/mcp', expect.any(Function)); + expect(mockApp.post).toHaveBeenCalledWith('/mcp', expect.any(Function)); expect(mcpHandler).toBeDefined(); }); + it('should register /mcp GET endpoint', async () => { + await (experimental_devServer as any)(mockApp, mockOptions); + + expect(mockApp.get).toHaveBeenCalledWith('/mcp', expect.any(Function)); + }); + it('should serve HTML for browser GET requests', async () => { + let getHandler: any; + mockApp.get = vi.fn((path, handler) => { + getHandler = handler; + }); + await (experimental_devServer as any)(mockApp, mockOptions); const mockReq = { - method: 'GET', headers: { accept: 'text/html', }, @@ -48,7 +59,7 @@ describe('experimental_devServer', () => { end: vi.fn(), } as any; - await mcpHandler(mockReq, mockRes); + await getHandler(mockReq, mockRes); expect(mockRes.writeHead).toHaveBeenCalledWith(200, { 'Content-Type': 'text/html', @@ -56,7 +67,7 @@ describe('experimental_devServer', () => { expect(mockRes.end).toHaveBeenCalledWith(expect.stringContaining(' { + it('should handle POST requests as MCP protocol', async () => { await (experimental_devServer as any)(mockApp, mockOptions); const initializeRequest = JSON.stringify({ @@ -78,8 +89,9 @@ describe('experimental_devServer', () => { headers: { accept: 'text/html', 'content-type': 'application/json', + host: 'localhost:6006', }, - socket: {}, + socket: { encrypted: false }, url: '/', [Symbol.asyncIterator]: async function* () { yield Buffer.from(initializeRequest); @@ -126,6 +138,7 @@ describe('experimental_devServer', () => { await (experimental_devServer as any)(mockApp, partialOptions); - expect(mockApp.use).toHaveBeenCalledWith('/mcp', expect.any(Function)); + expect(mockApp.post).toHaveBeenCalledWith('/mcp', expect.any(Function)); + expect(mockApp.get).toHaveBeenCalledWith('/mcp', expect.any(Function)); }); }); diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index 9d81fca6..5368f4a1 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -15,24 +15,31 @@ export const experimental_devServer: PresetProperty< }); const shouldRedirect = await isManifestAvailable(options); - - app!.use('/mcp', (req, res) => { - if (req.method === 'GET' && req.headers['accept']?.includes('text/html')) { - // Browser request - send HTML with redirect - res.writeHead(200, { 'Content-Type': 'text/html' }); - - const html = htmlTemplate.replace( - '{{REDIRECT_META}}', - shouldRedirect - ? // redirect the user to the component manifest page after 10 seconds - '' - : // ... or hide the message about redirection - '', - ); - res.end(html); - } else { + app!.post('/mcp', (req, res) => + mcpServerHandler({ + req, + res, + options, + addonOptions, + }), + ); + app!.get('/mcp', (req, res) => { + if (!req.headers['accept']?.includes('text/html')) { return mcpServerHandler({ req, res, options, addonOptions }); } + + // Browser request - send HTML with redirect + res.writeHead(200, { 'Content-Type': 'text/html' }); + + const html = htmlTemplate.replace( + '{{REDIRECT_META}}', + shouldRedirect + ? // redirect the user to the component manifest page after 10 seconds + '' + : // ... or hide the message about redirection + '', + ); + res.end(html); }); return app; }; From 35859a4624e4576c6714a1c0154b3e2e5bc51d87 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 12:00:04 +0100 Subject: [PATCH 13/27] update tests to match changes in base branch --- eval/package.json | 2 +- packages/mcp/src/index.test.ts | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/eval/package.json b/eval/package.json index 58b6b5b0..e925f6a4 100644 --- a/eval/package.json +++ b/eval/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "eval": "node eval.ts", - "storybook": "storybook dev -p 6006", + "storybook": "storybook dev --port 6007 --no-open", "typecheck": "tsc" }, "devDependencies": { diff --git a/packages/mcp/src/index.test.ts b/packages/mcp/src/index.test.ts index 9db594d1..857fb33b 100644 --- a/packages/mcp/src/index.test.ts +++ b/packages/mcp/src/index.test.ts @@ -95,7 +95,6 @@ describe('createStorybookMcpHandler', () => { .mockResolvedValue(JSON.stringify(smallManifestFixture)); const handler = await createStorybookMcpHandler({ - source: 'https://example.com/manifest.json', manifestProvider, }); await setupClient(handler); @@ -106,7 +105,8 @@ describe('createStorybookMcpHandler', () => { }); expect(manifestProvider).toHaveBeenCalledWith( - 'https://example.com/manifest.json', + expect.any(Request), + './manifests/components.json', ); expect(result.content).toHaveLength(1); expect((result.content as any)[0]).toMatchObject({ @@ -122,7 +122,6 @@ describe('createStorybookMcpHandler', () => { .mockResolvedValue(JSON.stringify(smallManifestFixture)); const handler = await createStorybookMcpHandler({ - source: 'https://example.com/manifest.json', manifestProvider, onListAllComponents, }); @@ -136,7 +135,7 @@ describe('createStorybookMcpHandler', () => { expect(onListAllComponents).toHaveBeenCalledTimes(1); expect(onListAllComponents).toHaveBeenCalledWith({ context: expect.objectContaining({ - source: 'https://example.com/manifest.json', + request: expect.any(Request), }), manifest: smallManifestFixture, }); @@ -149,7 +148,6 @@ describe('createStorybookMcpHandler', () => { .mockResolvedValue(JSON.stringify(smallManifestFixture)); const handler = await createStorybookMcpHandler({ - source: 'https://example.com/manifest.json', manifestProvider, onGetComponentDocumentation, }); @@ -165,7 +163,7 @@ describe('createStorybookMcpHandler', () => { expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); expect(onGetComponentDocumentation).toHaveBeenCalledWith({ context: expect.objectContaining({ - source: 'https://example.com/manifest.json', + request: expect.any(Request), }), input: { componentId: 'button' }, foundComponent: expect.objectContaining({ @@ -204,7 +202,6 @@ describe('createStorybookMcpHandler', () => { .mockResolvedValue(JSON.stringify(smallManifestFixture)); const handler = await createStorybookMcpHandler({ - source: 'https://example.com/manifest.json', manifestProvider, onGetComponentDocumentation, }); @@ -217,14 +214,13 @@ describe('createStorybookMcpHandler', () => { }, }); - // Should still call the handler with foundComponent: undefined + // Should still call the handler expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); expect(onGetComponentDocumentation).toHaveBeenCalledWith({ context: expect.objectContaining({ - source: 'https://example.com/manifest.json', + request: expect.any(Request), }), input: { componentId: 'non-existent' }, - foundComponent: undefined, }); expect(result.content).toHaveLength(1); From f38a3bf8f3a518d7b7229781cfe599ce9a22689c Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 12:02:18 +0100 Subject: [PATCH 14/27] cleanup --- packages/addon-mcp/src/preset.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index 5368f4a1..5b232f64 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -14,7 +14,6 @@ export const experimental_devServer: PresetProperty< toolsets: 'toolsets' in options ? options.toolsets : {}, }); - const shouldRedirect = await isManifestAvailable(options); app!.post('/mcp', (req, res) => mcpServerHandler({ req, @@ -23,6 +22,9 @@ export const experimental_devServer: PresetProperty< addonOptions, }), ); + + const shouldRedirect = await isManifestAvailable(options); + app!.get('/mcp', (req, res) => { if (!req.headers['accept']?.includes('text/html')) { return mcpServerHandler({ req, res, options, addonOptions }); From 85c6076dff81773301b583db897b058c69b901ce Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 12:07:12 +0100 Subject: [PATCH 15/27] await sb process kill --- apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index 7cd29822..71490019 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -97,10 +97,14 @@ describe('MCP Endpoint E2E Tests', () => { }, STARTUP_TIMEOUT); afterAll(async () => { - if (storybookProcess) { - storybookProcess.kill('SIGTERM'); - storybookProcess = null; + if (!storybookProcess || !storybookProcess.process) { + return; } + const kill = Promise.withResolvers(); + storybookProcess.process.on('exit', kill.resolve); + storybookProcess.kill('SIGTERM'); + await kill.promise; + storybookProcess = null; }); describe('Session Initialization', () => { From d615a8d9d546b6f60f2184d1699d45c6594812dc Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 15:01:59 +0100 Subject: [PATCH 16/27] refactor formatter, splitting into markdown and xml, configurable, defaulting to markdown --- .github/copilot-instructions.md | 8 +- .../tests/mcp-endpoint.e2e.test.ts | 140 +-- packages/addon-mcp/README.md | 1 + packages/addon-mcp/src/mcp-handler.ts | 1 + packages/addon-mcp/src/preset.ts | 1 + packages/addon-mcp/src/types.ts | 1 + packages/mcp/bin.ts | 12 +- packages/mcp/serve.ts | 18 +- packages/mcp/src/index.test.ts | 2 +- packages/mcp/src/index.ts | 1 + .../tools/get-component-documentation.test.ts | 94 +- .../src/tools/get-component-documentation.ts | 3 +- .../mcp/src/tools/list-all-components.test.ts | 78 +- packages/mcp/src/tools/list-all-components.ts | 3 +- packages/mcp/src/types.ts | 14 + .../format-manifest.test.ts.snap | 507 +++----- .../mcp/src/utils/format-manifest.test.ts | 582 ++++------ packages/mcp/src/utils/format-manifest.ts | 151 +-- .../__snapshots__/markdown.test.ts.snap | 357 ++++++ .../__snapshots__/xml.test.ts.snap | 560 +++++++++ .../utils/manifest-formatter/markdown.test.ts | 740 ++++++++++++ .../src/utils/manifest-formatter/markdown.ts | 141 +++ .../mcp/src/utils/manifest-formatter/types.ts | 29 + .../src/utils/manifest-formatter/xml.test.ts | 1018 +++++++++++++++++ .../mcp/src/utils/manifest-formatter/xml.ts | 132 +++ packages/mcp/tsconfig.json | 2 +- 26 files changed, 3602 insertions(+), 994 deletions(-) create mode 100644 packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap create mode 100644 packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap create mode 100644 packages/mcp/src/utils/manifest-formatter/markdown.test.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/markdown.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/types.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/xml.test.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/xml.ts diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5d58e9b4..7e5959e0 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -35,7 +35,8 @@ The addon supports configuring which toolsets are enabled: toolsets: { dev: true, // get-story-urls, get-ui-building-instructions docs: true, // list-all-components, get-component-documentation - } + }, + experimentalFormat: 'markdown' // Output format: 'markdown' (default) or 'xml' } } ``` @@ -73,6 +74,11 @@ The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic: - `onSessionInitialize`: Called when an MCP session is initialized - `onListAllComponents`: Called when the list-all-components tool is invoked - `onGetComponentDocumentation`: Called when the get-component-documentation tool is invoked +- **Output Format**: The `format` property in context controls output format: + - `'markdown'` (default): Token-efficient markdown with adaptive formatting + - `'xml'`: Legacy XML format + - Format is configurable via addon options or directly in `StorybookContext` + - Formatters are implemented in `packages/mcp/src/utils/manifest-formatter/` with separate files for XML and markdown ## Development Environment diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index 71490019..569ae7db 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -321,30 +321,12 @@ describe('MCP Endpoint E2E Tests', () => { { "content": [ { - "text": " - - example-button - Button - - A customizable button component for user interactions. - - - - header - Header - - - page - Page - - - other-ui-card - Card - - Card component with title, image, content, and action button - - - ", + "text": "# Components + + - Button (example-button): A customizable button component for user interactions. + - Header (header) + - Page (page) + - Card (other-ui-card): Card component with title, image, content, and action button", "type": "text", }, ], @@ -362,9 +344,9 @@ describe('MCP Endpoint E2E Tests', () => { }); const listText = listResponse.result.content[0].text; - const idMatch = listText.match(/([^<]+)<\/id>/); + // Match markdown format: - ComponentName (component-id) + const idMatch = listText.match(/- \w+ \(([^)]+)\)/); expect(idMatch).toBeTruthy(); - const componentId = idMatch![1]; // Now get documentation for that component @@ -379,89 +361,55 @@ describe('MCP Endpoint E2E Tests', () => { { "content": [ { - "text": " - example-button - Button - + "text": "# Button + + ID: example-button + Primary UI component for user interaction - - - Primary - + + ## Examples + + ### Primary + + \`\`\` import { Button } from "@my-org/my-component-library"; const Primary = () => ; - - - - Secondary - + \`\`\` + + ### Secondary + + \`\`\` import { Button } from "@my-org/my-component-library"; const Secondary = () => ; - - - - Large - + \`\`\` + + ### Large + + \`\`\` import { Button } from "@my-org/my-component-library"; const Large = () => ; - - - - Small - + \`\`\` + + ### Small + + \`\`\` import { Button } from "@my-org/my-component-library"; const Small = () => ; - - - - - primary - - Is this the principal call to action on the page? - - boolean - false - false - - - backgroundColor - - What background color to use - - string - false - - - size - - How large should the button be? - - 'small' | 'medium' | 'large' - false - 'medium' - - - label - - Button contents - - string - true - - - onClick - - Optional click handler - - () => void - false - - - ", + \`\`\` + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | primary | boolean | Is this the principal call to action on the page? | false | false | + | backgroundColor | string | What background color to use | false | | + | size | 'small' | 'medium' | 'large' | How large should the button be? | false | 'medium' | + | label | string | Button contents | true | | + | onClick | () => void | Optional click handler | false | |", "type": "text", }, ], diff --git a/packages/addon-mcp/README.md b/packages/addon-mcp/README.md index fdbbbc93..b2492b03 100644 --- a/packages/addon-mcp/README.md +++ b/packages/addon-mcp/README.md @@ -50,6 +50,7 @@ export default { dev: true, // Tools for story URL retrieval and UI building instructions (default: true) docs: true, // Tools for component manifest and documentation (default: true, requires experimental feature) }, + experimentalFormat: 'markdown', // Output format: 'markdown' (default) or 'xml' }, }, ], diff --git a/packages/addon-mcp/src/mcp-handler.ts b/packages/addon-mcp/src/mcp-handler.ts index eac9698a..557c4ab7 100644 --- a/packages/addon-mcp/src/mcp-handler.ts +++ b/packages/addon-mcp/src/mcp-handler.ts @@ -96,6 +96,7 @@ export const mcpServerHandler = async ({ const addonContext: AddonContext = { options, toolsets: getToolsets(webRequest, addonOptions), + format: addonOptions.experimentalFormat, origin: origin!, disableTelemetry: disableTelemetry!, request: webRequest, diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index 5b232f64..fe66e58e 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -12,6 +12,7 @@ export const experimental_devServer: PresetProperty< // ValiError: Invalid type: Expected boolean but received "false" const addonOptions = v.parse(AddonOptions, { toolsets: 'toolsets' in options ? options.toolsets : {}, + format: 'format' in options ? options.format : 'markdown', }); app!.post('/mcp', (req, res) => diff --git a/packages/addon-mcp/src/types.ts b/packages/addon-mcp/src/types.ts index 0443469f..2f181b58 100644 --- a/packages/addon-mcp/src/types.ts +++ b/packages/addon-mcp/src/types.ts @@ -14,6 +14,7 @@ export const AddonOptions = v.object({ docs: true, }, ), + experimentalFormat: v.optional(v.picklist(['xml', 'markdown']), 'markdown'), }); export type AddonOptionsInput = v.InferInput; diff --git a/packages/mcp/bin.ts b/packages/mcp/bin.ts index 6de06119..b1bcb404 100644 --- a/packages/mcp/bin.ts +++ b/packages/mcp/bin.ts @@ -1,14 +1,14 @@ /** * This is a way to start the @storybook/mcp server as a stdio MCP server, which is sometimes easier for testing. * You can run it like this: - * node bin.ts --manifestPath ./path/to/manifest.json + * node bin.ts --manifestPath ./path/to/manifest.json --format markdown * * Or when configuring it as an MCP server: * { * "storybook-mcp": { * "type": "stdio", * "command": "node", - * "args": ["bin.ts", "--manifestPath", "./path/to/manifest.json"] + * "args": ["bin.ts", "--manifestPath", "./path/to/manifest.json", "--format", "markdown"] * } * } */ @@ -19,6 +19,7 @@ import pkgJson from './package.json' with { type: 'json' }; import { addListAllComponentsTool } from './src/tools/list-all-components.ts'; import { addGetComponentDocumentationTool } from './src/tools/get-component-documentation.ts'; import type { StorybookContext } from './src/types.ts'; +import type { OutputFormat } from './src/utils/manifest-formatter/types.ts'; import { parseArgs } from 'node:util'; import * as fs from 'node:fs/promises'; @@ -47,11 +48,18 @@ const args = parseArgs({ type: 'string', default: './fixtures/full-manifest.fixture.json', }, + format: { + type: 'string', + default: 'markdown', + }, }, }); +const format = args.values.format as OutputFormat; + transport.listen({ source: args.values.manifestPath, + format, manifestProvider: async () => { const { manifestPath } = args.values; if ( diff --git a/packages/mcp/serve.ts b/packages/mcp/serve.ts index 9b965562..e8d4a1b4 100644 --- a/packages/mcp/serve.ts +++ b/packages/mcp/serve.ts @@ -2,9 +2,15 @@ import { createStorybookMcpHandler } from './src/index.ts'; import { serve } from 'srvx'; import fs from 'node:fs/promises'; import { parseArgs } from 'node:util'; +import type { OutputFormat } from './src/utils/manifest-formatter/types.ts'; -async function serveMcp(port: number, manifestPath: string) { +async function serveMcp( + port: number, + manifestPath: string, + format: OutputFormat, +) { const storybookMcpHandler = await createStorybookMcpHandler({ + format, // Use the local fixture file via manifestProvider manifestProvider: async () => { if ( @@ -44,7 +50,15 @@ if (import.meta.main) { type: 'string', default: './fixtures/full-manifest.fixture.json', }, + format: { + type: 'string', + default: 'markdown', + }, }, }); - await serveMcp(Number(args.values.port), args.values.manifestPath); + await serveMcp( + Number(args.values.port), + args.values.manifestPath, + args.values.format as OutputFormat, + ); } diff --git a/packages/mcp/src/index.test.ts b/packages/mcp/src/index.test.ts index 857fb33b..c85b093d 100644 --- a/packages/mcp/src/index.test.ts +++ b/packages/mcp/src/index.test.ts @@ -111,7 +111,7 @@ describe('createStorybookMcpHandler', () => { expect(result.content).toHaveLength(1); expect((result.content as any)[0]).toMatchObject({ type: 'text', - text: expect.stringContaining(''), + text: expect.stringContaining('# Components'), }); }); diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 252c3723..c81a4280 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -98,6 +98,7 @@ export const createStorybookMcpHandler = async ( return (async (req, context) => { return await transport.respond(req, { request: req, + format: context?.format ?? options.format ?? 'markdown', manifestProvider: context?.manifestProvider ?? options.manifestProvider, onListAllComponents: context?.onListAllComponents ?? options.onListAllComponents, diff --git a/packages/mcp/src/tools/get-component-documentation.test.ts b/packages/mcp/src/tools/get-component-documentation.test.ts index 5ebcca0f..b77fafc4 100644 --- a/packages/mcp/src/tools/get-component-documentation.test.ts +++ b/packages/mcp/src/tools/get-component-documentation.test.ts @@ -72,19 +72,19 @@ describe('getComponentDocumentationTool', () => { { "content": [ { - "text": " - button - Button - - Primary - + "text": "# Button + + ID: button + + ## Examples + + ### Primary + The primary button variant. - - + + \`\`\` const Primary = () => - - - ", + \`\`\`", "type": "text", }, ], @@ -251,6 +251,47 @@ describe('getComponentDocumentationTool', () => { custom: { request: mockHttpRequest }, }); + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "# Button + + ID: button + + A button component + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | variant | \`"primary" | "secondary"\` | Button style variant | false | "primary" | + | disabled | \`boolean\` | Disable the button | false | |", + "type": "text", + }, + ], + } + `); + }); + + it('should format component as XML when format is "xml"', async () => { + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + componentId: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest, format: 'xml' as const }, + }); + expect(response.result).toMatchInlineSnapshot(` { "content": [ @@ -258,28 +299,15 @@ describe('getComponentDocumentationTool', () => { "text": " button Button - - A button component - - - - variant - - Button style variant - - "primary" | "secondary" - false - "primary" - - - disabled - - Disable the button - - boolean - false - - + + Primary + + The primary button variant. + + + const Primary = () => + + ", "type": "text", }, diff --git a/packages/mcp/src/tools/get-component-documentation.ts b/packages/mcp/src/tools/get-component-documentation.ts index 47949deb..198c5769 100644 --- a/packages/mcp/src/tools/get-component-documentation.ts +++ b/packages/mcp/src/tools/get-component-documentation.ts @@ -58,11 +58,12 @@ export async function addGetComponentDocumentationTool( foundComponent: component, }); + const format = server.ctx.custom?.format ?? 'markdown'; return { content: [ { type: 'text' as const, - text: formatComponentManifest(component), + text: formatComponentManifest(component, format), }, ], }; diff --git a/packages/mcp/src/tools/list-all-components.test.ts b/packages/mcp/src/tools/list-all-components.test.ts index 5487e6d6..22ab0e48 100644 --- a/packages/mcp/src/tools/list-all-components.test.ts +++ b/packages/mcp/src/tools/list-all-components.test.ts @@ -70,29 +70,11 @@ describe('listAllComponentsTool', () => { { "content": [ { - "text": " - - button - Button - - A simple button component - - - - card - Card - - A container component for grouping related content. - - - - input - Input - - A text input component with validation support. - - - ", + "text": "# Components + + - Button (button): A simple button component + - Card (card): A container component for grouping related content. + - Input (input): A text input component with validation support.", "type": "text", }, ], @@ -195,4 +177,54 @@ describe('listAllComponentsTool', () => { manifest: smallManifestFixture, }); }); + + it('should format components as XML when format is "xml"', async () => { + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: LIST_TOOL_NAME, + arguments: {}, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest, format: 'xml' as const }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": " + + button + Button + + A simple button component + + + + card + Card + + A container component for grouping related content. + + + + input + Input + + A text input component with validation support. + + + ", + "type": "text", + }, + ], + } + `); + }); }); diff --git a/packages/mcp/src/tools/list-all-components.ts b/packages/mcp/src/tools/list-all-components.ts index d456581e..90c9278f 100644 --- a/packages/mcp/src/tools/list-all-components.ts +++ b/packages/mcp/src/tools/list-all-components.ts @@ -24,7 +24,8 @@ export async function addListAllComponentsTool( server.ctx.custom?.manifestProvider, ); - const componentList = formatComponentManifestMapToList(manifest); + const format = server.ctx.custom?.format ?? 'markdown'; + const componentList = formatComponentManifestMapToList(manifest, format); await server.ctx.custom?.onListAllComponents?.({ context: server.ctx.custom, diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index 42b29e9e..8d3b5be8 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -1,6 +1,15 @@ import type { Documentation } from 'react-docgen'; import * as v from 'valibot'; +/** + * Supported output formats for component manifest formatting. + */ +export const OutputFormat = v.optional( + v.picklist(['xml', 'markdown']), + 'markdown', +); +export type OutputFormatType = v.InferOutput; + /** * Custom context passed to MCP server and tools. * Contains the request object and optional manifest provider. @@ -10,6 +19,11 @@ export interface StorybookContext extends Record { * The incoming HTTP request being processed. */ request?: Request; + /** + * Output format for component manifests. + * @default 'markdown' + */ + format?: OutputFormatType; /** * Optional function to provide custom manifest retrieval logic. * If provided, this function will be called instead of the default fetch-based provider. diff --git a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap b/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap index dff5fc32..7c8abf8b 100644 --- a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap +++ b/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap @@ -1,10 +1,10 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`formatComponentManifest > formats all full fixtures 1`] = ` -" -button -Button - +"# Button + +ID: button + A versatile button component that supports multiple variants, sizes, and states. The Button component is a fundamental building block for user interactions. It can be styled as primary, secondary, or tertiary actions, and supports disabled and loading states. @@ -12,10 +12,11 @@ The Button component is a fundamental building block for user interactions. It c ## Usage Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. - - -Primary - + +## Examples + +### Primary + The primary button variant is used for the main call-to-action on a page. It has the highest visual prominence and should be used sparingly to guide users toward the most important action. ## Best Practices @@ -23,34 +24,32 @@ The primary button variant is used for the main call-to-action on a page. It has - Use only one primary button per section - Keep button text concise and action-oriented - Ensure sufficient contrast for accessibility - - + +\`\`\` import { Button } from '@storybook/design-system'; const Primary = () => - - - -Secondary - +\`\`\` + +### Secondary + The secondary button variant is used for secondary actions that are still important but not the primary focus of the page. Secondary buttons have less visual weight than primary buttons and can be used multiple times on a page. - - + +\`\`\` import { Button } from '@storybook/design-system'; const Secondary = () => - - - -With Sizes - +\`\`\` + +### With Sizes + Buttons are available in three sizes: small, medium (default), and large. Choose the appropriate size based on the context and hierarchy of actions. Larger buttons are more prominent and easier to tap on mobile devices. - - + +\`\`\` import { Button } from '@storybook/design-system'; const WithSizes = () => ( @@ -60,105 +59,50 @@ const WithSizes = () => ( ) - - - -Loading - +\`\`\` + +### Loading + The loading state provides visual feedback when an async operation is in progress. When loading is true, the button displays a spinner and is automatically disabled to prevent multiple submissions. The button text remains visible to maintain layout stability. - - + +\`\`\` import { Button } from '@storybook/design-system'; const Loading = () => - - - -Danger - +\`\`\` + +### Danger + The danger variant is used for destructive actions that cannot be easily undone, such as deleting data or canceling subscriptions. Use this variant to draw attention to the serious nature of the action. Consider adding a confirmation dialog for critical operations. - - + +\`\`\` import { Button } from '@storybook/design-system'; const Danger = () => - - - - -variant - -The visual style variant of the button - -"primary" | "secondary" | "tertiary" | "danger" -false -"primary" - - -size - -The size of the button - -"small" | "medium" | "large" -false -"medium" - - -disabled - -Whether the button is disabled - -boolean -false -false - - -loading - -Whether the button is in a loading state - -boolean -false -false - - -fullWidth - -Whether the button should take up the full width of its container - -boolean -false -false - - -onClick - -Callback function when the button is clicked - -(event: MouseEvent) => void -false - - -children - -The content of the button - -ReactNode -true - - -" +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| variant | \`"primary" | "secondary" | "tertiary" | "danger"\` | The visual style variant of the button | false | "primary" | +| size | \`"small" | "medium" | "large"\` | The size of the button | false | "medium" | +| disabled | \`boolean\` | Whether the button is disabled | false | false | +| loading | \`boolean\` | Whether the button is in a loading state | false | false | +| fullWidth | \`boolean\` | Whether the button should take up the full width of its container | false | false | +| onClick | \`(event: MouseEvent) => void\` | Callback function when the button is clicked | false | | +| children | \`ReactNode\` | The content of the button | true | |" `; exports[`formatComponentManifest > formats all full fixtures 2`] = ` -" -card -Card - +"# Card + +ID: card + A flexible container component for grouping related content with optional header, footer, and action areas. The Card component provides a consistent way to present information in a contained, elevated surface. It's commonly used for displaying articles, products, user profiles, or any grouped content that benefits from visual separation. @@ -169,15 +113,16 @@ The Card component provides a consistent way to present information in a contain - Maintain consistent padding and spacing - Use elevation to indicate interactive vs static cards - Keep content hierarchy clear with proper use of typography - - -Basic - + +## Examples + +### Basic + A basic card with just content. The default elevated variant provides subtle depth through shadow, making the card appear to float above the page. This is ideal for creating visual hierarchy and grouping related information. - - + +\`\`\` import { Card } from '@storybook/design-system'; const Basic = () => ( @@ -186,16 +131,15 @@ const Basic = () => (

This is some card content that provides information to the user.

) -
-
- -With Header And Footer - +\`\`\` + +### With Header And Footer + A card with distinct header and footer sections. Headers typically contain titles, subtitles, or avatars. Footers often contain actions like buttons or metadata like timestamps. The header and footer are visually separated from the main content area. - - + +\`\`\` import { Card } from '@storybook/design-system'; const WithHeaderAndFooter = () => ( @@ -206,11 +150,10 @@ const WithHeaderAndFooter = () => (

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

) -
-
- -Clickable - +\`\`\` + +### Clickable + An interactive card that responds to clicks. Clickable cards add hover effects and cursor changes to indicate interactivity. This pattern is useful for navigation cards, product cards, or any scenario where the entire card acts as a single interactive element. @@ -218,8 +161,8 @@ Clickable cards add hover effects and cursor changes to indicate interactivity. ## Accessibility Clickable cards are rendered as buttons with proper keyboard support and ARIA attributes. - - + +\`\`\` import { Card } from '@storybook/design-system'; const Clickable = () => ( @@ -228,11 +171,10 @@ const Clickable = () => (

Click anywhere on this card to view details.

) -
-
- -Variants - +\`\`\` + +### Variants + Different visual variants of the card component. - **Elevated**: Default variant with shadow for depth @@ -240,8 +182,8 @@ Different visual variants of the card component. - **Flat**: No border or shadow, minimal visual separation Choose variants based on your design system and the level of emphasis needed. - - + +\`\`\` import { Card } from '@storybook/design-system'; const Variants = () => ( @@ -257,16 +199,15 @@ const Variants = () => ( ) - - - -User Profile - +\`\`\` + +### User Profile + A real-world example of a user profile card. This example demonstrates how to compose the Card component with other design system components to create a complete, functional UI element. It includes an avatar, user information, stats, and action buttons. - - + +\`\`\` import { Card } from '@storybook/design-system'; const UserProfile = () => ( @@ -294,77 +235,26 @@ const UserProfile = () => ( ) - - - - -variant - -The visual style variant of the card - -"elevated" | "outlined" | "flat" -false -"elevated" - - -padding - -The amount of internal padding - -"none" | "small" | "medium" | "large" -false -"medium" - - -clickable - -Whether the entire card is clickable/interactive - -boolean -false -false - - -header - -Content to display in the card header - -ReactNode -false - - -footer - -Content to display in the card footer - -ReactNode -false - - -children - -The main content of the card - -ReactNode -true - - -onClick - -Callback function when the card is clicked (requires clickable=true) - -(event: MouseEvent) => void -false - - -
" +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| variant | \`"elevated" | "outlined" | "flat"\` | The visual style variant of the card | false | "elevated" | +| padding | \`"none" | "small" | "medium" | "large"\` | The amount of internal padding | false | "medium" | +| clickable | \`boolean\` | Whether the entire card is clickable/interactive | false | false | +| header | \`ReactNode\` | Content to display in the card header | false | | +| footer | \`ReactNode\` | Content to display in the card footer | false | | +| children | \`ReactNode\` | The main content of the card | true | | +| onClick | \`(event: MouseEvent) => void\` | Callback function when the card is clicked (requires clickable=true) | false | |" `; exports[`formatComponentManifest > formats all full fixtures 3`] = ` -" -input -Input - +"# Input + +ID: input + A flexible text input component that supports various input types, validation states, and accessibility features. The Input component is a foundational form element that wraps the native HTML input with consistent styling and behavior. It includes support for labels, error messages, helper text, and different visual states. @@ -372,54 +262,52 @@ The Input component is a foundational form element that wraps the native HTML in ## Accessibility The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. - - -Basic - + +## Examples + +### Basic + A basic text input with a label. This is the most common use case for the Input component. Always include a label for accessibility, even if it's visually hidden in your design. - - + +\`\`\` import { Input } from '@storybook/design-system'; const Basic = () => - - - -With Error - +\`\`\` + +### With Error + An input displaying an error state with an error message. Error messages should be clear, concise, and provide actionable guidance to help users fix the issue. The input border and message text are styled in red to indicate the error state. - - + +\`\`\` import { Input } from '@storybook/design-system'; const WithError = () => - - - -With Helper Text - +\`\`\` + +### With Helper Text + An input with helper text providing additional context or instructions. Helper text appears below the input and provides guidance without being an error. Use it to clarify format expectations, character limits, or provide helpful hints. - - + +\`\`\` import { Input } from '@storybook/design-system'; const WithHelperText = () => - - - -Types - +\`\`\` + +### Types + Different input types for various data formats. Using the correct input type improves the user experience by showing appropriate mobile keyboards and enabling browser validation features. - - + +\`\`\` import { Input } from '@storybook/design-system'; const Types = () => ( @@ -430,131 +318,40 @@ const Types = () => ( ) - - - -Disabled - +\`\`\` + +### Disabled + A disabled input that cannot be interacted with. Disabled inputs are useful for displaying non-editable data in forms or for inputs that become available only after certain conditions are met. - - + +\`\`\` import { Input } from '@storybook/design-system'; const Disabled = () => - - - - -type - -The type of input field - -"text" | "email" | "password" | "number" | "tel" | "url" -false -"text" - - -label - -The label text for the input - -string -false - - -placeholder - -Placeholder text shown when the input is empty - -string -false - - -value - -The controlled value of the input - -string -false - - -defaultValue - -The initial value for an uncontrolled input - -string -false - - -disabled - -Whether the input is disabled - -boolean -false -false - - -required - -Whether the input is required - -boolean -false -false - - -error - -Error message to display below the input - -string -false - - -helperText - -Helper text to display below the input - -string -false - - -onChange - -Callback function when the input value changes - -(event: ChangeEvent) => void -false - - -" +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| type | \`"text" | "email" | "password" | "number" | "tel" | "url"\` | The type of input field | false | "text" | +| label | \`string\` | The label text for the input | false | | +| placeholder | \`string\` | Placeholder text shown when the input is empty | false | | +| value | \`string\` | The controlled value of the input | false | | +| defaultValue | \`string\` | The initial value for an uncontrolled input | false | | +| disabled | \`boolean\` | Whether the input is disabled | false | false | +| required | \`boolean\` | Whether the input is required | false | false | +| error | \`string\` | Error message to display below the input | false | | +| helperText | \`string\` | Helper text to display below the input | false | | +| onChange | \`(event: ChangeEvent) => void\` | Callback function when the input value changes | false | |" `; exports[`formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` -" - -button -Button - -A versatile button component for user interactions - - - -card -Card - -A flexible container component for grouping related content - - - -input -Input - -A flexible text input component with validation support - - -" +"# Components + +- Button (button): A versatile button component for user interactions +- Card (card): A flexible container component for grouping related content +- Input (input): A flexible text input component with validation support" `; diff --git a/packages/mcp/src/utils/format-manifest.test.ts b/packages/mcp/src/utils/format-manifest.test.ts index b1484bd3..e7eb519b 100644 --- a/packages/mcp/src/utils/format-manifest.test.ts +++ b/packages/mcp/src/utils/format-manifest.test.ts @@ -31,10 +31,9 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - test-component - TestComponent - " + "# TestComponent + + ID: test-component" `); }); }); @@ -51,13 +50,11 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - A simple button component - - " + "# Button + + ID: button + + A simple button component" `); }); @@ -73,15 +70,13 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - + "# Button + + ID: button + A versatile button component. - Supports multiple variants and sizes. - - " + Supports multiple variants and sizes." `); }); @@ -95,10 +90,9 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - " + "# Button + + ID: button" `); }); }); @@ -122,21 +116,21 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - Primary - + "# Button + + ID: button + + ## Examples + + ### Primary + A primary button variant - - + + \`\`\` import { Button } from "@/components"; - - - " + \`\`\`" `); }); @@ -161,26 +155,27 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - Primary - + "# Button + + ID: button + + ## Examples + + ### Primary + + \`\`\` import { Button } from "@/components"; - - - - Secondary - + \`\`\` + + ### Secondary + + \`\`\` import { Button } from "@/components"; - - - " + \`\`\`" `); }); @@ -204,22 +199,23 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - With Icon - + "# Button + + ID: button + + ## Examples + + ### With Icon + + \`\`\` - - - - Disabled State - + \`\`\` + + ### Disabled State + + \`\`\` - - - " + \`\`\`" `); }); @@ -239,16 +235,17 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - Simple - + "# Button + + ID: button + + ## Examples + + ### Simple + + \`\`\` - - - " + \`\`\`" `); }); @@ -268,16 +265,17 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - No Import - + "# Button + + ID: button + + ## Examples + + ### No Import + + \`\`\` - - - " + \`\`\`" `); }); @@ -292,13 +290,11 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - A button component - - " + "# Button + + ID: button + + A button component" `); }); @@ -313,10 +309,9 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - " + "# Button + + ID: button" `); }); }); @@ -350,31 +345,31 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - + "# Button + + ID: button + A versatile button component. Supports multiple variants, sizes, and states. - - - Primary - + + ## Examples + + ### Primary + The primary button variant. - - + + \`\`\` import { Button } from "@storybook/design-system"; const Primary = () => - - - - With Sizes - + \`\`\` + + ### With Sizes + Buttons in different sizes. - - + + \`\`\` import { Button } from "@storybook/design-system"; const Sizes = () => ( @@ -383,9 +378,7 @@ describe('formatComponentManifest', () => { ) - - - " + \`\`\`" `); }); }); @@ -438,38 +431,17 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - - variant - - The visual style variant - - "primary" | "secondary" - false - "primary" - - - disabled - - Whether the button is disabled - - boolean - false - false - - - onClick - - Click handler - - (event: MouseEvent) => void - true - - - " + "# Button + + ID: button + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | variant | \`"primary" | "secondary"\` | The visual style variant | false | "primary" | + | disabled | \`boolean\` | Whether the button is disabled | false | false | + | onClick | \`(event: MouseEvent) => void\` | Click handler | true | |" `); }); @@ -492,16 +464,13 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - - children - string - - - " + "# Button + + ID: button + + ## Props + + - children: string" `); }); @@ -516,13 +485,11 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - - A button component - - " + "# Button + + ID: button + + A button component" `); }); @@ -539,10 +506,9 @@ describe('formatComponentManifest', () => { const result = formatComponentManifest(manifest); expect(result).toMatchInlineSnapshot(` - " - button - Button - " + "# Button + + ID: button" `); }); }); @@ -570,12 +536,9 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - " + "# Components + + - Button (button)" `); }); @@ -604,20 +567,11 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - - card - Card - - - input - Input - - " + "# Components + + - Button (button) + - Card (card) + - Input (input)" `); }); }); @@ -639,15 +593,9 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - A versatile button component - - - " + "# Components + + - Button (button): A versatile button component" `); }); @@ -687,15 +635,9 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - A simple button component - - - " + "# Components + + - Button (button): A simple button component" `); }); @@ -716,15 +658,9 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - This is a very long description that exceeds ninety characters and should be truncated wit... - - - " + "# Components + + - Button (button): This is a very long description that exceeds ninety characters and should be truncated wit..." `); }); @@ -766,12 +702,9 @@ describe('formatComponentManifestMapToList', () => { expect(result).not.toContain(''); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - " + "# Components + + - Button (button)" `); }); }); @@ -812,33 +745,12 @@ describe('formatComponentManifestMapToList', () => { const result = formatComponentManifestMapToList(manifest); expect(result).toMatchInlineSnapshot(` - " - - button - Button - - A versatile button component - - - - card - Card - - A flexible container for grouping content - - - - input - Input - - Text input with validation - - - - modal - Modal - - " + "# Components + + - Button (button): A versatile button component + - Card (card): A flexible container for grouping content + - Input (input): Text input with validation + - Modal (modal)" `); }); }); @@ -849,43 +761,30 @@ describe('formatComponentManifestMapToList', () => { withErrorsFixture.components['success-component-with-mixed-stories']; const result = formatComponentManifest(component); expect(result).toMatchInlineSnapshot(` - " - success-component-with-mixed-stories - SuccessWithMixedStories - + "# SuccessWithMixedStories + + ID: success-component-with-mixed-stories + A component that loaded successfully but has some stories that failed to generate. - - - Working - + + ## Examples + + ### Working + This story generated successfully. - - + + \`\`\` import { SuccessWithMixedStories } from '@storybook/design-system'; const Working = () => - - - - - text - - The text to display - - string - true - - - variant - - The visual variant - - "primary" | "secondary" - false - "primary" - - - " + \`\`\` + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | text | \`string\` | The text to display | true | | + | variant | \`"primary" | "secondary"\` | The visual variant | false | "primary" |" `); }); @@ -894,32 +793,31 @@ describe('formatComponentManifestMapToList', () => { withErrorsFixture.components['error-component-with-success-stories']; const result = formatComponentManifest(component); expect(result).toMatchInlineSnapshot(` - " - error-component-with-success-stories - ErrorWithSuccessStories - - Basic - + "# ErrorWithSuccessStories + + ID: error-component-with-success-stories + + ## Examples + + ### Basic + Even though the component parsing failed, this story's code snippet was generated. - - + + \`\`\` const Basic = () => Content - - - - Advanced - + \`\`\` + + ### Advanced + Another successfully generated story despite component-level errors. - - + + \`\`\` const Advanced = () => ( Advanced Content ) - - - " + \`\`\`" `); }); @@ -927,53 +825,40 @@ describe('formatComponentManifestMapToList', () => { const component = withErrorsFixture.components['partial-success']; const result = formatComponentManifest(component); expect(result).toMatchInlineSnapshot(` - " - partial-success - PartialSuccess - + "# PartialSuccess + + ID: partial-success + A component where everything worked except one story. - - - Default - + + ## Examples + + ### Default + Default usage of the component. - - + + \`\`\` import { PartialSuccess } from '@storybook/design-system'; const Default = () => - - - - With Subtitle - + \`\`\` + + ### With Subtitle + Component with both title and subtitle. - - + + \`\`\` import { PartialSuccess } from '@storybook/design-system'; const WithSubtitle = () => - - - - - title - - The title text - - string - true - - - subtitle - - Optional subtitle - - string - false - - - " + \`\`\` + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | title | \`string\` | The title text | true | | + | subtitle | \`string\` | Optional subtitle | false | |" `); }); @@ -982,34 +867,13 @@ describe('formatComponentManifestMapToList', () => { withErrorsFixture as ComponentManifestMap, ); expect(result).toMatchInlineSnapshot(` - " - - success-component-with-mixed-stories - SuccessWithMixedStories - - Success component with both working and failing stories - - - - error-component-with-success-stories - ErrorWithSuccessStories - - - error-component-with-error-stories - ErrorWithErrorStories - - - complete-error-component - CompleteError - - - partial-success - PartialSuccess - - Mostly working component with one failing story - - - " + "# Components + + - SuccessWithMixedStories (success-component-with-mixed-stories): Success component with both working and failing stories + - ErrorWithSuccessStories (error-component-with-success-stories) + - ErrorWithErrorStories (error-component-with-error-stories) + - CompleteError (complete-error-component) + - PartialSuccess (partial-success): Mostly working component with one failing story" `); }); }); diff --git a/packages/mcp/src/utils/format-manifest.ts b/packages/mcp/src/utils/format-manifest.ts index 6220ef4f..8a7c0f14 100644 --- a/packages/mcp/src/utils/format-manifest.ts +++ b/packages/mcp/src/utils/format-manifest.ts @@ -1,129 +1,42 @@ import type { ComponentManifest, ComponentManifestMap } from '../types.ts'; -import { dedent } from './dedent.ts'; -import { parseReactDocgen } from './parse-react-docgen.ts'; - +import type { + OutputFormat, + ManifestFormatter, +} from './manifest-formatter/types.ts'; +import { xmlFormatter } from './manifest-formatter/xml.ts'; +import { markdownFormatter } from './manifest-formatter/markdown.ts'; + +// Re-export utility functions for use by formatters +export { dedent } from './dedent.ts'; +export { parseReactDocgen } from './parse-react-docgen.ts'; + +const formatters: Record = { + xml: xmlFormatter, + markdown: markdownFormatter, +}; + +/** + * Format a single component manifest. + * @param componentManifest - The component manifest to format + * @param format - The desired output format (defaults to 'markdown') + * @returns Formatted string representation of the component + */ export function formatComponentManifest( componentManifest: ComponentManifest, + format: OutputFormat = 'markdown', ): string { - const parts: string[] = []; - - // Component opening tag - parts.push(dedent` - ${componentManifest.id} - ${componentManifest.name}`); - - // Description section - if (componentManifest.description) { - parts.push(dedent` - ${componentManifest.description} - `); - } - - // Stories section - only if there are stories - if (componentManifest.stories && componentManifest.stories.length > 0) { - for (const story of componentManifest.stories) { - if (!story.snippet) { - continue; - } - const storyParts: string[] = []; - // Convert PascalCase to Human Readable Case - // "WithSizes" -> "With Sizes" - storyParts.push(dedent` - ${story.name.replace(/([A-Z])/g, ' $1').trim()}`); - - if (story.description) { - storyParts.push(dedent` - ${story.description} - `); - } - - storyParts.push(''); - if (componentManifest.import) { - storyParts.push(`${componentManifest.import}\n`); - } - storyParts.push(dedent`${story.snippet} - - `); - - parts.push(storyParts.join('\n')); - } - } - - if (componentManifest.reactDocgen) { - const parsedDocgen = parseReactDocgen(componentManifest.reactDocgen); - const propEntries = Object.entries(parsedDocgen.props); - - if (propEntries.length > 0) { - parts.push(''); - for (const [propName, propInfo] of propEntries) { - parts.push(dedent` - ${propName}`); - - if (propInfo.description !== undefined) { - parts.push(dedent` - ${propInfo.description} - `); - } - - if (propInfo.type !== undefined) { - parts.push(dedent`${propInfo.type}`); - } - - if (propInfo.required !== undefined) { - parts.push( - dedent`${propInfo.required}`, - ); - } - - if (propInfo.defaultValue !== undefined) { - parts.push( - dedent`${propInfo.defaultValue}`, - ); - } - - parts.push(''); - } - parts.push(''); - } - } - - parts.push(''); - - return parts.join('\n'); + return formatters[format].formatComponentManifest(componentManifest); } -const MAX_SUMMARY_LENGTH = 90; - +/** + * Format a component manifest map into a list. + * @param manifest - The component manifest map to format + * @param format - The desired output format (defaults to 'markdown') + * @returns Formatted string representation of the component list + */ export function formatComponentManifestMapToList( manifest: ComponentManifestMap, + format: OutputFormat = 'markdown', ): string { - const parts: string[] = []; - - parts.push(''); - - for (const component of Object.values(manifest.components)) { - parts.push(dedent` - ${component.id} - ${component.name}`); - - const summary = - component.summary ?? - (component.description - ? component.description.length > MAX_SUMMARY_LENGTH - ? `${component.description.slice(0, MAX_SUMMARY_LENGTH)}...` - : component.description - : undefined); - - if (summary) { - parts.push(dedent` - ${summary} - `); - } - - parts.push(''); - } - - parts.push(''); - - return parts.join('\n'); + return formatters[format].formatComponentManifestMapToList(manifest); } diff --git a/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap new file mode 100644 index 00000000..01f48de8 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap @@ -0,0 +1,357 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`MarkdownFormatter - formatComponentManifest > formats all full fixtures 1`] = ` +"# Button + +ID: button + +A versatile button component that supports multiple variants, sizes, and states. + +The Button component is a fundamental building block for user interactions. It can be styled as primary, secondary, or tertiary actions, and supports disabled and loading states. + +## Usage + +Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. + +## Examples + +### Primary + +The primary button variant is used for the main call-to-action on a page. It has the highest visual prominence and should be used sparingly to guide users toward the most important action. + +## Best Practices + +- Use only one primary button per section +- Keep button text concise and action-oriented +- Ensure sufficient contrast for accessibility + +\`\`\` +import { Button } from '@storybook/design-system'; + +const Primary = () => +\`\`\` + +### Secondary + +The secondary button variant is used for secondary actions that are still important but not the primary focus of the page. + +Secondary buttons have less visual weight than primary buttons and can be used multiple times on a page. + +\`\`\` +import { Button } from '@storybook/design-system'; + +const Secondary = () => +\`\`\` + +### With Sizes + +Buttons are available in three sizes: small, medium (default), and large. + +Choose the appropriate size based on the context and hierarchy of actions. Larger buttons are more prominent and easier to tap on mobile devices. + +\`\`\` +import { Button } from '@storybook/design-system'; + +const WithSizes = () => ( + <> + + + + +) +\`\`\` + +### Loading + +The loading state provides visual feedback when an async operation is in progress. + +When loading is true, the button displays a spinner and is automatically disabled to prevent multiple submissions. The button text remains visible to maintain layout stability. + +\`\`\` +import { Button } from '@storybook/design-system'; + +const Loading = () => +\`\`\` + +### Danger + +The danger variant is used for destructive actions that cannot be easily undone, such as deleting data or canceling subscriptions. + +Use this variant to draw attention to the serious nature of the action. Consider adding a confirmation dialog for critical operations. + +\`\`\` +import { Button } from '@storybook/design-system'; + +const Danger = () => +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| variant | \`"primary" | "secondary" | "tertiary" | "danger"\` | The visual style variant of the button | false | "primary" | +| size | \`"small" | "medium" | "large"\` | The size of the button | false | "medium" | +| disabled | \`boolean\` | Whether the button is disabled | false | false | +| loading | \`boolean\` | Whether the button is in a loading state | false | false | +| fullWidth | \`boolean\` | Whether the button should take up the full width of its container | false | false | +| onClick | \`(event: MouseEvent) => void\` | Callback function when the button is clicked | false | | +| children | \`ReactNode\` | The content of the button | true | |" +`; + +exports[`MarkdownFormatter - formatComponentManifest > formats all full fixtures 2`] = ` +"# Card + +ID: card + +A flexible container component for grouping related content with optional header, footer, and action areas. + +The Card component provides a consistent way to present information in a contained, elevated surface. It's commonly used for displaying articles, products, user profiles, or any grouped content that benefits from visual separation. + +## Design Principles + +- Cards should contain a single subject or action +- Maintain consistent padding and spacing +- Use elevation to indicate interactive vs static cards +- Keep content hierarchy clear with proper use of typography + +## Examples + +### Basic + +A basic card with just content. + +The default elevated variant provides subtle depth through shadow, making the card appear to float above the page. This is ideal for creating visual hierarchy and grouping related information. + +\`\`\` +import { Card } from '@storybook/design-system'; + +const Basic = () => ( + +

Card Title

+

This is some card content that provides information to the user.

+
+) +\`\`\` + +### With Header And Footer + +A card with distinct header and footer sections. + +Headers typically contain titles, subtitles, or avatars. Footers often contain actions like buttons or metadata like timestamps. The header and footer are visually separated from the main content area. + +\`\`\` +import { Card } from '@storybook/design-system'; + +const WithHeaderAndFooter = () => ( + Article Title} + footer={} + > +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+) +\`\`\` + +### Clickable + +An interactive card that responds to clicks. + +Clickable cards add hover effects and cursor changes to indicate interactivity. This pattern is useful for navigation cards, product cards, or any scenario where the entire card acts as a single interactive element. + +## Accessibility + +Clickable cards are rendered as buttons with proper keyboard support and ARIA attributes. + +\`\`\` +import { Card } from '@storybook/design-system'; + +const Clickable = () => ( + alert('Card clicked!')}> +

Product Name

+

Click anywhere on this card to view details.

+
+) +\`\`\` + +### Variants + +Different visual variants of the card component. + +- **Elevated**: Default variant with shadow for depth +- **Outlined**: Border-only variant without shadow +- **Flat**: No border or shadow, minimal visual separation + +Choose variants based on your design system and the level of emphasis needed. + +\`\`\` +import { Card } from '@storybook/design-system'; + +const Variants = () => ( + <> + +

Elevated card with shadow

+
+ +

Outlined card with border

+
+ +

Flat card without border or shadow

+
+ +) +\`\`\` + +### User Profile + +A real-world example of a user profile card. + +This example demonstrates how to compose the Card component with other design system components to create a complete, functional UI element. It includes an avatar, user information, stats, and action buttons. + +\`\`\` +import { Card } from '@storybook/design-system'; + +const UserProfile = () => ( + + +
+

Jane Doe

+

Senior Developer

+
+ + } + footer={ +
+ + +
+ } + > +
+
1.2K
Followers
+
342
Following
+
89
Posts
+
+
+) +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| variant | \`"elevated" | "outlined" | "flat"\` | The visual style variant of the card | false | "elevated" | +| padding | \`"none" | "small" | "medium" | "large"\` | The amount of internal padding | false | "medium" | +| clickable | \`boolean\` | Whether the entire card is clickable/interactive | false | false | +| header | \`ReactNode\` | Content to display in the card header | false | | +| footer | \`ReactNode\` | Content to display in the card footer | false | | +| children | \`ReactNode\` | The main content of the card | true | | +| onClick | \`(event: MouseEvent) => void\` | Callback function when the card is clicked (requires clickable=true) | false | |" +`; + +exports[`MarkdownFormatter - formatComponentManifest > formats all full fixtures 3`] = ` +"# Input + +ID: input + +A flexible text input component that supports various input types, validation states, and accessibility features. + +The Input component is a foundational form element that wraps the native HTML input with consistent styling and behavior. It includes support for labels, error messages, helper text, and different visual states. + +## Accessibility + +The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. + +## Examples + +### Basic + +A basic text input with a label. + +This is the most common use case for the Input component. Always include a label for accessibility, even if it's visually hidden in your design. + +\`\`\` +import { Input } from '@storybook/design-system'; + +const Basic = () => +\`\`\` + +### With Error + +An input displaying an error state with an error message. + +Error messages should be clear, concise, and provide actionable guidance to help users fix the issue. The input border and message text are styled in red to indicate the error state. + +\`\`\` +import { Input } from '@storybook/design-system'; + +const WithError = () => +\`\`\` + +### With Helper Text + +An input with helper text providing additional context or instructions. + +Helper text appears below the input and provides guidance without being an error. Use it to clarify format expectations, character limits, or provide helpful hints. + +\`\`\` +import { Input } from '@storybook/design-system'; + +const WithHelperText = () => +\`\`\` + +### Types + +Different input types for various data formats. + +Using the correct input type improves the user experience by showing appropriate mobile keyboards and enabling browser validation features. + +\`\`\` +import { Input } from '@storybook/design-system'; + +const Types = () => ( + <> + + + + + +) +\`\`\` + +### Disabled + +A disabled input that cannot be interacted with. + +Disabled inputs are useful for displaying non-editable data in forms or for inputs that become available only after certain conditions are met. + +\`\`\` +import { Input } from '@storybook/design-system'; + +const Disabled = () => +\`\`\` + +## Props + +| Name | Type | Description | Required | Default | +|------|------|-------------|----------|---------| +| type | \`"text" | "email" | "password" | "number" | "tel" | "url"\` | The type of input field | false | "text" | +| label | \`string\` | The label text for the input | false | | +| placeholder | \`string\` | Placeholder text shown when the input is empty | false | | +| value | \`string\` | The controlled value of the input | false | | +| defaultValue | \`string\` | The initial value for an uncontrolled input | false | | +| disabled | \`boolean\` | Whether the input is disabled | false | false | +| required | \`boolean\` | Whether the input is required | false | false | +| error | \`string\` | Error message to display below the input | false | | +| helperText | \`string\` | Helper text to display below the input | false | | +| onChange | \`(event: ChangeEvent) => void\` | Callback function when the input value changes | false | |" +`; + +exports[`MarkdownFormatter - formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` +"# Components + +- Button (button): A versatile button component for user interactions +- Card (card): A flexible container component for grouping related content +- Input (input): A flexible text input component with validation support" +`; diff --git a/packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap b/packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap new file mode 100644 index 00000000..76fcdc4f --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap @@ -0,0 +1,560 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`XmlFormatter - formatComponentManifest > formats all full fixtures 1`] = ` +" +button +Button + +A versatile button component that supports multiple variants, sizes, and states. + +The Button component is a fundamental building block for user interactions. It can be styled as primary, secondary, or tertiary actions, and supports disabled and loading states. + +## Usage + +Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. + + +Primary + +The primary button variant is used for the main call-to-action on a page. It has the highest visual prominence and should be used sparingly to guide users toward the most important action. + +## Best Practices + +- Use only one primary button per section +- Keep button text concise and action-oriented +- Ensure sufficient contrast for accessibility + + +import { Button } from '@storybook/design-system'; + +const Primary = () => + + + +Secondary + +The secondary button variant is used for secondary actions that are still important but not the primary focus of the page. + +Secondary buttons have less visual weight than primary buttons and can be used multiple times on a page. + + +import { Button } from '@storybook/design-system'; + +const Secondary = () => + + + +With Sizes + +Buttons are available in three sizes: small, medium (default), and large. + +Choose the appropriate size based on the context and hierarchy of actions. Larger buttons are more prominent and easier to tap on mobile devices. + + +import { Button } from '@storybook/design-system'; + +const WithSizes = () => ( + <> + + + + +) + + + +Loading + +The loading state provides visual feedback when an async operation is in progress. + +When loading is true, the button displays a spinner and is automatically disabled to prevent multiple submissions. The button text remains visible to maintain layout stability. + + +import { Button } from '@storybook/design-system'; + +const Loading = () => + + + +Danger + +The danger variant is used for destructive actions that cannot be easily undone, such as deleting data or canceling subscriptions. + +Use this variant to draw attention to the serious nature of the action. Consider adding a confirmation dialog for critical operations. + + +import { Button } from '@storybook/design-system'; + +const Danger = () => + + + + +variant + +The visual style variant of the button + +"primary" | "secondary" | "tertiary" | "danger" +false +"primary" + + +size + +The size of the button + +"small" | "medium" | "large" +false +"medium" + + +disabled + +Whether the button is disabled + +boolean +false +false + + +loading + +Whether the button is in a loading state + +boolean +false +false + + +fullWidth + +Whether the button should take up the full width of its container + +boolean +false +false + + +onClick + +Callback function when the button is clicked + +(event: MouseEvent) => void +false + + +children + +The content of the button + +ReactNode +true + + +" +`; + +exports[`XmlFormatter - formatComponentManifest > formats all full fixtures 2`] = ` +" +card +Card + +A flexible container component for grouping related content with optional header, footer, and action areas. + +The Card component provides a consistent way to present information in a contained, elevated surface. It's commonly used for displaying articles, products, user profiles, or any grouped content that benefits from visual separation. + +## Design Principles + +- Cards should contain a single subject or action +- Maintain consistent padding and spacing +- Use elevation to indicate interactive vs static cards +- Keep content hierarchy clear with proper use of typography + + +Basic + +A basic card with just content. + +The default elevated variant provides subtle depth through shadow, making the card appear to float above the page. This is ideal for creating visual hierarchy and grouping related information. + + +import { Card } from '@storybook/design-system'; + +const Basic = () => ( + +

Card Title

+

This is some card content that provides information to the user.

+
+) +
+
+ +With Header And Footer + +A card with distinct header and footer sections. + +Headers typically contain titles, subtitles, or avatars. Footers often contain actions like buttons or metadata like timestamps. The header and footer are visually separated from the main content area. + + +import { Card } from '@storybook/design-system'; + +const WithHeaderAndFooter = () => ( + Article Title} + footer={} + > +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+) +
+
+ +Clickable + +An interactive card that responds to clicks. + +Clickable cards add hover effects and cursor changes to indicate interactivity. This pattern is useful for navigation cards, product cards, or any scenario where the entire card acts as a single interactive element. + +## Accessibility + +Clickable cards are rendered as buttons with proper keyboard support and ARIA attributes. + + +import { Card } from '@storybook/design-system'; + +const Clickable = () => ( + alert('Card clicked!')}> +

Product Name

+

Click anywhere on this card to view details.

+
+) +
+
+ +Variants + +Different visual variants of the card component. + +- **Elevated**: Default variant with shadow for depth +- **Outlined**: Border-only variant without shadow +- **Flat**: No border or shadow, minimal visual separation + +Choose variants based on your design system and the level of emphasis needed. + + +import { Card } from '@storybook/design-system'; + +const Variants = () => ( + <> + +

Elevated card with shadow

+
+ +

Outlined card with border

+
+ +

Flat card without border or shadow

+
+ +) +
+
+ +User Profile + +A real-world example of a user profile card. + +This example demonstrates how to compose the Card component with other design system components to create a complete, functional UI element. It includes an avatar, user information, stats, and action buttons. + + +import { Card } from '@storybook/design-system'; + +const UserProfile = () => ( + + +
+

Jane Doe

+

Senior Developer

+
+ + } + footer={ +
+ + +
+ } + > +
+
1.2K
Followers
+
342
Following
+
89
Posts
+
+
+) +
+
+ + +variant + +The visual style variant of the card + +"elevated" | "outlined" | "flat" +false +"elevated" + + +padding + +The amount of internal padding + +"none" | "small" | "medium" | "large" +false +"medium" + + +clickable + +Whether the entire card is clickable/interactive + +boolean +false +false + + +header + +Content to display in the card header + +ReactNode +false + + +footer + +Content to display in the card footer + +ReactNode +false + + +children + +The main content of the card + +ReactNode +true + + +onClick + +Callback function when the card is clicked (requires clickable=true) + +(event: MouseEvent) => void +false + + +
" +`; + +exports[`XmlFormatter - formatComponentManifest > formats all full fixtures 3`] = ` +" +input +Input + +A flexible text input component that supports various input types, validation states, and accessibility features. + +The Input component is a foundational form element that wraps the native HTML input with consistent styling and behavior. It includes support for labels, error messages, helper text, and different visual states. + +## Accessibility + +The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. + + +Basic + +A basic text input with a label. + +This is the most common use case for the Input component. Always include a label for accessibility, even if it's visually hidden in your design. + + +import { Input } from '@storybook/design-system'; + +const Basic = () => + + + +With Error + +An input displaying an error state with an error message. + +Error messages should be clear, concise, and provide actionable guidance to help users fix the issue. The input border and message text are styled in red to indicate the error state. + + +import { Input } from '@storybook/design-system'; + +const WithError = () => + + + +With Helper Text + +An input with helper text providing additional context or instructions. + +Helper text appears below the input and provides guidance without being an error. Use it to clarify format expectations, character limits, or provide helpful hints. + + +import { Input } from '@storybook/design-system'; + +const WithHelperText = () => + + + +Types + +Different input types for various data formats. + +Using the correct input type improves the user experience by showing appropriate mobile keyboards and enabling browser validation features. + + +import { Input } from '@storybook/design-system'; + +const Types = () => ( + <> + + + + + +) + + + +Disabled + +A disabled input that cannot be interacted with. + +Disabled inputs are useful for displaying non-editable data in forms or for inputs that become available only after certain conditions are met. + + +import { Input } from '@storybook/design-system'; + +const Disabled = () => + + + + +type + +The type of input field + +"text" | "email" | "password" | "number" | "tel" | "url" +false +"text" + + +label + +The label text for the input + +string +false + + +placeholder + +Placeholder text shown when the input is empty + +string +false + + +value + +The controlled value of the input + +string +false + + +defaultValue + +The initial value for an uncontrolled input + +string +false + + +disabled + +Whether the input is disabled + +boolean +false +false + + +required + +Whether the input is required + +boolean +false +false + + +error + +Error message to display below the input + +string +false + + +helperText + +Helper text to display below the input + +string +false + + +onChange + +Callback function when the input value changes + +(event: ChangeEvent) => void +false + + +" +`; + +exports[`XmlFormatter - formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` +" + +button +Button + +A versatile button component for user interactions + + + +card +Card + +A flexible container component for grouping related content + + + +input +Input + +A flexible text input component with validation support + + +" +`; diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts new file mode 100644 index 00000000..f92994c6 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts @@ -0,0 +1,740 @@ +import { describe, it, expect } from 'vitest'; +import { markdownFormatter } from './markdown.ts'; +import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import fullManifestFixture from '../../../fixtures/full-manifest.fixture.json' with { type: 'json' }; + +describe('MarkdownFormatter - formatComponentManifest', () => { + it('formats all full fixtures', () => { + expect( + markdownFormatter.formatComponentManifest( + fullManifestFixture.components.button, + ), + ).toMatchSnapshot(); + expect( + markdownFormatter.formatComponentManifest( + fullManifestFixture.components.card, + ), + ).toMatchSnapshot(); + expect( + markdownFormatter.formatComponentManifest( + fullManifestFixture.components.input, + ), + ).toMatchSnapshot(); + }); + + describe('component header', () => { + it('should include component name and ID', () => { + const manifest: ComponentManifest = { + id: 'test-component', + path: 'src/components/TestComponent.tsx', + name: 'TestComponent', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + "# TestComponent + + ID: test-component" + `); + }); + }); + + describe('description section', () => { + it('should include description when provided', () => { + const manifest: ComponentManifest = { + id: 'button', + path: 'src/components/Button.tsx', + name: 'Button', + description: 'A simple button component', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + A simple button component" + `); + }); + + it('should handle multi-line descriptions', () => { + const manifest: ComponentManifest = { + id: 'button', + path: 'src/components/Button.tsx', + name: 'Button', + description: + 'A versatile button component.\n\nSupports multiple variants and sizes.', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + A versatile button component. + + Supports multiple variants and sizes." + `); + }); + + it('should omit description section when not provided', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('A simple button component'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button" + `); + }); + }); + + describe('stories section', () => { + it('should format a single story', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + import: 'import { Button } from "@/components";', + stories: [ + { + name: 'Default', + snippet: '', + }, + ], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Examples + + ### Default + + \`\`\` + import { Button } from "@/components"; + + + \`\`\`" + `); + }); + + it('should format multiple stories', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + import: 'import { Button } from "@/components";', + stories: [ + { + name: 'Default', + snippet: '', + }, + { + name: 'Primary', + snippet: '', + }, + ], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('### Default'); + expect(result).toContain('### Primary'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Examples + + ### Default + + \`\`\` + import { Button } from "@/components"; + + + \`\`\` + + ### Primary + + \`\`\` + import { Button } from "@/components"; + + + \`\`\`" + `); + }); + + it('should format PascalCase story names correctly', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'WithIcon', + snippet: '', + }, + ], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('### With Icon'); + }); + + it('should handle stories with description', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'Primary', + description: 'The primary action button style', + snippet: '', + }, + ], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('The primary action button style'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Examples + + ### Primary + + The primary action button style + + \`\`\` + + \`\`\`" + `); + }); + + it('should handle stories without import', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'Default', + snippet: '', + }, + ], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('import'); + }); + + it('should omit stories when no stories are provided', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('## Examples'); + }); + + it('should omit stories when stories array is empty', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [], + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('## Examples'); + }); + }); + + describe('props section - table format', () => { + it('should format props with rich metadata as table', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + description: 'The visual style variant', + type: { name: 'union', value: ['primary', 'secondary'] }, + required: false, + defaultValue: { value: 'primary', computed: false }, + }, + disabled: { + description: 'Whether the button is disabled', + type: { name: 'bool' }, + required: false, + defaultValue: { value: 'false', computed: false }, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('## Props'); + expect(result).toContain( + '| Name | Type | Description | Required | Default |', + ); + expect(result).toContain('| variant |'); + expect(result).toContain('| disabled |'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Props + + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | variant | \`union\` | The visual style variant | false | primary | + | disabled | \`bool\` | Whether the button is disabled | false | false |" + `); + }); + }); + + describe('props section - bullet list format', () => { + it('should format props with only name and type as bullet list', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'union', value: ['primary', 'secondary'] }, + }, + size: { + type: { name: 'union', value: ['small', 'medium', 'large'] }, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('## Props'); + expect(result).toContain('- variant: union'); + expect(result).toContain('- size: union'); + expect(result).not.toContain('| Name |'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Props + + - variant: union + - size: union" + `); + }); + + it('should format props with name, type, and description as bullet list', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'union', value: ['primary', 'secondary'] }, + description: 'The visual style variant', + }, + size: { + type: { name: 'union', value: ['small', 'medium', 'large'] }, + description: 'The size of the button', + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('## Props'); + expect(result).toContain('- variant: union - The visual style variant'); + expect(result).toContain('- size: union - The size of the button'); + expect(result).not.toContain('| Name |'); + expect(result).toMatchInlineSnapshot(` + "# Button + + ID: button + + ## Props + + - variant: union - The visual style variant + - size: union - The size of the button" + `); + }); + + it('should omit props section when reactDocgen is not present', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A button component', + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('## Props'); + }); + + it('should omit props section when reactDocgen has no props', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: {}, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).not.toContain('## Props'); + }); + }); + + describe('props section - format decision logic', () => { + it('should use bullet list when props have only name and type', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('- variant: string'); + expect(result).not.toContain('| Name | Type |'); + }); + + it('should use bullet list when props have name, type, and description but no required/default', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + description: 'The button variant', + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('- variant: string - The button variant'); + expect(result).not.toContain('| Name | Type |'); + }); + + it('should use table when props have required metadata', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + required: true, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain('| variant | `string` | | true | |'); + }); + + it('should use table when props have default value metadata', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + defaultValue: { value: 'primary', computed: false }, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain('| variant | `string` | | | primary |'); + }); + + it('should use table when props have all metadata fields', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + description: 'The button variant', + required: false, + defaultValue: { value: 'primary', computed: false }, + }, + }, + }, + }; + + const result = markdownFormatter.formatComponentManifest(manifest); + + expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain( + '| variant | `string` | The button variant | false | primary |', + ); + }); + }); +}); + +describe('MarkdownFormatter - formatComponentManifestMapToList', () => { + it('formats the full manifest fixture', () => { + const result = + markdownFormatter.formatComponentManifestMapToList(fullManifestFixture); + expect(result).toMatchSnapshot(); + }); + + describe('component list structure', () => { + it('should format a single component', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button)" + `); + }); + + it('should format multiple components', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + card: { + id: 'card', + name: 'Card', + path: 'src/components/Card.tsx', + }, + input: { + id: 'input', + name: 'Input', + path: 'src/components/Input.tsx', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button) + - Card (card) + - Input (input)" + `); + }); + }); + + describe('summary section', () => { + it('should include summary when provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'A versatile button component', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button): A versatile button component" + `); + }); + + it('should prefer summary over description', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'Button summary', + description: 'Button description', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toContain('Button summary'); + expect(result).not.toContain('Button description'); + }); + + it('should use description when summary is not provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A simple button component', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button): A simple button component" + `); + }); + + it('should truncate long descriptions to 90 characters', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: + 'This is a very long description that exceeds ninety characters and should be truncated with ellipsis', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toContain('...'); + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button): This is a very long description that exceeds ninety characters and should be truncated wit..." + `); + }); + + it('should not truncate descriptions under 90 characters', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A button component for user interactions', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).not.toContain('...'); + expect(result).toContain('A button component for user interactions'); + }); + + it('should omit summary when neither summary nor description provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }; + + const result = + markdownFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button)" + `); + }); + }); +}); diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.ts b/packages/mcp/src/utils/manifest-formatter/markdown.ts new file mode 100644 index 00000000..4921dba6 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/markdown.ts @@ -0,0 +1,141 @@ +import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import type { ManifestFormatter } from './types.ts'; +import { parseReactDocgen } from '../parse-react-docgen.ts'; + +const MAX_SUMMARY_LENGTH = 90; + +/** + * Markdown formatter for component manifests. + * Formats component data into token-efficient markdown with headers, lists, and tables. + * Uses adaptive formatting based on prop complexity to optimize token usage. + */ +export const markdownFormatter: ManifestFormatter = { + formatComponentManifest(componentManifest: ComponentManifest): string { + const parts: string[] = []; + + // Component header + parts.push(`# ${componentManifest.name}`); + parts.push(''); + parts.push(`ID: ${componentManifest.id}`); + parts.push(''); + + // Description section + if (componentManifest.description) { + parts.push(componentManifest.description); + parts.push(''); + } + + // Stories section + if (componentManifest.stories && componentManifest.stories.length > 0) { + parts.push('## Examples'); + parts.push(''); + + for (const story of componentManifest.stories) { + if (!story.snippet) { + continue; + } + + // Convert PascalCase to Human Readable Case + const storyName = story.name.replace(/([A-Z])/g, ' $1').trim(); + parts.push(`### ${storyName}`); + + if (story.description) { + parts.push(''); + parts.push(story.description); + } + + parts.push(''); + parts.push('```'); + if (componentManifest.import) { + parts.push(componentManifest.import); + parts.push(''); + } + parts.push(story.snippet); + parts.push('```'); + parts.push(''); + } + } + + // Props section + if (componentManifest.reactDocgen) { + const parsedDocgen = parseReactDocgen(componentManifest.reactDocgen); + const propEntries = Object.entries(parsedDocgen.props); + + if (propEntries.length > 0) { + parts.push('## Props'); + parts.push(''); + + // Determine if we should use table format or bullet list + // Use table if any prop has description, required, or defaultValue + const hasRichMetadata = propEntries.some( + ([, propInfo]) => + propInfo.required !== undefined || + propInfo.defaultValue !== undefined, + ); + + if (hasRichMetadata) { + // Use table format for props with rich metadata + parts.push('| Name | Type | Description | Required | Default |'); + parts.push('|------|------|-------------|----------|---------|'); + + for (const [propName, propInfo] of propEntries) { + const type = propInfo.type ?? ''; + const description = propInfo.description ?? ''; + const required = + propInfo.required !== undefined + ? propInfo.required + ? 'true' + : 'false' + : ''; + const defaultValue = propInfo.defaultValue ?? ''; + + parts.push( + `| ${propName} | \`${type}\` | ${description} | ${required} | ${defaultValue} |`, + ); + } + } else { + // Use bullet list format for simple props (name + type only) + for (const [propName, propInfo] of propEntries) { + const type = propInfo.type ?? ''; + let propString = `- ${propName}: ${type}`; + if (propInfo.description) { + propString += ` - ${propInfo.description}`; + } + parts.push(propString); + } + } + + parts.push(''); + } + } + + return parts.join('\n').trim(); + }, + + formatComponentManifestMapToList(manifest: ComponentManifestMap): string { + const parts: string[] = []; + + parts.push('# Components'); + parts.push(''); + + for (const component of Object.values(manifest.components)) { + const summary = + component.summary ?? + (component.description + ? component.description.length > MAX_SUMMARY_LENGTH + ? `${component.description.slice(0, MAX_SUMMARY_LENGTH)}...` + : component.description + : undefined); + + if (summary) { + parts.push(`- ${component.name} (${component.id}): ${summary}`); + } else { + parts.push(`- ${component.name} (${component.id})`); + } + } + + parts.push(''); + + return parts.join('\n').trim(); + }, +}; diff --git a/packages/mcp/src/utils/manifest-formatter/types.ts b/packages/mcp/src/utils/manifest-formatter/types.ts new file mode 100644 index 00000000..7195b701 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/types.ts @@ -0,0 +1,29 @@ +import * as v from 'valibot'; +import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; + +/** + * Supported output formats for component manifest formatting. + */ +export const OutputFormat = v.picklist(['xml', 'markdown']); +export type OutputFormat = v.InferOutput; + +/** + * Interface for manifest formatters. + * Implementations must provide methods to format both single components + * and component lists in their respective formats (XML, Markdown, etc). + */ +export interface ManifestFormatter { + /** + * Format a single component manifest into the target format. + * @param componentManifest - The component manifest to format + * @returns Formatted string representation of the component + */ + formatComponentManifest(componentManifest: ComponentManifest): string; + + /** + * Format a component manifest map into a list in the target format. + * @param manifest - The component manifest map to format + * @returns Formatted string representation of the component list + */ + formatComponentManifestMapToList(manifest: ComponentManifestMap): string; +} diff --git a/packages/mcp/src/utils/manifest-formatter/xml.test.ts b/packages/mcp/src/utils/manifest-formatter/xml.test.ts new file mode 100644 index 00000000..158ca81c --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/xml.test.ts @@ -0,0 +1,1018 @@ +import { describe, it, expect } from 'vitest'; +import { xmlFormatter } from './xml.ts'; +import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import fullManifestFixture from '../../../fixtures/full-manifest.fixture.json' with { type: 'json' }; +import withErrorsFixture from '../../../fixtures/with-errors.fixture.json' with { type: 'json' }; + +describe('XmlFormatter - formatComponentManifest', () => { + it('formats all full fixtures', () => { + expect( + xmlFormatter.formatComponentManifest( + fullManifestFixture.components.button, + ), + ).toMatchSnapshot(); + expect( + xmlFormatter.formatComponentManifest(fullManifestFixture.components.card), + ).toMatchSnapshot(); + expect( + xmlFormatter.formatComponentManifest( + fullManifestFixture.components.input, + ), + ).toMatchSnapshot(); + }); + + describe('component name', () => { + it('should include component name in component_name tag', () => { + const manifest: ComponentManifest = { + id: 'test-component', + path: 'src/components/TestComponent.tsx', + name: 'TestComponent', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + test-component + TestComponent + " + `); + }); + }); + + describe('description section', () => { + it('should include description when provided', () => { + const manifest: ComponentManifest = { + id: 'button', + path: 'src/components/Button.tsx', + name: 'Button', + description: 'A simple button component', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + A simple button component + + " + `); + }); + + it('should handle multi-line descriptions', () => { + const manifest: ComponentManifest = { + id: 'button', + path: 'src/components/Button.tsx', + name: 'Button', + description: + 'A versatile button component.\n\nSupports multiple variants and sizes.', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + A versatile button component. + + Supports multiple variants and sizes. + + " + `); + }); + + it('should omit description section when not provided', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + " + `); + }); + }); + + describe('stories section', () => { + it('should format a single story', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + import: 'import { Button } from "@/components";', + stories: [ + { + name: 'Primary', + description: 'A primary button variant', + snippet: '', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + Primary + + A primary button variant + + + import { Button } from "@/components"; + + + + + " + `); + }); + + it('should format multiple stories', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + import: 'import { Button } from "@/components";', + stories: [ + { + name: 'Primary', + snippet: '', + }, + { + name: 'Secondary', + snippet: '', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + Primary + + import { Button } from "@/components"; + + + + + + Secondary + + import { Button } from "@/components"; + + + + + " + `); + }); + + it('should format PascalCase story names correctly', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'WithIcon', + snippet: '', + }, + { + name: 'DisabledState', + snippet: '', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + With Icon + + + + + + Disabled State + + + + + " + `); + }); + + it('should handle stories without description', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'Simple', + snippet: '', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + Simple + + + + + " + `); + }); + + it('should handle stories without import', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [ + { + name: 'NoImport', + snippet: '', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + No Import + + + + + " + `); + }); + + it('should omit stories when no stories are provided', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A button component', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + A button component + + " + `); + }); + + it('should omit stories when stories array is empty', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + stories: [], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + " + `); + }); + }); + + describe('complete component', () => { + it('should format a complete component with description and multiple stories', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: + 'A versatile button component.\n\nSupports multiple variants, sizes, and states.', + summary: 'A button for user interactions', + import: 'import { Button } from "@storybook/design-system";', + stories: [ + { + name: 'Primary', + description: 'The primary button variant.', + snippet: + 'const Primary = () => ', + }, + { + name: 'WithSizes', + description: 'Buttons in different sizes.', + snippet: + 'const Sizes = () => (\n <>\n \n \n \n)', + }, + ], + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + A versatile button component. + + Supports multiple variants, sizes, and states. + + + Primary + + The primary button variant. + + + import { Button } from "@storybook/design-system"; + + const Primary = () => + + + + With Sizes + + Buttons in different sizes. + + + import { Button } from "@storybook/design-system"; + + const Sizes = () => ( + <> + + + + ) + + + " + `); + }); + }); + + describe('props section', () => { + it('should format props from reactDocgen', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + description: 'The visual style variant', + required: false, + defaultValue: { value: '"primary"', computed: false }, + tsType: { + name: 'union', + raw: '"primary" | "secondary"', + elements: [ + { name: 'literal', value: '"primary"' }, + { name: 'literal', value: '"secondary"' }, + ], + }, + }, + disabled: { + description: 'Whether the button is disabled', + required: false, + defaultValue: { value: 'false', computed: false }, + tsType: { + name: 'boolean', + }, + }, + onClick: { + description: 'Click handler', + required: true, + tsType: { + name: 'signature', + type: 'function', + signature: { + arguments: [{ name: 'event', type: { name: 'MouseEvent' } }], + return: { name: 'void' }, + }, + }, + }, + }, + }, + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + + variant + + The visual style variant + + "primary" | "secondary" + false + "primary" + + + disabled + + Whether the button is disabled + + boolean + false + false + + + onClick + + Click handler + + (event: MouseEvent) => void + true + + + " + `); + }); + + it('should handle props with minimal information', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + children: { + tsType: { + name: 'string', + }, + }, + }, + }, + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + + children + string + + + " + `); + }); + + it('should omit props section when reactDocgen is not present', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A button component', + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + + A button component + + " + `); + }); + + it('should omit props section when reactDocgen has no props', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: {}, + }, + }; + + const result = xmlFormatter.formatComponentManifest(manifest); + + expect(result).toMatchInlineSnapshot(` + " + button + Button + " + `); + }); + }); +}); + +describe('XmlFormatter - formatComponentManifestMapToList', () => { + it('formats the full manifest fixture', () => { + const result = + xmlFormatter.formatComponentManifestMapToList(fullManifestFixture); + expect(result).toMatchSnapshot(); + }); + + describe('component list structure', () => { + it('should format a single component', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + " + `); + }); + + it('should format multiple components', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + card: { + id: 'card', + name: 'Card', + path: 'src/components/Card.tsx', + }, + input: { + id: 'input', + name: 'Input', + path: 'src/components/Input.tsx', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + + card + Card + + + input + Input + + " + `); + }); + }); + + describe('summary section', () => { + it('should include summary when provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'A versatile button component', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + A versatile button component + + + " + `); + }); + + it('should prefer summary over description', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'Short summary', + description: 'This is a longer description that should be ignored', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toContain('Short summary'); + expect(result).not.toContain('longer description'); + }); + + it('should use description when summary is not provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A simple button component', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + A simple button component + + + " + `); + }); + + it('should truncate long descriptions to 90 characters', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: + 'This is a very long description that exceeds ninety characters and should be truncated with ellipsis', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + This is a very long description that exceeds ninety characters and should be truncated wit... + + + " + `); + }); + + it('should not truncate descriptions under 90 characters', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: + 'A description with exactly eighty characters is fine and should not be truncated', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toContain( + 'A description with exactly eighty characters is fine and should not be truncated', + ); + expect(result).not.toContain('...'); + }); + + it('should omit summary section when neither summary nor description provided', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).not.toContain(''); + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + " + `); + }); + }); + + describe('complete manifest', () => { + it('should format a complete manifest with varied components', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'A versatile button component', + }, + card: { + id: 'card', + name: 'Card', + path: 'src/components/Card.tsx', + description: 'A flexible container for grouping content', + }, + input: { + id: 'input', + name: 'Input', + path: 'src/components/Input.tsx', + summary: 'Text input with validation', + description: + 'A comprehensive input component with validation, error states, and accessibility features', + }, + modal: { + id: 'modal', + name: 'Modal', + path: 'src/components/Modal.tsx', + }, + }, + }; + + const result = xmlFormatter.formatComponentManifestMapToList(manifest); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + A versatile button component + + + + card + Card + + A flexible container for grouping content + + + + input + Input + + Text input with validation + + + + modal + Modal + + " + `); + }); + }); + + describe('with-errors fixture', () => { + it('should format success component with mixed stories (only successful ones)', () => { + const component = + withErrorsFixture.components['success-component-with-mixed-stories']; + const result = xmlFormatter.formatComponentManifest(component); + expect(result).toMatchInlineSnapshot(` + " + success-component-with-mixed-stories + SuccessWithMixedStories + + A component that loaded successfully but has some stories that failed to generate. + + + Working + + This story generated successfully. + + + import { SuccessWithMixedStories } from '@storybook/design-system'; + + const Working = () => + + + + + text + + The text to display + + string + true + + + variant + + The visual variant + + "primary" | "secondary" + false + "primary" + + + " + `); + }); + + it('should format error component with success stories', () => { + const component = + withErrorsFixture.components['error-component-with-success-stories']; + const result = xmlFormatter.formatComponentManifest(component); + expect(result).toMatchInlineSnapshot(` + " + error-component-with-success-stories + ErrorWithSuccessStories + + Basic + + Even though the component parsing failed, this story's code snippet was generated. + + + const Basic = () => Content + + + + Advanced + + Another successfully generated story despite component-level errors. + + + const Advanced = () => ( + + Advanced Content + + ) + + + " + `); + }); + + it('should format partial success component (skips failed story)', () => { + const component = withErrorsFixture.components['partial-success']; + const result = xmlFormatter.formatComponentManifest(component); + expect(result).toMatchInlineSnapshot(` + " + partial-success + PartialSuccess + + A component where everything worked except one story. + + + Default + + Default usage of the component. + + + import { PartialSuccess } from '@storybook/design-system'; + + const Default = () => + + + + With Subtitle + + Component with both title and subtitle. + + + import { PartialSuccess } from '@storybook/design-system'; + + const WithSubtitle = () => + + + + + title + + The title text + + string + true + + + subtitle + + Optional subtitle + + string + false + + + " + `); + }); + + it('should format list of components with errors', () => { + const result = xmlFormatter.formatComponentManifestMapToList( + withErrorsFixture as ComponentManifestMap, + ); + expect(result).toMatchInlineSnapshot(` + " + + success-component-with-mixed-stories + SuccessWithMixedStories + + Success component with both working and failing stories + + + + error-component-with-success-stories + ErrorWithSuccessStories + + + error-component-with-error-stories + ErrorWithErrorStories + + + complete-error-component + CompleteError + + + partial-success + PartialSuccess + + Mostly working component with one failing story + + + " + `); + }); + }); +}); diff --git a/packages/mcp/src/utils/manifest-formatter/xml.ts b/packages/mcp/src/utils/manifest-formatter/xml.ts new file mode 100644 index 00000000..126ac6b7 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/xml.ts @@ -0,0 +1,132 @@ +import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import type { ManifestFormatter } from './types.ts'; +import { dedent } from '../dedent.ts'; +import { parseReactDocgen } from '../parse-react-docgen.ts'; + +const MAX_SUMMARY_LENGTH = 90; + +/** + * XML formatter for component manifests. + * Formats component data into XML structure with tags like , , etc. + */ +export const xmlFormatter: ManifestFormatter = { + formatComponentManifest(componentManifest: ComponentManifest): string { + const parts: string[] = []; + + // Component opening tag + parts.push(dedent` + ${componentManifest.id} + ${componentManifest.name}`); + + // Description section + if (componentManifest.description) { + parts.push(dedent` + ${componentManifest.description} + `); + } + + // Stories section - only if there are stories + if (componentManifest.stories && componentManifest.stories.length > 0) { + for (const story of componentManifest.stories) { + if (!story.snippet) { + continue; + } + const storyParts: string[] = []; + // Convert PascalCase to Human Readable Case + // "WithSizes" -> "With Sizes" + storyParts.push(dedent` + ${story.name.replace(/([A-Z])/g, ' $1').trim()}`); + + if (story.description) { + storyParts.push(dedent` + ${story.description} + `); + } + + storyParts.push(''); + if (componentManifest.import) { + storyParts.push(`${componentManifest.import}\n`); + } + storyParts.push(dedent`${story.snippet} + + `); + + parts.push(storyParts.join('\n')); + } + } + + if (componentManifest.reactDocgen) { + const parsedDocgen = parseReactDocgen(componentManifest.reactDocgen); + const propEntries = Object.entries(parsedDocgen.props); + + if (propEntries.length > 0) { + parts.push(''); + for (const [propName, propInfo] of propEntries) { + parts.push(dedent` + ${propName}`); + + if (propInfo.description !== undefined) { + parts.push(dedent` + ${propInfo.description} + `); + } + + if (propInfo.type !== undefined) { + parts.push(dedent`${propInfo.type}`); + } + + if (propInfo.required !== undefined) { + parts.push( + dedent`${propInfo.required}`, + ); + } + + if (propInfo.defaultValue !== undefined) { + parts.push( + dedent`${propInfo.defaultValue}`, + ); + } + + parts.push(''); + } + parts.push(''); + } + } + + parts.push(''); + + return parts.join('\n'); + }, + + formatComponentManifestMapToList(manifest: ComponentManifestMap): string { + const parts: string[] = []; + + parts.push(''); + + for (const component of Object.values(manifest.components)) { + parts.push(dedent` + ${component.id} + ${component.name}`); + + const summary = + component.summary ?? + (component.description + ? component.description.length > MAX_SUMMARY_LENGTH + ? `${component.description.slice(0, MAX_SUMMARY_LENGTH)}...` + : component.description + : undefined); + + if (summary) { + parts.push(dedent` + ${summary} + `); + } + + parts.push(''); + } + + parts.push(''); + + return parts.join('\n'); + }, +}; diff --git a/packages/mcp/tsconfig.json b/packages/mcp/tsconfig.json index cfe03d38..26492992 100644 --- a/packages/mcp/tsconfig.json +++ b/packages/mcp/tsconfig.json @@ -1,5 +1,5 @@ { "extends": ["../../tsconfig.json"], - "include": ["src/**/*", "**/*.ts"], + "include": ["src/**/*", "*.ts"], "exclude": ["dist"] } From 02e29143396335328b8e172244a4b7e3dedddaf1 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 19:56:24 +0100 Subject: [PATCH 17/27] globally mock storybook deps --- packages/addon-mcp/src/mcp-handler.test.ts | 4 - packages/addon-mcp/src/telemetry.test.ts | 12 - .../src/tools/get-story-urls.test.ts | 4 - .../get-ui-building-instructions.test.ts | 4 - packages/addon-mcp/vitest.setup.ts | 6 + pnpm-lock.yaml | 1413 +++++++++++++---- 6 files changed, 1147 insertions(+), 296 deletions(-) diff --git a/packages/addon-mcp/src/mcp-handler.test.ts b/packages/addon-mcp/src/mcp-handler.test.ts index 090f2710..6c41e8c0 100644 --- a/packages/addon-mcp/src/mcp-handler.test.ts +++ b/packages/addon-mcp/src/mcp-handler.test.ts @@ -8,10 +8,6 @@ import { import type { IncomingMessage, ServerResponse } from 'node:http'; import { PassThrough } from 'node:stream'; -vi.mock('storybook/internal/telemetry', () => ({ - telemetry: vi.fn(), -})); - // Test helpers to reduce boilerplate function createMockIncomingMessage(options: { method?: string; diff --git a/packages/addon-mcp/src/telemetry.test.ts b/packages/addon-mcp/src/telemetry.test.ts index 8c79b7ba..194b2099 100644 --- a/packages/addon-mcp/src/telemetry.test.ts +++ b/packages/addon-mcp/src/telemetry.test.ts @@ -1,18 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import type { McpServer } from 'tmcp'; import type { AddonContext } from './types.ts'; - -// Mock external dependencies only -vi.mock('storybook/internal/node-logger', () => ({ - logger: { - debug: vi.fn(), - }, -})); - -vi.mock('storybook/internal/telemetry', () => ({ - telemetry: vi.fn(), -})); - import { collectTelemetry } from './telemetry.ts'; import { logger } from 'storybook/internal/node-logger'; import { telemetry } from 'storybook/internal/telemetry'; diff --git a/packages/addon-mcp/src/tools/get-story-urls.test.ts b/packages/addon-mcp/src/tools/get-story-urls.test.ts index 2ef8ed14..83583f7d 100644 --- a/packages/addon-mcp/src/tools/get-story-urls.test.ts +++ b/packages/addon-mcp/src/tools/get-story-urls.test.ts @@ -13,10 +13,6 @@ vi.mock('storybook/internal/csf', () => ({ storyNameFromExport: (exportName: string) => exportName, })); -vi.mock('storybook/internal/telemetry', () => ({ - telemetry: vi.fn(), -})); - describe('getStoryUrlsTool', () => { let server: McpServer; let fetchStoryIndexSpy: any; diff --git a/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts b/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts index 3bbba0ee..da55840f 100644 --- a/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts +++ b/packages/addon-mcp/src/tools/get-ui-building-instructions.test.ts @@ -8,10 +8,6 @@ import { import type { AddonContext } from '../types.ts'; import { GET_STORY_URLS_TOOL_NAME } from './get-story-urls.ts'; -vi.mock('storybook/internal/telemetry', () => ({ - telemetry: vi.fn(), -})); - describe('getUIBuildingInstructionsTool', () => { let server: McpServer; diff --git a/packages/addon-mcp/vitest.setup.ts b/packages/addon-mcp/vitest.setup.ts index c99b1382..23d8e287 100644 --- a/packages/addon-mcp/vitest.setup.ts +++ b/packages/addon-mcp/vitest.setup.ts @@ -9,5 +9,11 @@ vi.mock('storybook/internal/node-logger', () => ({ warn: vi.fn(), error: vi.fn(), log: vi.fn(), + verbose: vi.fn(), }, })); + +// Global mock for storybook/internal/telemetry +vi.mock('storybook/internal/telemetry', () => ({ + telemetry: vi.fn(), +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c1501e3..f163a3cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -289,7 +289,298 @@ importers: specifier: ^7.1.12 version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - eval/evals/100-flight-booking-plain/experiments/no-context-claude-code-2025-11-18T14-58-56/project: + eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-02-42/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-02-55/project: + dependencies: + '@radix-ui/colors': + specifier: ^3.0.0 + version: 3.0.0 + '@radix-ui/react-popover': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle': + specifier: ^1.1.10 + version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle-group': + specifier: ^1.1.11 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + radix-ui: + specifier: ^1.4.3 + version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-03-41/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-03-47/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-42/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-52/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@types/node': + specifier: catalog:experiments + version: 24.10.1 + '@types/react': + specifier: catalog:experiments + version: 19.2.6 + '@types/react-dom': + specifier: catalog:experiments + version: 19.2.3(@types/react@19.2.6) + '@vitejs/plugin-react-swc': + specifier: catalog:experiments + version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + eslint: + specifier: catalog:experiments + version: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: catalog:experiments + version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: catalog:experiments + version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) + globals: + specifier: catalog:experiments + version: 16.5.0 + typescript: + specifier: catalog:experiments + version: 5.9.3 + typescript-eslint: + specifier: catalog:experiments + version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vite: + specifier: catalog:experiments + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-56/project: dependencies: react: specifier: catalog:experiments @@ -622,298 +913,148 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} - engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.25.12': @@ -922,12 +1063,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -3328,11 +3463,6 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -5822,159 +5952,81 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.11': - optional: true - '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.11': - optional: true - '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm@0.25.11': - optional: true - '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-x64@0.25.11': - optional: true - '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.11': - optional: true - '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.11': - optional: true - '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.25.11': - optional: true - '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.11': - optional: true - '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/linux-arm64@0.25.11': - optional: true - '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm@0.25.11': - optional: true - '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.11': - optional: true - '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.11': - optional: true - '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-mips64el@0.25.11': - optional: true - '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.11': - optional: true - '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.25.11': - optional: true - '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-s390x@0.25.11': - optional: true - '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-x64@0.25.11': - optional: true - '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.25.11': - optional: true - '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.25.11': - optional: true - '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.11': - optional: true - '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.25.11': - optional: true - '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.25.11': - optional: true - '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/sunos-x64@0.25.11': - optional: true - '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/win32-arm64@0.25.11': - optional: true - '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-ia32@0.25.11': - optional: true - '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-x64@0.25.11': - optional: true - '@esbuild/win32-x64@0.25.12': optional: true @@ -6453,6 +6505,15 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6470,6 +6531,23 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6484,6 +6562,20 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -6502,6 +6594,15 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -6511,6 +6612,15 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6524,6 +6634,19 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6556,6 +6679,22 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6572,6 +6711,22 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6596,6 +6751,18 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6608,6 +6775,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6622,6 +6795,20 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-context@1.1.2(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6634,6 +6821,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-context@1.1.2(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6678,6 +6871,28 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-direction@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6690,6 +6905,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-direction@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6716,6 +6937,19 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6731,6 +6965,21 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6743,6 +6992,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6765,6 +7020,17 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6779,6 +7045,20 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6796,6 +7076,23 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-icons@1.3.2(react@18.3.1)': dependencies: react: 18.3.1 @@ -6814,6 +7111,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-id@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -6832,6 +7136,15 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6858,6 +7171,32 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6876,6 +7215,24 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6898,6 +7255,28 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -6918,6 +7297,26 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6934,6 +7333,22 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6980,6 +7395,29 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7016,6 +7454,24 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/rect': 1.1.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7036,6 +7492,16 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7056,6 +7522,16 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@18.3.26)(react@19.2.0) @@ -7074,6 +7550,15 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7084,6 +7569,16 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7102,6 +7597,24 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7136,6 +7649,23 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7153,6 +7683,23 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7211,6 +7758,35 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + aria-hidden: 1.2.6 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7220,6 +7796,15 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7239,6 +7824,25 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-slot@1.2.3(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7253,6 +7857,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-slot@1.2.3(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7283,6 +7894,21 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7315,6 +7941,22 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7355,6 +7997,26 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7370,6 +8032,21 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7381,6 +8058,17 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7396,6 +8084,21 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7436,6 +8139,26 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -7448,6 +8171,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.26)(react@19.2.0) @@ -7464,6 +8193,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -7478,6 +8215,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -7492,6 +8236,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -7499,6 +8250,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.26 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + use-sync-external-store: 1.6.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -7511,6 +8269,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -7523,6 +8287,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/rect': 1.1.1 @@ -7537,6 +8307,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -7551,6 +8328,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.6 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7569,6 +8353,15 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + '@radix-ui/rect@1.1.1': {} '@rolldown/binding-android-arm64@1.0.0-beta.50': @@ -8956,35 +9749,6 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild@0.25.11: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 - esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -10178,6 +10942,69 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + '@types/react-dom': 19.2.3(@types/react@19.2.6) + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -10246,6 +11073,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + react-remove-scroll-bar@2.3.8(@types/react@19.2.6)(react@19.2.0): + dependencies: + react: 19.2.0 + react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.6 + react-remove-scroll@2.7.1(@types/react@18.3.26)(react@19.2.0): dependencies: react: 19.2.0 @@ -10268,6 +11103,17 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + react-remove-scroll@2.7.1(@types/react@19.2.6)(react@19.2.0): + dependencies: + react: 19.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.6)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.6)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.6)(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.6 + react-simple-code-editor@0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -10289,6 +11135,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + react-style-singleton@2.2.3(@types/react@19.2.6)(react@19.2.0): + dependencies: + get-nonce: 1.0.1 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.6 + react-use-set@1.0.0(react@19.2.0): dependencies: react: 19.2.0 @@ -11020,6 +11874,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + use-callback-ref@1.3.3(@types/react@19.2.6)(react@19.2.0): + dependencies: + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.6 + use-sidecar@1.1.3(@types/react@18.3.26)(react@19.2.0): dependencies: detect-node-es: 1.1.0 @@ -11036,6 +11897,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 + use-sidecar@1.1.3(@types/react@19.2.6)(react@19.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.6 + use-sync-external-store@1.6.0(react@18.3.1): dependencies: react: 18.3.1 @@ -11069,7 +11938,7 @@ snapshots: vite@7.2.2(@types/node@20.19.0)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0): dependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -11084,7 +11953,7 @@ snapshots: vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0): dependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 From 6f01bc63252fb434ce1923b983262ea90eca683e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 20:04:01 +0100 Subject: [PATCH 18/27] clean lock file --- pnpm-lock.yaml | 1472 +----------------------------------------------- 1 file changed, 5 insertions(+), 1467 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f163a3cd..db7a821b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,49 +36,6 @@ catalogs: vite: specifier: 7.2.2 version: 7.2.2 - experiments: - '@eslint/js': - specifier: 9.39.1 - version: 9.39.1 - '@types/node': - specifier: 24.10.1 - version: 24.10.1 - '@types/react': - specifier: 19.2.6 - version: 19.2.6 - '@types/react-dom': - specifier: 19.2.3 - version: 19.2.3 - '@vitejs/plugin-react-swc': - specifier: 4.2.2 - version: 4.2.2 - eslint: - specifier: 9.39.1 - version: 9.39.1 - eslint-plugin-react-hooks: - specifier: 7.0.1 - version: 7.0.1 - eslint-plugin-react-refresh: - specifier: 0.4.24 - version: 0.4.24 - globals: - specifier: 16.5.0 - version: 16.5.0 - react: - specifier: 19.2.0 - version: 19.2.0 - react-dom: - specifier: 19.2.0 - version: 19.2.0 - typescript: - specifier: 5.9.3 - version: 5.9.3 - typescript-eslint: - specifier: 8.47.0 - version: 8.47.0 - vite: - specifier: 7.2.2 - version: 7.2.2 importers: @@ -289,343 +246,6 @@ importers: specifier: ^7.1.12 version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-02-42/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-02-55/project: - dependencies: - '@radix-ui/colors': - specifier: ^3.0.0 - version: 3.0.0 - '@radix-ui/react-popover': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle': - specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle-group': - specifier: ^1.1.11 - version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - radix-ui: - specifier: ^1.4.3 - version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-03-41/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/components-manifest-claude-code-2025-11-19T15-03-47/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-42/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-52/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - - eval/evals/120-flight-booking-radix/experiments/storybook-mcp-claude-code-2025-11-19T15-05-56/project: - dependencies: - react: - specifier: catalog:experiments - version: 19.2.0 - react-dom: - specifier: catalog:experiments - version: 19.2.0(react@19.2.0) - devDependencies: - '@eslint/js': - specifier: catalog:experiments - version: 9.39.1 - '@types/node': - specifier: catalog:experiments - version: 24.10.1 - '@types/react': - specifier: catalog:experiments - version: 19.2.6 - '@types/react-dom': - specifier: catalog:experiments - version: 19.2.3(@types/react@19.2.6) - '@vitejs/plugin-react-swc': - specifier: catalog:experiments - version: 4.2.2(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) - eslint: - specifier: catalog:experiments - version: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: catalog:experiments - version: 7.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: catalog:experiments - version: 0.4.24(eslint@9.39.1(jiti@2.6.1)) - globals: - specifier: catalog:experiments - version: 16.5.0 - typescript: - specifier: catalog:experiments - version: 5.9.3 - typescript-eslint: - specifier: catalog:experiments - version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: catalog:experiments - version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) - packages/addon-mcp: dependencies: '@storybook/mcp': @@ -2658,65 +2278,6 @@ packages: '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} - '@typescript-eslint/eslint-plugin@8.47.0': - resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.47.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.47.0': - resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.47.0': - resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.47.0': - resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.47.0': - resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.47.0': - resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.47.0': - resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.47.0': - resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.47.0': - resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.47.0': - resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@valibot/to-json-schema@1.3.0': resolution: {integrity: sha512-82Vv6x7sOYhv5YmTRgSppSqj1nn2pMCk5BqCMGWYp0V/fq+qirrbGncqZAtZ09/lrO40ne/7z8ejwE728aVreg==} peerDependencies: @@ -3479,17 +3040,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-react-hooks@7.0.1: - resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react-refresh@0.4.24: - resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} - peerDependencies: - eslint: '>=8.40' - eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -3750,9 +3300,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3765,12 +3312,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hermes-estree@0.25.1: - resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - - hermes-parser@0.25.1: - resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -3797,10 +3338,6 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} @@ -5129,12 +4666,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - ts-dedent@2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} @@ -5245,13 +4776,6 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - typescript-eslint@8.47.0: - resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -5561,12 +5085,6 @@ packages: peerDependencies: zod: ^3.24.1 - zod-validation-error@4.0.2: - resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -6505,15 +6023,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6531,23 +6040,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6562,20 +6054,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -6594,15 +6072,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -6612,15 +6081,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6634,19 +6094,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6679,23 +6126,7 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6711,22 +6142,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -6751,18 +6166,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6775,12 +6178,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6795,20 +6192,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-context@1.1.2(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6821,12 +6204,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-context@1.1.2(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6871,28 +6248,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-direction@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6905,12 +6260,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-direction@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6937,19 +6286,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6965,21 +6301,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -6992,12 +6313,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7020,17 +6335,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7045,20 +6349,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7076,23 +6366,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-icons@1.3.2(react@18.3.1)': dependencies: react: 18.3.1 @@ -7111,13 +6384,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-id@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7136,15 +6402,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7171,32 +6428,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7215,24 +6446,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7255,28 +6468,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7297,26 +6488,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7333,22 +6504,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7395,29 +6550,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7454,24 +6586,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/rect': 1.1.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7492,16 +6606,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7522,16 +6626,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@18.3.26)(react@19.2.0) @@ -7550,15 +6644,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7569,16 +6654,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7597,24 +6672,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7649,23 +6706,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7683,23 +6723,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7758,35 +6781,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.6)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -7796,15 +6790,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -7824,25 +6809,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-slot@1.2.3(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@19.2.0) @@ -7857,13 +6823,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-slot@1.2.3(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7894,21 +6853,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7941,22 +6885,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7997,26 +6925,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -8032,21 +6940,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -8058,17 +6951,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -8084,21 +6966,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -8139,26 +7006,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -8171,12 +7018,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.26)(react@19.2.0) @@ -8193,14 +7034,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -8215,13 +7048,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -8236,13 +7062,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -8250,13 +7069,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.26 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - use-sync-external-store: 1.6.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -8269,12 +7081,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: react: 19.2.0 @@ -8287,12 +7093,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/rect': 1.1.1 @@ -8307,13 +7107,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-use-size@1.1.1(@types/react@18.3.26)(react@19.2.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@19.2.0) @@ -8328,13 +7121,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.6)(react@19.2.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - '@types/react': 19.2.6 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -8353,15 +7139,6 @@ snapshots: '@types/react': 19.2.6 '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - '@radix-ui/rect@1.1.1': {} '@rolldown/binding-android-arm64@1.0.0-beta.50': @@ -8857,6 +7634,7 @@ snapshots: '@types/react-dom@19.2.3(@types/react@19.2.6)': dependencies: '@types/react': 19.2.6 + optional: true '@types/react-window@1.8.8': dependencies: @@ -8870,102 +7648,10 @@ snapshots: '@types/react@19.2.6': dependencies: csstype: 3.2.3 + optional: true '@types/resolve@1.20.6': {} - '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.47.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.47.0': - dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 - - '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.47.0': {} - - '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.47.0': - dependencies: - '@typescript-eslint/types': 8.47.0 - eslint-visitor-keys: 4.2.1 - '@valibot/to-json-schema@1.3.0(valibot@1.1.0(typescript@5.9.3))': dependencies: valibot: 1.1.0(typescript@5.9.3) @@ -9614,7 +8300,8 @@ snapshots: csstype@3.1.3: {} - csstype@3.2.3: {} + csstype@3.2.3: + optional: true culori@4.0.2: {} @@ -9784,21 +8471,6 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - eslint: 9.39.1(jiti@2.6.1) - hermes-parser: 0.25.1 - zod: 3.25.76 - zod-validation-error: 4.0.2(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@2.6.1)): - dependencies: - eslint: 9.39.1(jiti@2.6.1) - eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -10106,8 +8778,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -10116,12 +8786,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hermes-estree@0.25.1: {} - - hermes-parser@0.25.1: - dependencies: - hermes-estree: 0.25.1 - hookable@5.5.3: {} html-escaper@2.0.2: {} @@ -10146,8 +8810,6 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.5: {} - image-size@0.5.5: optional: true @@ -10942,69 +9604,6 @@ snapshots: '@types/react': 18.3.26 '@types/react-dom': 19.2.3(@types/react@18.3.26) - radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - '@types/react-dom': 19.2.3(@types/react@19.2.6) - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -11073,14 +9672,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - react-remove-scroll-bar@2.3.8(@types/react@19.2.6)(react@19.2.0): - dependencies: - react: 19.2.0 - react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.6 - react-remove-scroll@2.7.1(@types/react@18.3.26)(react@19.2.0): dependencies: react: 19.2.0 @@ -11103,17 +9694,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - react-remove-scroll@2.7.1(@types/react@19.2.6)(react@19.2.0): - dependencies: - react: 19.2.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.6)(react@19.2.0) - react-style-singleton: 2.2.3(@types/react@19.2.6)(react@19.2.0) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.6)(react@19.2.0) - use-sidecar: 1.1.3(@types/react@19.2.6)(react@19.2.0) - optionalDependencies: - '@types/react': 19.2.6 - react-simple-code-editor@0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -11135,14 +9715,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - react-style-singleton@2.2.3(@types/react@19.2.6)(react@19.2.0): - dependencies: - get-nonce: 1.0.1 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.6 - react-use-set@1.0.0(react@19.2.0): dependencies: react: 19.2.0 @@ -11680,10 +10252,6 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - ts-dedent@2.2.0: {} ts-node@10.9.2(@swc/core@1.13.5)(@types/node@20.19.0)(typescript@5.9.3): @@ -11787,17 +10355,6 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.1 - typescript-eslint@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - typescript@5.9.3: {} ufo@1.6.1: {} @@ -11874,13 +10431,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - use-callback-ref@1.3.3(@types/react@19.2.6)(react@19.2.0): - dependencies: - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.6 - use-sidecar@1.1.3(@types/react@18.3.26)(react@19.2.0): dependencies: detect-node-es: 1.1.0 @@ -11897,14 +10447,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.6 - use-sidecar@1.1.3(@types/react@19.2.6)(react@19.2.0): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.6 - use-sync-external-store@1.6.0(react@18.3.1): dependencies: react: 18.3.1 @@ -12117,8 +10659,4 @@ snapshots: dependencies: zod: 3.25.76 - zod-validation-error@4.0.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod@3.25.76: {} From 58e0a751cff5ff3b794c5c904e13ad5a7d1a832c Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 21:47:07 +0100 Subject: [PATCH 19/27] fix context arg --- eval/lib/collect-args.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eval/lib/collect-args.ts b/eval/lib/collect-args.ts index 7cdbcc2a..188bcbbd 100644 --- a/eval/lib/collect-args.ts +++ b/eval/lib/collect-args.ts @@ -182,7 +182,7 @@ export async function collectArgs() { case 'components-manifest': { rerunCommandParts.push( '--context', - `'${JSON.stringify(parsedArgValues.context.manifestPath)}'`, + JSON.stringify(parsedArgValues.context.manifestPath), ); return { type: 'components-manifest', From 920dea2c2bb5d9a10b02f2b123e5d622db66e37d Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 21:56:11 +0100 Subject: [PATCH 20/27] fix tests --- .../tests/mcp-endpoint.e2e.test.ts | 10 +++++----- packages/mcp/bin.test.ts | 2 +- packages/mcp/src/tools/list-all-components.ts | 5 ++++- .../src/utils/manifest-formatter/markdown.test.ts | 12 +++++++++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index 569ae7db..6c5d9314 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -405,11 +405,11 @@ describe('MCP Endpoint E2E Tests', () => { | Name | Type | Description | Required | Default | |------|------|-------------|----------|---------| - | primary | boolean | Is this the principal call to action on the page? | false | false | - | backgroundColor | string | What background color to use | false | | - | size | 'small' | 'medium' | 'large' | How large should the button be? | false | 'medium' | - | label | string | Button contents | true | | - | onClick | () => void | Optional click handler | false | |", + | primary | \`boolean\` | Is this the principal call to action on the page? | false | false | + | backgroundColor | \`string\` | What background color to use | false | | + | size | \`'small' | 'medium' | 'large'\` | How large should the button be? | false | 'medium' | + | label | \`string\` | Button contents | true | | + | onClick | \`() => void\` | Optional click handler | false | |", "type": "text", }, ], diff --git a/packages/mcp/bin.test.ts b/packages/mcp/bin.test.ts index 142f8bb3..731bb1a9 100644 --- a/packages/mcp/bin.test.ts +++ b/packages/mcp/bin.test.ts @@ -178,7 +178,7 @@ describe('bin.ts stdio MCP server', () => { content: [ { type: 'text', - text: expect.stringContaining(''), + text: expect.stringContaining('# Components'), }, ], }, diff --git a/packages/mcp/src/tools/list-all-components.ts b/packages/mcp/src/tools/list-all-components.ts index 90c9278f..faa48d85 100644 --- a/packages/mcp/src/tools/list-all-components.ts +++ b/packages/mcp/src/tools/list-all-components.ts @@ -25,7 +25,10 @@ export async function addListAllComponentsTool( ); const format = server.ctx.custom?.format ?? 'markdown'; - const componentList = formatComponentManifestMapToList(manifest, format); + const componentList = formatComponentManifestMapToList( + manifest, + format, + ); await server.ctx.custom?.onListAllComponents?.({ context: server.ctx.custom, diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts index f92994c6..adf7a5cf 100644 --- a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts +++ b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts @@ -485,7 +485,9 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain( + '| Name | Type | Description | Required | Default |', + ); expect(result).toContain('| variant | `string` | | true | |'); }); @@ -506,7 +508,9 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain( + '| Name | Type | Description | Required | Default |', + ); expect(result).toContain('| variant | `string` | | | primary |'); }); @@ -529,7 +533,9 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('| Name | Type | Description | Required | Default |'); + expect(result).toContain( + '| Name | Type | Description | Required | Default |', + ); expect(result).toContain( '| variant | `string` | The button variant | false | primary |', ); From 26a9874c2fdfb2e746347c0c13d42e2ede349c5d Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 22:01:40 +0100 Subject: [PATCH 21/27] fix types --- packages/addon-mcp/src/mcp-handler.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/addon-mcp/src/mcp-handler.test.ts b/packages/addon-mcp/src/mcp-handler.test.ts index 6c41e8c0..166fa73f 100644 --- a/packages/addon-mcp/src/mcp-handler.test.ts +++ b/packages/addon-mcp/src/mcp-handler.test.ts @@ -399,6 +399,7 @@ describe('getToolsets', () => { dev: true, docs: false, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); @@ -418,6 +419,7 @@ describe('getToolsets', () => { dev: true, docs: true, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); @@ -439,6 +441,7 @@ describe('getToolsets', () => { dev: false, docs: false, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); @@ -460,6 +463,7 @@ describe('getToolsets', () => { dev: false, docs: false, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); @@ -481,6 +485,7 @@ describe('getToolsets', () => { dev: false, docs: false, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); @@ -500,6 +505,7 @@ describe('getToolsets', () => { dev: true, docs: true, }, + experimentalFormat: 'markdown' as const, }; const result = getToolsets(request, addonOptions); From ca77d825d0ef26100c9c715de5c3f4838973017e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 22:23:09 +0100 Subject: [PATCH 22/27] "Examples" -> "Stories", simplify tests --- packages/addon-mcp/src/preset.ts | 3 +- .../tools/get-component-documentation.test.ts | 2 +- packages/mcp/src/types.ts | 4 +- .../format-manifest.test.ts.snap | 6 +- .../mcp/src/utils/format-manifest.test.ts | 18 +- .../__snapshots__/markdown.test.ts.snap | 6 +- .../utils/manifest-formatter/markdown.test.ts | 202 ++++-------------- .../src/utils/manifest-formatter/markdown.ts | 2 +- 8 files changed, 58 insertions(+), 185 deletions(-) diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index fe66e58e..ac74df2e 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -12,7 +12,8 @@ export const experimental_devServer: PresetProperty< // ValiError: Invalid type: Expected boolean but received "false" const addonOptions = v.parse(AddonOptions, { toolsets: 'toolsets' in options ? options.toolsets : {}, - format: 'format' in options ? options.format : 'markdown', + experimentalFormat: + 'experimentalFormat' in options ? options.experimentalFormat : 'markdown', }); app!.post('/mcp', (req, res) => diff --git a/packages/mcp/src/tools/get-component-documentation.test.ts b/packages/mcp/src/tools/get-component-documentation.test.ts index b77fafc4..5980438f 100644 --- a/packages/mcp/src/tools/get-component-documentation.test.ts +++ b/packages/mcp/src/tools/get-component-documentation.test.ts @@ -76,7 +76,7 @@ describe('getComponentDocumentationTool', () => { ID: button - ## Examples + ## Stories ### Primary diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index 75fb9649..b8fa0464 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -8,7 +8,7 @@ export const OutputFormat = v.optional( v.picklist(['xml', 'markdown']), 'markdown', ); -export type OutputFormatType = v.InferOutput; +export type OutputFormat = v.InferOutput; /** * Custom context passed to MCP server and tools. @@ -23,7 +23,7 @@ export interface StorybookContext extends Record { * Output format for component manifests. * @default 'markdown' */ - format?: OutputFormatType; + format?: OutputFormat; /** * Optional function to provide custom manifest retrieval logic. * If provided, this function will be called instead of the default fetch-based provider. diff --git a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap b/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap index 7c8abf8b..2013bc99 100644 --- a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap +++ b/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap @@ -13,7 +13,7 @@ The Button component is a fundamental building block for user interactions. It c Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. -## Examples +## Stories ### Primary @@ -114,7 +114,7 @@ The Card component provides a consistent way to present information in a contain - Use elevation to indicate interactive vs static cards - Keep content hierarchy clear with proper use of typography -## Examples +## Stories ### Basic @@ -263,7 +263,7 @@ The Input component is a foundational form element that wraps the native HTML in The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. -## Examples +## Stories ### Basic diff --git a/packages/mcp/src/utils/format-manifest.test.ts b/packages/mcp/src/utils/format-manifest.test.ts index e7eb519b..e12272f9 100644 --- a/packages/mcp/src/utils/format-manifest.test.ts +++ b/packages/mcp/src/utils/format-manifest.test.ts @@ -120,7 +120,7 @@ describe('formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Primary @@ -159,7 +159,7 @@ describe('formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Primary @@ -203,7 +203,7 @@ describe('formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### With Icon @@ -239,7 +239,7 @@ describe('formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Simple @@ -269,7 +269,7 @@ describe('formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### No Import @@ -353,7 +353,7 @@ describe('formatComponentManifest', () => { Supports multiple variants, sizes, and states. - ## Examples + ## Stories ### Primary @@ -767,7 +767,7 @@ describe('formatComponentManifestMapToList', () => { A component that loaded successfully but has some stories that failed to generate. - ## Examples + ## Stories ### Working @@ -797,7 +797,7 @@ describe('formatComponentManifestMapToList', () => { ID: error-component-with-success-stories - ## Examples + ## Stories ### Basic @@ -831,7 +831,7 @@ describe('formatComponentManifestMapToList', () => { A component where everything worked except one story. - ## Examples + ## Stories ### Default diff --git a/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap index 01f48de8..9ef95769 100644 --- a/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap +++ b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap @@ -13,7 +13,7 @@ The Button component is a fundamental building block for user interactions. It c Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. -## Examples +## Stories ### Primary @@ -114,7 +114,7 @@ The Card component provides a consistent way to present information in a contain - Use elevation to indicate interactive vs static cards - Keep content hierarchy clear with proper use of typography -## Examples +## Stories ### Basic @@ -263,7 +263,7 @@ The Input component is a foundational form element that wraps the native HTML in The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. -## Examples +## Stories ### Basic diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts index adf7a5cf..44d41ee8 100644 --- a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts +++ b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts @@ -60,28 +60,6 @@ describe('MarkdownFormatter - formatComponentManifest', () => { `); }); - it('should handle multi-line descriptions', () => { - const manifest: ComponentManifest = { - id: 'button', - path: 'src/components/Button.tsx', - name: 'Button', - description: - 'A versatile button component.\n\nSupports multiple variants and sizes.', - }; - - const result = markdownFormatter.formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A versatile button component. - - Supports multiple variants and sizes." - `); - }); - it('should omit description section when not provided', () => { const manifest: ComponentManifest = { id: 'button', @@ -122,7 +100,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Default @@ -161,7 +139,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Default @@ -221,7 +199,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { ID: button - ## Examples + ## Stories ### Primary @@ -233,24 +211,6 @@ describe('MarkdownFormatter - formatComponentManifest', () => { `); }); - it('should handle stories without import', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'Default', - snippet: '', - }, - ], - }; - - const result = markdownFormatter.formatComponentManifest(manifest); - - expect(result).not.toContain('import'); - }); - it('should omit stories when no stories are provided', () => { const manifest: ComponentManifest = { id: 'button', @@ -260,7 +220,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).not.toContain('## Examples'); + expect(result).not.toContain('## Stories'); }); it('should omit stories when stories array is empty', () => { @@ -273,7 +233,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).not.toContain('## Examples'); + expect(result).not.toContain('## Stories'); }); }); @@ -303,12 +263,6 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('## Props'); - expect(result).toContain( - '| Name | Type | Description | Required | Default |', - ); - expect(result).toContain('| variant |'); - expect(result).toContain('| disabled |'); expect(result).toMatchInlineSnapshot(` "# Button @@ -324,7 +278,7 @@ describe('MarkdownFormatter - formatComponentManifest', () => { }); }); - describe('props section - bullet list format', () => { + describe('props section', () => { it('should format props with only name and type as bullet list', () => { const manifest: ComponentManifest = { id: 'button', @@ -344,10 +298,6 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('## Props'); - expect(result).toContain('- variant: union'); - expect(result).toContain('- size: union'); - expect(result).not.toContain('| Name |'); expect(result).toMatchInlineSnapshot(` "# Button @@ -381,10 +331,6 @@ describe('MarkdownFormatter - formatComponentManifest', () => { const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain('## Props'); - expect(result).toContain('- variant: union - The visual style variant'); - expect(result).toContain('- size: union - The size of the button'); - expect(result).not.toContain('| Name |'); expect(result).toMatchInlineSnapshot(` "# Button @@ -426,120 +372,46 @@ describe('MarkdownFormatter - formatComponentManifest', () => { }); }); - describe('props section - format decision logic', () => { - it('should use bullet list when props have only name and type', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - type: { name: 'string' }, - }, - }, - }, - }; - - const result = markdownFormatter.formatComponentManifest(manifest); - - expect(result).toContain('- variant: string'); - expect(result).not.toContain('| Name | Type |'); - }); - - it('should use bullet list when props have name, type, and description but no required/default', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - type: { name: 'string' }, - description: 'The button variant', - }, + it('should use table when props have rich metadata', () => { + const manifest: ComponentManifest = { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + reactDocgen: { + props: { + variant: { + type: { name: 'string' }, + description: 'The button variant', + required: false, + defaultValue: { value: 'primary', computed: false }, }, - }, - }; - - const result = markdownFormatter.formatComponentManifest(manifest); - - expect(result).toContain('- variant: string - The button variant'); - expect(result).not.toContain('| Name | Type |'); - }); - - it('should use table when props have required metadata', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - type: { name: 'string' }, - required: true, - }, + disabled: { + type: { name: 'bool' }, + required: true, }, - }, - }; - - const result = markdownFormatter.formatComponentManifest(manifest); - - expect(result).toContain( - '| Name | Type | Description | Required | Default |', - ); - expect(result).toContain('| variant | `string` | | true | |'); - }); - - it('should use table when props have default value metadata', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - type: { name: 'string' }, - defaultValue: { value: 'primary', computed: false }, - }, + size: { + type: { name: 'union', value: ['small', 'medium', 'large'] }, + defaultValue: { value: 'medium', computed: false }, }, }, - }; + }, + }; - const result = markdownFormatter.formatComponentManifest(manifest); + const result = markdownFormatter.formatComponentManifest(manifest); - expect(result).toContain( - '| Name | Type | Description | Required | Default |', - ); - expect(result).toContain('| variant | `string` | | | primary |'); - }); + expect(result).toMatchInlineSnapshot(` + "# Button - it('should use table when props have all metadata fields', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - type: { name: 'string' }, - description: 'The button variant', - required: false, - defaultValue: { value: 'primary', computed: false }, - }, - }, - }, - }; + ID: button - const result = markdownFormatter.formatComponentManifest(manifest); + ## Props - expect(result).toContain( - '| Name | Type | Description | Required | Default |', - ); - expect(result).toContain( - '| variant | `string` | The button variant | false | primary |', - ); - }); + | Name | Type | Description | Required | Default | + |------|------|-------------|----------|---------| + | variant | \`string\` | The button variant | false | primary | + | disabled | \`bool\` | | true | | + | size | \`union\` | | | medium |" + `); }); }); diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.ts b/packages/mcp/src/utils/manifest-formatter/markdown.ts index 4921dba6..8b5df6a9 100644 --- a/packages/mcp/src/utils/manifest-formatter/markdown.ts +++ b/packages/mcp/src/utils/manifest-formatter/markdown.ts @@ -27,7 +27,7 @@ export const markdownFormatter: ManifestFormatter = { // Stories section if (componentManifest.stories && componentManifest.stories.length > 0) { - parts.push('## Examples'); + parts.push('## Stories'); parts.push(''); for (const story of componentManifest.stories) { From d6065b5f9cc6098ecad6209a71163b6e7f812698 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 19 Nov 2025 22:31:37 +0100 Subject: [PATCH 23/27] simplify tests and types --- packages/addon-mcp/src/types.ts | 7 +- packages/mcp/bin.ts | 3 +- packages/mcp/serve.ts | 2 +- packages/mcp/src/index.ts | 2 +- packages/mcp/src/types.ts | 6 +- packages/mcp/src/utils/format-manifest.ts | 7 +- .../mcp/src/utils/manifest-formatter/types.ts | 7 - .../src/utils/manifest-formatter/xml.test.ts | 136 ------------------ 8 files changed, 13 insertions(+), 157 deletions(-) diff --git a/packages/addon-mcp/src/types.ts b/packages/addon-mcp/src/types.ts index 2f181b58..6d448c58 100644 --- a/packages/addon-mcp/src/types.ts +++ b/packages/addon-mcp/src/types.ts @@ -1,6 +1,6 @@ import * as v from 'valibot'; import type { Options } from 'storybook/internal/types'; -import type { StorybookContext } from '@storybook/mcp'; +import type { StorybookContext, OutputFormat } from '@storybook/mcp'; export const AddonOptions = v.object({ toolsets: v.optional( @@ -14,7 +14,10 @@ export const AddonOptions = v.object({ docs: true, }, ), - experimentalFormat: v.optional(v.picklist(['xml', 'markdown']), 'markdown'), + experimentalFormat: v.optional( + v.custom((val) => val === 'xml' || val === 'markdown'), + 'markdown', + ), }); export type AddonOptionsInput = v.InferInput; diff --git a/packages/mcp/bin.ts b/packages/mcp/bin.ts index b1bcb404..481c10bd 100644 --- a/packages/mcp/bin.ts +++ b/packages/mcp/bin.ts @@ -18,8 +18,7 @@ import { StdioTransport } from '@tmcp/transport-stdio'; import pkgJson from './package.json' with { type: 'json' }; import { addListAllComponentsTool } from './src/tools/list-all-components.ts'; import { addGetComponentDocumentationTool } from './src/tools/get-component-documentation.ts'; -import type { StorybookContext } from './src/types.ts'; -import type { OutputFormat } from './src/utils/manifest-formatter/types.ts'; +import type { StorybookContext, OutputFormat } from './src/types.ts'; import { parseArgs } from 'node:util'; import * as fs from 'node:fs/promises'; diff --git a/packages/mcp/serve.ts b/packages/mcp/serve.ts index e8d4a1b4..d00bee37 100644 --- a/packages/mcp/serve.ts +++ b/packages/mcp/serve.ts @@ -2,7 +2,7 @@ import { createStorybookMcpHandler } from './src/index.ts'; import { serve } from 'srvx'; import fs from 'node:fs/promises'; import { parseArgs } from 'node:util'; -import type { OutputFormat } from './src/utils/manifest-formatter/types.ts'; +import type { OutputFormat } from './src/types.ts'; async function serveMcp( port: number, diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index c81a4280..9bfe828c 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -20,7 +20,7 @@ export { export { MANIFEST_PATH } from './utils/get-manifest.ts'; // Export types for reuse -export type { StorybookContext } from './types.ts'; +export type { StorybookContext, OutputFormat } from './types.ts'; // copied from tmcp internals as it's not exposed type InitializeRequestParams = { diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index b8fa0464..7b4c5974 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -4,11 +4,7 @@ import * as v from 'valibot'; /** * Supported output formats for component manifest formatting. */ -export const OutputFormat = v.optional( - v.picklist(['xml', 'markdown']), - 'markdown', -); -export type OutputFormat = v.InferOutput; +export type OutputFormat = 'xml' | 'markdown'; /** * Custom context passed to MCP server and tools. diff --git a/packages/mcp/src/utils/format-manifest.ts b/packages/mcp/src/utils/format-manifest.ts index 8a7c0f14..0e2b5402 100644 --- a/packages/mcp/src/utils/format-manifest.ts +++ b/packages/mcp/src/utils/format-manifest.ts @@ -1,8 +1,9 @@ -import type { ComponentManifest, ComponentManifestMap } from '../types.ts'; import type { + ComponentManifest, + ComponentManifestMap, OutputFormat, - ManifestFormatter, -} from './manifest-formatter/types.ts'; +} from '../types.ts'; +import type { ManifestFormatter } from './manifest-formatter/types.ts'; import { xmlFormatter } from './manifest-formatter/xml.ts'; import { markdownFormatter } from './manifest-formatter/markdown.ts'; diff --git a/packages/mcp/src/utils/manifest-formatter/types.ts b/packages/mcp/src/utils/manifest-formatter/types.ts index 7195b701..e34a7ccf 100644 --- a/packages/mcp/src/utils/manifest-formatter/types.ts +++ b/packages/mcp/src/utils/manifest-formatter/types.ts @@ -1,12 +1,5 @@ -import * as v from 'valibot'; import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; -/** - * Supported output formats for component manifest formatting. - */ -export const OutputFormat = v.picklist(['xml', 'markdown']); -export type OutputFormat = v.InferOutput; - /** * Interface for manifest formatters. * Implementations must provide methods to format both single components diff --git a/packages/mcp/src/utils/manifest-formatter/xml.test.ts b/packages/mcp/src/utils/manifest-formatter/xml.test.ts index 158ca81c..f2673e83 100644 --- a/packages/mcp/src/utils/manifest-formatter/xml.test.ts +++ b/packages/mcp/src/utils/manifest-formatter/xml.test.ts @@ -62,30 +62,6 @@ describe('XmlFormatter - formatComponentManifest', () => { `); }); - it('should handle multi-line descriptions', () => { - const manifest: ComponentManifest = { - id: 'button', - path: 'src/components/Button.tsx', - name: 'Button', - description: - 'A versatile button component.\n\nSupports multiple variants and sizes.', - }; - - const result = xmlFormatter.formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - " - button - Button - - A versatile button component. - - Supports multiple variants and sizes. - - " - `); - }); - it('should omit description section when not provided', () => { const manifest: ComponentManifest = { id: 'button', @@ -224,64 +200,6 @@ describe('XmlFormatter - formatComponentManifest', () => { `); }); - it('should handle stories without description', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'Simple', - snippet: '', - }, - ], - }; - - const result = xmlFormatter.formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - " - button - Button - - Simple - - - - - " - `); - }); - - it('should handle stories without import', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'NoImport', - snippet: '', - }, - ], - }; - - const result = xmlFormatter.formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - " - button - Button - - No Import - - - - - " - `); - }); - it('should omit stories when no stories are provided', () => { const manifest: ComponentManifest = { id: 'button', @@ -925,60 +843,6 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { `); }); - it('should format partial success component (skips failed story)', () => { - const component = withErrorsFixture.components['partial-success']; - const result = xmlFormatter.formatComponentManifest(component); - expect(result).toMatchInlineSnapshot(` - " - partial-success - PartialSuccess - - A component where everything worked except one story. - - - Default - - Default usage of the component. - - - import { PartialSuccess } from '@storybook/design-system'; - - const Default = () => - - - - With Subtitle - - Component with both title and subtitle. - - - import { PartialSuccess } from '@storybook/design-system'; - - const WithSubtitle = () => - - - - - title - - The title text - - string - true - - - subtitle - - Optional subtitle - - string - false - - - " - `); - }); - it('should format list of components with errors', () => { const result = xmlFormatter.formatComponentManifestMapToList( withErrorsFixture as ComponentManifestMap, From 18a51dd16973ad6522bb2e9276a6206f5254783a Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 20 Nov 2025 09:04:06 +0100 Subject: [PATCH 24/27] simplify --- .../instructions/addon-mcp.instructions.md | 1 - packages/addon-mcp/package.json | 3 +- packages/addon-mcp/src/types.ts | 7 +- packages/mcp/src/index.ts | 2 +- .../format-manifest.test.ts.snap | 357 ------- .../mcp/src/utils/format-manifest.test.ts | 899 +----------------- packages/mcp/src/utils/format-manifest.ts | 4 - 7 files changed, 46 insertions(+), 1227 deletions(-) delete mode 100644 packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap diff --git a/.github/instructions/addon-mcp.instructions.md b/.github/instructions/addon-mcp.instructions.md index eb6ef9ff..a1fa386d 100644 --- a/.github/instructions/addon-mcp.instructions.md +++ b/.github/instructions/addon-mcp.instructions.md @@ -417,7 +417,6 @@ This addon implements MCP using `tmcp`: - `storybook` - Peer dependency (Storybook framework) - `valibot` - Schema validation for tool inputs/outputs -- `ts-dedent` - Template string formatting - `tsdown` - Build tool (rolldown-based) - `vite` - Peer dependency for middleware injection diff --git a/packages/addon-mcp/package.json b/packages/addon-mcp/package.json index 68d8a195..898b584a 100644 --- a/packages/addon-mcp/package.json +++ b/packages/addon-mcp/package.json @@ -40,8 +40,7 @@ "valibot": "catalog:" }, "devDependencies": { - "storybook": "catalog:", - "ts-dedent": "^2.2.0" + "storybook": "catalog:" }, "peerDependencies": { "storybook": "^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" diff --git a/packages/addon-mcp/src/types.ts b/packages/addon-mcp/src/types.ts index 6d448c58..2f181b58 100644 --- a/packages/addon-mcp/src/types.ts +++ b/packages/addon-mcp/src/types.ts @@ -1,6 +1,6 @@ import * as v from 'valibot'; import type { Options } from 'storybook/internal/types'; -import type { StorybookContext, OutputFormat } from '@storybook/mcp'; +import type { StorybookContext } from '@storybook/mcp'; export const AddonOptions = v.object({ toolsets: v.optional( @@ -14,10 +14,7 @@ export const AddonOptions = v.object({ docs: true, }, ), - experimentalFormat: v.optional( - v.custom((val) => val === 'xml' || val === 'markdown'), - 'markdown', - ), + experimentalFormat: v.optional(v.picklist(['xml', 'markdown']), 'markdown'), }); export type AddonOptionsInput = v.InferInput; diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 9bfe828c..c81a4280 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -20,7 +20,7 @@ export { export { MANIFEST_PATH } from './utils/get-manifest.ts'; // Export types for reuse -export type { StorybookContext, OutputFormat } from './types.ts'; +export type { StorybookContext } from './types.ts'; // copied from tmcp internals as it's not exposed type InitializeRequestParams = { diff --git a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap b/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap deleted file mode 100644 index 2013bc99..00000000 --- a/packages/mcp/src/utils/__snapshots__/format-manifest.test.ts.snap +++ /dev/null @@ -1,357 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`formatComponentManifest > formats all full fixtures 1`] = ` -"# Button - -ID: button - -A versatile button component that supports multiple variants, sizes, and states. - -The Button component is a fundamental building block for user interactions. It can be styled as primary, secondary, or tertiary actions, and supports disabled and loading states. - -## Usage - -Buttons should be used for actions that affect the current page or trigger operations. For navigation, consider using a Link component instead. - -## Stories - -### Primary - -The primary button variant is used for the main call-to-action on a page. It has the highest visual prominence and should be used sparingly to guide users toward the most important action. - -## Best Practices - -- Use only one primary button per section -- Keep button text concise and action-oriented -- Ensure sufficient contrast for accessibility - -\`\`\` -import { Button } from '@storybook/design-system'; - -const Primary = () => -\`\`\` - -### Secondary - -The secondary button variant is used for secondary actions that are still important but not the primary focus of the page. - -Secondary buttons have less visual weight than primary buttons and can be used multiple times on a page. - -\`\`\` -import { Button } from '@storybook/design-system'; - -const Secondary = () => -\`\`\` - -### With Sizes - -Buttons are available in three sizes: small, medium (default), and large. - -Choose the appropriate size based on the context and hierarchy of actions. Larger buttons are more prominent and easier to tap on mobile devices. - -\`\`\` -import { Button } from '@storybook/design-system'; - -const WithSizes = () => ( - <> - - - - -) -\`\`\` - -### Loading - -The loading state provides visual feedback when an async operation is in progress. - -When loading is true, the button displays a spinner and is automatically disabled to prevent multiple submissions. The button text remains visible to maintain layout stability. - -\`\`\` -import { Button } from '@storybook/design-system'; - -const Loading = () => -\`\`\` - -### Danger - -The danger variant is used for destructive actions that cannot be easily undone, such as deleting data or canceling subscriptions. - -Use this variant to draw attention to the serious nature of the action. Consider adding a confirmation dialog for critical operations. - -\`\`\` -import { Button } from '@storybook/design-system'; - -const Danger = () => -\`\`\` - -## Props - -| Name | Type | Description | Required | Default | -|------|------|-------------|----------|---------| -| variant | \`"primary" | "secondary" | "tertiary" | "danger"\` | The visual style variant of the button | false | "primary" | -| size | \`"small" | "medium" | "large"\` | The size of the button | false | "medium" | -| disabled | \`boolean\` | Whether the button is disabled | false | false | -| loading | \`boolean\` | Whether the button is in a loading state | false | false | -| fullWidth | \`boolean\` | Whether the button should take up the full width of its container | false | false | -| onClick | \`(event: MouseEvent) => void\` | Callback function when the button is clicked | false | | -| children | \`ReactNode\` | The content of the button | true | |" -`; - -exports[`formatComponentManifest > formats all full fixtures 2`] = ` -"# Card - -ID: card - -A flexible container component for grouping related content with optional header, footer, and action areas. - -The Card component provides a consistent way to present information in a contained, elevated surface. It's commonly used for displaying articles, products, user profiles, or any grouped content that benefits from visual separation. - -## Design Principles - -- Cards should contain a single subject or action -- Maintain consistent padding and spacing -- Use elevation to indicate interactive vs static cards -- Keep content hierarchy clear with proper use of typography - -## Stories - -### Basic - -A basic card with just content. - -The default elevated variant provides subtle depth through shadow, making the card appear to float above the page. This is ideal for creating visual hierarchy and grouping related information. - -\`\`\` -import { Card } from '@storybook/design-system'; - -const Basic = () => ( - -

Card Title

-

This is some card content that provides information to the user.

-
-) -\`\`\` - -### With Header And Footer - -A card with distinct header and footer sections. - -Headers typically contain titles, subtitles, or avatars. Footers often contain actions like buttons or metadata like timestamps. The header and footer are visually separated from the main content area. - -\`\`\` -import { Card } from '@storybook/design-system'; - -const WithHeaderAndFooter = () => ( - Article Title} - footer={} - > -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

-
-) -\`\`\` - -### Clickable - -An interactive card that responds to clicks. - -Clickable cards add hover effects and cursor changes to indicate interactivity. This pattern is useful for navigation cards, product cards, or any scenario where the entire card acts as a single interactive element. - -## Accessibility - -Clickable cards are rendered as buttons with proper keyboard support and ARIA attributes. - -\`\`\` -import { Card } from '@storybook/design-system'; - -const Clickable = () => ( - alert('Card clicked!')}> -

Product Name

-

Click anywhere on this card to view details.

-
-) -\`\`\` - -### Variants - -Different visual variants of the card component. - -- **Elevated**: Default variant with shadow for depth -- **Outlined**: Border-only variant without shadow -- **Flat**: No border or shadow, minimal visual separation - -Choose variants based on your design system and the level of emphasis needed. - -\`\`\` -import { Card } from '@storybook/design-system'; - -const Variants = () => ( - <> - -

Elevated card with shadow

-
- -

Outlined card with border

-
- -

Flat card without border or shadow

-
- -) -\`\`\` - -### User Profile - -A real-world example of a user profile card. - -This example demonstrates how to compose the Card component with other design system components to create a complete, functional UI element. It includes an avatar, user information, stats, and action buttons. - -\`\`\` -import { Card } from '@storybook/design-system'; - -const UserProfile = () => ( - - -
-

Jane Doe

-

Senior Developer

-
- - } - footer={ -
- - -
- } - > -
-
1.2K
Followers
-
342
Following
-
89
Posts
-
-
-) -\`\`\` - -## Props - -| Name | Type | Description | Required | Default | -|------|------|-------------|----------|---------| -| variant | \`"elevated" | "outlined" | "flat"\` | The visual style variant of the card | false | "elevated" | -| padding | \`"none" | "small" | "medium" | "large"\` | The amount of internal padding | false | "medium" | -| clickable | \`boolean\` | Whether the entire card is clickable/interactive | false | false | -| header | \`ReactNode\` | Content to display in the card header | false | | -| footer | \`ReactNode\` | Content to display in the card footer | false | | -| children | \`ReactNode\` | The main content of the card | true | | -| onClick | \`(event: MouseEvent) => void\` | Callback function when the card is clicked (requires clickable=true) | false | |" -`; - -exports[`formatComponentManifest > formats all full fixtures 3`] = ` -"# Input - -ID: input - -A flexible text input component that supports various input types, validation states, and accessibility features. - -The Input component is a foundational form element that wraps the native HTML input with consistent styling and behavior. It includes support for labels, error messages, helper text, and different visual states. - -## Accessibility - -The Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility. - -## Stories - -### Basic - -A basic text input with a label. - -This is the most common use case for the Input component. Always include a label for accessibility, even if it's visually hidden in your design. - -\`\`\` -import { Input } from '@storybook/design-system'; - -const Basic = () => -\`\`\` - -### With Error - -An input displaying an error state with an error message. - -Error messages should be clear, concise, and provide actionable guidance to help users fix the issue. The input border and message text are styled in red to indicate the error state. - -\`\`\` -import { Input } from '@storybook/design-system'; - -const WithError = () => -\`\`\` - -### With Helper Text - -An input with helper text providing additional context or instructions. - -Helper text appears below the input and provides guidance without being an error. Use it to clarify format expectations, character limits, or provide helpful hints. - -\`\`\` -import { Input } from '@storybook/design-system'; - -const WithHelperText = () => -\`\`\` - -### Types - -Different input types for various data formats. - -Using the correct input type improves the user experience by showing appropriate mobile keyboards and enabling browser validation features. - -\`\`\` -import { Input } from '@storybook/design-system'; - -const Types = () => ( - <> - - - - - -) -\`\`\` - -### Disabled - -A disabled input that cannot be interacted with. - -Disabled inputs are useful for displaying non-editable data in forms or for inputs that become available only after certain conditions are met. - -\`\`\` -import { Input } from '@storybook/design-system'; - -const Disabled = () => -\`\`\` - -## Props - -| Name | Type | Description | Required | Default | -|------|------|-------------|----------|---------| -| type | \`"text" | "email" | "password" | "number" | "tel" | "url"\` | The type of input field | false | "text" | -| label | \`string\` | The label text for the input | false | | -| placeholder | \`string\` | Placeholder text shown when the input is empty | false | | -| value | \`string\` | The controlled value of the input | false | | -| defaultValue | \`string\` | The initial value for an uncontrolled input | false | | -| disabled | \`boolean\` | Whether the input is disabled | false | false | -| required | \`boolean\` | Whether the input is required | false | false | -| error | \`string\` | Error message to display below the input | false | | -| helperText | \`string\` | Helper text to display below the input | false | | -| onChange | \`(event: ChangeEvent) => void\` | Callback function when the input value changes | false | |" -`; - -exports[`formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` -"# Components - -- Button (button): A versatile button component for user interactions -- Card (card): A flexible container component for grouping related content -- Input (input): A flexible text input component with validation support" -`; diff --git a/packages/mcp/src/utils/format-manifest.test.ts b/packages/mcp/src/utils/format-manifest.test.ts index e12272f9..df983e19 100644 --- a/packages/mcp/src/utils/format-manifest.test.ts +++ b/packages/mcp/src/utils/format-manifest.test.ts @@ -4,877 +4,62 @@ import { formatComponentManifestMapToList, } from './format-manifest'; import type { ComponentManifest, ComponentManifestMap } from '../types'; -import fullManifestFixture from '../../fixtures/full-manifest.fixture.json' with { type: 'json' }; -import withErrorsFixture from '../../fixtures/with-errors.fixture.json' with { type: 'json' }; describe('formatComponentManifest', () => { - it('formats all full fixtures', () => { - expect( - formatComponentManifest(fullManifestFixture.components.button), - ).toMatchSnapshot(); - expect( - formatComponentManifest(fullManifestFixture.components.card), - ).toMatchSnapshot(); - expect( - formatComponentManifest(fullManifestFixture.components.input), - ).toMatchSnapshot(); + const manifest: ComponentManifest = { + id: 'test-component', + path: 'src/components/TestComponent.tsx', + name: 'TestComponent', + }; + + it('should use markdown formatter by default', () => { + const result = formatComponentManifest(manifest); + expect(result).toContain('# TestComponent'); + expect(result).toContain('ID: test-component'); }); - describe('component name', () => { - it('should include component name in component_name tag', () => { - const manifest: ComponentManifest = { - id: 'test-component', - path: 'src/components/TestComponent.tsx', - name: 'TestComponent', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# TestComponent - - ID: test-component" - `); - }); - }); - - describe('description section', () => { - it('should include description when provided', () => { - const manifest: ComponentManifest = { - id: 'button', - path: 'src/components/Button.tsx', - name: 'Button', - description: 'A simple button component', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A simple button component" - `); - }); - - it('should handle multi-line descriptions', () => { - const manifest: ComponentManifest = { - id: 'button', - path: 'src/components/Button.tsx', - name: 'Button', - description: - 'A versatile button component.\n\nSupports multiple variants and sizes.', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A versatile button component. - - Supports multiple variants and sizes." - `); - }); - - it('should omit description section when not provided', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button" - `); - }); + it('should use markdown formatter when format is "markdown"', () => { + const result = formatComponentManifest(manifest, 'markdown'); + expect(result).toContain('# TestComponent'); + expect(result).toContain('ID: test-component'); }); - describe('stories section', () => { - it('should format a single story', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - import: 'import { Button } from "@/components";', - stories: [ - { - name: 'Primary', - description: 'A primary button variant', - snippet: '', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Stories - - ### Primary - - A primary button variant - - \`\`\` - import { Button } from "@/components"; - - - \`\`\`" - `); - }); - - it('should format multiple stories', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - import: 'import { Button } from "@/components";', - stories: [ - { - name: 'Primary', - snippet: '', - }, - { - name: 'Secondary', - snippet: '', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Stories - - ### Primary - - \`\`\` - import { Button } from "@/components"; - - - \`\`\` - - ### Secondary - - \`\`\` - import { Button } from "@/components"; - - - \`\`\`" - `); - }); - - it('should format PascalCase story names correctly', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'WithIcon', - snippet: '', - }, - { - name: 'DisabledState', - snippet: '', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Stories - - ### With Icon - - \`\`\` - - \`\`\` - - ### Disabled State - - \`\`\` - - \`\`\`" - `); - }); - - it('should handle stories without description', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'Simple', - snippet: '', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Stories - - ### Simple - - \`\`\` - - \`\`\`" - `); - }); - - it('should handle stories without import', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [ - { - name: 'NoImport', - snippet: '', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Stories - - ### No Import - - \`\`\` - - \`\`\`" - `); - }); - - it('should omit stories when no stories are provided', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: 'A button component', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A button component" - `); - }); - - it('should omit stories when stories array is empty', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - stories: [], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button" - `); - }); + it('should use xml formatter when format is "xml"', () => { + const result = formatComponentManifest(manifest, 'xml'); + expect(result).toContain(''); + expect(result).toContain('TestComponent'); + expect(result).toContain('test-component'); }); +}); - describe('complete component', () => { - it('should format a complete component with description and multiple stories', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: - 'A versatile button component.\n\nSupports multiple variants, sizes, and states.', - summary: 'A button for user interactions', - import: 'import { Button } from "@storybook/design-system";', - stories: [ - { - name: 'Primary', - description: 'The primary button variant.', - snippet: - 'const Primary = () => ', - }, - { - name: 'WithSizes', - description: 'Buttons in different sizes.', - snippet: - 'const Sizes = () => (\n <>\n \n \n \n)', - }, - ], - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A versatile button component. - - Supports multiple variants, sizes, and states. - - ## Stories - - ### Primary - - The primary button variant. - - \`\`\` - import { Button } from "@storybook/design-system"; - - const Primary = () => - \`\`\` - - ### With Sizes - - Buttons in different sizes. - - \`\`\` - import { Button } from "@storybook/design-system"; - - const Sizes = () => ( - <> - - - - ) - \`\`\`" - `); - }); - }); - - describe('props section', () => { - it('should format props from reactDocgen', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - variant: { - description: 'The visual style variant', - required: false, - defaultValue: { value: '"primary"', computed: false }, - tsType: { - name: 'union', - raw: '"primary" | "secondary"', - elements: [ - { name: 'literal', value: '"primary"' }, - { name: 'literal', value: '"secondary"' }, - ], - }, - }, - disabled: { - description: 'Whether the button is disabled', - required: false, - defaultValue: { value: 'false', computed: false }, - tsType: { - name: 'boolean', - }, - }, - onClick: { - description: 'Click handler', - required: true, - tsType: { - name: 'signature', - type: 'function', - signature: { - arguments: [{ name: 'event', type: { name: 'MouseEvent' } }], - return: { name: 'void' }, - }, - }, - }, - }, - }, - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Props - - | Name | Type | Description | Required | Default | - |------|------|-------------|----------|---------| - | variant | \`"primary" | "secondary"\` | The visual style variant | false | "primary" | - | disabled | \`boolean\` | Whether the button is disabled | false | false | - | onClick | \`(event: MouseEvent) => void\` | Click handler | true | |" - `); - }); - - it('should handle props with minimal information', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - reactDocgen: { - props: { - children: { - tsType: { - name: 'string', - }, - }, - }, - }, - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - ## Props - - - children: string" - `); - }); - - it('should omit props section when reactDocgen is not present', () => { - const manifest: ComponentManifest = { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: 'A button component', - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button - - A button component" - `); - }); - - it('should omit props section when reactDocgen has no props', () => { - const manifest: ComponentManifest = { +describe('formatComponentManifestMapToList', () => { + const manifest: ComponentManifestMap = { + v: 1, + components: { + button: { id: 'button', name: 'Button', path: 'src/components/Button.tsx', - reactDocgen: { - props: {}, - }, - }; - - const result = formatComponentManifest(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Button - - ID: button" - `); - }); - }); -}); - -describe('formatComponentManifestMapToList', () => { - it('formats the full manifest fixture', () => { - const result = formatComponentManifestMapToList(fullManifestFixture); - expect(result).toMatchSnapshot(); + }, + }, + }; + + it('should use markdown formatter by default', () => { + const result = formatComponentManifestMapToList(manifest); + expect(result).toContain('# Components'); + expect(result).toContain('- Button (button)'); }); - describe('component list structure', () => { - it('should format a single component', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button)" - `); - }); - - it('should format multiple components', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - }, - card: { - id: 'card', - name: 'Card', - path: 'src/components/Card.tsx', - }, - input: { - id: 'input', - name: 'Input', - path: 'src/components/Input.tsx', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button) - - Card (card) - - Input (input)" - `); - }); + it('should use markdown formatter when format is "markdown"', () => { + const result = formatComponentManifestMapToList(manifest, 'markdown'); + expect(result).toContain('# Components'); + expect(result).toContain('- Button (button)'); }); - describe('summary section', () => { - it('should include summary when provided', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - summary: 'A versatile button component', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button): A versatile button component" - `); - }); - - it('should prefer summary over description', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - summary: 'Short summary', - description: 'This is a longer description that should be ignored', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toContain('Short summary'); - expect(result).not.toContain('longer description'); - }); - - it('should use description when summary is not provided', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: 'A simple button component', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button): A simple button component" - `); - }); - - it('should truncate long descriptions to 90 characters', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: - 'This is a very long description that exceeds ninety characters and should be truncated with ellipsis', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button): This is a very long description that exceeds ninety characters and should be truncated wit..." - `); - }); - - it('should not truncate descriptions under 90 characters', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - description: - 'A description with exactly eighty characters is fine and should not be truncated', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toContain( - 'A description with exactly eighty characters is fine and should not be truncated', - ); - expect(result).not.toContain('...'); - }); - - it('should omit summary section when neither summary nor description provided', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).not.toContain(''); - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button)" - `); - }); - }); - - describe('complete manifest', () => { - it('should format a complete manifest with varied components', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', - summary: 'A versatile button component', - }, - card: { - id: 'card', - name: 'Card', - path: 'src/components/Card.tsx', - description: 'A flexible container for grouping content', - }, - input: { - id: 'input', - name: 'Input', - path: 'src/components/Input.tsx', - summary: 'Text input with validation', - description: - 'A comprehensive input component with validation, error states, and accessibility features', - }, - modal: { - id: 'modal', - name: 'Modal', - path: 'src/components/Modal.tsx', - }, - }, - }; - - const result = formatComponentManifestMapToList(manifest); - - expect(result).toMatchInlineSnapshot(` - "# Components - - - Button (button): A versatile button component - - Card (card): A flexible container for grouping content - - Input (input): Text input with validation - - Modal (modal)" - `); - }); - }); - - describe('with-errors fixture', () => { - it('should format success component with mixed stories (only successful ones)', () => { - const component = - withErrorsFixture.components['success-component-with-mixed-stories']; - const result = formatComponentManifest(component); - expect(result).toMatchInlineSnapshot(` - "# SuccessWithMixedStories - - ID: success-component-with-mixed-stories - - A component that loaded successfully but has some stories that failed to generate. - - ## Stories - - ### Working - - This story generated successfully. - - \`\`\` - import { SuccessWithMixedStories } from '@storybook/design-system'; - - const Working = () => - \`\`\` - - ## Props - - | Name | Type | Description | Required | Default | - |------|------|-------------|----------|---------| - | text | \`string\` | The text to display | true | | - | variant | \`"primary" | "secondary"\` | The visual variant | false | "primary" |" - `); - }); - - it('should format error component with success stories', () => { - const component = - withErrorsFixture.components['error-component-with-success-stories']; - const result = formatComponentManifest(component); - expect(result).toMatchInlineSnapshot(` - "# ErrorWithSuccessStories - - ID: error-component-with-success-stories - - ## Stories - - ### Basic - - Even though the component parsing failed, this story's code snippet was generated. - - \`\`\` - const Basic = () => Content - \`\`\` - - ### Advanced - - Another successfully generated story despite component-level errors. - - \`\`\` - const Advanced = () => ( - - Advanced Content - - ) - \`\`\`" - `); - }); - - it('should format partial success component (skips failed story)', () => { - const component = withErrorsFixture.components['partial-success']; - const result = formatComponentManifest(component); - expect(result).toMatchInlineSnapshot(` - "# PartialSuccess - - ID: partial-success - - A component where everything worked except one story. - - ## Stories - - ### Default - - Default usage of the component. - - \`\`\` - import { PartialSuccess } from '@storybook/design-system'; - - const Default = () => - \`\`\` - - ### With Subtitle - - Component with both title and subtitle. - - \`\`\` - import { PartialSuccess } from '@storybook/design-system'; - - const WithSubtitle = () => - \`\`\` - - ## Props - - | Name | Type | Description | Required | Default | - |------|------|-------------|----------|---------| - | title | \`string\` | The title text | true | | - | subtitle | \`string\` | Optional subtitle | false | |" - `); - }); - - it('should format list of components with errors', () => { - const result = formatComponentManifestMapToList( - withErrorsFixture as ComponentManifestMap, - ); - expect(result).toMatchInlineSnapshot(` - "# Components - - - SuccessWithMixedStories (success-component-with-mixed-stories): Success component with both working and failing stories - - ErrorWithSuccessStories (error-component-with-success-stories) - - ErrorWithErrorStories (error-component-with-error-stories) - - CompleteError (complete-error-component) - - PartialSuccess (partial-success): Mostly working component with one failing story" - `); - }); + it('should use xml formatter when format is "xml"', () => { + const result = formatComponentManifestMapToList(manifest, 'xml'); + expect(result).toContain(''); + expect(result).toContain('Button'); + expect(result).toContain('button'); }); }); diff --git a/packages/mcp/src/utils/format-manifest.ts b/packages/mcp/src/utils/format-manifest.ts index 0e2b5402..4f60ce84 100644 --- a/packages/mcp/src/utils/format-manifest.ts +++ b/packages/mcp/src/utils/format-manifest.ts @@ -7,10 +7,6 @@ import type { ManifestFormatter } from './manifest-formatter/types.ts'; import { xmlFormatter } from './manifest-formatter/xml.ts'; import { markdownFormatter } from './manifest-formatter/markdown.ts'; -// Re-export utility functions for use by formatters -export { dedent } from './dedent.ts'; -export { parseReactDocgen } from './parse-react-docgen.ts'; - const formatters: Record = { xml: xmlFormatter, markdown: markdownFormatter, From c7150af18bcf067855d6a9f1dd6624727df1d508 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 20 Nov 2025 09:37:10 +0100 Subject: [PATCH 25/27] use ts-like prop type docs format --- .../tests/mcp-endpoint.e2e.test.ts | 33 ++++- .../tools/get-component-documentation.test.ts | 16 +- .../__snapshots__/markdown.test.ts.snap | 138 ++++++++++++++---- .../utils/manifest-formatter/markdown.test.ts | 67 ++++++--- .../src/utils/manifest-formatter/markdown.ts | 68 ++++----- 5 files changed, 224 insertions(+), 98 deletions(-) diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index 6c5d9314..b307a26e 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -367,7 +367,7 @@ describe('MCP Endpoint E2E Tests', () => { Primary UI component for user interaction - ## Examples + ## Stories ### Primary @@ -403,13 +403,30 @@ describe('MCP Endpoint E2E Tests', () => { ## Props - | Name | Type | Description | Required | Default | - |------|------|-------------|----------|---------| - | primary | \`boolean\` | Is this the principal call to action on the page? | false | false | - | backgroundColor | \`string\` | What background color to use | false | | - | size | \`'small' | 'medium' | 'large'\` | How large should the button be? | false | 'medium' | - | label | \`string\` | Button contents | true | | - | onClick | \`() => void\` | Optional click handler | false | |", + \`\`\` + export type Props = { + /** + Is this the principal call to action on the page? + */ + primary?: boolean = false; + /** + What background color to use + */ + backgroundColor?: string; + /** + How large should the button be? + */ + size?: 'small' | 'medium' | 'large' = 'medium'; + /** + Button contents + */ + label: string; + /** + Optional click handler + */ + onClick?: () => void; + } + \`\`\`", "type": "text", }, ], diff --git a/packages/mcp/src/tools/get-component-documentation.test.ts b/packages/mcp/src/tools/get-component-documentation.test.ts index 5980438f..c221393f 100644 --- a/packages/mcp/src/tools/get-component-documentation.test.ts +++ b/packages/mcp/src/tools/get-component-documentation.test.ts @@ -263,10 +263,18 @@ describe('getComponentDocumentationTool', () => { ## Props - | Name | Type | Description | Required | Default | - |------|------|-------------|----------|---------| - | variant | \`"primary" | "secondary"\` | Button style variant | false | "primary" | - | disabled | \`boolean\` | Disable the button | false | |", + \`\`\` + export type Props = { + /** + Button style variant + */ + variant?: "primary" | "secondary" = "primary"; + /** + Disable the button + */ + disabled?: boolean; + } + \`\`\`", "type": "text", }, ], diff --git a/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap index 9ef95769..eb3a41dd 100644 --- a/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap +++ b/packages/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap @@ -87,15 +87,38 @@ const Danger = () =>