From 85a71ece6f472704c1260d569a4ad95cade986a4 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 30 Dec 2025 09:30:38 +0100 Subject: [PATCH 01/18] make getManifest handle both component and docs manifests --- packages/mcp/src/index.ts | 5 +- packages/mcp/src/types.ts | 36 ++- packages/mcp/src/utils/get-manifest.test.ts | 268 +++++++++++++++----- packages/mcp/src/utils/get-manifest.ts | 114 ++++++--- 4 files changed, 315 insertions(+), 108 deletions(-) diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index c81a4280..9923e58d 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -17,7 +17,10 @@ export { } from './tools/get-component-documentation.ts'; // Export manifest constants -export { MANIFEST_PATH } from './utils/get-manifest.ts'; +export { + COMPONENT_MANIFEST_PATH, + DOCS_MANIFEST_PATH, +} from './utils/get-manifest.ts'; // Export types for reuse export type { StorybookContext } from './types.ts'; diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index 5895e3ab..a5f12806 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -54,16 +54,16 @@ export type StorybookContext = { const JSDocTag = v.record(v.string(), v.array(v.string())); +const Error = v.object({ + name: v.string(), + message: v.string(), +}); + const BaseManifest = v.object({ name: v.string(), description: v.optional(v.string()), jsDocTags: v.optional(JSDocTag), - error: v.optional( - v.object({ - name: v.string(), - message: v.string(), - }), - ), + error: v.optional(Error), }); const Story = v.object({ @@ -71,6 +71,19 @@ const Story = v.object({ snippet: v.optional(v.string()), }); +/** + * A docs entry represents MDX documentation that can be attached to a component + * or standalone (unattached). + */ +const Doc = v.object({ + id: v.string(), + name: v.string(), + title: v.string(), + path: v.string(), + content: v.string(), + error: v.optional(Error), +}); + export const ComponentManifest = v.object({ ...BaseManifest.entries, id: v.string(), @@ -80,6 +93,7 @@ export const ComponentManifest = v.object({ stories: v.optional(v.array(Story)), // loose schema for react-docgen types, as they are pretty complex reactDocgen: v.optional(v.custom(() => true)), + docs: v.optional(v.record(v.string(), Doc)), }); export type ComponentManifest = v.InferOutput; @@ -88,3 +102,13 @@ export const ComponentManifestMap = v.object({ components: v.record(v.string(), ComponentManifest), }); export type ComponentManifestMap = v.InferOutput; + +/** + * Manifest for unattached/standalone documentation entries. + * Served at /manifests/docs.json + */ +export const DocsManifestMap = v.object({ + v: v.number(), + docs: v.record(v.string(), Doc), +}); +export type DocsManifestMap = v.InferOutput; diff --git a/packages/mcp/src/utils/get-manifest.test.ts b/packages/mcp/src/utils/get-manifest.test.ts index c13b5bfa..bf909c00 100644 --- a/packages/mcp/src/utils/get-manifest.test.ts +++ b/packages/mcp/src/utils/get-manifest.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { getManifest, ManifestGetError } from './get-manifest'; -import type { ComponentManifestMap } from '../types'; +import { getManifests, ManifestGetError } from './get-manifest.ts'; +import type { ComponentManifestMap, DocsManifestMap } from '../types.ts'; global.fetch = vi.fn(); @@ -13,6 +13,92 @@ function createMockRequest(url: string): Request { }); } +/** + * Helper to create a successful JSON fetch response + */ +function createJsonResponse(data: unknown) { + return { + ok: true, + headers: { + get: vi.fn().mockReturnValue('application/json'), + }, + text: vi.fn().mockResolvedValue(JSON.stringify(data)), + }; +} + +/** + * Helper to create a 404 response + */ +function create404Response() { + return { + ok: false, + status: 404, + statusText: 'Not Found', + }; +} + +/** + * Helper to create a fetch mock that returns different responses based on URL + */ +function createFetchMock(responses: { + components?: unknown | Error; + docs?: unknown | Error; +}) { + return vi.fn().mockImplementation((url: string) => { + if (url.includes('components.json')) { + if (responses.components instanceof Error) { + return Promise.reject(responses.components); + } + return Promise.resolve( + responses.components !== undefined + ? createJsonResponse(responses.components) + : create404Response(), + ); + } + if (url.includes('docs.json')) { + if (responses.docs instanceof Error) { + return Promise.reject(responses.docs); + } + return Promise.resolve( + responses.docs !== undefined + ? createJsonResponse(responses.docs) + : create404Response(), + ); + } + return Promise.resolve(create404Response()); + }); +} + +/** + * Helper to create a manifestProvider mock that returns different responses based on path + */ +function createManifestProviderMock(responses: { + components?: string | Error; + docs?: string | Error; +}) { + return vi + .fn() + .mockImplementation((_request: Request | undefined, path: string) => { + if (path.includes('components.json')) { + if (responses.components instanceof Error) { + return Promise.reject(responses.components); + } + return responses.components !== undefined + ? Promise.resolve(responses.components) + : Promise.reject(new Error('Components not found')); + } + if (path.includes('docs.json')) { + if (responses.docs instanceof Error) { + return Promise.reject(responses.docs); + } + return responses.docs !== undefined + ? Promise.resolve(responses.docs) + : Promise.reject(new Error('Docs not found')); + } + return Promise.reject(new Error('Unknown path')); + }); +} + describe('getManifest', () => { beforeEach(() => { // Reset the fetch mock between tests since we're checking call counts @@ -21,14 +107,14 @@ describe('getManifest', () => { describe('error cases', () => { it('should throw ManifestGetError when request is not provided and using default provider', async () => { - await expect(getManifest()).rejects.toThrow(ManifestGetError); - await expect(getManifest()).rejects.toThrow( + await expect(getManifests()).rejects.toThrow(ManifestGetError); + await expect(getManifests()).rejects.toThrow( "You must either pass the original request forward to the server context, or set a custom manifestProvider that doesn't need the request", ); }); it('should throw ManifestGetError when request is undefined and using default provider', async () => { - await expect(getManifest(undefined)).rejects.toThrow(ManifestGetError); - await expect(getManifest(undefined)).rejects.toThrow( + await expect(getManifests(undefined)).rejects.toThrow(ManifestGetError); + await expect(getManifests(undefined)).rejects.toThrow( "You must either pass the original request forward to the server context, or set a custom manifestProvider that doesn't need the request", ); }); @@ -40,8 +126,8 @@ describe('getManifest', () => { }); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow(ManifestGetError); - await expect(getManifest(request)).rejects.toThrow( + await expect(getManifests(request)).rejects.toThrow(ManifestGetError); + await expect(getManifests(request)).rejects.toThrow( 'Failed to fetch manifest: 404 Not Found', ); }); @@ -54,7 +140,7 @@ describe('getManifest', () => { }); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow( + await expect(getManifests(request)).rejects.toThrow( 'Failed to fetch manifest: 500 Internal Server Error', ); }); @@ -68,25 +154,30 @@ describe('getManifest', () => { }); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow(ManifestGetError); - await expect(getManifest(request)).rejects.toThrow( + await expect(getManifests(request)).rejects.toThrow(ManifestGetError); + await expect(getManifests(request)).rejects.toThrow( 'Invalid content type: expected application/json, got text/html', ); }); it('should throw ManifestGetError when response is not valid JSON', async () => { - global.fetch = vi.fn().mockResolvedValue({ - ok: true, - headers: { - get: vi.fn().mockReturnValue('application/json'), - }, - text: vi.fn().mockResolvedValue('not valid json{'), + global.fetch = vi.fn().mockImplementation((url: string) => { + if (url.includes('components.json')) { + return Promise.resolve({ + ok: true, + headers: { + get: vi.fn().mockReturnValue('application/json'), + }, + text: vi.fn().mockResolvedValue('not valid json{'), + }); + } + return Promise.resolve(create404Response()); }); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow(ManifestGetError); - await expect(getManifest(request)).rejects.toThrow( - 'Failed to get manifest:', + await expect(getManifests(request)).rejects.toThrow(ManifestGetError); + await expect(getManifests(request)).rejects.toThrow( + 'Failed to parse component manifest:', ); }); @@ -106,9 +197,9 @@ describe('getManifest', () => { const request = createMockRequest('https://example.com/mcp'); await expect( - getManifest(request), + getManifests(request), ).rejects.toThrowErrorMatchingInlineSnapshot( - `[ManifestGetError: Failed to get manifest: Invalid key: Expected "v" but received undefined]`, + `[ManifestGetError: Failed to parse component manifest: Invalid key: Expected "v" but received undefined]`, ); }); @@ -127,8 +218,8 @@ describe('getManifest', () => { }); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow(ManifestGetError); - await expect(getManifest(request)).rejects.toThrow( + await expect(getManifests(request)).rejects.toThrow(ManifestGetError); + await expect(getManifests(request)).rejects.toThrow( 'No components found in the manifest', ); }); @@ -139,8 +230,8 @@ describe('getManifest', () => { .mockRejectedValue(new Error('Network connection failed')); const request = createMockRequest('https://example.com/mcp'); - await expect(getManifest(request)).rejects.toThrow(ManifestGetError); - await expect(getManifest(request)).rejects.toThrow( + await expect(getManifests(request)).rejects.toThrow(ManifestGetError); + await expect(getManifests(request)).rejects.toThrow( 'Network connection failed', ); }); @@ -154,7 +245,7 @@ describe('getManifest', () => { const request = createMockRequest('https://example.com/mcp'); try { - await getManifest(request); + await getManifests(request); } catch (error) { expect(error).toBeInstanceOf(ManifestGetError); expect((error as ManifestGetError).url).toBe( @@ -178,21 +269,60 @@ describe('getManifest', () => { }, }; - global.fetch = vi.fn().mockResolvedValue({ - ok: true, - headers: { - get: vi.fn().mockReturnValue('application/json'), - }, - text: vi.fn().mockResolvedValue(JSON.stringify(validManifest)), - }); + global.fetch = createFetchMock({ components: validManifest }); const request = createMockRequest('https://example.com/mcp'); - const result = await getManifest(request); + const result = await getManifests(request); - expect(result).toEqual(validManifest); - expect(global.fetch).toHaveBeenCalledExactlyOnceWith( + expect(result).toEqual({ componentManifest: validManifest }); + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(global.fetch).toHaveBeenCalledWith( 'https://example.com/manifests/components.json', ); + expect(global.fetch).toHaveBeenCalledWith( + 'https://example.com/manifests/docs.json', + ); + }); + + it('should successfully fetch and parse both component and docs manifests', async () => { + const validComponentManifest: ComponentManifestMap = { + v: 1, + components: { + button: { + id: 'button', + path: 'src/components/Button.tsx', + name: 'Button', + description: 'A button component', + }, + }, + }; + + const validDocsManifest: DocsManifestMap = { + v: 1, + docs: { + 'getting-started': { + id: 'getting-started', + name: 'Getting Started', + title: 'Getting Started Guide', + path: 'docs/getting-started.mdx', + content: '# Getting Started\n\nWelcome to our component library.', + }, + }, + }; + + global.fetch = createFetchMock({ + components: validComponentManifest, + docs: validDocsManifest, + }); + + const request = createMockRequest('https://example.com/mcp'); + const result = await getManifests(request); + + expect(result).toEqual({ + componentManifest: validComponentManifest, + docsManifest: validDocsManifest, + }); + expect(global.fetch).toHaveBeenCalledTimes(2); }); }); @@ -211,17 +341,22 @@ describe('getManifest', () => { }; const request = createMockRequest('https://example.com/mcp'); - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(validManifest)); + const manifestProvider = createManifestProviderMock({ + components: JSON.stringify(validManifest), + }); - const result = await getManifest(request, manifestProvider); + const result = await getManifests(request, manifestProvider); - expect(result).toEqual(validManifest); - expect(manifestProvider).toHaveBeenCalledExactlyOnceWith( + expect(result).toEqual({ componentManifest: validManifest }); + expect(manifestProvider).toHaveBeenCalledTimes(2); + expect(manifestProvider).toHaveBeenCalledWith( request, './manifests/components.json', ); + expect(manifestProvider).toHaveBeenCalledWith( + request, + './manifests/docs.json', + ); // fetch should not be called when manifestProvider is used expect(global.fetch).not.toHaveBeenCalled(); }); @@ -239,15 +374,15 @@ describe('getManifest', () => { }, }; - // Custom provider that doesn't need the request - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(validManifest)); + const manifestProvider = createManifestProviderMock({ + components: JSON.stringify(validManifest), + }); - const result = await getManifest(undefined, manifestProvider); + const result = await getManifests(undefined, manifestProvider); - expect(result).toEqual(validManifest); - expect(manifestProvider).toHaveBeenCalledExactlyOnceWith( + expect(result).toEqual({ componentManifest: validManifest }); + expect(manifestProvider).toHaveBeenCalledTimes(2); + expect(manifestProvider).toHaveBeenCalledWith( undefined, './manifests/components.json', ); @@ -268,42 +403,39 @@ describe('getManifest', () => { }, }; - global.fetch = vi.fn().mockResolvedValue({ - ok: true, - headers: { - get: vi.fn().mockReturnValue('application/json'), - }, - text: vi.fn().mockResolvedValue(JSON.stringify(validManifest)), - }); + global.fetch = createFetchMock({ components: validManifest }); const request = createMockRequest('https://example.com/mcp'); - const result = await getManifest(request); + const result = await getManifests(request); - expect(result).toEqual(validManifest); - expect(global.fetch).toHaveBeenCalledExactlyOnceWith( + expect(result).toEqual({ componentManifest: validManifest }); + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(global.fetch).toHaveBeenCalledWith( 'https://example.com/manifests/components.json', ); }); it('should handle errors from manifestProvider', async () => { const request = createMockRequest('https://example.com/mcp'); - const manifestProvider = vi - .fn() - .mockRejectedValue(new Error('File not found')); + const manifestProvider = createManifestProviderMock({ + components: new Error('File not found'), + }); - await expect(getManifest(request, manifestProvider)).rejects.toThrow( + await expect(getManifests(request, manifestProvider)).rejects.toThrow( ManifestGetError, ); - await expect(getManifest(request, manifestProvider)).rejects.toThrow( - 'Failed to get manifest: File not found', + await expect(getManifests(request, manifestProvider)).rejects.toThrow( + 'Failed to get component manifest: File not found', ); }); it('should handle invalid JSON from manifestProvider', async () => { const request = createMockRequest('https://example.com/mcp'); - const manifestProvider = vi.fn().mockResolvedValue('not valid json{'); + const manifestProvider = createManifestProviderMock({ + components: 'not valid json{', + }); - await expect(getManifest(request, manifestProvider)).rejects.toThrow( + await expect(getManifests(request, manifestProvider)).rejects.toThrow( ManifestGetError, ); }); diff --git a/packages/mcp/src/utils/get-manifest.ts b/packages/mcp/src/utils/get-manifest.ts index c5354107..86b3b2c3 100644 --- a/packages/mcp/src/utils/get-manifest.ts +++ b/packages/mcp/src/utils/get-manifest.ts @@ -1,10 +1,11 @@ -import { ComponentManifestMap } from '../types.ts'; +import { ComponentManifestMap, DocsManifestMap } from '../types.ts'; import * as v from 'valibot'; /** - * The path to the component manifest file relative to the Storybook build + * The paths to the manifest files relative to the Storybook build */ -export const MANIFEST_PATH = './manifests/components.json'; +export const COMPONENT_MANIFEST_PATH = './manifests/components.json'; +export const DOCS_MANIFEST_PATH = './manifests/docs.json'; /** * Error thrown when getting or parsing a manifest fails @@ -62,53 +63,100 @@ export const errorToMCPContent = (error: unknown): MCPErrorResult => { }; /** - * Gets a component manifest from a request or using a custom provider + * Parses a JSON string and validates it against a Valibot schema + */ +function parseManifest< + T extends v.BaseSchema>, +>({ + jsonString, + schema, + name, + url, +}: { + jsonString: string; + schema: T; + name: string; + url: string; +}): v.InferOutput { + try { + return v.parse(v.pipe(v.string(), v.parseJson(), schema), jsonString); + } catch (error) { + throw new ManifestGetError( + `Failed to parse ${name} manifest: +${error instanceof v.ValiError ? error.issues.map((i) => i.message).join('\n') : String(error)}`, + url, + ); + } +} + +/** + * Gets component and docs manifest from a request or using a custom provider * * @param request - The HTTP request to get the manifest for (optional when using custom manifestProvider) * @param manifestProvider - Optional custom function to get the manifest * @returns A promise that resolves to the parsed ComponentManifestMap * @throws {ManifestGetError} If getting the manifest fails or the response is invalid */ -export async function getManifest( +export async function getManifests( request?: Request, manifestProvider?: ( request: Request | undefined, path: string, ) => Promise, -): Promise { - try { - // Use custom manifestProvider if provided, otherwise fallback to default - const manifestString = await (manifestProvider ?? defaultManifestProvider)( - request, - MANIFEST_PATH, - ); - const manifestData: unknown = JSON.parse(manifestString); - - const manifest = v.parse(ComponentManifestMap, manifestData); +): Promise<{ + componentManifest: ComponentManifestMap; + docsManifest?: DocsManifestMap; +}> { + const provider = manifestProvider ?? defaultManifestProvider; + + // Fetch both component and docs manifests in parallel + const [componentResult, docsResult] = await Promise.allSettled([ + provider(request, COMPONENT_MANIFEST_PATH), + provider(request, DOCS_MANIFEST_PATH), + ]); + + const getUrl = (path: string) => + request + ? getManifestUrlFromRequest(request, path) + : 'Unknown manifest source'; - if (Object.keys(manifest.components).length === 0) { - const url = request - ? getManifestUrlFromRequest(request, MANIFEST_PATH) - : 'Unknown manifest source'; - throw new ManifestGetError(`No components found in the manifest`, url); - } + if (componentResult.status === 'rejected') { + throw new ManifestGetError( + `Failed to get component manifest: ${componentResult.reason instanceof Error ? componentResult.reason.message : String(componentResult.reason)}`, + getUrl(COMPONENT_MANIFEST_PATH), + componentResult.reason instanceof Error + ? componentResult.reason + : undefined, + ); + } - return manifest; - } catch (error) { - if (error instanceof ManifestGetError) { - throw error; - } + const componentManifest = parseManifest({ + jsonString: componentResult.value, + schema: ComponentManifestMap, + name: 'component', + url: getUrl(COMPONENT_MANIFEST_PATH), + }); - // Wrap network errors and other unexpected errors - const url = request - ? getManifestUrlFromRequest(request, MANIFEST_PATH) - : 'Unknown manifest source'; + if (Object.keys(componentManifest.components).length === 0) { throw new ManifestGetError( - `Failed to get manifest: ${error instanceof Error ? error.message : String(error)}`, - url, - error instanceof Error ? error : undefined, + `No components found in the manifest`, + getUrl(COMPONENT_MANIFEST_PATH), ); } + + if (docsResult.status === 'rejected') { + return { componentManifest }; + } + + // Handle docs manifest result (optional - only exists when addon-docs is used) + const docsManifest = parseManifest({ + jsonString: docsResult.value, + schema: DocsManifestMap, + name: 'docs', + url: getUrl(DOCS_MANIFEST_PATH), + }); + + return { componentManifest, docsManifest }; } /** From b577091848053b6e1e00b1b351db0c61228da1e5 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 30 Dec 2025 11:21:53 +0100 Subject: [PATCH 02/18] add support for docs manifest in both tools --- .github/copilot-instructions.md | 4 +- .../instructions/addon-mcp.instructions.md | 14 +- .github/instructions/mcp.instructions.md | 2 +- .../tests/mcp-endpoint.e2e.test.ts | 24 +- packages/addon-mcp/src/mcp-handler.test.ts | 2 +- packages/addon-mcp/src/mcp-handler.ts | 12 +- packages/mcp/bin.test.ts | 2 +- packages/mcp/bin.ts | 4 +- .../fixtures/small-docs-manifest.fixture.json | 19 + packages/mcp/src/index.test.ts | 182 +++++-- packages/mcp/src/index.ts | 13 +- .../tools/get-component-documentation.test.ts | 52 +- .../src/tools/get-component-documentation.ts | 75 --- .../mcp/src/tools/get-documentation.test.ts | 508 ++++++++++++++++++ packages/mcp/src/tools/get-documentation.ts | 84 +++ .../mcp/src/tools/list-all-components.test.ts | 162 +++++- packages/mcp/src/tools/list-all-components.ts | 15 +- packages/mcp/src/types.ts | 14 +- .../mcp/src/utils/format-manifest.test.ts | 32 +- packages/mcp/src/utils/format-manifest.ts | 27 +- packages/mcp/src/utils/get-manifest.test.ts | 8 +- packages/mcp/src/utils/get-manifest.ts | 11 +- .../__snapshots__/markdown.test.ts.snap | 2 +- .../__snapshots__/xml.test.ts.snap | 2 +- .../extract-docs-summary.test.ts | 201 +++++++ .../extract-docs-summary.ts | 61 +++ .../utils/manifest-formatter/markdown.test.ts | 300 +++++++---- .../src/utils/manifest-formatter/markdown.ts | 32 +- .../mcp/src/utils/manifest-formatter/types.ts | 21 +- .../src/utils/manifest-formatter/xml.test.ts | 367 +++++++++---- .../mcp/src/utils/manifest-formatter/xml.ts | 45 +- 31 files changed, 1845 insertions(+), 452 deletions(-) create mode 100644 packages/mcp/fixtures/small-docs-manifest.fixture.json delete mode 100644 packages/mcp/src/tools/get-component-documentation.ts create mode 100644 packages/mcp/src/tools/get-documentation.test.ts create mode 100644 packages/mcp/src/tools/get-documentation.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/extract-docs-summary.test.ts create mode 100644 packages/mcp/src/utils/manifest-formatter/extract-docs-summary.ts diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c72ea5ac..1ef16bd6 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -73,7 +73,7 @@ The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic: - **Optional handlers**: `StorybookContext` supports optional handlers that are called at various points, allowing consumers to track usage or collect telemetry: - `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 + - `onGetDocumentation`: 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 @@ -268,7 +268,7 @@ export { addMyTool, MY_TOOL_NAME } from './tools/my-tool.ts'; - **Optional handlers for tracking**: - `onSessionInitialize`: Called when an MCP session is initialized, receives context - `onListAllComponents`: Called when list tool is invoked, receives context and manifest - - `onGetComponentDocumentation`: Called when get tool is invoked, receives context, input with componentId, and optional foundComponent + - `onGetDocumentation`: Called when get tool is invoked, receives context, input with id, and optional foundDocumentation - Addon-mcp uses these handlers to collect telemetry on tool usage **Storybook internals used:** diff --git a/.github/instructions/addon-mcp.instructions.md b/.github/instructions/addon-mcp.instructions.md index a1fa386d..85b18497 100644 --- a/.github/instructions/addon-mcp.instructions.md +++ b/.github/instructions/addon-mcp.instructions.md @@ -430,25 +430,23 @@ The addon collects anonymous usage data: **For addon-specific tools**: Telemetry is collected directly in the tool implementation using `collectTelemetry()`. -**For reused tools from `@storybook/mcp`**: The addon uses optional handlers (`onListAllComponents`, `onGetComponentDocumentation`) provided by the `StorybookContext` to track usage. These handlers are passed in the context when calling `transport.respond()` in `mcp-handler.ts`: +**For reused tools from `@storybook/mcp`**: The addon uses optional handlers (`onListAllComponents`, `onGetDocumentation`) provided by the `StorybookContext` to track usage. These handlers are passed in the context when calling `transport.respond()` in `mcp-handler.ts`: ```typescript const addonContext: AddonContext = { // ... other context properties - onListAllComponents: async ({ manifest }) => { + onListAllComponents: async ({ manifests }) => { if (!disableTelemetry && server) { await collectTelemetry({ event: 'tool:listAllComponents', server, - componentCount: Object.keys(manifest.components).length, + componentCount: Object.keys(manifests.componentManifest.components) + .length, + docsCount: Object.keys(manifests.docsManifest.docs).length, }); } }, - onGetComponentDocumentation: async ({ - input, - foundComponents, - notFoundIds, - }) => { + onGetDocumentation: async ({ input, foundComponents, notFoundIds }) => { if (!disableTelemetry && server) { await collectTelemetry({ event: 'tool:getComponentDocumentation', diff --git a/.github/instructions/mcp.instructions.md b/.github/instructions/mcp.instructions.md index 4ca8918b..d0296136 100644 --- a/.github/instructions/mcp.instructions.md +++ b/.github/instructions/mcp.instructions.md @@ -17,7 +17,7 @@ This is a Model Context Protocol (MCP) server for Storybook that serves knowledg - **Component Manifest**: Parses and formats component documentation including React prop information from react-docgen - **Schema Validation**: Uses Valibot for JSON schema validation via `@tmcp/adapter-valibot` - **HTTP Transport**: Provides HTTP-based MCP communication via `@tmcp/transport-http` -- **Context System**: `StorybookContext` allows passing optional handlers (`onSessionInitialize`, `onListAllComponents`, `onGetComponentDocumentation`) that are called at various points when provided. The `onGetComponentDocumentation` handler receives a single `componentId` input and an optional `foundComponent` result. +- **Context System**: `StorybookContext` allows passing optional handlers (`onSessionInitialize`, `onListAllComponents`, `onGetDocumentation`) that are called at various points when provided. The `onGetDocumentation` handler receives a single `id` input and an optional `foundDocumentation` result. ### File Structure diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index b307a26e..f3e958c7 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -222,21 +222,21 @@ describe('MCP Endpoint E2E Tests', () => { "title": "List All Components", }, { - "description": "Get detailed documentation for a specific UI component", + "description": "Get detailed documentation for a specific UI component or docs entry", "inputSchema": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { - "componentId": { + "id": { "type": "string", }, }, "required": [ - "componentId", + "id", ], "type": "object", }, - "name": "get-component-documentation", - "title": "Get Documentation for Component", + "name": "get-documentation", + "title": "Get Documentation", }, ] `); @@ -335,7 +335,7 @@ describe('MCP Endpoint E2E Tests', () => { }); }); - describe('Tool: get-component-documentation', () => { + describe('Tool: get-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', { @@ -351,9 +351,9 @@ describe('MCP Endpoint E2E Tests', () => { // Now get documentation for that component const response = await mcpRequest('tools/call', { - name: 'get-component-documentation', + name: 'get-documentation', arguments: { - componentId, + id: componentId, }, }); @@ -436,9 +436,9 @@ describe('MCP Endpoint E2E Tests', () => { it('should return error for non-existent component', async () => { const response = await mcpRequest('tools/call', { - name: 'get-component-documentation', + name: 'get-documentation', arguments: { - componentId: 'non-existent-component-id', + id: 'non-existent-component-id', }, }); @@ -446,7 +446,7 @@ describe('MCP Endpoint E2E Tests', () => { { "content": [ { - "text": "Component not found: "non-existent-component-id". Use the list-all-components tool to see available components.", + "text": "Component or Docs Entry not found: \"non-existent-component-id\". Use the list-all-components tool to see available components and documentation entries.", "type": "text", }, ], @@ -500,7 +500,7 @@ describe('MCP Endpoint E2E Tests', () => { expect(toolNames).toMatchInlineSnapshot(` [ "list-all-components", - "get-component-documentation", + "get-documentation", ] `); }); diff --git a/packages/addon-mcp/src/mcp-handler.test.ts b/packages/addon-mcp/src/mcp-handler.test.ts index 166fa73f..50ffd922 100644 --- a/packages/addon-mcp/src/mcp-handler.test.ts +++ b/packages/addon-mcp/src/mcp-handler.test.ts @@ -387,7 +387,7 @@ describe('mcpServerHandler', () => { // 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'); + expect(toolNames).toContain('get-documentation'); }); }); diff --git a/packages/addon-mcp/src/mcp-handler.ts b/packages/addon-mcp/src/mcp-handler.ts index 891c8613..78305530 100644 --- a/packages/addon-mcp/src/mcp-handler.ts +++ b/packages/addon-mcp/src/mcp-handler.ts @@ -103,21 +103,23 @@ export const mcpServerHandler = async ({ request: webRequest, // Telemetry handlers for component manifest tools ...(!disableTelemetry && { - onListAllComponents: async ({ manifest }) => { + onListAllComponents: async ({ manifests }) => { await collectTelemetry({ event: 'tool:listAllComponents', server, toolset: 'docs', - componentCount: Object.keys(manifest.components).length, + componentCount: Object.keys(manifests.componentManifest.components) + .length, + docsCount: Object.keys(manifests.docsManifest?.docs || {}).length, }); }, - onGetComponentDocumentation: async ({ input, foundComponent }) => { + onGetDocumentation: async ({ input, foundDocumentation }) => { await collectTelemetry({ event: 'tool:getComponentDocumentation', server, toolset: 'docs', - componentId: input.componentId, - found: !!foundComponent, + componentId: input.id, + found: !!foundDocumentation, }); }, }), diff --git a/packages/mcp/bin.test.ts b/packages/mcp/bin.test.ts index 731bb1a9..9c559a47 100644 --- a/packages/mcp/bin.test.ts +++ b/packages/mcp/bin.test.ts @@ -151,7 +151,7 @@ describe('bin.ts stdio MCP server', () => { name: 'list-all-components', }), expect.objectContaining({ - name: 'get-component-documentation', + name: 'get-documentation', }), ]), }, diff --git a/packages/mcp/bin.ts b/packages/mcp/bin.ts index abfe829a..54b01729 100644 --- a/packages/mcp/bin.ts +++ b/packages/mcp/bin.ts @@ -17,7 +17,7 @@ import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot'; 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 { addGetDocumentationTool } from './src/tools/get-documentation.ts'; import type { StorybookContext, OutputFormat } from './src/types.ts'; import { parseArgs } from 'node:util'; import * as fs from 'node:fs/promises'; @@ -38,7 +38,7 @@ const server = new McpServer( ).withContext(); await addListAllComponentsTool(server); -await addGetComponentDocumentationTool(server); +await addGetDocumentationTool(server); const transport = new StdioTransport(server); const args = parseArgs({ diff --git a/packages/mcp/fixtures/small-docs-manifest.fixture.json b/packages/mcp/fixtures/small-docs-manifest.fixture.json new file mode 100644 index 00000000..5242d31e --- /dev/null +++ b/packages/mcp/fixtures/small-docs-manifest.fixture.json @@ -0,0 +1,19 @@ +{ + "v": 1, + "docs": { + "getting-started": { + "id": "getting-started", + "name": "Getting Started", + "title": "Getting Started Guide", + "path": "docs/getting-started.mdx", + "content": "# Getting Started\n\nWelcome to the component library. This guide will help you get up and running.\n\n## Installation\n\n```bash\nnpm install my-component-library\n```\n\n## Usage\n\nImport components and use them in your application." + }, + "theming": { + "id": "theming", + "name": "Theming", + "title": "Theming and Customization", + "path": "docs/theming.mdx", + "content": "# Theming\n\nLearn how to customize the look and feel of components using our theming system.\n\n## Theme Provider\n\nWrap your app with the ThemeProvider." + } + } +} diff --git a/packages/mcp/src/index.test.ts b/packages/mcp/src/index.test.ts index c85b093d..27bc6161 100644 --- a/packages/mcp/src/index.test.ts +++ b/packages/mcp/src/index.test.ts @@ -3,6 +3,36 @@ 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' }; +import smallDocsManifestFixture from '../fixtures/small-docs-manifest.fixture.json' with { type: 'json' }; + +/** + * Creates a manifestProvider mock that returns component manifest for components.json + * and throws an error for docs.json (simulating no docs manifest available) + */ +function createManifestProviderMock() { + return vi.fn().mockImplementation((_request: Request, path: string) => { + if (path.includes('components.json')) { + return Promise.resolve(JSON.stringify(smallManifestFixture)); + } + // Simulate docs.json not found + return Promise.reject(new Error('Not found')); + }); +} + +/** + * Creates a manifestProvider mock that returns both component and docs manifests + */ +function createManifestProviderMockWithDocs() { + return vi.fn().mockImplementation((_request: Request, path: string) => { + if (path.includes('components.json')) { + return Promise.resolve(JSON.stringify(smallManifestFixture)); + } + if (path.includes('docs.json')) { + return Promise.resolve(JSON.stringify(smallDocsManifestFixture)); + } + return Promise.reject(new Error('Not found')); + }); +} describe('createStorybookMcpHandler', () => { let client: Client; @@ -63,8 +93,8 @@ describe('createStorybookMcpHandler', () => { title: 'List All Components', }), expect.objectContaining({ - name: 'get-component-documentation', - title: 'Get Documentation for Component', + name: 'get-documentation', + title: 'Get Documentation', }), ]), ); @@ -90,9 +120,7 @@ describe('createStorybookMcpHandler', () => { }); it('should use manifestProvider when calling list-all-components', async () => { - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(smallManifestFixture)); + const manifestProvider = createManifestProviderMock(); const handler = await createStorybookMcpHandler({ manifestProvider, @@ -117,9 +145,7 @@ describe('createStorybookMcpHandler', () => { it('should call onListAllComponents handler when tool is invoked', async () => { const onListAllComponents = vi.fn(); - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(smallManifestFixture)); + const manifestProvider = createManifestProviderMock(); const handler = await createStorybookMcpHandler({ manifestProvider, @@ -137,36 +163,34 @@ describe('createStorybookMcpHandler', () => { context: expect.objectContaining({ request: expect.any(Request), }), - manifest: smallManifestFixture, + manifests: { componentManifest: smallManifestFixture }, }); }); - it('should call onGetComponentDocumentation handler when tool is invoked', async () => { - const onGetComponentDocumentation = vi.fn(); - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(smallManifestFixture)); + it('should call onGetDocumentation handler when tool is invoked', async () => { + const onGetDocumentation = vi.fn(); + const manifestProvider = createManifestProviderMock(); const handler = await createStorybookMcpHandler({ manifestProvider, - onGetComponentDocumentation, + onGetDocumentation, }); await setupClient(handler); const result = await client.callTool({ - name: 'get-component-documentation', + name: 'get-documentation', arguments: { - componentId: 'button', + id: 'button', }, }); - expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); - expect(onGetComponentDocumentation).toHaveBeenCalledWith({ + expect(onGetDocumentation).toHaveBeenCalledTimes(1); + expect(onGetDocumentation).toHaveBeenCalledWith({ context: expect.objectContaining({ request: expect.any(Request), }), - input: { componentId: 'button' }, - foundComponent: expect.objectContaining({ + input: { id: 'button' }, + foundDocumentation: expect.objectContaining({ id: 'button', name: 'Button', }), @@ -195,38 +219,130 @@ describe('createStorybookMcpHandler', () => { }); }); - it('should handle non-existent component ID in get-component-documentation', async () => { - const onGetComponentDocumentation = vi.fn(); - const manifestProvider = vi - .fn() - .mockResolvedValue(JSON.stringify(smallManifestFixture)); + it('should handle non-existent component ID in get-documentation', async () => { + const onGetDocumentation = vi.fn(); + const manifestProvider = createManifestProviderMock(); const handler = await createStorybookMcpHandler({ manifestProvider, - onGetComponentDocumentation, + onGetDocumentation, }); await setupClient(handler); const result = await client.callTool({ - name: 'get-component-documentation', + name: 'get-documentation', arguments: { - componentId: 'non-existent', + id: 'non-existent', }, }); // Should still call the handler - expect(onGetComponentDocumentation).toHaveBeenCalledTimes(1); - expect(onGetComponentDocumentation).toHaveBeenCalledWith({ + expect(onGetDocumentation).toHaveBeenCalledTimes(1); + expect(onGetDocumentation).toHaveBeenCalledWith({ context: expect.objectContaining({ request: expect.any(Request), }), - input: { componentId: 'non-existent' }, + input: { id: 'non-existent' }, }); expect(result.content).toHaveLength(1); expect((result.content as any)[0]).toMatchObject({ type: 'text', - text: expect.stringContaining('Component not found'), + text: expect.stringContaining('not found'), + }); + }); + + describe('with docs manifest', () => { + it('should return docs entries in list-all-components when docs manifest is available', async () => { + const manifestProvider = createManifestProviderMockWithDocs(); + + const handler = await createStorybookMcpHandler({ + manifestProvider, + }); + await setupClient(handler); + + const result = await client.callTool({ + name: 'list-all-components', + arguments: {}, + }); + + expect(manifestProvider).toHaveBeenCalledWith( + expect.any(Request), + './manifests/components.json', + ); + expect(manifestProvider).toHaveBeenCalledWith( + expect.any(Request), + './manifests/docs.json', + ); + + expect(result.content).toHaveLength(1); + const text = (result.content as any)[0].text; + expect(text).toContain('# Components'); + expect(text).toContain('# Docs'); + expect(text).toContain('Getting Started'); + }); + + it('should include docs manifest in onListAllComponents handler', async () => { + const onListAllComponents = vi.fn(); + const manifestProvider = createManifestProviderMockWithDocs(); + + const handler = await createStorybookMcpHandler({ + manifestProvider, + onListAllComponents, + }); + await setupClient(handler); + + await client.callTool({ + name: 'list-all-components', + arguments: {}, + }); + + expect(onListAllComponents).toHaveBeenCalledTimes(1); + expect(onListAllComponents).toHaveBeenCalledWith({ + context: expect.objectContaining({ + request: expect.any(Request), + }), + manifests: { + componentManifest: smallManifestFixture, + docsManifest: smallDocsManifestFixture, + }, + }); + }); + + it('should return documentation for a docs entry', async () => { + const onGetDocumentation = vi.fn(); + const manifestProvider = createManifestProviderMockWithDocs(); + + const handler = await createStorybookMcpHandler({ + manifestProvider, + onGetDocumentation, + }); + await setupClient(handler); + + const result = await client.callTool({ + name: 'get-documentation', + arguments: { + id: 'getting-started', + }, + }); + + expect(onGetDocumentation).toHaveBeenCalledTimes(1); + expect(onGetDocumentation).toHaveBeenCalledWith({ + context: expect.objectContaining({ + request: expect.any(Request), + }), + input: { id: 'getting-started' }, + foundDocumentation: expect.objectContaining({ + id: 'getting-started', + name: 'Getting Started', + }), + }); + + expect(result.content).toHaveLength(1); + expect((result.content as any)[0]).toMatchObject({ + type: 'text', + text: expect.stringContaining('Getting Started'), + }); }); }); }); diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 9923e58d..114b734a 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -3,7 +3,7 @@ import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot'; import { HttpTransport } from '@tmcp/transport-http'; import pkgJson from '../package.json' with { type: 'json' }; import { addListAllComponentsTool } from './tools/list-all-components.ts'; -import { addGetComponentDocumentationTool } from './tools/get-component-documentation.ts'; +import { addGetDocumentationTool } from './tools/get-documentation.ts'; import type { StorybookContext } from './types.ts'; // Export tools for reuse by addon-mcp @@ -12,9 +12,9 @@ export { LIST_TOOL_NAME, } from './tools/list-all-components.ts'; export { - addGetComponentDocumentationTool, + addGetDocumentationTool as addGetComponentDocumentationTool, GET_TOOL_NAME, -} from './tools/get-component-documentation.ts'; +} from './tools/get-documentation.ts'; // Export manifest constants export { @@ -94,7 +94,7 @@ export const createStorybookMcpHandler = async ( } await addListAllComponentsTool(server); - await addGetComponentDocumentationTool(server); + await addGetDocumentationTool(server); const transport = new HttpTransport(server, { path: null }); @@ -105,9 +105,8 @@ export const createStorybookMcpHandler = async ( manifestProvider: context?.manifestProvider ?? options.manifestProvider, onListAllComponents: context?.onListAllComponents ?? options.onListAllComponents, - onGetComponentDocumentation: - context?.onGetComponentDocumentation ?? - options.onGetComponentDocumentation, + onGetDocumentation: + context?.onGetDocumentation ?? options.onGetDocumentation, }); }) as Handler; }; diff --git a/packages/mcp/src/tools/get-component-documentation.test.ts b/packages/mcp/src/tools/get-component-documentation.test.ts index c221393f..c7deb8bc 100644 --- a/packages/mcp/src/tools/get-component-documentation.test.ts +++ b/packages/mcp/src/tools/get-component-documentation.test.ts @@ -1,17 +1,14 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { McpServer } from 'tmcp'; import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot'; -import { - addGetComponentDocumentationTool, - GET_TOOL_NAME, -} from './get-component-documentation.ts'; +import { addGetDocumentationTool, GET_TOOL_NAME } from './get-documentation.ts'; import type { StorybookContext } from '../types.ts'; import smallManifestFixture from '../../fixtures/small-manifest.fixture.json' with { type: 'json' }; import * as getManifest from '../utils/get-manifest.ts'; -describe('getComponentDocumentationTool', () => { +describe('getDocumentationTool', () => { let server: McpServer; - let getManifestSpy: any; + let getManifestsSpy: any; beforeEach(async () => { const adapter = new ValibotJsonSchemaAdapter(); @@ -43,11 +40,13 @@ describe('getComponentDocumentationTool', () => { }, { sessionId: 'test-session' }, ); - await addGetComponentDocumentationTool(server); + await addGetDocumentationTool(server); - // Mock getManifest to return the fixture - getManifestSpy = vi.spyOn(getManifest, 'getManifest'); - getManifestSpy.mockResolvedValue(smallManifestFixture); + // Mock getManifests to return the fixture + getManifestsSpy = vi.spyOn(getManifest, 'getManifests'); + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + }); }); it('should return formatted documentation for a single component', async () => { @@ -58,7 +57,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'button', + id: 'button', }, }, }; @@ -100,7 +99,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'nonexistent', + id: 'nonexistent', }, }, }; @@ -114,7 +113,7 @@ describe('getComponentDocumentationTool', () => { { "content": [ { - "text": "Component not found: "nonexistent". Use the list-all-components tool to see available components.", + "text": "Component or Docs Entry not found: "nonexistent". Use the list-all-components tool to see available components and documentation entries.", "type": "text", }, ], @@ -124,7 +123,7 @@ describe('getComponentDocumentationTool', () => { }); it('should handle fetch errors gracefully', async () => { - getManifestSpy.mockRejectedValue( + getManifestsSpy.mockRejectedValue( new getManifest.ManifestGetError( 'Failed to fetch manifest: 404 Not Found', 'https://example.com/manifest.json', @@ -138,7 +137,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'button', + id: 'button', }, }, }; @@ -161,7 +160,7 @@ describe('getComponentDocumentationTool', () => { `); }); - it('should call onGetComponentDocumentation handler when provided', async () => { + it('should call onGetDocumentation handler when provided', async () => { const handler = vi.fn(); const request = { @@ -171,7 +170,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'button', + id: 'button', }, }, }; @@ -181,7 +180,7 @@ describe('getComponentDocumentationTool', () => { await server.receive(request, { custom: { request: mockHttpRequest, - onGetComponentDocumentation: handler, + onGetDocumentation: handler, }, }); @@ -189,10 +188,13 @@ describe('getComponentDocumentationTool', () => { expect(handler).toHaveBeenCalledWith({ context: expect.objectContaining({ request: mockHttpRequest, - onGetComponentDocumentation: handler, + onGetDocumentation: handler, + }), + input: { id: 'button' }, + foundDocumentation: expect.objectContaining({ + id: 'button', + name: 'Button', }), - input: { componentId: 'button' }, - foundComponent: expect.objectContaining({ id: 'button', name: 'Button' }), }); }); @@ -232,7 +234,9 @@ describe('getComponentDocumentationTool', () => { }, }; - getManifestSpy.mockResolvedValue(manifestWithReactDocgen); + getManifestsSpy.mockResolvedValue({ + componentManifest: manifestWithReactDocgen, + }); const request = { jsonrpc: '2.0' as const, @@ -241,7 +245,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'button', + id: 'button', }, }, }; @@ -290,7 +294,7 @@ describe('getComponentDocumentationTool', () => { params: { name: GET_TOOL_NAME, arguments: { - componentId: 'button', + id: 'button', }, }, }; diff --git a/packages/mcp/src/tools/get-component-documentation.ts b/packages/mcp/src/tools/get-component-documentation.ts deleted file mode 100644 index 198c5769..00000000 --- a/packages/mcp/src/tools/get-component-documentation.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as v from 'valibot'; -import type { McpServer } from 'tmcp'; -import type { StorybookContext } from '../types.ts'; -import { getManifest, errorToMCPContent } from '../utils/get-manifest.ts'; -import { formatComponentManifest } from '../utils/format-manifest.ts'; - -export const GET_TOOL_NAME = 'get-component-documentation'; - -const GetComponentDocumentationInput = v.object({ - componentId: v.string(), -}); - -type GetComponentDocumentationInput = v.InferOutput< - typeof GetComponentDocumentationInput ->; - -export async function addGetComponentDocumentationTool( - server: McpServer, - enabled?: Parameters['tool']>[0]['enabled'], -) { - server.tool( - { - name: GET_TOOL_NAME, - title: 'Get Documentation for Component', - description: 'Get detailed documentation for a specific UI component', - schema: GetComponentDocumentationInput, - enabled, - }, - async (input: GetComponentDocumentationInput) => { - try { - const manifest = await getManifest( - server.ctx.custom?.request, - server.ctx.custom?.manifestProvider, - ); - - const component = manifest.components[input.componentId]; - - if (!component) { - await server.ctx.custom?.onGetComponentDocumentation?.({ - context: server.ctx.custom, - input: { componentId: input.componentId }, - }); - - return { - content: [ - { - type: 'text' as const, - text: `Component not found: "${input.componentId}". Use the list-all-components tool to see available components.`, - }, - ], - isError: true, - }; - } - - await server.ctx.custom?.onGetComponentDocumentation?.({ - context: server.ctx.custom, - input: { componentId: input.componentId }, - foundComponent: component, - }); - - const format = server.ctx.custom?.format ?? 'markdown'; - return { - content: [ - { - type: 'text' as const, - text: formatComponentManifest(component, format), - }, - ], - }; - } catch (error) { - return errorToMCPContent(error); - } - }, - ); -} diff --git a/packages/mcp/src/tools/get-documentation.test.ts b/packages/mcp/src/tools/get-documentation.test.ts new file mode 100644 index 00000000..2bf1b88e --- /dev/null +++ b/packages/mcp/src/tools/get-documentation.test.ts @@ -0,0 +1,508 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { McpServer } from 'tmcp'; +import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot'; +import { addGetDocumentationTool, GET_TOOL_NAME } from './get-documentation.ts'; +import type { StorybookContext } from '../types.ts'; +import smallManifestFixture from '../../fixtures/small-manifest.fixture.json' with { type: 'json' }; +import smallDocsManifestFixture from '../../fixtures/small-docs-manifest.fixture.json' with { type: 'json' }; +import * as getManifest from '../utils/get-manifest.ts'; + +describe('getDocumentationTool', () => { + let server: McpServer; + let getManifestsSpy: any; + + beforeEach(async () => { + const adapter = new ValibotJsonSchemaAdapter(); + server = new McpServer( + { + name: 'test-server', + version: '1.0.0', + description: 'Test server for get tool', + }, + { + adapter, + capabilities: { + tools: { listChanged: true }, + }, + }, + ).withContext(); + + // initialize test session + await server.receive( + { + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2025-06-18', + capabilities: {}, + clientInfo: { name: 'test', version: '1.0.0' }, + }, + }, + { sessionId: 'test-session' }, + ); + await addGetDocumentationTool(server); + + // Mock getManifests to return the fixture + getManifestsSpy = vi.spyOn(getManifest, 'getManifests'); + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + }); + }); + + it('should return formatted documentation for a single component', async () => { + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "# Button + + ID: button + + ## Stories + + ### Primary + + The primary button variant. + + \`\`\` + const Primary = () => + \`\`\`", + "type": "text", + }, + ], + } + `); + }); + + it('should return an error when a component is not found', async () => { + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'nonexistent', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "Component or Docs Entry not found: "nonexistent". Use the list-all-components tool to see available components and documentation entries.", + "type": "text", + }, + ], + "isError": true, + } + `); + }); + + it('should handle fetch errors gracefully', async () => { + getManifestsSpy.mockRejectedValue( + new getManifest.ManifestGetError( + 'Failed to fetch manifest: 404 Not Found', + 'https://example.com/manifest.json', + ), + ); + + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "Error getting manifest: Failed to fetch manifest: 404 Not Found", + "type": "text", + }, + ], + "isError": true, + } + `); + }); + + it('should call onGetDocumentation handler when provided', async () => { + const handler = vi.fn(); + + const request = { + jsonrpc: '2.0' as const, + id: 2, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + // Pass the handler and request in the context for this specific request + await server.receive(request, { + custom: { + request: mockHttpRequest, + onGetDocumentation: handler, + }, + }); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + context: expect.objectContaining({ + request: mockHttpRequest, + onGetDocumentation: handler, + }), + input: { id: 'button' }, + foundDocumentation: expect.objectContaining({ + id: 'button', + name: 'Button', + }), + }); + }); + + it('should include props section when reactDocgen is present', async () => { + const manifestWithReactDocgen = { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + description: 'A button component', + reactDocgen: { + props: { + variant: { + description: 'Button 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: 'Disable the button', + required: false, + tsType: { + name: 'boolean', + }, + }, + }, + }, + }, + }, + }; + + getManifestsSpy.mockResolvedValue({ + componentManifest: manifestWithReactDocgen, + }); + + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "# Button + + ID: button + + A button component + + ## Props + + \`\`\` + export type Props = { + /** + Button style variant + */ + variant?: "primary" | "secondary" = "primary"; + /** + Disable the button + */ + disabled?: boolean; + } + \`\`\`", + "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: { + id: '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": [ + { + "text": " + button + Button + + Primary + + The primary button variant. + + + const Primary = () => + + + ", + "type": "text", + }, + ], + } + `); + }); + + describe('docs manifest entries', () => { + beforeEach(() => { + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + docsManifest: smallDocsManifestFixture, + }); + }); + + it('should return formatted documentation for a docs entry', async () => { + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'getting-started', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "text": "# Getting Started Guide + + # Getting Started + + Welcome to the component library. This guide will help you get up and running. + + ## Installation + + \`\`\`bash + npm install my-component-library + \`\`\` + + ## Usage + + Import components and use them in your application.", + "type": "text", + }, + ], + } + `); + }); + + it('should return component documentation when id matches both component and docs entry', async () => { + // When an ID exists in both manifests, prefer component documentation + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + docsManifest: { + v: 1, + docs: { + button: { + id: 'button', + name: 'Button Docs', + title: 'Button Documentation', + path: 'docs/button.mdx', + content: 'This is the button docs entry', + }, + }, + }, + }); + + const request = { + jsonrpc: '2.0' as const, + id: 1, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'button', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + const response = await server.receive(request, { + custom: { request: mockHttpRequest }, + }); + + // Should return the component, not the docs entry + expect((response.result as any).content[0].text).toContain('## Stories'); + expect((response.result as any).content[0].text).toContain('Primary'); + }); + + it('should format docs entry 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: { + id: 'getting-started', + }, + }, + }; + + 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": " + Getting Started Guide + + # Getting Started + + Welcome to the component library. This guide will help you get up and running. + + ## Installation + + \`\`\`bash + npm install my-component-library + \`\`\` + + ## Usage + + Import components and use them in your application. + + ", + "type": "text", + }, + ], + } + `); + }); + + it('should call onGetDocumentation handler with docs entry when found', async () => { + const handler = vi.fn(); + + const request = { + jsonrpc: '2.0' as const, + id: 2, + method: 'tools/call', + params: { + name: GET_TOOL_NAME, + arguments: { + id: 'getting-started', + }, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + await server.receive(request, { + custom: { + request: mockHttpRequest, + onGetDocumentation: handler, + }, + }); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + context: expect.objectContaining({ + request: mockHttpRequest, + onGetDocumentation: handler, + }), + input: { id: 'getting-started' }, + foundDocumentation: expect.objectContaining({ + id: 'getting-started', + name: 'Getting Started', + }), + }); + }); + }); +}); diff --git a/packages/mcp/src/tools/get-documentation.ts b/packages/mcp/src/tools/get-documentation.ts new file mode 100644 index 00000000..3f8687f1 --- /dev/null +++ b/packages/mcp/src/tools/get-documentation.ts @@ -0,0 +1,84 @@ +import * as v from 'valibot'; +import type { McpServer } from 'tmcp'; +import type { ComponentManifest, Doc, StorybookContext } from '../types.ts'; +import { getManifests, errorToMCPContent } from '../utils/get-manifest.ts'; +import { + formatComponentManifest, + formatDocsManifest, +} from '../utils/format-manifest.ts'; +import { LIST_TOOL_NAME } from './list-all-components.ts'; + +export const GET_TOOL_NAME = 'get-documentation'; + +const GetDocumentationInput = v.object({ + id: v.string(), +}); + +export async function addGetDocumentationTool( + server: McpServer, + enabled?: Parameters['tool']>[0]['enabled'], +) { + server.tool( + { + name: GET_TOOL_NAME, + title: 'Get Documentation', + description: + 'Get detailed documentation for a specific UI component or docs entry', + schema: GetDocumentationInput, + enabled, + }, + async (input: v.InferOutput) => { + try { + const { componentManifest, docsManifest } = await getManifests( + server.ctx.custom?.request, + server.ctx.custom?.manifestProvider, + ); + + const component = componentManifest.components[input.id]; + const docsEntry = docsManifest?.docs[input.id]; + + if (!component && !docsEntry) { + await server.ctx.custom?.onGetDocumentation?.({ + context: server.ctx.custom, + input, + }); + + return { + content: [ + { + type: 'text' as const, + text: `Component or Docs Entry not found: "${input.id}". Use the ${LIST_TOOL_NAME} tool to see available components and documentation entries.`, + }, + ], + isError: true, + }; + } + + const documentation = component ?? docsEntry; + + await server.ctx.custom?.onGetDocumentation?.({ + context: server.ctx.custom, + input, + foundDocumentation: documentation, + }); + + const format = server.ctx.custom?.format ?? 'markdown'; + return { + content: [ + { + type: 'text' as const, + text: component + ? formatComponentManifest( + documentation as ComponentManifest, + format, + ) + : formatDocsManifest(documentation as Doc, format), + }, + ], + }; + } catch (error) { + return errorToMCPContent(error); + } + }, + ); +} diff --git a/packages/mcp/src/tools/list-all-components.test.ts b/packages/mcp/src/tools/list-all-components.test.ts index 22ab0e48..76a89c79 100644 --- a/packages/mcp/src/tools/list-all-components.test.ts +++ b/packages/mcp/src/tools/list-all-components.test.ts @@ -7,11 +7,12 @@ import { } from './list-all-components.ts'; import type { StorybookContext } from '../types.ts'; import smallManifestFixture from '../../fixtures/small-manifest.fixture.json' with { type: 'json' }; +import smallDocsManifestFixture from '../../fixtures/small-docs-manifest.fixture.json' with { type: 'json' }; import * as getManifest from '../utils/get-manifest.ts'; describe('listAllComponentsTool', () => { let server: McpServer; - let getManifestSpy: any; + let getManifestsSpy: any; beforeEach(async () => { const adapter = new ValibotJsonSchemaAdapter(); @@ -45,9 +46,11 @@ describe('listAllComponentsTool', () => { ); await addListAllComponentsTool(server); - // Mock getManifest to return the fixture - getManifestSpy = vi.spyOn(getManifest, 'getManifest'); - getManifestSpy.mockResolvedValue(smallManifestFixture); + // Mock getManifests to return the fixture + getManifestsSpy = vi.spyOn(getManifest, 'getManifests'); + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + }); }); it('should return a list of all components', async () => { @@ -83,7 +86,7 @@ describe('listAllComponentsTool', () => { }); it('should handle fetch errors gracefully', async () => { - getManifestSpy.mockRejectedValue( + getManifestsSpy.mockRejectedValue( new getManifest.ManifestGetError( 'Failed to fetch manifest: 404 Not Found', 'https://example.com/manifest.json', @@ -119,7 +122,7 @@ describe('listAllComponentsTool', () => { }); it('should handle unexpected errors gracefully', async () => { - getManifestSpy.mockRejectedValue(new Error('Network timeout')); + getManifestsSpy.mockRejectedValue(new Error('Network timeout')); const request = { jsonrpc: '2.0' as const, @@ -174,7 +177,9 @@ describe('listAllComponentsTool', () => { request: mockHttpRequest, onListAllComponents: handler, }), - manifest: smallManifestFixture, + manifests: { + componentManifest: smallManifestFixture, + }, }); }); @@ -227,4 +232,147 @@ describe('listAllComponentsTool', () => { } `); }); + + describe('with docs manifest', () => { + beforeEach(() => { + getManifestsSpy.mockResolvedValue({ + componentManifest: smallManifestFixture, + docsManifest: smallDocsManifestFixture, + }); + }); + + it('should return both components and docs entries', 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 }, + }); + + expect(response.result).toMatchInlineSnapshot(` + { + "content": [ + { + "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. + + # Docs + + - Getting Started Guide (getting-started): # Getting Started Welcome to the component library. This guide will help you get up and ru... + - Theming and Customization (theming): # Theming Learn how to customize the look and feel of components using our theming system....", + "type": "text", + }, + ], + } + `); + }); + + it('should format both components and docs entries 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. + + + + + + getting-started + Getting Started Guide + + # Getting Started Welcome to the component library. This guide will help you get up and ru... + + + + theming + Theming and Customization + + # Theming Learn how to customize the look and feel of components using our theming system.... + + + ", + "type": "text", + }, + ], + } + `); + }); + + it('should include docs manifest in onListAllComponents handler call', async () => { + const handler = vi.fn(); + + const request = { + jsonrpc: '2.0' as const, + id: 2, + method: 'tools/call', + params: { + name: LIST_TOOL_NAME, + arguments: {}, + }, + }; + + const mockHttpRequest = new Request('https://example.com/mcp'); + await server.receive(request, { + custom: { request: mockHttpRequest, onListAllComponents: handler }, + }); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + context: expect.objectContaining({ + request: mockHttpRequest, + onListAllComponents: handler, + }), + manifests: { + componentManifest: smallManifestFixture, + docsManifest: smallDocsManifestFixture, + }, + }); + }); + }); }); diff --git a/packages/mcp/src/tools/list-all-components.ts b/packages/mcp/src/tools/list-all-components.ts index faa48d85..2deac3cd 100644 --- a/packages/mcp/src/tools/list-all-components.ts +++ b/packages/mcp/src/tools/list-all-components.ts @@ -1,7 +1,7 @@ import type { McpServer } from 'tmcp'; import type { StorybookContext } from '../types.ts'; -import { getManifest, errorToMCPContent } from '../utils/get-manifest.ts'; -import { formatComponentManifestMapToList } from '../utils/format-manifest.ts'; +import { getManifests, errorToMCPContent } from '../utils/get-manifest.ts'; +import { formatManifestsToLists as formatManifestsToLists } from '../utils/format-manifest.ts'; export const LIST_TOOL_NAME = 'list-all-components'; @@ -19,27 +19,24 @@ export async function addListAllComponentsTool( }, async () => { try { - const manifest = await getManifest( + const manifests = await getManifests( server.ctx.custom?.request, server.ctx.custom?.manifestProvider, ); const format = server.ctx.custom?.format ?? 'markdown'; - const componentList = formatComponentManifestMapToList( - manifest, - format, - ); + const lists = formatManifestsToLists(manifests, format); await server.ctx.custom?.onListAllComponents?.({ context: server.ctx.custom, - manifest, + manifests, }); return { content: [ { type: 'text', - text: componentList, + text: lists, }, ], }; diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index a5f12806..82b82630 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -39,16 +39,16 @@ export type StorybookContext = { */ onListAllComponents?: (params: { context: StorybookContext; - manifest: ComponentManifestMap; + manifests: AllManifests; }) => void | Promise; /** * Optional handler called when get-component-documentation tool is invoked. * Receives the context, input parameters, and the found component (if any). */ - onGetComponentDocumentation?: (params: { + onGetDocumentation?: (params: { context: StorybookContext; - input: { componentId: string }; - foundComponent?: ComponentManifest; + input: { id: string }; + foundDocumentation?: ComponentManifest | Doc; }) => void | Promise; }; @@ -83,6 +83,7 @@ const Doc = v.object({ content: v.string(), error: v.optional(Error), }); +export type Doc = v.InferOutput; export const ComponentManifest = v.object({ ...BaseManifest.entries, @@ -112,3 +113,8 @@ export const DocsManifestMap = v.object({ docs: v.record(v.string(), Doc), }); export type DocsManifestMap = v.InferOutput; + +export type AllManifests = { + componentManifest: ComponentManifestMap; + docsManifest?: DocsManifestMap; +}; diff --git a/packages/mcp/src/utils/format-manifest.test.ts b/packages/mcp/src/utils/format-manifest.test.ts index df983e19..c28e1533 100644 --- a/packages/mcp/src/utils/format-manifest.test.ts +++ b/packages/mcp/src/utils/format-manifest.test.ts @@ -1,9 +1,13 @@ import { describe, it, expect } from 'vitest'; import { formatComponentManifest, - formatComponentManifestMapToList, + formatManifestsToLists, } from './format-manifest'; -import type { ComponentManifest, ComponentManifestMap } from '../types'; +import type { + AllManifests, + ComponentManifest, + ComponentManifestMap, +} from '../types'; describe('formatComponentManifest', () => { const manifest: ComponentManifest = { @@ -32,32 +36,34 @@ describe('formatComponentManifest', () => { }); }); -describe('formatComponentManifestMapToList', () => { - const manifest: ComponentManifestMap = { - v: 1, - components: { - button: { - id: 'button', - name: 'Button', - path: 'src/components/Button.tsx', +describe('formatManifestsToLists', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, }, }, }; it('should use markdown formatter by default', () => { - const result = formatComponentManifestMapToList(manifest); + const result = formatManifestsToLists(manifests); expect(result).toContain('# Components'); expect(result).toContain('- Button (button)'); }); it('should use markdown formatter when format is "markdown"', () => { - const result = formatComponentManifestMapToList(manifest, 'markdown'); + const result = formatManifestsToLists(manifests, 'markdown'); expect(result).toContain('# Components'); expect(result).toContain('- Button (button)'); }); it('should use xml formatter when format is "xml"', () => { - const result = formatComponentManifestMapToList(manifest, 'xml'); + const result = formatManifestsToLists(manifests, '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 4f60ce84..6a7e33ff 100644 --- a/packages/mcp/src/utils/format-manifest.ts +++ b/packages/mcp/src/utils/format-manifest.ts @@ -1,6 +1,9 @@ import type { + AllManifests, ComponentManifest, ComponentManifestMap, + Doc, + DocsManifestMap, OutputFormat, } from '../types.ts'; import type { ManifestFormatter } from './manifest-formatter/types.ts'; @@ -14,9 +17,6 @@ const formatters: Record = { /** * 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, @@ -26,14 +26,21 @@ export function formatComponentManifest( } /** - * 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 + * Format a single docs manifest. */ -export function formatComponentManifestMapToList( - manifest: ComponentManifestMap, +export function formatDocsManifest( + doc: Doc, format: OutputFormat = 'markdown', ): string { - return formatters[format].formatComponentManifestMapToList(manifest); + return formatters[format].formatDocsManifest(doc); +} + +/** + * Format a component manifest and optionally a docs manifest into lists. + */ +export function formatManifestsToLists( + manifests: AllManifests, + format: OutputFormat = 'markdown', +): string { + return formatters[format].formatManifestsToLists(manifests); } diff --git a/packages/mcp/src/utils/get-manifest.test.ts b/packages/mcp/src/utils/get-manifest.test.ts index bf909c00..b74cada3 100644 --- a/packages/mcp/src/utils/get-manifest.test.ts +++ b/packages/mcp/src/utils/get-manifest.test.ts @@ -196,11 +196,9 @@ describe('getManifest', () => { }); const request = createMockRequest('https://example.com/mcp'); - await expect( - getManifests(request), - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[ManifestGetError: Failed to parse component manifest: Invalid key: Expected "v" but received undefined]`, - ); + await expect(getManifests(request)).rejects + .toThrowErrorMatchingInlineSnapshot(`[ManifestGetError: Failed to parse component manifest: +Invalid key: Expected "v" but received undefined]`); }); it('should throw ManifestGetError when components object is empty', async () => { diff --git a/packages/mcp/src/utils/get-manifest.ts b/packages/mcp/src/utils/get-manifest.ts index 86b3b2c3..aae45de6 100644 --- a/packages/mcp/src/utils/get-manifest.ts +++ b/packages/mcp/src/utils/get-manifest.ts @@ -1,4 +1,8 @@ -import { ComponentManifestMap, DocsManifestMap } from '../types.ts'; +import { + ComponentManifestMap, + DocsManifestMap, + type AllManifests, +} from '../types.ts'; import * as v from 'valibot'; /** @@ -103,10 +107,7 @@ export async function getManifests( request: Request | undefined, path: string, ) => Promise, -): Promise<{ - componentManifest: ComponentManifestMap; - docsManifest?: DocsManifestMap; -}> { +): Promise { const provider = manifestProvider ?? defaultManifestProvider; // Fetch both component and docs manifests in parallel 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 eb3a41dd..fbdb979a 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 @@ -426,7 +426,7 @@ export type Props = { \`\`\`" `; -exports[`MarkdownFormatter - formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` +exports[`MarkdownFormatter - formatManifestsToLists > formats the full manifest fixture 1`] = ` "# Components - Button (button): A versatile button component for user interactions 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 index 76fcdc4f..2ea00b52 100644 --- a/packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap +++ b/packages/mcp/src/utils/manifest-formatter/__snapshots__/xml.test.ts.snap @@ -533,7 +533,7 @@ Callback function when the input value changes " `; -exports[`XmlFormatter - formatComponentManifestMapToList > formats the full manifest fixture 1`] = ` +exports[`XmlFormatter - formatManifestsToLists > formats the full manifest fixture 1`] = ` " button diff --git a/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.test.ts b/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.test.ts new file mode 100644 index 00000000..dada8638 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.test.ts @@ -0,0 +1,201 @@ +import { describe, it, expect } from 'vitest'; +import { extractDocsSummary } from './extract-docs-summary.ts'; + +describe('extractDocsSummary', () => { + describe('import statement removal', () => { + it('should remove single import statements', () => { + const content = `import { Button } from './Button'; + +This is the actual content.`; + expect(extractDocsSummary(content)).toBe('This is the actual content.'); + }); + + it('should remove multiple import statements', () => { + const content = `import { Button } from './Button'; +import { Meta, Story } from '@storybook/blocks'; +import React from 'react'; + +This is the content after imports.`; + expect(extractDocsSummary(content)).toBe( + 'This is the content after imports.', + ); + }); + + it('should remove default imports', () => { + const content = `import Button from './Button'; + +Button documentation here.`; + expect(extractDocsSummary(content)).toBe('Button documentation here.'); + }); + + it('should remove namespace imports', () => { + const content = `import * as Icons from './icons'; + +Icons documentation.`; + expect(extractDocsSummary(content)).toBe('Icons documentation.'); + }); + + it('should remove side-effect imports', () => { + const content = `import './styles.css'; + +Styled content.`; + expect(extractDocsSummary(content)).toBe('Styled content.'); + }); + }); + + describe('expression removal', () => { + it('should remove simple expressions', () => { + const content = `Some text {expression} more text.`; + expect(extractDocsSummary(content)).toBe('Some text more text.'); + }); + + it('should remove JSX comments', () => { + const content = `Some text {/* comment */} more text.`; + expect(extractDocsSummary(content)).toBe('Some text more text.'); + }); + + it('should remove nested expressions', () => { + const content = `Text {outer {inner} value} end.`; + expect(extractDocsSummary(content)).toBe('Text end.'); + }); + + it('should remove complex expressions with function calls', () => { + const content = `Count: {items.length} items.`; + expect(extractDocsSummary(content)).toBe('Count: items.'); + }); + + it('should remove expressions with template literals', () => { + const content = 'Text {`template ${value}`} end.'; + expect(extractDocsSummary(content)).toBe('Text end.'); + }); + }); + + describe('JSX/HTML element text extraction', () => { + it('should extract text from simple elements', () => { + const content = `

This is a paragraph.

`; + expect(extractDocsSummary(content)).toBe('This is a paragraph.'); + }); + + it('should extract text from nested elements', () => { + const content = `

Nested bold text.

`; + expect(extractDocsSummary(content)).toBe('Nested bold text.'); + }); + + it('should remove self-closing tags', () => { + const content = `Text before
text after end.`; + expect(extractDocsSummary(content)).toBe('Text before text after end.'); + }); + + it('should handle PascalCase component names', () => { + const content = `Component content`; + expect(extractDocsSummary(content)).toBe('Component content'); + }); + + it('should handle elements with attributes', () => { + const content = `
Content here
`; + expect(extractDocsSummary(content)).toBe('Content here'); + }); + + it('should handle multiple sibling elements', () => { + const content = `

First paragraph.

Second paragraph.

`; + expect(extractDocsSummary(content)).toBe( + 'First paragraph. Second paragraph.', + ); + }); + }); + + describe('combined scenarios', () => { + it('should handle typical MDX file content', () => { + const content = `import { Button } from './Button'; +import { Meta, Story } from '@storybook/blocks'; + + + +# Button Component + +The Button component is used for user interactions. + +
+ + + +
`; + const result = extractDocsSummary(content); + expect(result).toBe( + '# Button Component The Button component is used for user interactions. Click me', + ); + }); + + it('should handle MDX with expressions and components', () => { + const content = `
+ Welcome to {appName}! + +

Inner text content.

+
+
`; + expect(extractDocsSummary(content)).toBe( + 'Welcome to ! Inner text content.', + ); + }); + + it('should handle empty content', () => { + expect(extractDocsSummary('')).toBeUndefined(); + }); + + it('should handle content with only imports', () => { + const content = `import { Button } from './Button'; +import { Meta } from '@storybook/blocks';`; + expect(extractDocsSummary(content)).toBeUndefined(); + }); + + it('should handle content with only expressions and tags', () => { + const content = `{expression} `; + expect(extractDocsSummary(content)).toBeUndefined(); + }); + }); + + describe('truncation', () => { + it('should truncate content longer than 90 characters', () => { + const content = + 'This is a very long description that exceeds the maximum summary length limit and should be truncated with an ellipsis.'; + const result = extractDocsSummary(content); + expect(result).toBe( + 'This is a very long description that exceeds the maximum summary length limit and should b...', + ); + expect(result?.length).toBe(93); // 90 chars + '...' + }); + + it('should not truncate content at exactly 90 characters', () => { + const content = 'A'.repeat(90); + expect(extractDocsSummary(content)).toBe(content); + }); + + it('should not truncate content shorter than 90 characters', () => { + const content = 'Short description.'; + expect(extractDocsSummary(content)).toBe(content); + }); + }); + + describe('whitespace handling', () => { + it('should collapse multiple spaces', () => { + const content = `Text with multiple spaces.`; + expect(extractDocsSummary(content)).toBe('Text with multiple spaces.'); + }); + + it('should collapse newlines into spaces', () => { + const content = `Line one. +Line two. +Line three.`; + expect(extractDocsSummary(content)).toBe( + 'Line one. Line two. Line three.', + ); + }); + + it('should trim leading and trailing whitespace', () => { + const content = ` + Trimmed content. + `; + expect(extractDocsSummary(content)).toBe('Trimmed content.'); + }); + }); +}); diff --git a/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.ts b/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.ts new file mode 100644 index 00000000..34fd2c29 --- /dev/null +++ b/packages/mcp/src/utils/manifest-formatter/extract-docs-summary.ts @@ -0,0 +1,61 @@ +import { MAX_SUMMARY_LENGTH } from './types.ts'; + +/** + * Extracts a summary from MDX content. + * The summary is created by: + * 1. Removing import statements + * 2. Removing JSX/MDX expressions + * 3. Extracting only text content from JSX/HTML elements + * 4. Truncating to MAX_SUMMARY_LENGTH characters if needed + * + * @param content - The MDX content string + * @returns A summary string, or undefined if no meaningful text content is found + */ +export function extractDocsSummary(content: string): string | undefined { + let result = content; + + // Step 1: Remove import statements (can be single or multi-line) + // Matches: import ... from '...' or import '...' + result = result.replace( + /^\s*import\s+(?:[\s\S]*?from\s+)?['"][^'"]+['"];?\s*$/gm, + '', + ); + + // Step 2: Remove JSX/MDX expressions like {expression} or {/* comments */} + // Handle nested braces by iteratively removing innermost expressions + let prevResult = ''; + while (prevResult !== result) { + prevResult = result; + result = result.replace(/\{[^{}]*\}/g, ''); + } + + // Step 3: Remove self-closing tags like or
+ result = result.replace(/<[^>]+\/>/g, ''); + + // Step 4: Extract text content from JSX/HTML elements + // Iteratively remove tags from innermost to outermost + prevResult = ''; + while (prevResult !== result) { + prevResult = result; + // Remove tags but keep content between them + // Match opening and closing tags of the same type + result = result.replace(/<(\w+)[^>]*>([\s\S]*?)<\/\1>/g, '$2'); + } + + // Step 5: Remove any remaining standalone opening or closing tags + result = result.replace(/<[^>]+>/g, ''); + + // Step 6: Clean up whitespace + // Replace multiple whitespace characters (including newlines) with single space + result = result.replace(/\s+/g, ' ').trim(); + + if (!result) { + return undefined; + } + + if (result.length > MAX_SUMMARY_LENGTH) { + return `${result.slice(0, MAX_SUMMARY_LENGTH)}...`; + } + + return result; +} diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts index 0c5dc30e..04603a8f 100644 --- a/packages/mcp/src/utils/manifest-formatter/markdown.test.ts +++ b/packages/mcp/src/utils/manifest-formatter/markdown.test.ts @@ -1,6 +1,10 @@ import { describe, it, expect } from 'vitest'; import { markdownFormatter } from './markdown.ts'; -import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import type { + AllManifests, + ComponentManifest, + ComponentManifestMap, +} from '../../types.ts'; import fullManifestFixture from '../../../fixtures/full-manifest.fixture.json' with { type: 'json' }; describe('MarkdownFormatter - formatComponentManifest', () => { @@ -442,28 +446,30 @@ describe('MarkdownFormatter - formatComponentManifest', () => { }); }); -describe('MarkdownFormatter - formatComponentManifestMapToList', () => { +describe('MarkdownFormatter - formatManifestsToLists', () => { it('formats the full manifest fixture', () => { - const result = - markdownFormatter.formatComponentManifestMapToList(fullManifestFixture); + const result = markdownFormatter.formatManifestsToLists({ + componentManifest: fullManifestFixture as ComponentManifestMap, + }); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, }, }, }; - const result = - markdownFormatter.formatComponentManifestMapToList(manifest); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` "# Components @@ -473,29 +479,30 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` "# Components @@ -509,20 +516,21 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'A versatile button component', + }, }, }, }; - const result = - markdownFormatter.formatComponentManifestMapToList(manifest); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` "# Components @@ -532,41 +540,43 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'Button summary', + description: 'Button description', + }, }, }, }; - const result = - markdownFormatter.formatComponentManifestMapToList(manifest); + const result = markdownFormatter.formatManifestsToLists(manifests); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A simple button component', + }, }, }, }; - const result = - markdownFormatter.formatComponentManifestMapToList(manifest); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` "# Components @@ -576,21 +586,22 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toContain('...'); expect(result).toMatchInlineSnapshot(` @@ -601,39 +612,41 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = markdownFormatter.formatManifestsToLists(manifests); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, }, }, }; - const result = - markdownFormatter.formatComponentManifestMapToList(manifest); + const result = markdownFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` "# Components @@ -642,4 +655,105 @@ describe('MarkdownFormatter - formatComponentManifestMapToList', () => { `); }); }); + + describe('docs manifest section', () => { + it('should include docs section when docsManifest is provided', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + docsManifest: { + v: 1, + docs: { + 'getting-started': { + id: 'getting-started', + name: 'Getting Started', + title: 'Getting Started Guide', + path: 'docs/getting-started.mdx', + content: 'Welcome to our component library.', + }, + }, + }, + }; + + const result = markdownFormatter.formatManifestsToLists(manifests); + + expect(result).toMatchInlineSnapshot(` + "# Components + + - Button (button) + + # Docs + + - Getting Started Guide (getting-started): Welcome to our component library." + `); + }); + + it('should format multiple docs entries', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + docsManifest: { + v: 1, + docs: { + 'getting-started': { + id: 'getting-started', + name: 'Getting Started', + title: 'Getting Started Guide', + path: 'docs/getting-started.mdx', + content: 'Welcome to our component library.', + }, + theming: { + id: 'theming', + name: 'Theming', + title: 'Theming Guide', + path: 'docs/theming.mdx', + content: 'Learn how to customize the theme.', + }, + }, + }, + }; + + const result = markdownFormatter.formatManifestsToLists(manifests); + + expect(result).toContain('# Components'); + expect(result).toContain('# Docs'); + expect(result).toContain('- Getting Started Guide (getting-started)'); + expect(result).toContain('- Theming Guide (theming)'); + }); + + it('should omit docs section when docsManifest is not provided', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + }; + + const result = markdownFormatter.formatManifestsToLists(manifests); + + expect(result).not.toContain('# Docs'); + }); + }); }); diff --git a/packages/mcp/src/utils/manifest-formatter/markdown.ts b/packages/mcp/src/utils/manifest-formatter/markdown.ts index 32874667..33efc634 100644 --- a/packages/mcp/src/utils/manifest-formatter/markdown.ts +++ b/packages/mcp/src/utils/manifest-formatter/markdown.ts @@ -1,8 +1,8 @@ -import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; -import type { ManifestFormatter } from './types.ts'; +import type { ComponentManifest } from '../../types.ts'; +import { MAX_SUMMARY_LENGTH, type ManifestFormatter } from './types.ts'; import { parseReactDocgen } from '../parse-react-docgen.ts'; - -const MAX_SUMMARY_LENGTH = 90; +import { dedent } from '../dedent.ts'; +import { extractDocsSummary } from './extract-docs-summary.ts'; /** * Markdown formatter for component manifests. @@ -108,13 +108,21 @@ export const markdownFormatter: ManifestFormatter = { return parts.join('\n').trim(); }, - formatComponentManifestMapToList(manifest: ComponentManifestMap): string { + formatDocsManifest(doc) { + return dedent`# ${doc.title} + + ${doc.content}`; + }, + + formatManifestsToLists(manifests) { const parts: string[] = []; parts.push('# Components'); parts.push(''); - for (const component of Object.values(manifest.components)) { + for (const component of Object.values( + manifests.componentManifest.components, + )) { const summary = component.summary ?? (component.description @@ -132,6 +140,18 @@ export const markdownFormatter: ManifestFormatter = { parts.push(''); + if (!manifests.docsManifest) { + return parts.join('\n').trim(); + } + + parts.push('# Docs'); + parts.push(''); + + for (const doc of Object.values(manifests.docsManifest.docs)) { + const summary = extractDocsSummary(doc.content); + parts.push(`- ${doc.title} (${doc.id})${summary ? `: ${summary}` : ''}`); + } + 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 index e34a7ccf..c15dab89 100644 --- a/packages/mcp/src/utils/manifest-formatter/types.ts +++ b/packages/mcp/src/utils/manifest-formatter/types.ts @@ -1,4 +1,14 @@ -import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import type { + AllManifests, + ComponentManifest, + ComponentManifestMap, + Doc, +} from '../../types.ts'; + +/** + * Maximum length for a summary before truncation. + */ +export const MAX_SUMMARY_LENGTH = 90; /** * Interface for manifest formatters. @@ -8,15 +18,18 @@ import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; 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 single doc manifest into the target format. + */ + formatDocsManifest(doc: Doc): 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; + formatManifestsToLists(manifests: AllManifests): string; } diff --git a/packages/mcp/src/utils/manifest-formatter/xml.test.ts b/packages/mcp/src/utils/manifest-formatter/xml.test.ts index f2673e83..627d8bca 100644 --- a/packages/mcp/src/utils/manifest-formatter/xml.test.ts +++ b/packages/mcp/src/utils/manifest-formatter/xml.test.ts @@ -1,6 +1,10 @@ import { describe, it, expect } from 'vitest'; import { xmlFormatter } from './xml.ts'; -import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; +import type { + AllManifests, + 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' }; @@ -467,27 +471,30 @@ describe('XmlFormatter - formatComponentManifest', () => { }); }); -describe('XmlFormatter - formatComponentManifestMapToList', () => { +describe('XmlFormatter - formatManifestsToLists', () => { it('formats the full manifest fixture', () => { - const result = - xmlFormatter.formatComponentManifestMapToList(fullManifestFixture); + const result = xmlFormatter.formatManifestsToLists({ + componentManifest: fullManifestFixture as ComponentManifestMap, + }); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, }, }, }; - const result = xmlFormatter.formatComponentManifestMapToList(manifest); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -500,28 +507,30 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -544,19 +553,21 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + summary: 'A versatile button component', + }, }, }, }; - const result = xmlFormatter.formatComponentManifestMapToList(manifest); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -572,39 +583,44 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = xmlFormatter.formatManifestsToLists(manifests); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + description: 'A simple button component', + }, }, }, }; - const result = xmlFormatter.formatComponentManifestMapToList(manifest); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -620,20 +636,22 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -649,20 +667,22 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toContain( 'A description with exactly eighty characters is fine and should not be truncated', @@ -671,18 +691,20 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); 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 manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, }, }, }; - const result = xmlFormatter.formatComponentManifestMapToList(manifest); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).not.toContain(''); expect(result).toMatchInlineSnapshot(` @@ -698,38 +720,40 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { 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 manifests: AllManifests = { + componentManifest: { + 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); + const result = xmlFormatter.formatManifestsToLists(manifests); expect(result).toMatchInlineSnapshot(` " @@ -763,6 +787,115 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); }); + describe('docs manifest section', () => { + it('should include docs section when docsManifest is provided', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + docsManifest: { + v: 1, + docs: { + 'getting-started': { + id: 'getting-started', + name: 'Getting Started', + title: 'Getting Started Guide', + path: 'docs/getting-started.mdx', + content: 'Welcome to our component library.', + }, + }, + }, + }; + + const result = xmlFormatter.formatManifestsToLists(manifests); + + expect(result).toMatchInlineSnapshot(` + " + + button + Button + + + + + getting-started + Getting Started Guide + + Welcome to our component library. + + + " + `); + }); + + it('should format multiple docs entries', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + docsManifest: { + v: 1, + docs: { + 'getting-started': { + id: 'getting-started', + name: 'Getting Started', + title: 'Getting Started Guide', + path: 'docs/getting-started.mdx', + content: 'Welcome to our component library.', + }, + theming: { + id: 'theming', + name: 'Theming', + title: 'Theming Guide', + path: 'docs/theming.mdx', + content: 'Learn how to customize the theme.', + }, + }, + }, + }; + + const result = xmlFormatter.formatManifestsToLists(manifests); + + expect(result).toContain(''); + expect(result).toContain(''); + expect(result).toContain('Getting Started Guide'); + expect(result).toContain('Theming Guide'); + }); + + it('should omit docs section when docsManifest is not provided', () => { + const manifests: AllManifests = { + componentManifest: { + v: 1, + components: { + button: { + id: 'button', + name: 'Button', + path: 'src/components/Button.tsx', + }, + }, + }, + }; + + const result = xmlFormatter.formatManifestsToLists(manifests); + + expect(result).not.toContain(''); + }); + }); + describe('with-errors fixture', () => { it('should format success component with mixed stories (only successful ones)', () => { const component = @@ -844,9 +977,9 @@ describe('XmlFormatter - formatComponentManifestMapToList', () => { }); it('should format list of components with errors', () => { - const result = xmlFormatter.formatComponentManifestMapToList( - withErrorsFixture as ComponentManifestMap, - ); + const result = xmlFormatter.formatManifestsToLists({ + componentManifest: withErrorsFixture as ComponentManifestMap, + }); expect(result).toMatchInlineSnapshot(` " diff --git a/packages/mcp/src/utils/manifest-formatter/xml.ts b/packages/mcp/src/utils/manifest-formatter/xml.ts index 126ac6b7..01d8fedc 100644 --- a/packages/mcp/src/utils/manifest-formatter/xml.ts +++ b/packages/mcp/src/utils/manifest-formatter/xml.ts @@ -1,16 +1,15 @@ import type { ComponentManifest, ComponentManifestMap } from '../../types.ts'; -import type { ManifestFormatter } from './types.ts'; +import { MAX_SUMMARY_LENGTH, type ManifestFormatter } from './types.ts'; +import { extractDocsSummary } from './extract-docs-summary.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 { + formatComponentManifest(componentManifest) { const parts: string[] = []; // Component opening tag @@ -98,12 +97,23 @@ export const xmlFormatter: ManifestFormatter = { return parts.join('\n'); }, - formatComponentManifestMapToList(manifest: ComponentManifestMap): string { + formatDocsManifest(doc) { + return dedent` + ${doc.title} + + ${doc.content} + + `; + }, + + formatManifestsToLists(manifests) { const parts: string[] = []; parts.push(''); - for (const component of Object.values(manifest.components)) { + for (const component of Object.values( + manifests.componentManifest.components, + )) { parts.push(dedent` ${component.id} ${component.name}`); @@ -127,6 +137,29 @@ export const xmlFormatter: ManifestFormatter = { parts.push(''); + if (!manifests.docsManifest) { + return parts.join('\n'); + } + + parts.push(''); + + for (const doc of Object.values(manifests.docsManifest.docs)) { + const summary = extractDocsSummary(doc.content); + parts.push(dedent` + ${doc.id} + ${doc.title}`); + + if (summary) { + parts.push(dedent` + ${summary} + `); + } + + parts.push(''); + } + + parts.push(''); + return parts.join('\n'); }, }; From b62ba38530f04958d869cd224441fb3aead56568 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 30 Dec 2025 11:27:47 +0100 Subject: [PATCH 03/18] update bin and eval to support manifest split --- eval/lib/collect-args.ts | 3 ++- eval/lib/save/google-sheet.ts | 2 +- packages/mcp/bin.test.ts | 2 +- packages/mcp/bin.ts | 46 ++++++++++++++++++++++++++--------- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/eval/lib/collect-args.ts b/eval/lib/collect-args.ts index f573b8dc..039d9d8a 100644 --- a/eval/lib/collect-args.ts +++ b/eval/lib/collect-args.ts @@ -579,6 +579,7 @@ export async function collectArgs(): Promise { return result; } +// TODO: add support for docs manifests too function manifestPathToMcpServerConfig(manifestPath: string): McpServerConfig { return { 'storybook-mcp': { @@ -586,7 +587,7 @@ function manifestPathToMcpServerConfig(manifestPath: string): McpServerConfig { command: 'node', args: [ path.join(process.cwd(), '..', 'packages', 'mcp', 'bin.ts'), - '--manifestPath', + '--componentManifestPath', manifestPath, ], }, diff --git a/eval/lib/save/google-sheet.ts b/eval/lib/save/google-sheet.ts index 668a3569..843b8339 100644 --- a/eval/lib/save/google-sheet.ts +++ b/eval/lib/save/google-sheet.ts @@ -47,7 +47,7 @@ function getContextDetails(context: Context): string { // Extract manifest path from MCP server config args const mcpConfig = Object.values(context.mcpServerConfig)[0]; if (mcpConfig?.type === 'stdio' && mcpConfig.args) { - const manifestIndex = mcpConfig.args.indexOf('--manifestPath'); + const manifestIndex = mcpConfig.args.indexOf('--componentManifestPath'); const manifestIndexValue = mcpConfig.args[manifestIndex + 1]; if (manifestIndex !== -1 && manifestIndexValue) { return path.basename(manifestIndexValue); diff --git a/packages/mcp/bin.test.ts b/packages/mcp/bin.test.ts index 9c559a47..24e7027b 100644 --- a/packages/mcp/bin.test.ts +++ b/packages/mcp/bin.test.ts @@ -75,7 +75,7 @@ describe('bin.ts stdio MCP server', () => { './fixtures/full-manifest.fixture.json', ); - const proc = x('node', [binPath, '--manifestPath', fixturePath]); + const proc = x('node', [binPath, '--componentManifestPath', fixturePath]); child = proc.process as ChildProcess; diff --git a/packages/mcp/bin.ts b/packages/mcp/bin.ts index 54b01729..c80d5f8e 100644 --- a/packages/mcp/bin.ts +++ b/packages/mcp/bin.ts @@ -1,14 +1,17 @@ /** * 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 --format markdown + * node bin.ts --componentManifestPath ./path/to/components.json --format markdown + * + * Optionally, you can also provide a docs manifest: + * node bin.ts --componentManifestPath ./path/to/components.json --docsManifestPath ./path/to/docs.json * * Or when configuring it as an MCP server: * { * "storybook-mcp": { * "type": "stdio", * "command": "node", - * "args": ["bin.ts", "--manifestPath", "./path/to/manifest.json", "--format", "markdown"] + * "args": ["bin.ts", "--componentManifestPath", "./path/to/components.json", "--format", "markdown"] * } * } */ @@ -43,10 +46,13 @@ await addGetDocumentationTool(server); const transport = new StdioTransport(server); const args = parseArgs({ options: { - manifestPath: { + componentManifestPath: { type: 'string', default: './fixtures/full-manifest.fixture.json', }, + docsManifestPath: { + type: 'string', + }, format: { type: 'string', default: 'markdown', @@ -56,17 +62,33 @@ const args = parseArgs({ const format = args.values.format as OutputFormat; +async function readManifest(manifestPath: string): Promise { + if ( + manifestPath.startsWith('http://') || + manifestPath.startsWith('https://') + ) { + const res = await fetch(manifestPath); + return await res.text(); + } + return await fs.readFile(manifestPath, 'utf-8'); +} + transport.listen({ format, - manifestProvider: async () => { - const { manifestPath } = args.values; - if ( - manifestPath.startsWith('http://') || - manifestPath.startsWith('https://') - ) { - const res = await fetch(manifestPath); - return await res.text(); + manifestProvider: async (_request, path) => { + const { componentManifestPath, docsManifestPath } = args.values; + + // Determine which manifest to load based on the requested path + if (path.includes('docs')) { + if (!docsManifestPath) { + throw new Error( + 'Docs manifest requested but --docsManifestPath was not provided', + ); + } + return await readManifest(docsManifestPath); } - return await fs.readFile(manifestPath, 'utf-8'); + + // Default to component manifest + return await readManifest(componentManifestPath); }, }); From 65870cdbfd960eea14681a9c20eb20ee6e56774b Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 5 Jan 2026 12:46:42 +0100 Subject: [PATCH 04/18] fix manifest feature detection --- packages/addon-mcp/src/preset.ts | 2 +- .../src/tools/is-manifest-available.test.ts | 26 +++++++++---------- .../src/tools/is-manifest-available.ts | 14 +++++----- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/addon-mcp/src/preset.ts b/packages/addon-mcp/src/preset.ts index d96f6b05..19c80ddc 100644 --- a/packages/addon-mcp/src/preset.ts +++ b/packages/addon-mcp/src/preset.ts @@ -40,7 +40,7 @@ export const experimental_devServer: PresetProperty< res.writeHead(200, { 'Content-Type': 'text/html' }); let docsNotice = ''; - if (!manifestStatus.hasGenerator) { + if (!manifestStatus.hasManifests) { docsNotice = `
This toolset is only supported in React-based setups.
`; diff --git a/packages/addon-mcp/src/tools/is-manifest-available.test.ts b/packages/addon-mcp/src/tools/is-manifest-available.test.ts index 17718897..4800931a 100644 --- a/packages/addon-mcp/src/tools/is-manifest-available.test.ts +++ b/packages/addon-mcp/src/tools/is-manifest-available.test.ts @@ -4,11 +4,11 @@ import type { Options } from 'storybook/internal/types'; function createMockOptions({ featureFlag = false, - hasGenerator = false, + hasManifests = false, hasFeaturesObject = true, }: { featureFlag?: boolean; - hasGenerator?: boolean; + hasManifests?: boolean; hasFeaturesObject?: boolean; } = {}): Options { return { @@ -20,7 +20,7 @@ function createMockOptions({ : {}; } if (key === 'experimental_componentManifestGenerator') { - return hasGenerator ? vi.fn() : undefined; + return hasManifests ? vi.fn() : undefined; } return undefined; }), @@ -32,32 +32,32 @@ describe('getManifestStatus', () => { it.each([ { description: 'both feature flag and generator are present', - options: { featureFlag: true, hasGenerator: true }, - expected: { available: true, hasGenerator: true, hasFeatureFlag: true }, + options: { featureFlag: true, hasManifests: true }, + expected: { available: true, hasManifests: true, hasFeatureFlag: true }, }, { description: 'missing generator (unsupported framework)', - options: { featureFlag: true, hasGenerator: false }, - expected: { available: false, hasGenerator: false, hasFeatureFlag: true }, + options: { featureFlag: true, hasManifests: false }, + expected: { available: false, hasManifests: false, hasFeatureFlag: true }, }, { description: 'missing feature flag', - options: { featureFlag: false, hasGenerator: true }, - expected: { available: false, hasGenerator: true, hasFeatureFlag: false }, + options: { featureFlag: false, hasManifests: true }, + expected: { available: false, hasManifests: true, hasFeatureFlag: false }, }, { description: 'both are missing', - options: { featureFlag: false, hasGenerator: false }, + options: { featureFlag: false, hasManifests: false }, expected: { available: false, - hasGenerator: false, + hasManifests: false, hasFeatureFlag: false, }, }, { description: 'features object is missing the flag', - options: { hasGenerator: true, hasFeaturesObject: false }, - expected: { available: false, hasGenerator: true, hasFeatureFlag: false }, + options: { hasManifests: true, hasFeaturesObject: false }, + expected: { available: false, hasManifests: true, hasFeatureFlag: false }, }, ])( 'should return correct status when $description', diff --git a/packages/addon-mcp/src/tools/is-manifest-available.ts b/packages/addon-mcp/src/tools/is-manifest-available.ts index b33b4e51..b7ad952c 100644 --- a/packages/addon-mcp/src/tools/is-manifest-available.ts +++ b/packages/addon-mcp/src/tools/is-manifest-available.ts @@ -2,24 +2,26 @@ import type { Options } from 'storybook/internal/types'; export type ManifestStatus = { available: boolean; - hasGenerator: boolean; + hasManifests: boolean; hasFeatureFlag: boolean; }; export const getManifestStatus = async ( options: Options, ): Promise => { - const [features, componentManifestGenerator] = await Promise.all([ + const [features, manifests] = await Promise.all([ options.presets.apply('features') as any, - options.presets.apply('experimental_componentManifestGenerator'), + options.presets.apply('experimental_manifests', undefined, { + manifestEntries: [], + }), ]); - const hasGenerator = !!componentManifestGenerator; + const hasManifests = !!manifests; const hasFeatureFlag = !!features?.experimentalComponentsManifest; return { - available: hasFeatureFlag && hasGenerator, - hasGenerator, + available: hasFeatureFlag && hasManifests, + hasManifests, hasFeatureFlag, }; }; From 57643f5638ddc41d9fe8cd7c1d3ed9039c83140d Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 5 Jan 2026 12:50:46 +0100 Subject: [PATCH 05/18] add MDX files to test support --- apps/internal-storybook/stories/components/Button.mdx | 10 ++++++++++ apps/internal-storybook/stories/getting-started.mdx | 7 +++++++ 2 files changed, 17 insertions(+) create mode 100644 apps/internal-storybook/stories/components/Button.mdx create mode 100644 apps/internal-storybook/stories/getting-started.mdx diff --git a/apps/internal-storybook/stories/components/Button.mdx b/apps/internal-storybook/stories/components/Button.mdx new file mode 100644 index 00000000..d7735651 --- /dev/null +++ b/apps/internal-storybook/stories/components/Button.mdx @@ -0,0 +1,10 @@ +import { Meta, Canvas } from '@storybook/addon-docs/blocks'; +import * as ButtonStories from './Button.stories'; + + + +It is critical when using the Button component, that all child content of the button uses the 🍌-emoji instead of spaces. + +Here is the button: + + diff --git a/apps/internal-storybook/stories/getting-started.mdx b/apps/internal-storybook/stories/getting-started.mdx new file mode 100644 index 00000000..789df7be --- /dev/null +++ b/apps/internal-storybook/stories/getting-started.mdx @@ -0,0 +1,7 @@ +# Getting Started + +This is the getting started documentation of this design system. + +## Usage + +The most critical information to know, is that when using the Button component, you MUST put the 🍇-emoji before and after the component, otherwise it will break. From fc7c0a27053bc2e1190ebc2d77449b97406382e3 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 5 Jan 2026 12:51:07 +0100 Subject: [PATCH 06/18] Use canary version of https://github.com/storybookjs/storybook/pull/33408 --- pnpm-lock.yaml | 308 +++++++++++++++++++++++--------------------- pnpm-workspace.yaml | 24 ++-- 2 files changed, 170 insertions(+), 162 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c8267fb..c32e6452 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,14 +7,14 @@ settings: catalogs: default: '@storybook/addon-a11y': - specifier: 10.1.2 - version: 10.1.2 + specifier: 0.0.0-pr-33408-sha-d91d75c7 + version: 0.0.0-pr-33408-sha-d91d75c7 '@storybook/addon-docs': - specifier: 10.1.2 - version: 10.1.2 + specifier: 0.0.0-pr-33408-sha-d91d75c7 + version: 0.0.0-pr-33408-sha-d91d75c7 '@storybook/react-vite': - specifier: 10.1.2 - version: 10.1.2 + specifier: 0.0.0-pr-33408-sha-d91d75c7 + version: 0.0.0-pr-33408-sha-d91d75c7 '@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.2 - version: 10.1.2 + specifier: 0.0.0-pr-33408-sha-d91d75c7 + version: 0.0.0-pr-33408-sha-d91d75c7 tmcp: specifier: ^1.16.0 version: 1.18.1 @@ -106,13 +106,13 @@ importers: devDependencies: '@storybook/addon-docs': specifier: 'catalog:' - version: 10.1.2(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + version: 0.0.0-pr-33408-sha-d91d75c7(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(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.2(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + version: 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@types/react': specifier: ^18.2.65 version: 18.3.26 @@ -130,7 +130,7 @@ importers: version: 18.3.1(react@18.3.1) storybook: specifier: 'catalog:' - version: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tinyexec: specifier: ^1.0.2 version: 1.0.2 @@ -163,13 +163,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.2(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + version: 0.0.0-pr-33408-sha-d91d75c7(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) '@storybook/mcp': specifier: workspace:* version: link:../packages/mcp '@storybook/react-vite': specifier: 'catalog:' - version: 10.1.2(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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)) + version: 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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 @@ -241,10 +241,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.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) storybook-addon-test-codegen: specifier: ^3.0.0 - version: 3.0.0(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + version: 3.0.0(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) tinyexec: specifier: ^1.0.1 version: 1.0.2 @@ -278,7 +278,7 @@ importers: devDependencies: storybook: specifier: 'catalog:' - version: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) packages/mcp: dependencies: @@ -849,12 +849,20 @@ packages: '@types/node': optional: true + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1': - resolution: {integrity: sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw==} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.3': + resolution: {integrity: sha512-9TGZuAX+liGkNKkwuo3FYJu7gHWT0vkBcf7GkOe7s7fmC19XwH/4u5u7sDIFrMooe558ORcmuBvBz7Ur5PlbHw==} peerDependencies: typescript: '>= 4.3.x' vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -1077,10 +1085,6 @@ packages: cpu: [x64] os: [win32] - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -2009,28 +2013,28 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@storybook/addon-a11y@10.1.2': - resolution: {integrity: sha512-O4Rp+ftREulVLREOKwx3IKkUiSLx+F3V5zQ++h6bzlmwRuaknMHs5P3qSnjJprYV6K8qXTRbvgR2qKBgga8Qxw==} + '@storybook/addon-a11y@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-giz+3mnt6TFgJi3k96k3Pc/9ZQs7HnRfq7kkro7Xn06s0sRboTl9xpFxvMjsGYOsDRdgXSOwibLZCGme5FpwXg==} peerDependencies: - storybook: ^10.1.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 - '@storybook/addon-docs@10.1.2': - resolution: {integrity: sha512-2D89qp6WwNxbiyylixJDC9C8tU8qgRS68HFcYruSNVX3dcCoty7xVytdWJIoDdTjvYlKZZdK23eD9r7+AEA3oA==} + '@storybook/addon-docs@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-Vq2/9EAmAe4RYstUImDpvW0V6pjsEA7ZzIb57R4YKChP+nnX4x+DCBMoWNpQL3KOyVCBQLknGcgP+Uur0MaAHQ==} peerDependencies: - storybook: ^10.1.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 - '@storybook/builder-vite@10.1.2': - resolution: {integrity: sha512-gEIduoOUQZL0xS3LJu/9WjPRppg2wptNp6ifLZiRYF6R3T0q4IBSzQ3oXIeHOcwhKUW//vRSuci2NDe1llUjMw==} + '@storybook/builder-vite@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-Olg0RLSn2YebUsLAszUFHYC9PPXXq63X02+sNaLNdtfs/eedJ1poIO2GX3E8tklra0YZwxBVTBloXnPazEmSYg==} peerDependencies: - storybook: ^10.1.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/csf-plugin@10.1.2': - resolution: {integrity: sha512-dwFKoKsV73SEKdaA78/AZlMa8+pAt2YS8f8cAvRLtsCxus9u0sJqxR/5axlZk0KLHnoJ+exZDD/zpK/HCsqtNw==} + '@storybook/csf-plugin@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-e8z0u/C0Oen+oSZ7OI/iCKoBoEKc91AL6ssZN4+XgZZRdth+wQQgUtrJadkCQJZJYZdJkVOEQW9C5E5HIjX/wg==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.1.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 vite: '*' webpack: '*' peerDependenciesMeta: @@ -2052,27 +2056,27 @@ packages: 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.2': - resolution: {integrity: sha512-HxmL6rD99qaZervm3S/g0QjflSpCk31kZX6+guLBD85KzH+sgo7XNjlipNqfdzAOfWpri0rh6zEGyNI0erFlag==} + '@storybook/react-dom-shim@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-iYW8LA0EXIpJe8+e2p92eiBjdiFpdbWpfY9ozAzXRjTQIMZKID5DB8po1QU+La4gBYAna+5cAulyLY7qvrHD6g==} 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.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 - '@storybook/react-vite@10.1.2': - resolution: {integrity: sha512-S7as4gJtoYTmFqlpOhLcLiUlA4uxg7zsUTbNtmbszk3P7ZNCHU2WvVuE/h4CXZJNzhtsuzbNjXudKtMNcRXL1A==} + '@storybook/react-vite@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-6T3Hc05jNZO8t9tzPFIQ3M+9O1OReU5GVu7KslWtsq6R4IgEAxc3at6PliwTg8R990XmBKXy4vVNDy2VWShfFA==} 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.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/react@10.1.2': - resolution: {integrity: sha512-9fWP4EdQ2nYCtKr9wHGFrzxb/osPv0wJ3bbcZxZ1xN6RyDFDFBkwBJpl7NN+3KTFloXZJXYOkkc5rqmbMPi0fA==} + '@storybook/react@0.0.0-pr-33408-sha-d91d75c7': + resolution: {integrity: sha512-AaTBPuXnSHxYwTUW2J6xD24VDQgTPFqt/moYrRZI5mtc2ihyuseqjAuI8OfvJr7rbr2M4S3OwMbhf7FoqiDk0A==} 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.2 + storybook: ^0.0.0-pr-33408-sha-d91d75c7 typescript: '>= 4.9.x' peerDependenciesMeta: typescript: @@ -2601,9 +2605,6 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -3307,8 +3308,9 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} hasBin: true globals@14.0.0: @@ -3490,8 +3492,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} @@ -3599,8 +3602,9 @@ packages: lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3696,13 +3700,13 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -3887,9 +3891,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} @@ -4590,8 +4594,8 @@ packages: peerDependencies: storybook: ^10.0.0 - storybook@10.1.2: - resolution: {integrity: sha512-yFL15WVQJeagmptyRadd2cwJlMVCo6xPoTPt/R+lQXIJmsTDHOFl5cZooIsvgALe3hTi5hsuVL3pG2bPEUuYGg==} + storybook@0.0.0-pr-33408-sha-d91d75c7: + resolution: {integrity: sha512-wlG+8HcGIttgk8vb6O+0sXiux+4+es8XOOJDC1fZo7o6KaXER9Yl+TYaSZvcFJPFFERH3bgaaJ04dzrnHdJhaw==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -5682,7 +5686,7 @@ snapshots: dependencies: '@floating-ui/dom': 1.7.4 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: @@ -5753,6 +5757,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.0 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -5762,10 +5772,9 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@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))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.3(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))': dependencies: - glob: 10.4.5 - magic-string: 0.30.21 + glob: 11.1.0 react-docgen-typescript: 2.4.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) optionalDependencies: @@ -5863,7 +5872,7 @@ snapshots: pkce-challenge: 4.1.0 prismjs: 1.30.0 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) react-simple-code-editor: 0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) serve-handler: 6.1.6 tailwind-merge: 2.6.0 @@ -6069,9 +6078,6 @@ snapshots: '@oxlint/win32-x64@1.28.0': optional: true - '@pkgjs/parseargs@0.11.0': - optional: true - '@polka/url@1.0.0-next.29': optional: true @@ -6140,7 +6146,7 @@ snapshots: dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6194,7 +6200,7 @@ snapshots: '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6234,7 +6240,7 @@ snapshots: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6315,7 +6321,7 @@ snapshots: '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) react-remove-scroll: 2.7.1(@types/react@19.2.7)(react@18.3.1) optionalDependencies: '@types/react': 19.2.7 @@ -6354,7 +6360,7 @@ snapshots: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6403,7 +6409,7 @@ snapshots: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6470,7 +6476,7 @@ snapshots: dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6617,7 +6623,7 @@ snapshots: '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) react-remove-scroll: 2.7.1(@types/react@19.2.7)(react@18.3.1) optionalDependencies: '@types/react': 19.2.7 @@ -6654,7 +6660,7 @@ snapshots: '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/rect': 1.1.1 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6674,7 +6680,7 @@ snapshots: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6694,7 +6700,7 @@ snapshots: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6712,7 +6718,7 @@ snapshots: dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6774,7 +6780,7 @@ snapshots: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6848,7 +6854,7 @@ snapshots: '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) react-remove-scroll: 2.7.1(@types/react@19.2.7)(react@18.3.1) optionalDependencies: '@types/react': 19.2.7 @@ -6921,7 +6927,7 @@ snapshots: '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6953,7 +6959,7 @@ snapshots: '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -6993,7 +6999,7 @@ snapshots: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -7074,7 +7080,7 @@ snapshots: '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -7207,7 +7213,7 @@ snapshots: dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) optionalDependencies: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) @@ -7347,21 +7353,21 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@storybook/addon-a11y@10.1.2(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': + '@storybook/addon-a11y@0.0.0-pr-33408-sha-d91d75c7(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.0 - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/addon-docs@10.1.2(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/addon-docs@0.0.0-pr-33408-sha-d91d75c7(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(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.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/csf-plugin': 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(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.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@storybook/react-dom-shim': 0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -7370,11 +7376,11 @@ snapshots: - vite - webpack - '@storybook/builder-vite@10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/builder-vite@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/csf-plugin': 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@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)) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 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: @@ -7383,11 +7389,11 @@ snapshots: - rollup - webpack - '@storybook/builder-vite@10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/builder-vite@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/csf-plugin': 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@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)) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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: @@ -7396,9 +7402,9 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/csf-plugin@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) unplugin: 2.3.10 optionalDependencies: esbuild: 0.25.12 @@ -7406,9 +7412,9 @@ snapshots: 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/csf-plugin@10.1.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/csf-plugin@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) unplugin: 2.3.10 optionalDependencies: esbuild: 0.25.12 @@ -7428,37 +7434,37 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@storybook/react-dom-shim@10.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@storybook/react-dom-shim@0.0.0-pr-33408-sha-d91d75c7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(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: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim@10.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@storybook/react-dom-shim@0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim@10.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': + '@storybook/react-dom-shim@0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(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: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/react-vite@10.1.2(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/react-vite@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(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)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(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.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) - '@storybook/react': 10.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3) + '@storybook/builder-vite': 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 0.0.0-pr-33408-sha-d91d75c7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(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.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 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: @@ -7469,19 +7475,19 @@ snapshots: - typescript - webpack - '@storybook/react-vite@10.1.2(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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))': + '@storybook/react-vite@0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(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.2(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.1.2(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) - '@storybook/react': 10.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3) + '@storybook/builder-vite': 0.0.0-pr-33408-sha-d91d75c7(esbuild@0.25.12)(rollup@4.52.5)(storybook@0.0.0-pr-33408-sha-d91d75c7(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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: @@ -7492,27 +7498,27 @@ snapshots: - typescript - webpack - '@storybook/react@10.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)': + '@storybook/react@0.0.0-pr-33408-sha-d91d75c7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@storybook/react-dom-shim': 0.0.0-pr-33408-sha-d91d75c7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: 18.3.1 react-docgen: 8.0.2 react-dom: 18.3.1(react@18.3.1) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@storybook/react@10.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3)': + '@storybook/react@0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + '@storybook/react-dom-shim': 0.0.0-pr-33408-sha-d91d75c7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) react: 19.2.0 react-docgen: 8.0.2 react-dom: 19.2.0(react@19.2.0) - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -8117,10 +8123,6 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -8230,7 +8232,7 @@ snapshots: '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@18.3.1) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -8838,14 +8840,14 @@ snapshots: glob-to-regexp@0.4.1: optional: true - glob@10.4.5: + glob@11.1.0: dependencies: foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 + jackspeak: 4.1.1 + minimatch: 10.1.1 minipass: 7.1.2 package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 + path-scurry: 2.0.1 globals@14.0.0: {} @@ -9001,11 +9003,9 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: + jackspeak@4.1.1: dependencies: '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 jest-worker@27.5.1: dependencies: @@ -9109,7 +9109,7 @@ snapshots: fault: 1.0.4 highlight.js: 10.7.3 - lru-cache@10.4.3: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: @@ -9190,13 +9190,13 @@ snapshots: min-indent@1.0.1: {} - minimatch@3.1.2: + minimatch@10.1.1: dependencies: - brace-expansion: 1.1.12 + '@isaacs/brace-expansion': 5.0.0 - minimatch@9.0.5: + minimatch@3.1.2: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 1.1.12 minimist@1.2.8: {} @@ -9371,9 +9371,9 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: + path-scurry@2.0.1: dependencies: - lru-cache: 10.4.3 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@3.3.0: {} @@ -9775,6 +9775,12 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-dom@18.3.1(react@19.2.0): + dependencies: + loose-envify: 1.4.0 + react: 19.2.0 + scheduler: 0.23.2 + react-dom@19.2.0(react@19.2.0): dependencies: react: 19.2.0 @@ -9827,7 +9833,7 @@ snapshots: 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 - react-dom: 18.3.1(react@18.3.1) + react-dom: 18.3.1(react@19.2.0) react-style-singleton@2.2.3(@types/react@18.3.26)(react@19.2.0): dependencies: @@ -10219,11 +10225,11 @@ snapshots: std-env@3.10.0: {} - storybook-addon-test-codegen@3.0.0(storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): + storybook-addon-test-codegen@3.0.0(storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): dependencies: - storybook: 10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -10232,6 +10238,7 @@ snapshots: '@vitest/expect': 3.2.4 '@vitest/spy': 3.2.4 esbuild: 0.25.12 + open: 10.2.0 recast: 0.23.11 semver: 7.7.3 use-sync-external-store: 1.6.0(react@18.3.1) @@ -10245,7 +10252,7 @@ snapshots: - react-dom - utf-8-validate - storybook@10.1.2(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + storybook@0.0.0-pr-33408-sha-d91d75c7(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -10254,6 +10261,7 @@ snapshots: '@vitest/expect': 3.2.4 '@vitest/spy': 3.2.4 esbuild: 0.25.12 + open: 10.2.0 recast: 0.23.11 semver: 7.7.3 use-sync-external-store: 1.6.0(react@19.2.0) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 80233c9e..8b823683 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,15 +6,15 @@ packages: - eval/evals/*/experiments/*/project catalog: - '@storybook/addon-a11y': 10.1.2 - '@storybook/addon-docs': 10.1.2 - '@storybook/addon-vitest': 10.1.2 - '@storybook/react-vite': 10.1.2 + '@storybook/addon-a11y': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/addon-docs': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/addon-vitest': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/react-vite': 0.0.0-pr-33408-sha-d91d75c7 '@tmcp/adapter-valibot': ^0.1.4 '@tmcp/transport-http': ^0.8.0 '@tmcp/transport-stdio': ^0.4.1 - eslint-plugin-storybook: 10.1.2 - storybook: 10.1.2 + eslint-plugin-storybook: 0.0.0-pr-33408-sha-d91d75c7 + storybook: 0.0.0-pr-33408-sha-d91d75c7 tmcp: ^1.16.0 tsdown: ^0.15.12 typescript: ^5.9.3 @@ -25,10 +25,10 @@ catalog: catalogs: experiments: '@eslint/js': 9.39.1 - '@storybook/addon-a11y': 10.1.2 - '@storybook/addon-docs': 10.1.2 - '@storybook/addon-vitest': 10.1.2 - '@storybook/react-vite': 10.1.2 + '@storybook/addon-a11y': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/addon-docs': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/addon-vitest': 0.0.0-pr-33408-sha-d91d75c7 + '@storybook/react-vite': 0.0.0-pr-33408-sha-d91d75c7 '@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.2 + eslint-plugin-storybook: 0.0.0-pr-33408-sha-d91d75c7 globals: 16.5.0 react: 19.2.0 react-dom: 19.2.0 - storybook: 10.1.2 + storybook: 0.0.0-pr-33408-sha-d91d75c7 typescript: 5.9.3 typescript-eslint: 8.47.0 vite: 7.2.2 From 4c052cd4c1939e978507adcd988cf886d0ceb22e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 5 Jan 2026 12:55:40 +0100 Subject: [PATCH 07/18] fix tests --- .github/copilot-instructions.md | 4 ++-- .../tests/mcp-endpoint.e2e.test.ts | 14 +++++++++----- packages/addon-mcp/src/mcp-handler.test.ts | 2 +- .../src/tools/is-manifest-available.test.ts | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1ef16bd6..2c45a806 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -261,7 +261,7 @@ export { addMyTool, MY_TOOL_NAME } from './tools/my-tool.ts'; - `AddonContext` extends `StorybookContext` to ensure type compatibility - Component manifest tools are conditionally registered based on feature flags: - Checks `features.experimentalComponentsManifest` flag - - Checks for `experimental_componentManifestGenerator` preset + - Checks for `experimental_manifests` preset - Only registers `addListAllComponentsTool` and `addGetComponentDocumentationTool` when enabled - Context includes `request` (HTTP Request object) which tools use to determine manifest location - Default manifest URL is constructed from request origin, replacing `/mcp` with `/manifests/components.json` @@ -278,7 +278,7 @@ export { addMyTool, MY_TOOL_NAME } from './tools/my-tool.ts'; - `storybook/internal/node-logger` - Logging utilities - Framework detection via `options.presets.apply('framework')` - Feature flags via `options.presets.apply('features')` -- Component manifest generator via `options.presets.apply('experimental_componentManifestGenerator')` +- Component manifest generator via `options.presets.apply('experimental_manifests')` **Story URL generation:** diff --git a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts index f3e958c7..980624a3 100644 --- a/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts +++ b/apps/internal-storybook/tests/mcp-endpoint.e2e.test.ts @@ -326,7 +326,11 @@ describe('MCP Endpoint E2E Tests', () => { - 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", + - Card (other-ui-card): Card component with title, image, content, and action button + + # Docs + + - getting-started (getting-started--docs): # Getting Started This is the getting started documentation of this design system. ## Usag...", "type": "text", }, ], @@ -374,7 +378,7 @@ describe('MCP Endpoint E2E Tests', () => { \`\`\` import { Button } from "@my-org/my-component-library"; - const Primary = () => ; + const Primary = () => ; + const Secondary = () => ; + const Large = () => ; + const Small = () => " + }, + { + "id": "button--secondary", + "name": "Secondary", + "description": "The secondary button variant is used for secondary actions that are still important but not the primary focus of the page.\n\nSecondary buttons have less visual weight than primary buttons and can be used multiple times on a page.", + "summary": "Secondary button for supporting actions", + "import": "import { Button } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Secondary Button" + } + ], + "snippet": "const Secondary = () => " + }, + { + "id": "button--with-sizes", + "name": "WithSizes", + "description": "Buttons are available in three sizes: small, medium (default), and large.\n\nChoose the appropriate size based on the context and hierarchy of actions. Larger buttons are more prominent and easier to tap on mobile devices.", + "summary": "Button size variations", + "import": "import { Button } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Button Sizes" + } + ], + "snippet": "const WithSizes = () => (\n <>\n \n \n \n \n)" + }, + { + "id": "button--loading", + "name": "Loading", + "description": "The loading state provides visual feedback when an async operation is in progress.\n\nWhen 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.", + "summary": "Button in loading state during async operations", + "import": "import { Button } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Loading Button" + } + ], + "snippet": "const Loading = () => " + }, + { + "id": "button--danger", + "name": "Danger", + "description": "The danger variant is used for destructive actions that cannot be easily undone, such as deleting data or canceling subscriptions.\n\nUse this variant to draw attention to the serious nature of the action. Consider adding a confirmation dialog for critical operations.", + "summary": "Danger button for destructive actions", + "import": "import { Button } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Danger Button" + }, + { + "key": "warning", + "value": "Use with caution for destructive actions" + } + ], + "snippet": "const Danger = () => " + } + ], + "jsDocTag": [ + { + "key": "summary", + "value": "A versatile button component for user interactions" + }, + { + "key": "since", + "value": "1.0.0" + }, + { + "key": "component", + "value": "Button" + } + ] + }, + "card": { + "id": "card", + "path": "src/components/Card.tsx", + "name": "Card", + "description": "A flexible container component for grouping related content with optional header, footer, and action areas.\n\nThe 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.\n\n## Design Principles\n\n- Cards should contain a single subject or action\n- Maintain consistent padding and spacing\n- Use elevation to indicate interactive vs static cards\n- Keep content hierarchy clear with proper use of typography", + "summary": "A flexible container component for grouping related content", + "import": "import { Card } from '@storybook/design-system';", + "reactDocgen": { + "props": { + "variant": { + "description": "The visual style variant of the card", + "required": false, + "tsType": { + "name": "union", + "raw": "\"elevated\" | \"outlined\" | \"flat\"", + "elements": [ + { "name": "literal", "value": "\"elevated\"" }, + { "name": "literal", "value": "\"outlined\"" }, + { "name": "literal", "value": "\"flat\"" } + ] + }, + "defaultValue": { "value": "\"elevated\"", "computed": false } + }, + "padding": { + "description": "The amount of internal padding", + "required": false, + "tsType": { + "name": "union", + "raw": "\"none\" | \"small\" | \"medium\" | \"large\"", + "elements": [ + { "name": "literal", "value": "\"none\"" }, + { "name": "literal", "value": "\"small\"" }, + { "name": "literal", "value": "\"medium\"" }, + { "name": "literal", "value": "\"large\"" } + ] + }, + "defaultValue": { "value": "\"medium\"", "computed": false } + }, + "clickable": { + "description": "Whether the entire card is clickable/interactive", + "required": false, + "tsType": { "name": "boolean" }, + "defaultValue": { "value": "false", "computed": false } + }, + "header": { + "description": "Content to display in the card header", + "required": false, + "tsType": { "name": "ReactNode" } + }, + "footer": { + "description": "Content to display in the card footer", + "required": false, + "tsType": { "name": "ReactNode" } + }, + "children": { + "description": "The main content of the card", + "required": true, + "tsType": { "name": "ReactNode" } + }, + "onClick": { + "description": "Callback function when the card is clicked (requires clickable=true)", + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "signature": { + "arguments": [ + { "name": "event", "type": { "name": "MouseEvent" } } + ], + "return": { "name": "void" } + } + } + } + } + }, + "stories": [ + { + "id": "card--basic", + "name": "Basic", + "description": "A basic card with just content.\n\nThe 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.", + "summary": "Basic card with content only", + "import": "import { Card } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Basic Card" + } + ], + "snippet": "const Basic = () => (\n \n

Card Title

\n

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

\n
\n)" + }, + { + "id": "card--with-header-and-footer", + "name": "WithHeaderAndFooter", + "description": "A card with distinct header and footer sections.\n\nHeaders 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.", + "summary": "Card with header and footer sections", + "import": "import { Card } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Card with Header and Footer" + } + ], + "snippet": "const WithHeaderAndFooter = () => (\n Article Title}\n footer={}\n >\n

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

\n \n)" + }, + { + "id": "card--clickable", + "name": "Clickable", + "description": "An interactive card that responds to clicks.\n\nClickable 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.\n\n## Accessibility\n\nClickable cards are rendered as buttons with proper keyboard support and ARIA attributes.", + "summary": "Interactive clickable card", + "import": "import { Card } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Clickable Card" + } + ], + "snippet": "const Clickable = () => (\n alert('Card clicked!')}>\n

Product Name

\n

Click anywhere on this card to view details.

\n
\n)" + }, + { + "id": "card--variants", + "name": "Variants", + "description": "Different visual variants of the card component.\n\n- **Elevated**: Default variant with shadow for depth\n- **Outlined**: Border-only variant without shadow\n- **Flat**: No border or shadow, minimal visual separation\n\nChoose variants based on your design system and the level of emphasis needed.", + "summary": "Card visual variants (elevated, outlined, flat)", + "import": "import { Card } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Card Variants" + } + ], + "snippet": "const Variants = () => (\n <>\n \n

Elevated card with shadow

\n
\n \n

Outlined card with border

\n
\n \n

Flat card without border or shadow

\n
\n \n)" + }, + { + "id": "card--user-profile", + "name": "UserProfile", + "description": "A real-world example of a user profile card.\n\nThis 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.", + "summary": "Complete user profile card example", + "import": "import { Card, Avatar, Button } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "User Profile Card" + }, + { + "key": "composition", + "value": "Uses Avatar and Button components" + } + ], + "snippet": "const UserProfile = () => (\n \n \n
\n

Jane Doe

\n

Senior Developer

\n
\n \n }\n footer={\n
\n \n \n
\n }\n >\n
\n
1.2K
Followers
\n
342
Following
\n
89
Posts
\n
\n \n)" + } + ], + "jsDocTag": [ + { + "key": "summary", + "value": "A flexible container component for grouping related content" + }, + { + "key": "since", + "value": "1.0.0" + }, + { + "key": "component", + "value": "Card" + }, + { + "key": "pattern", + "value": "Container" + } + ] + }, + "input": { + "id": "input", + "path": "src/components/Input.tsx", + "name": "Input", + "description": "A flexible text input component that supports various input types, validation states, and accessibility features.\n\nThe 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.\n\n## Accessibility\n\nThe Input component automatically manages ARIA attributes for labels, descriptions, and error messages to ensure screen reader compatibility.", + "summary": "A flexible text input component with validation support", + "import": "import { Input } from '@storybook/design-system';", + "reactDocgen": { + "props": { + "type": { + "description": "The type of input field", + "required": false, + "tsType": { + "name": "union", + "raw": "\"text\" | \"email\" | \"password\" | \"number\" | \"tel\" | \"url\"", + "elements": [ + { "name": "literal", "value": "\"text\"" }, + { "name": "literal", "value": "\"email\"" }, + { "name": "literal", "value": "\"password\"" }, + { "name": "literal", "value": "\"number\"" }, + { "name": "literal", "value": "\"tel\"" }, + { "name": "literal", "value": "\"url\"" } + ] + }, + "defaultValue": { "value": "\"text\"", "computed": false } + }, + "label": { + "description": "The label text for the input", + "required": false, + "tsType": { "name": "string" } + }, + "placeholder": { + "description": "Placeholder text shown when the input is empty", + "required": false, + "tsType": { "name": "string" } + }, + "value": { + "description": "The controlled value of the input", + "required": false, + "tsType": { "name": "string" } + }, + "defaultValue": { + "description": "The initial value for an uncontrolled input", + "required": false, + "tsType": { "name": "string" } + }, + "disabled": { + "description": "Whether the input is disabled", + "required": false, + "tsType": { "name": "boolean" }, + "defaultValue": { "value": "false", "computed": false } + }, + "required": { + "description": "Whether the input is required", + "required": false, + "tsType": { "name": "boolean" }, + "defaultValue": { "value": "false", "computed": false } + }, + "error": { + "description": "Error message to display below the input", + "required": false, + "tsType": { "name": "string" } + }, + "helperText": { + "description": "Helper text to display below the input", + "required": false, + "tsType": { "name": "string" } + }, + "onChange": { + "description": "Callback function when the input value changes", + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "signature": { + "arguments": [ + { + "name": "event", + "type": { + "name": "ChangeEvent", + "elements": [{ "name": "HTMLInputElement" }] + } + } + ], + "return": { "name": "void" } + } + } + } + } + }, + "stories": [ + { + "id": "input--basic", + "name": "Basic", + "description": "A basic text input with a label.\n\nThis 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.", + "summary": "Basic text input with label", + "import": "import { Input } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Basic Input" + } + ], + "snippet": "const Basic = () => " + }, + { + "id": "input--with-error", + "name": "WithError", + "description": "An input displaying an error state with an error message.\n\nError 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.", + "summary": "Input with validation error", + "import": "import { Input } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Input with Error" + } + ], + "snippet": "const WithError = () => " + }, + { + "id": "input--with-helper-text", + "name": "WithHelperText", + "description": "An input with helper text providing additional context or instructions.\n\nHelper text appears below the input and provides guidance without being an error. Use it to clarify format expectations, character limits, or provide helpful hints.", + "summary": "Input with helper text for guidance", + "import": "import { Input } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Input with Helper Text" + } + ], + "snippet": "const WithHelperText = () => " + }, + { + "id": "input--types", + "name": "Types", + "description": "Different input types for various data formats.\n\nUsing the correct input type improves the user experience by showing appropriate mobile keyboards and enabling browser validation features.", + "summary": "Various input types (email, tel, url, number)", + "import": "import { Input } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Input Types" + } + ], + "snippet": "const Types = () => (\n <>\n \n \n \n \n \n)" + }, + { + "id": "input--disabled", + "name": "Disabled", + "description": "A disabled input that cannot be interacted with.\n\nDisabled inputs are useful for displaying non-editable data in forms or for inputs that become available only after certain conditions are met.", + "summary": "Disabled input state", + "import": "import { Input } from '@storybook/design-system';", + "jsDocTag": [ + { + "key": "example", + "value": "Disabled Input" + } + ], + "snippet": "const Disabled = () => " + } + ], + "jsDocTag": [ + { + "key": "summary", + "value": "A flexible text input component with validation support" + }, + { + "key": "since", + "value": "1.0.0" + }, + { + "key": "component", + "value": "Input" + }, + { + "key": "accessibility", + "value": "WCAG 2.1 Level AA compliant" + } + ] + } + } +} diff --git a/packages/mcp/fixtures/default/docs.json b/packages/mcp/fixtures/default/docs.json new file mode 100644 index 00000000..5242d31e --- /dev/null +++ b/packages/mcp/fixtures/default/docs.json @@ -0,0 +1,19 @@ +{ + "v": 1, + "docs": { + "getting-started": { + "id": "getting-started", + "name": "Getting Started", + "title": "Getting Started Guide", + "path": "docs/getting-started.mdx", + "content": "# Getting Started\n\nWelcome to the component library. This guide will help you get up and running.\n\n## Installation\n\n```bash\nnpm install my-component-library\n```\n\n## Usage\n\nImport components and use them in your application." + }, + "theming": { + "id": "theming", + "name": "Theming", + "title": "Theming and Customization", + "path": "docs/theming.mdx", + "content": "# Theming\n\nLearn how to customize the look and feel of components using our theming system.\n\n## Theme Provider\n\nWrap your app with the ThemeProvider." + } + } +} diff --git a/packages/mcp/serve.ts b/packages/mcp/serve.ts index d00bee37..2f6f793b 100644 --- a/packages/mcp/serve.ts +++ b/packages/mcp/serve.ts @@ -3,24 +3,25 @@ import { serve } from 'srvx'; import fs from 'node:fs/promises'; import { parseArgs } from 'node:util'; import type { OutputFormat } from './src/types.ts'; +import { basename } from 'node:path'; async function serveMcp( port: number, - manifestPath: string, + manifestsDir: string, format: OutputFormat, ) { const storybookMcpHandler = await createStorybookMcpHandler({ format, // Use the local fixture file via manifestProvider - manifestProvider: async () => { + manifestProvider: async (_request, path) => { if ( - manifestPath.startsWith('http://') || - manifestPath.startsWith('https://') + manifestsDir.startsWith('http://') || + manifestsDir.startsWith('https://') ) { - const res = await fetch(manifestPath); + const res = await fetch(`${manifestsDir}/${basename(path)}`); return await res.text(); } - return await fs.readFile(manifestPath, 'utf-8'); + return await fs.readFile(`${manifestsDir}/${basename(path)}`, 'utf-8'); }, }); @@ -46,9 +47,9 @@ if (import.meta.main) { type: 'string', default: '13316', }, - manifestPath: { + manifestsDir: { type: 'string', - default: './fixtures/full-manifest.fixture.json', + default: './fixtures/default', }, format: { type: 'string', @@ -58,7 +59,7 @@ if (import.meta.main) { }); await serveMcp( Number(args.values.port), - args.values.manifestPath, + args.values.manifestsDir, args.values.format as OutputFormat, ); } From 861b5a4af1fffa04cded76ff38092eb81bd20649 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 8 Jan 2026 14:18:47 +0100 Subject: [PATCH 18/18] update storybook --- pnpm-lock.yaml | 902 +++++++++++++++++++++++++++++++++++++++----- pnpm-workspace.yaml | 24 +- 2 files changed, 825 insertions(+), 101 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0574418c..93d719ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,14 +7,14 @@ settings: catalogs: default: '@storybook/addon-a11y': - specifier: 10.2.0-alpha.12 - version: 10.2.0-alpha.12 + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 '@storybook/addon-docs': - specifier: 10.2.0-alpha.12 - version: 10.2.0-alpha.12 + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 '@storybook/react-vite': - specifier: 10.2.0-alpha.12 - version: 10.2.0-alpha.12 + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 '@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.2.0-alpha.12 - version: 10.2.0-alpha.12 + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 tmcp: specifier: ^1.16.0 version: 1.18.1 @@ -36,6 +36,73 @@ catalogs: vite: specifier: 7.2.2 version: 7.2.2 + experiments: + '@eslint/js': + specifier: 9.39.1 + version: 9.39.1 + '@storybook/addon-a11y': + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + '@storybook/addon-docs': + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + '@storybook/addon-vitest': + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + '@storybook/react-vite': + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + '@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 + '@vitest/browser-playwright': + specifier: 4.0.6 + version: 4.0.6 + 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 + eslint-plugin-storybook: + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + 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 + storybook: + specifier: 10.2.0-alpha.14 + version: 10.2.0-alpha.14 + 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 + vitest: + specifier: 4.0.6 + version: 4.0.6 importers: @@ -106,13 +173,13 @@ importers: devDependencies: '@storybook/addon-docs': specifier: 'catalog:' - version: 10.2.0-alpha.12(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + version: 10.2.0-alpha.14(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(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.2.0-alpha.12(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + version: 10.2.0-alpha.14(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@types/react': specifier: ^18.2.65 version: 18.3.26 @@ -130,7 +197,7 @@ importers: version: 18.3.1(react@18.3.1) storybook: specifier: 'catalog:' - version: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tinyexec: specifier: ^1.0.2 version: 1.0.2 @@ -163,13 +230,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.2.0-alpha.12(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + version: 10.2.0-alpha.14(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) '@storybook/mcp': specifier: workspace:* version: link:../packages/mcp '@storybook/react-vite': specifier: 'catalog:' - version: 10.2.0-alpha.12(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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)) + version: 10.2.0-alpha.14(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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 @@ -241,10 +308,10 @@ importers: version: 5.83.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) storybook: specifier: 'catalog:' - version: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) storybook-addon-test-codegen: specifier: ^3.0.0 - version: 3.0.0(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + version: 3.0.0(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) tinyexec: specifier: ^1.0.1 version: 1.0.2 @@ -258,6 +325,131 @@ 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/110-flight-booking-reshaped/experiments/storybook-mcp-docs-claude-code-claude-sonnet-4.5-2026-01-08T14-02-29/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + reshaped: + specifier: ^3.9.0 + version: 3.9.0(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + devDependencies: + '@eslint/js': + specifier: catalog:experiments + version: 9.39.1 + '@storybook/addon-a11y': + specifier: catalog:experiments + version: 10.2.0-alpha.14(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + '@storybook/addon-docs': + specifier: catalog:experiments + version: 10.2.0-alpha.14(@types/react@19.2.6)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/addon-mcp': + specifier: workspace:* + version: link:../../../../../../packages/addon-mcp + '@storybook/addon-vitest': + specifier: catalog:experiments + version: 10.2.0-alpha.14(@vitest/browser-playwright@4.0.6)(@vitest/browser@4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6))(@vitest/runner@4.0.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vitest@4.0.6) + '@storybook/react-vite': + specifier: catalog:experiments + version: 10.2.0-alpha.14(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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/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)) + '@vitest/browser-playwright': + specifier: catalog:experiments + version: 4.0.6(playwright@1.56.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6) + 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)) + eslint-plugin-storybook: + specifier: catalog:experiments + version: 10.2.0-alpha.14(eslint@9.39.1(jiti@2.6.1))(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3) + globals: + specifier: catalog:experiments + version: 16.5.0 + storybook: + specifier: catalog:experiments + version: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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) + vitest: + specifier: catalog:experiments + version: 4.0.6(@types/node@24.10.1)(@vitest/browser-playwright@4.0.6)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + + eval/evals/110-flight-booking-reshaped/experiments/storybook-mcp-docs-claude-code-claude-sonnet-4.5-2026-01-08T14-06-26/project: + dependencies: + react: + specifier: catalog:experiments + version: 19.2.0 + react-dom: + specifier: catalog:experiments + version: 19.2.0(react@19.2.0) + reshaped: + specifier: ^3.9.0 + version: 3.9.0(postcss@8.5.6)(react-dom@19.2.0(react@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': @@ -278,7 +470,7 @@ importers: devDependencies: storybook: specifier: 'catalog:' - version: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) packages/mcp: dependencies: @@ -712,6 +904,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -2013,28 +2211,46 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@storybook/addon-a11y@10.2.0-alpha.12': - resolution: {integrity: sha512-cWte+liYxej9SDRGUPcwolV8IHgHWNdHYz8vS3owBomOsbUU1dQ1Ny7X9aSqWY0GUO1BhPrapnaVmfl+J7xVwA==} + '@storybook/addon-a11y@10.2.0-alpha.14': + resolution: {integrity: sha512-RdW2P3QLta8p4FGrQZWrcgdhuyA6PqoLVQ1Xr+hOOYDkO1Zhr1KgcoGDYtGYM0GMm//Z40dLkYkqYPWCs4g2pg==} peerDependencies: - storybook: ^10.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 - '@storybook/addon-docs@10.2.0-alpha.12': - resolution: {integrity: sha512-ikj2oIqC7GxWS1orYRepc/Q3imjJcYJxTALSNkAsMyEyhH77yW8SXur0eEmdBG9RggM7btb0a2URJL6hOWwFuQ==} + '@storybook/addon-docs@10.2.0-alpha.14': + resolution: {integrity: sha512-4kQUfHk2uxrEfLVYyE5N7f94K33iWqIRUfxRd+NhT+SUwJkVHPQ8BqQ29Z9qwJ7RO6otAH2RwZO/BmZBqTgECA==} peerDependencies: - storybook: ^10.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 - '@storybook/builder-vite@10.2.0-alpha.12': - resolution: {integrity: sha512-3gfkjM5+tBnIVMviSD3kTix0jo84ZpD10q6V1nwU3px0hIezCqO68nUWyKKOKfskYFDHkLbjmcWgrDtlQ+u6tw==} + '@storybook/addon-vitest@10.2.0-alpha.14': + resolution: {integrity: sha512-wCb3lQyul+7TFjyJEu12GYfbd0bmc8qoPCFX6te1D79sS+SK4PBK2sjq8/3OH3VLtUbSrutw2uaTD7aQQsFrDw==} peerDependencies: - storybook: ^10.2.0-alpha.12 + '@vitest/browser': ^3.0.0 || ^4.0.0 + '@vitest/browser-playwright': ^4.0.0 + '@vitest/runner': ^3.0.0 || ^4.0.0 + storybook: ^10.2.0-alpha.14 + vitest: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/runner': + optional: true + vitest: + optional: true + + '@storybook/builder-vite@10.2.0-alpha.14': + resolution: {integrity: sha512-PZQ0oSVSfKaGe+h48MSGoH3wmcYRPbcmuX8G7GLORViW+x7gILLr0e3D5MF5C9rc4BHpfxikaXa3Gq5krUYeKQ==} + peerDependencies: + storybook: ^10.2.0-alpha.14 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/csf-plugin@10.2.0-alpha.12': - resolution: {integrity: sha512-cuWyacP/faHtL0pikyD/3oVvNftobGCn5XVPS3AnGi7B6x51ZQLQpiOd4lW8tw8EK/barxVcRm7CThDeiyibSg==} + '@storybook/csf-plugin@10.2.0-alpha.14': + resolution: {integrity: sha512-uACCM01OIzeBPOFeNDygxjIqLTlYDQHpzVvMveRz8kcHrP+1jOi6Cq5efMXMmmtb2ltwhr39bblkqLcZCz0lkg==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 vite: '*' webpack: '*' peerDependenciesMeta: @@ -2056,27 +2272,27 @@ packages: 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.2.0-alpha.12': - resolution: {integrity: sha512-3L5nzOGAcbrFXzJcew1j9mssRuszE8KaoDomxMZeirywo/GR8ealMe8DCqtkOOCnQj/2T3FQXN6+K+5waRJTow==} + '@storybook/react-dom-shim@10.2.0-alpha.14': + resolution: {integrity: sha512-QXIfC1XF1wRV2Y3/K7+yhqNUiWkStixk08SNOQ83RUI8ULzKb4GltRHyvt16gjQHHp+8LB5duV6PY8GI/3Il2g==} 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.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 - '@storybook/react-vite@10.2.0-alpha.12': - resolution: {integrity: sha512-o9D6kbly662VRG8N3e/En6ebEHNbmCPTENh3IwOM7IydvTX7eeuR8tosnOKYV+4UCkKyYsh5znnY9rXIyO5o1Q==} + '@storybook/react-vite@10.2.0-alpha.14': + resolution: {integrity: sha512-mrT0kUObQwc4G73f8FKEXdvPT1cZ/iORW8S7KrIyeV83u0FcMPxHcZcYGLIfdvpOgLNRBK6pCSadIKLV5pOD6w==} 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.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/react@10.2.0-alpha.12': - resolution: {integrity: sha512-+fE9Fd7K10uSFp45tgXgKRzHBkL8RBQ5TBa3S98R8pwyqqK1ebUdSDSNxqjwhQvvY0eFJGraYOLbafU8ksqjYA==} + '@storybook/react@10.2.0-alpha.14': + resolution: {integrity: sha512-IsBgaa+3Erluy5asRTwxZcMeSfo9x8TVuixUXd3LTOpdbi5rjgF7MrEDyNe8oDnxH4vEiQnX59QzMaMZ7CDZmw==} 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.2.0-alpha.12 + storybook: ^10.2.0-alpha.14 typescript: '>= 4.9.x' peerDependenciesMeta: typescript: @@ -2296,6 +2512,9 @@ packages: '@types/react@18.3.26': resolution: {integrity: sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==} + '@types/react@19.2.6': + resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==} + '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} @@ -2308,6 +2527,102 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@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/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} + 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/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} + 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/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} + 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/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} + 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/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} + 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/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} + 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} + + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} + 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: @@ -2605,6 +2920,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -2754,6 +3072,10 @@ packages: resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} engines: {node: '>=20'} + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -3067,6 +3389,23 @@ 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-plugin-storybook@10.2.0-alpha.14: + resolution: {integrity: sha512-W5K/nx+HWNKqWL3bY+9Cn+oJpE+StDAt1s8RDPmWAEj+SnHD7MUY3JZlZ7y5I4tEFZS6yMQKZKri7iz/kKq7Vw==} + peerDependencies: + eslint: '>=8' + storybook: ^10.2.0-alpha.14 + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -3335,6 +3674,9 @@ 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'} @@ -3353,6 +3695,12 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -3385,6 +3733,10 @@ 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'} @@ -3707,6 +4059,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -4363,6 +4719,14 @@ packages: react: ^18 || ^19 react-dom: ^18 || ^19 + reshaped@3.9.0: + resolution: {integrity: sha512-lIrTyxgAiCpZtcZoH4QfQb+hO0MeLDDZZ71BRwnYMsvXSkPtqilj5luJfGd9HfoioSUQ5j5oXl0BPBsTXPRClQ==} + hasBin: true + peerDependencies: + postcss: ^8 + react: ^18 || ^19 + react-dom: ^18 || ^19 + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -4594,8 +4958,8 @@ packages: peerDependencies: storybook: ^10.0.0 - storybook@10.2.0-alpha.12: - resolution: {integrity: sha512-PtQRDRox+I2zLKLiSmKPmI/GNs/YoKESophHKXoABoP/fCXll0xPrD3wTG4G+gdbXKud4y2g461IfW7BLP3PyQ==} + storybook@10.2.0-alpha.14: + resolution: {integrity: sha512-ZyGO3qVK1ARL08nyVSpb88k8KGIJTJ62nsVNdUCTeyDct/0G7OXFS3F/Ogk5o0EAAS3iymVBKnX/76v/PH3keA==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -4745,6 +5109,12 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + 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'} @@ -4855,6 +5225,13 @@ 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'} @@ -5158,6 +5535,12 @@ 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==} @@ -5630,6 +6013,11 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.1(jiti@2.6.1))': + dependencies: + eslint: 9.39.1(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.1': @@ -5841,6 +6229,12 @@ snapshots: '@types/react': 18.3.26 react: 19.2.0 + '@mdx-js/react@3.1.1(@types/react@19.2.6)(react@19.2.0)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.2.6 + react: 19.2.0 + '@modelcontextprotocol/inspector-cli@0.17.2': dependencies: '@modelcontextprotocol/sdk': 1.22.0 @@ -6078,8 +6472,7 @@ snapshots: '@oxlint/win32-x64@1.28.0': optional: true - '@polka/url@1.0.0-next.29': - optional: true + '@polka/url@1.0.0-next.29': {} '@publint/pack@0.1.2': {} @@ -7353,21 +7746,21 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@storybook/addon-a11y@10.2.0-alpha.12(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': + '@storybook/addon-a11y@10.2.0-alpha.14(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.0 - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/addon-docs@10.2.0-alpha.12(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/addon-docs@10.2.0-alpha.14(@types/react@18.3.26)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(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.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/csf-plugin': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(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.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@storybook/react-dom-shim': 10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -7376,11 +7769,42 @@ snapshots: - vite - webpack - '@storybook/builder-vite@10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/addon-docs@10.2.0-alpha.14(@types/react@19.2.6)(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@mdx-js/react': 3.1.1(@types/react@19.2.6)(react@19.2.0) + '@storybook/csf-plugin': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(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.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(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) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - esbuild + - rollup + - vite + - webpack + + '@storybook/addon-vitest@10.2.0-alpha.14(@vitest/browser-playwright@4.0.6)(@vitest/browser@4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6))(@vitest/runner@4.0.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vitest@4.0.6)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + optionalDependencies: + '@vitest/browser': 4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6) + '@vitest/browser-playwright': 4.0.6(playwright@1.56.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6) + '@vitest/runner': 4.0.6 + vitest: 4.0.6(@types/node@24.10.1)(@vitest/browser-playwright@4.0.6)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + transitivePeerDependencies: + - react + - react-dom + + '@storybook/builder-vite@10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + dependencies: + '@storybook/csf-plugin': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@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)) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 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: @@ -7389,11 +7813,11 @@ snapshots: - rollup - webpack - '@storybook/builder-vite@10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/builder-vite@10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - '@storybook/csf-plugin': 10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/csf-plugin': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) '@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)) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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: @@ -7402,9 +7826,9 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/csf-plugin@10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) unplugin: 2.3.10 optionalDependencies: esbuild: 0.25.12 @@ -7412,9 +7836,9 @@ snapshots: 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/csf-plugin@10.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/csf-plugin@10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) unplugin: 2.3.10 optionalDependencies: esbuild: 0.25.12 @@ -7434,37 +7858,37 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@storybook/react-dom-shim@10.2.0-alpha.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@storybook/react-dom-shim@10.2.0-alpha.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(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: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim@10.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@storybook/react-dom-shim@10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim@10.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))': + '@storybook/react-dom-shim@10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(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: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/react-vite@10.2.0-alpha.12(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': + '@storybook/react-vite@10.2.0-alpha.14(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.3(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.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) - '@storybook/react': 10.2.0-alpha.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3) + '@storybook/builder-vite': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 10.2.0-alpha.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(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.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 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: @@ -7475,19 +7899,19 @@ snapshots: - typescript - webpack - '@storybook/react-vite@10.2.0-alpha.12(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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))': + '@storybook/react-vite@10.2.0-alpha.14(esbuild@0.25.12)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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.3(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.2.0-alpha.12(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.12(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) - '@storybook/react': 10.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3) + '@storybook/builder-vite': 10.2.0-alpha.14(esbuild@0.25.12)(rollup@4.52.5)(storybook@10.2.0-alpha.14(@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))(webpack@5.102.1(@swc/core@1.13.5)(esbuild@0.25.12)) + '@storybook/react': 10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.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: @@ -7498,27 +7922,27 @@ snapshots: - typescript - webpack - '@storybook/react@10.2.0-alpha.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)': + '@storybook/react@10.2.0-alpha.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.2.0-alpha.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@storybook/react-dom-shim': 10.2.0-alpha.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) react: 18.3.1 react-docgen: 8.0.2 react-dom: 18.3.1(react@18.3.1) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@storybook/react@10.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3)': + '@storybook/react@10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.2.0-alpha.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + '@storybook/react-dom-shim': 10.2.0-alpha.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) react: 19.2.0 react-docgen: 8.0.2 react-dom: 19.2.0(react@19.2.0) - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7718,6 +8142,10 @@ snapshots: '@types/react': 18.3.26 optional: true + '@types/react-dom@19.2.3(@types/react@19.2.6)': + dependencies: + '@types/react': 19.2.6 + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 @@ -7732,6 +8160,10 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.2.3 + '@types/react@19.2.6': + dependencies: + csstype: 3.2.3 + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -7743,6 +8175,150 @@ snapshots: '@types/unist@3.0.3': {} + '@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.4.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/project-service@8.52.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.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/scope-manager@8.52.0': + dependencies: + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 + + '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/tsconfig-utils@8.52.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.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.47.0': {} + + '@typescript-eslint/types@8.52.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.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.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/utils@8.52.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.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 + + '@typescript-eslint/visitor-keys@8.52.0': + dependencies: + '@typescript-eslint/types': 8.52.0 + eslint-visitor-keys: 4.2.1 + '@valibot/to-json-schema@1.3.0(valibot@1.2.0(typescript@5.9.3))': dependencies: valibot: 1.2.0(typescript@5.9.3) @@ -7781,6 +8357,19 @@ snapshots: - vite optional: true + '@vitest/browser-playwright@4.0.6(playwright@1.56.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6)': + dependencies: + '@vitest/browser': 4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6) + '@vitest/mocker': 4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + playwright: 1.56.1 + tinyrainbow: 3.0.3 + vitest: 4.0.6(@types/node@24.10.1)(@vitest/browser-playwright@4.0.6)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/browser@4.0.6(vite@7.2.2(@types/node@20.19.0)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6)': dependencies: '@vitest/mocker': 4.0.6(vite@7.2.2(@types/node@20.19.0)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) @@ -7799,6 +8388,23 @@ snapshots: - vite optional: true + '@vitest/browser@4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6)': + dependencies: + '@vitest/mocker': 4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + '@vitest/utils': 4.0.6 + magic-string: 0.30.21 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.0.3 + vitest: 4.0.6(@types/node@24.10.1)(@vitest/browser-playwright@4.0.6)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/coverage-v8@4.0.6(@vitest/browser@4.0.6(vite@7.2.2(@types/node@20.19.0)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6))(vitest@4.0.6)': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -7851,6 +8457,14 @@ snapshots: optionalDependencies: vite: 7.2.2(@types/node@20.19.0)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + '@vitest/mocker@4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))': + dependencies: + '@vitest/spy': 4.0.6 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 @@ -8123,6 +8737,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -8253,6 +8871,8 @@ snapshots: commander@14.0.1: {} + commander@14.0.2: {} + commander@2.20.3: optional: true @@ -8555,6 +9175,30 @@ 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-plugin-storybook@10.2.0-alpha.14(eslint@9.39.1(jiti@2.6.1))(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/utils': 8.52.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - supports-color + - typescript + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -8868,6 +9512,8 @@ snapshots: graceful-fs@4.2.11: {} + graphemer@1.4.0: {} + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -8888,6 +9534,12 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@10.7.3: {} highlightjs-vue@1.0.0: {} @@ -8916,6 +9568,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.5: {} + image-size@0.5.5: optional: true @@ -9198,6 +9852,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + minimist@1.2.8: {} minipass@7.1.2: {} @@ -9211,8 +9869,7 @@ snapshots: mri@1.2.0: {} - mrmime@2.0.1: - optional: true + mrmime@2.0.1: {} ms@2.1.3: {} @@ -9397,7 +10054,6 @@ snapshots: pixelmatch@7.1.0: dependencies: pngjs: 7.0.0 - optional: true pkce-challenge@4.1.0: {} @@ -9434,8 +10090,7 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - pngjs@7.0.0: - optional: true + pngjs@7.0.0: {} postcss-calc@10.1.1(postcss@8.5.6): dependencies: @@ -9921,6 +10576,19 @@ snapshots: transitivePeerDependencies: - encoding + reshaped@3.9.0(postcss@8.5.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@csstools/postcss-global-data': 3.1.0(postcss@8.5.6) + chalk: 4.1.2 + commander: 14.0.2 + cssnano: 7.1.1(postcss@8.5.6) + csstype: 3.1.3 + culori: 4.0.2 + postcss: 8.5.6 + postcss-custom-media: 11.0.6(postcss@8.5.6) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -10173,7 +10841,6 @@ snapshots: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 - optional: true sisteransi@1.0.5: {} @@ -10219,11 +10886,11 @@ snapshots: std-env@3.10.0: {} - storybook-addon-test-codegen@3.0.0(storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): + storybook-addon-test-codegen@3.0.0(storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): dependencies: - storybook: 10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + storybook: 10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -10246,7 +10913,7 @@ snapshots: - react-dom - utf-8-validate - storybook@10.2.0-alpha.12(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + storybook@10.2.0-alpha.14(@testing-library/dom@10.4.1)(prettier@3.6.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -10390,13 +11057,16 @@ snapshots: toidentifier@1.0.1: {} - totalist@3.0.1: - optional: true + totalist@3.0.1: {} tr46@0.0.3: {} tree-kill@1.2.2: {} + ts-api-utils@2.4.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): @@ -10500,6 +11170,17 @@ 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: {} @@ -10686,6 +11367,45 @@ snapshots: - tsx - yaml + vitest@4.0.6(@types/node@24.10.1)(@vitest/browser-playwright@4.0.6)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0): + dependencies: + '@vitest/expect': 4.0.6 + '@vitest/mocker': 4.0.6(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0)) + '@vitest/pretty-format': 4.0.6 + '@vitest/runner': 4.0.6 + '@vitest/snapshot': 4.0.6 + '@vitest/spy': 4.0.6 + '@vitest/utils': 4.0.6 + debug: 4.4.3 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.10.1 + '@vitest/browser-playwright': 4.0.6(playwright@1.56.1)(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(terser@5.44.0))(vitest@4.0.6) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 @@ -10798,4 +11518,8 @@ 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: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 91f928df..5b3a1b09 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,15 +6,15 @@ packages: - eval/evals/*/experiments/*/project catalog: - '@storybook/addon-a11y': 10.2.0-alpha.12 - '@storybook/addon-docs': 10.2.0-alpha.12 - '@storybook/addon-vitest': 10.2.0-alpha.12 - '@storybook/react-vite': 10.2.0-alpha.12 + '@storybook/addon-a11y': 10.2.0-alpha.14 + '@storybook/addon-docs': 10.2.0-alpha.14 + '@storybook/addon-vitest': 10.2.0-alpha.14 + '@storybook/react-vite': 10.2.0-alpha.14 '@tmcp/adapter-valibot': ^0.1.4 '@tmcp/transport-http': ^0.8.0 '@tmcp/transport-stdio': ^0.4.1 - eslint-plugin-storybook: 10.2.0-alpha.12 - storybook: 10.2.0-alpha.12 + eslint-plugin-storybook: 10.2.0-alpha.14 + storybook: 10.2.0-alpha.14 tmcp: ^1.16.0 tsdown: ^0.15.12 typescript: ^5.9.3 @@ -25,10 +25,10 @@ catalog: catalogs: experiments: '@eslint/js': 9.39.1 - '@storybook/addon-a11y': 10.2.0-alpha.12 - '@storybook/addon-docs': 10.2.0-alpha.12 - '@storybook/addon-vitest': 10.2.0-alpha.12 - '@storybook/react-vite': 10.2.0-alpha.12 + '@storybook/addon-a11y': 10.2.0-alpha.14 + '@storybook/addon-docs': 10.2.0-alpha.14 + '@storybook/addon-vitest': 10.2.0-alpha.14 + '@storybook/react-vite': 10.2.0-alpha.14 '@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.2.0-alpha.12 + eslint-plugin-storybook: 10.2.0-alpha.14 globals: 16.5.0 react: 19.2.0 react-dom: 19.2.0 - storybook: 10.2.0-alpha.12 + storybook: 10.2.0-alpha.14 typescript: 5.9.3 typescript-eslint: 8.47.0 vite: 7.2.2