/** * Public Fixture API * * This module is the public API for defining fixtures. * It's designed to be stable across versions - projects can use a different * @vscode/component-explorer version than the explorer viewer uses. * * Use Symbol.for() instead of Symbol() so the same symbol is shared across * different versions of this package. * * @module fixtureApi */ /** * Disposable resource that can be cleaned up. */ export interface IDisposable { dispose(): void; } /** @deprecated Use IDisposable instead */ export type Disposable = IDisposable; /** * Result of a render operation. * Supports both sync and async rendering with cancellation. * All fields are optional for simple use cases. */ export interface RenderResult { /** * Resolves when the component is fully rendered and ready. * For sync renders, this can be omitted (treated as immediately ready). * Rejects if the render is aborted via the AbortSignal. */ readonly ready?: Promise; /** * Cleanup function. Called when the fixture is unmounted. * Can be omitted if no cleanup is needed. */ dispose?(): void; /** * Arbitrary data to include in the render report. * Visible to the CLI and MCP tools. */ readonly data?: unknown; } /** * What the render function can return. * - undefined: no cleanup needed, immediately ready * - RenderResult: object with optional ready/dispose * - Promise<...>: async render that resolves to undefined or RenderResult */ export type RenderReturn = RenderResult | undefined | void | Promise; /** * Context passed to the render function. */ export interface RenderContext { /** Property values based on the property schema */ readonly props: Record; /** AbortSignal for cancellation; check signal.aborted or listen to 'abort' event */ readonly signal: AbortSignal; } /** * Style definition for shadow DOM injection. */ export type StyleDefinition = { readonly type: 'css'; readonly content: string; } | { readonly type: 'url'; readonly href: string; } | { readonly type: 'adopted'; readonly sheet: CSSStyleSheet; }; /** * Display mode for a component. */ export type DisplayMode = PageMode | ComponentMode; /** * Page mode - component fills a viewport with device presets. */ export interface PageMode { readonly type: 'page'; readonly viewports: ViewportPreset[]; } /** * Component mode - renders at natural size. */ export interface ComponentMode { readonly type: 'component'; } /** * Viewport preset - either a named preset or custom dimensions. */ export type ViewportPreset = ViewportPresetName | { readonly name: string; readonly width: number; readonly height: number; }; export type ViewportPresetName = 'mobile' | 'tablet' | 'desktop'; /** * Schema for a component property. */ export type PropertySchema = BooleanProperty | StringProperty | NumberProperty | EnumProperty; export interface BooleanProperty { readonly type: 'boolean'; readonly name: string; readonly defaultValue: boolean; readonly description?: string; } export interface StringProperty { readonly type: 'string'; readonly name: string; readonly defaultValue: string; readonly description?: string; readonly multiline?: boolean; } export interface NumberProperty { readonly type: 'number'; readonly name: string; readonly defaultValue: number; readonly description?: string; readonly min?: number; readonly max?: number; readonly step?: number; } export interface EnumProperty { readonly type: 'enum'; readonly name: string; readonly defaultValue: string; readonly description?: string; readonly options: readonly string[]; } /** Brand symbol to identify single fixtures */ export declare const singleFixtureBrand: unique symbol; /** Brand symbol to identify fixture groups */ export declare const fixtureGroupBrand: unique symbol; /** Brand symbol to identify fixture variants */ export declare const fixtureVariantsBrand: unique symbol; /** * Options for defining a single component fixture. */ export interface DefineFixtureOptions { /** * Path in the explorer tree. * - `undefined` (default): use fixture filename only * - `'Foo/Bar'`: exact path (fixture filename NOT appended) * - `'Foo/Bar/'`: prefix (fixture filename IS appended → Foo/Bar/{filename}) * * @throws Error if path contains empty segments (e.g., '/foo', 'foo//bar') */ path?: string; /** Optional description for documentation */ description?: string; /** How to isolate: 'none' (default) renders in light DOM, 'shadow-dom' for CSS isolation, 'iframe' for full isolation */ isolation?: 'none' | 'shadow-dom' | 'iframe'; /** Display mode: defaults to { type: 'component' } */ displayMode?: DisplayMode; /** Styles to inject (into document head for 'none', into shadow root for 'shadow-dom', into iframe head for 'iframe') */ styles?: StyleDefinition[]; /** Background pattern for the preview canvas: 'light' (default) or 'dark' for dark transparent pattern */ background?: 'light' | 'dark'; /** Labels for categorization and filtering (e.g. 'animation', 'blocks-ci') */ labels?: readonly string[]; /** Property definitions */ properties?: PropertySchema[]; /** * Render the component into the container. * * @param container - The DOM element to render into * @param context - Render context containing props and abort signal * @returns Optional RenderResult, or a Promise that resolves to one */ render: (container: HTMLElement, context: RenderContext) => RenderReturn; } /** @deprecated Use DefineFixtureOptions instead */ export type DefineComponentOptions = DefineFixtureOptions; /** * A single fixture export created by defineFixture(). */ export interface SingleFixtureExport { readonly [singleFixtureBrand]: true; readonly _options: DefineFixtureOptions; readonly _path?: string; } /** * Group entry: either a single fixture, a nested group, or variants. */ export type FixtureGroupEntry = SingleFixtureExport | FixtureGroupExport | FixtureVariantsExport; /** * A fixture group export created by defineFixtureGroup(). */ export interface FixtureGroupExport { readonly [fixtureGroupBrand]: true; readonly _entries: { [key: string]: FixtureGroupEntry; }; readonly _path?: string; readonly _labels?: readonly string[]; } /** * A fixture variants export created by defineFixtureVariants(). */ export interface FixtureVariantsExport { readonly [fixtureVariantsBrand]: true; readonly _variants: { [key: string]: SingleFixtureExport; }; readonly _path?: string; readonly _labels?: readonly string[]; } /** * Fixture export type - a single fixture, a group, or variants. */ export type FixtureExport = SingleFixtureExport | FixtureGroupExport | FixtureVariantsExport; /** * Input for defineFixtureGroup. */ export type FixtureGroupInput = { [key: string]: FixtureGroupEntry; }; /** * Options for defineFixtureGroup with optional path. */ export interface DefineFixtureGroupOptions { /** * Path in the explorer tree. * - `undefined` (default): use fixture filename only * - `'Foo/Bar'`: exact path (fixture filename NOT appended) * - `'Foo/Bar/'`: prefix (fixture filename IS appended → Foo/Bar/{filename}) */ path?: string; /** Labels for categorization and filtering (e.g. 'animation', 'blocks-ci') */ labels?: readonly string[]; } /** * Input for defineFixtureVariants (only single fixtures, no nesting). */ export type FixtureVariantsInput = { [key: string]: SingleFixtureExport; }; /** * Options for defineFixtureVariants with optional path. */ export interface DefineFixtureVariantsOptions { /** * Path in the explorer tree. * - `undefined` (default): use fixture filename only * - `'Foo/Bar'`: exact path (fixture filename NOT appended) * - `'Foo/Bar/'`: prefix (fixture filename IS appended → Foo/Bar/{filename}) */ path?: string; /** Labels for categorization and filtering (e.g. 'animation', 'blocks-ci') */ labels?: readonly string[]; } /** * Defines a single fixture. * * @example * ```ts * // Simple - no cleanup needed * export default defineFixture({ * render: (container, { props }) => { * container.innerHTML = ``; * }, * }); * * // With cleanup * export default defineFixture({ * render: (container, { props }) => { * const root = createRoot(container); * root.render(