@ggui-ai/ui-gen 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-Cg_rkpGq.d.ts +201 -0
- package/dist/adapters/generation-dispatch.d.ts +58 -15
- package/dist/adapters/generation-dispatch.js +1418 -180
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +3 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +12 -8
- package/dist/advanced/index.js +1467 -239
- package/dist/advanced/index.js.map +1 -1
- package/dist/{axes-CzLEMDeB.d.ts → axes-CKVCzR_x.d.ts} +1 -1
- package/dist/bank-CT4eEkta.d.ts +118 -0
- package/dist/blueprint-validator.js +153 -59
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +24 -12
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +12 -4
- package/dist/check/index.js +94 -2
- package/dist/check/index.js.map +1 -1
- package/dist/classifier/index.d.ts +2 -2
- package/dist/compose.d.ts +2 -2
- package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-DFnnArrv.d.ts} +43 -11
- package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-CStl9eAw.d.ts} +5 -3
- package/dist/evaluation/axis-checks/index.d.ts +6 -5
- package/dist/evaluation/axis-checks/index.js +90 -2
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +4 -3
- package/dist/evaluation/axis-checks/registry.js +88 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +301 -23
- package/dist/evaluation/index.js +746 -30
- package/dist/evaluation/index.js.map +1 -1
- package/dist/evaluation/types.d.ts +9 -1
- package/dist/evaluation/types.js.map +1 -1
- package/dist/fragments/index.d.ts +3 -3
- package/dist/harness/check/runtime-render/index.d.ts +30 -11
- package/dist/harness/check/runtime-render/index.js +203 -60
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +5 -4
- package/dist/harness/index.js +33 -15
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +4 -3
- package/dist/harness/runtime.d.ts +11 -4
- package/dist/harness/runtime.js +26 -14
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +13 -4
- package/dist/harness/types-public.js +33 -15
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +4 -3
- package/dist/hash.d.ts +1 -1
- package/dist/index.d.ts +59 -8
- package/dist/index.js +1476 -241
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-DwwUK1bz.d.ts → llm-router-nfO7wPec.d.ts} +3 -1
- package/dist/patch.d.ts +4 -3
- package/dist/policy.d.ts +8 -5
- package/dist/policy.js.map +1 -1
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/tools/render-check-worker.d.ts +7 -1
- package/dist/tools/render-check-worker.js +55 -23
- package/dist/tools/render-check-worker.js.map +1 -1
- package/dist/{types-BOvHNG7K.d.ts → types-Dh1s4K9j.d.ts} +1 -1
- package/dist/{types-public-Ow-tNtFE.d.ts → types-public-BJTLpzWn.d.ts} +208 -15
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +4 -3
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +173 -98
- package/src/adapters/tools.ts +2 -1
- package/src/boilerplate/free-design-prompt.ts +1 -1
- package/src/boilerplate/generate.ts +26 -1
- package/src/boilerplate/hard-sections.ts +13 -13
- package/src/boilerplate/system-prompt.ts +1 -1
- package/src/check/index.ts +1 -0
- package/src/check/run-tier0.ts +80 -1
- package/src/check/sample-props.ts +63 -0
- package/src/coding-agent/tools.ts +4 -1
- package/src/create-harness.ts +3 -3
- package/src/create-ui-generator.ts +204 -95
- package/src/evaluation/__fixtures__/png.ts +100 -0
- package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
- package/src/evaluation/axis-checks/checks/universal.ts +111 -0
- package/src/evaluation/axis-checks/dispatch.ts +4 -1
- package/src/evaluation/criteria/bank.ts +85 -0
- package/src/evaluation/criteria/context.ts +52 -0
- package/src/evaluation/criteria/resolve.ts +159 -0
- package/src/evaluation/criteria/select.ts +79 -0
- package/src/evaluation/index.ts +23 -0
- package/src/evaluation/ink-extent.ts +200 -0
- package/src/evaluation/theme-css.ts +12 -5
- package/src/evaluation/types-public.ts +200 -11
- package/src/evaluation/types.ts +9 -0
- package/src/evaluation/visual-evaluator.ts +636 -30
- package/src/harness/check/runtime-render/adapter.ts +221 -75
- package/src/harness/check/runtime-render/index.ts +75 -2
- package/src/harness/check/runtime-render/render-check-host.ts +82 -19
- package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
- package/src/harness/check/runtime-render/render-check.ts +154 -117
- package/src/harness/coding/generate-task-runner.ts +60 -4
- package/src/harness/coding/init-session.ts +13 -0
- package/src/harness/coding/run-coding-turn.ts +7 -3
- package/src/harness/coding/run-eval-round.ts +257 -15
- package/src/harness/policy.ts +11 -0
- package/src/harness/primitive-slice.ts +104 -0
- package/src/harness/runtime.ts +5 -0
- package/src/harness/types-public.ts +9 -1
- package/src/harness-tool-names.ts +38 -0
- package/src/index.ts +16 -0
- package/src/policy.ts +7 -4
- package/src/run-check.ts +2 -0
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools/get-wire.ts +1 -1
- package/src/tools/render-check-worker.ts +74 -23
- package/src/tools/render-check.ts +0 -60
- package/src/tools.ts +11 -1
- package/src/validation/primitives.ts +1 -1
- package/dist/render-check-Blwj8gB6.d.ts +0 -105
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tools/get-primitives-ts.ts"],"names":[],"mappings":";AAQO,IAAM,2BAAA,GAA8B","file":"get-primitives-ts.js","sourcesContent":["/**\n * AUTO-GENERATED from @ggui-ai/design JSDoc (TS-interface format).\n * Do not edit manually. Run: pnpm --filter @ggui-ai/design generate:docs-ts\n *\n * Experiment #57 (Option A) — processed/compacted doc for LLM consumption.\n * Companion to get-primitives.ts (markdown-table format). Swap between\n * them via ContextPolicy.primitiveDocFormat.\n */\nexport const PRIMITIVES_DOCUMENTATION_TS = \"// ════════════════════════════════════════════════════════════\\n// GGUI DESIGN SYSTEM — COMPONENT REFERENCE\\n// ════════════════════════════════════════════════════════════\\n//\\n// CRITICAL — Props take enum STRING LITERALS, not CSS variables:\\n// <Text size=\\\"sm\\\" weight=\\\"bold\\\" /> ✓\\n// <Text size=\\\"var(--ggui-font-size-sm)\\\" /> ✗ (breaks)\\n// The enum strings map to CSS variables internally.\\n//\\n// CRITICAL — onChange receives the VALUE directly, not an event:\\n// <Input value={x} onChange={setX} /> ✓\\n// <Input onChange={(e) => setX(e.target.value)} /> ✗\\n//\\n// Imports allowed: 'react', '@ggui-ai/design', '@ggui-ai/wire'.\\n// The WHOLE design system — primitives, components, compositions,\\n// traits — is one import: import { Card, Grid, Modal, Clickable }\\n// from '@ggui-ai/design'. No subpaths. No external libs, no\\n// fetch(), no eval().\\n//\\n// All components accept 'style' + 'className' (omitted below).\\n// Structural primitives (Box, Stack, Row, Card) also take a trait via\\n// 'as': 'as={Clickable}' adds onClick + keyboard a11y; likewise\\n// 'as={Hoverable}' and 'as={Pressable}'. Semantic components\\n// (Button/Link/Input) are already interactive and take no 'as'.\\n// ════════════════════════════════════════════════════════════\\n\\n// ════════════════════════════════════════════════════════════\\n// Primitives (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// Container — Width-constrained wrapper that centers content horizontally\\ninterface ContainerProps {\\n children?: ReactNode;\\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full' | string; // default 'lg'; Maximum width constraint\\n center?: boolean; // default true; Whether to center the container horizontally via `margin: 0 auto`\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no padding); Padding applied to all sides\\n}\\n\\n// Example:\\n<Container maxWidth=\\\"xl\\\" padding=\\\"var(--ggui-spacing-6)\\\">\\n <Stack gap=\\\"var(--ggui-spacing-4)\\\">\\n <Heading level={1}>Dashboard</Heading>\\n <Card shadow=\\\"md\\\" padding=\\\"var(--ggui-spacing-5)\\\">\\n <Text>Welcome back!</Text>\\n </Card>\\n </Stack>\\n</Container>\\n\\n// Card — Container with background, shadow, and optional border\\ninterface CardProps {\\n children?: ReactNode;\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'lg'; Padding applied to all sides\\n shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl'; // default 'sm'; Shadow elevation level\\n border?: boolean; // default true; Whether to render a 1px border using `var(--ggui-color-outlineVariant)`\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default 'lg'; Corner radius\\n surface?: 'default' | 'elevated' | 'sunken' | 'accent' | 'inverted' | 'hero' | 'transparent'; // default 'default'; Semantic surface slot\\n bleed?: boolean; // default false; Span the expanded frame's inset edge to edge (ggui#1083)\\n}\\n\\n// Example:\\n<Card shadow=\\\"md\\\" padding=\\\"lg\\\" radius=\\\"lg\\\">\\n <Stack gap=\\\"md\\\">\\n <Text size=\\\"sm\\\" weight=\\\"medium\\\">Settings</Text>\\n <Input label=\\\"Name\\\" value={name} onChange={setName} />\\n <Button variant=\\\"primary\\\">Save</Button>\\n </Stack>\\n</Card>\\n\\n// Stack — Flexbox layout primitive for arranging children along a single axis\\ninterface StackProps {\\n children?: ReactNode;\\n direction?: 'vertical' | 'horizontal'; // default 'vertical'; Main axis direction\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'sm'; Gap between children\\n align?: 'start' | 'center' | 'end' | 'stretch'; // default 'stretch'; Cross-axis alignment (maps to `align-items`)\\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; // default 'start'; Main-axis content distribution (maps to `justify-content`)\\n wrap?: boolean; // default false; Whether children wrap to the next line when they overflow\\n}\\n\\n// Example:\\n<Stack gap=\\\"lg\\\" align=\\\"center\\\">\\n <Heading level={2}>Profile</Heading>\\n <Text>Edit your account details below.</Text>\\n <Stack direction=\\\"horizontal\\\" gap=\\\"sm\\\" justify=\\\"end\\\">\\n <Button variant=\\\"ghost\\\">Cancel</Button>\\n <Button variant=\\\"primary\\\">Save</Button>\\n </Stack>\\n</Stack>\\n\\n// Grid — 2-D layout primitive\\ninterface GridProps {\\n children?: ReactNode;\\n columns?: number | ResponsiveColumns; // default 2; Column count\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'md'; Gap between cells\\n minColumnWidth?: number | string; // default undefined (use `columns`); When set, the grid becomes responsive — it fits as many equal\\n}\\n\\n// Example:\\n<Grid columns={3} gap=\\\"md\\\">\\n {items.map((it) => <Card key={it.id}>{it.name}</Card>)}\\n</Grid>\\n\\n// Skeleton — a pulsing placeholder for content that has not loaded\\ninterface SkeletonProps {\\n variant?: 'rect' | 'text' | 'circle'; // default 'rect'; Shape preset\\n width?: number | string;\\n height?: number | string;\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default 'sm'; Corner radius\\n}\\n\\n// Example:\\n{user === undefined\\n ? <Skeleton variant=\\\"text\\\" width=\\\"40%\\\" />\\n : <Text>{user.name}</Text>}\\n\\n// Box — Generic container with padding, margin, background, and border-radius\\ninterface BoxProps {\\n children?: ReactNode;\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no padding); Padding applied to all four sides\\n paddingX?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined; Horizontal (left + right) padding\\n paddingY?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined; Vertical (top + bottom) padding\\n margin?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no margin); Margin applied to all four sides\\n surface?: 'default' | 'elevated' | 'sunken' | 'accent' | 'inverted' | 'hero' | 'transparent'; // default undefined (transparent); Semantic surface slot\\n bleed?: boolean; // default false; Span the expanded frame's inset edge to edge (ggui#1083)\\n assetColor?: string; // default undefined; Asset color escape — the typed valve for legitimate non-theme\\n assetSemantic?: string; // default undefined; Human-readable semantic label that documents why\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default undefined (no rounding); Corner radius\\n}\\n\\n// Example:\\n<Box paddingX=\\\"xl\\\" paddingY=\\\"lg\\\" surface=\\\"accent\\\" radius=\\\"lg\\\">\\n <Text size=\\\"sm\\\" tone=\\\"emphasized\\\">\\n Tip: You can customize your theme in Settings.\\n </Text>\\n</Box>\\n\\n// Divider — A 1px line to visually separate content sections\\ninterface DividerProps {\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Line direction\\n margin?: number | string; // default 16; Spacing around the divider\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-outlineVariant)`); Semantic color slot\\n}\\n\\n// Example:\\n<Stack gap={0}>\\n <Text>Section A</Text>\\n <Divider margin=\\\"var(--ggui-spacing-3)\\\" />\\n <Text>Section B</Text>\\n</Stack>\\n\\n// Spacer — Invisible spacing element, either fixed-size or flexible\\ninterface SpacerProps {\\n size?: number | 'flex'; // default 16; Spacing amount\\n}\\n\\n// Example:\\n<Stack direction=\\\"horizontal\\\" align=\\\"center\\\">\\n <Heading level={3}>Logo</Heading>\\n <Spacer size=\\\"flex\\\" />\\n <Button variant=\\\"ghost\\\">Login</Button>\\n</Stack>\\n\\n// Text — Versatile typography primitive for body copy, captions, and labels\\ninterface TextProps {\\n children?: ReactNode;\\n size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; // default 'base'; Font size\\n weight?: 'normal' | 'medium' | 'semibold' | 'bold'; // default 'normal'; Font weight\\n caps?: boolean; // default false; When true, uppercases the text and adds 0\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default 'default' (var(--ggui-color-onContainer)); Semantic color slot\\n align?: 'left' | 'center' | 'right'; // default undefined (inherits from parent); Horizontal text alignment\\n truncate?: boolean; // default false; When true, clips overflowing text with an ellipsis\\n is?: 'p' | 'span' | 'div' | 'label'; // default 'p'; HTML element to render\\n id?: string; // `id` for the rendered element — anchor an in-page link, or pair\\n htmlFor?: string; // Associates an `is=\\\"label\\\"` element with a form control by the\\n}\\n\\n// Example:\\n<Stack gap=\\\"var(--ggui-spacing-1)\\\">\\n <Text size=\\\"xs\\\" weight=\\\"semibold\\\" caps tone=\\\"muted\\\">{props.section}</Text>\\n <Text size=\\\"lg\\\">Welcome back, Jane.</Text>\\n <Text size=\\\"xs\\\" tone=\\\"muted\\\">\\n Last login: 2 hours ago\\n </Text>\\n</Stack>\\n\\n// Heading — Semantic heading element (h1-h6) with preset typography styles\\ninterface HeadingProps {\\n children?: ReactNode;\\n level?: 1 | 2 | 3 | 4 | 5 | 6; // default 2; Semantic heading level\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default 'default' (var(--ggui-color-onContainer)); Semantic color slot\\n align?: 'left' | 'center' | 'right'; // default undefined (inherits from parent); Horizontal text alignment\\n}\\n\\n// Example:\\n<Stack gap=\\\"var(--ggui-spacing-2)\\\">\\n <Heading level={1}>Page Title</Heading>\\n <Heading level={3} tone=\\\"emphasized\\\">\\n Subsection\\n </Heading>\\n <Text>Body content goes here.</Text>\\n</Stack>\\n\\n// Button — A clickable button primitive with multiple visual variants and sizes\\ninterface ButtonProps {\\n children?: ReactNode;\\n variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'; // default 'primary'; Visual style\\n size?: 'xs' | 'sm' | 'md' | 'lg'; // default 'md'; Controls padding, font size, and minimum height:\\n fullWidth?: boolean; // default false; When true, sets `width: 100%` so the button fills its container\\n loading?: boolean; // default false; When true, replaces children with a 16px `Spinner` (color: `currentColor`)\\n leftIcon?: ReactNode; // ReactNode rendered before children, inside the flex layout with `var(--ggui-s...\\n rightIcon?: ReactNode; // ReactNode rendered after children, inside the flex layout with `var(--ggui-sp...\\n onPress?: () => void; // Alias for `onClick` for cross-platform compatibility (React Native convention)\\n}\\n\\n// Example:\\n<Button variant=\\\"primary\\\" size=\\\"md\\\" leftIcon={<Icon name=\\\"save\\\" />} onClick={handleSave}>\\n Save Changes\\n</Button>\\n\\n// Input — A single-line text input with label, validation, and helper text\\ninterface InputProps {\\n label?: string; // Label rendered above the input\\n placeholder?: string; // Placeholder text shown when the input is empty\\n value?: string; // Controlled value of the input\\n onChange?: (value: string) => void; // Change handler\\n type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local'; // default 'text'; HTML input type\\n error?: string; // Error message displayed below the input in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the input in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n}\\n\\n// Example:\\n<Input label=\\\"Email\\\" type=\\\"email\\\" value={email} onChange={setEmail} error={emailError} />\\n\\n// TextArea — A multiline text input with label, validation, character count, a...\\ninterface TextAreaProps {\\n label?: string; // Label rendered above the textarea\\n placeholder?: string; // Placeholder text shown when the textarea is empty\\n value?: string; // Controlled value of the textarea\\n onChange?: (value: string) => void; // Change handler\\n rows?: number; // default 4; Number of visible text rows (native `rows` attribute on `<textarea>`)\\n error?: string; // Error message displayed below the textarea in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the textarea in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n maxLength?: number; // Maximum character length (native `maxLength` attribute)\\n showCount?: boolean; // default false; When true AND `maxLength` is set, displays a `{current}/{max}` character\\n autoResize?: boolean; // default false; When true, sets CSS `resize: none` on the textarea\\n}\\n\\n// Example:\\n<TextArea label=\\\"Bio\\\" value={bio} onChange={setBio} rows={6} maxLength={500} showCount />\\n\\n// Select — A native dropdown selection primitive with label and validation\\ninterface SelectProps {\\n label?: string; // Label rendered above the select\\n value?: string; // Controlled value\\n onChange?: (value: string) => void; // Change handler\\n options: SelectOption[]; // Array of selectable options\\n placeholder?: string; // Placeholder text rendered as a disabled `<option value=\\\"\\\">` at the top of the\\n error?: string; // Error message displayed below the select in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the select in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n}\\n\\n// Example:\\n<Select\\n label=\\\"Country\\\"\\n value={country}\\n onChange={setCountry}\\n options={[\\n { value: 'us', label: 'United States' },\\n { value: 'uk', label: 'United Kingdom' },\\n ]}\\n placeholder=\\\"Select a country\\\"\\n/>\\n\\n// Checkbox — A custom-styled checkbox with label and description\\ninterface CheckboxProps {\\n label?: string; // Primary label text rendered beside the checkbox box\\n checked?: boolean; // Controlled checked state\\n onChange?: (checked: boolean) => void; // Change handler\\n disabled?: boolean; // default false; When true, sets `opacity: 0\\n description?: string; // Secondary description text rendered below the label in smaller, muted type\\n indeterminate?: boolean; // default false; When true, displays a horizontal dash instead of a checkmark\\n}\\n\\n// Example:\\n<Checkbox\\n label=\\\"Accept terms\\\"\\n description=\\\"You agree to the Terms of Service and Privacy Policy\\\"\\n checked={accepted}\\n onChange={setAccepted}\\n/>\\n\\n// Toggle — A switch/toggle input rendered as a pill-shaped track with a slidin...\\ninterface ToggleProps {\\n label?: string; // Label text rendered to the right of the toggle track\\n checked?: boolean; // Controlled checked (on/off) state\\n onChange?: (checked: boolean) => void; // Change handler\\n disabled?: boolean; // default false; When true, sets `opacity: 0\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls track and knob dimensions:\\n}\\n\\n// Example:\\n<Toggle label=\\\"Enable notifications\\\" checked={enabled} onChange={setEnabled} size=\\\"md\\\" />\\n\\n// RadioGroup — A group of mutually exclusive radio options with optional label...\\ninterface RadioGroupProps {\\n label?: string; // Group label rendered above the options\\n value?: string; // Controlled value\\n onChange?: (value: string) => void; // Change handler\\n options: RadioOption[]; // Array of radio options\\n direction?: 'vertical' | 'horizontal'; // default 'vertical'; Layout direction for the options container:\\n disabled?: boolean; // default false; When true, disables ALL options (individual `RadioOption\\n error?: string; // Error message displayed below all options in `var(--ggui-color-error-500)`\\n}\\n\\n// Example:\\n<RadioGroup\\n label=\\\"Plan\\\"\\n value={plan}\\n onChange={setPlan}\\n options={[\\n { value: 'free', label: 'Free', description: 'Up to 5 projects' },\\n { value: 'pro', label: 'Pro', description: 'Unlimited projects' },\\n ]}\\n/>\\n\\n// Slider — A range input with a custom-styled track, fill, and thumb\\ninterface SliderProps {\\n label?: string; // Label rendered above the slider track (left-aligned)\\n value?: number; // default 0; Controlled numeric value\\n onChange?: (value: number) => void; // Change handler\\n min?: number; // default 0; Minimum allowed value\\n max?: number; // default 100; Maximum allowed value\\n step?: number; // default 1; Step increment for the slider\\n disabled?: boolean; // default false; When true, sets `cursor: not-allowed` on the native input\\n showValue?: boolean; // default false; When true, displays the current numeric value right-aligned in the header row\\n}\\n\\n// Example:\\n<Slider label=\\\"Volume\\\" value={volume} onChange={setVolume} min={0} max={100} step={5} showValue />\\n\\n// Badge — Inline label for status indicators, counts, or categories\\ninterface BadgeProps {\\n children?: ReactNode;\\n variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'tertiary'; // default 'default'; Visual style\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n pill?: boolean; // default true; When true, uses fully rounded corners (`border-radius: 9999px`)\\n}\\n\\n// Example:\\n<Badge variant=\\\"success\\\" size=\\\"sm\\\">Active</Badge>\\n\\n// Spinner — Animated SVG loading indicator\\ninterface SpinnerProps {\\n size?: number; // default 24; Width and height of the SVG element in pixels\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-primary-600)`); Semantic color slot for the animated foreground arc\\n}\\n\\n// Example:\\n<Spinner size={32} tone=\\\"success\\\" />\\n\\n// Avatar — User or entity representation with image or auto-generated initials\\ninterface AvatarProps {\\n src?: string; // Image URL\\n name?: string; // Name used for two purposes:\\n size?: number | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; // default 'md'; Avatar dimensions\\n shape?: 'circle' | 'square'; // default 'circle'; Container shape\\n}\\n\\n// Example:\\n<Avatar src=\\\"/photos/jane.jpg\\\" name=\\\"Jane Doe\\\" size=\\\"lg\\\" shape=\\\"circle\\\" />\\n\\n// Alert — Contextual message box for important information with icon and optio...\\ninterface AlertProps {\\n children?: ReactNode;\\n variant?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Visual style\\n title?: string; // Optional title rendered above the body in semibold (`var(--ggui-font-weight-s...\\n closable?: boolean; // default false; When true, renders a close button (X icon) in the top-right area\\n onClose?: () => void; // Callback fired when the close button is clicked\\n icon?: ReactNode; // Custom icon ReactNode to replace the default variant icon\\n}\\n\\n// Example:\\n<Alert variant=\\\"warning\\\" title=\\\"Rate limit\\\" closable onClose={() => setShow(false)}>\\n You have 3 requests remaining this minute.\\n</Alert>\\n\\n// Progress — Horizontal progress bar with determinate and indeterminate modes\\ninterface ProgressProps {\\n value: number; // Current progress value\\n max?: number; // default 100; Maximum value representing 100% progress\\n variant?: 'default' | 'success' | 'warning' | 'error'; // default 'default'; Fill bar color\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls track height in pixels:\\n label?: string; // Accessible name describing what this bar measures, e\\n showLabel?: boolean; // default false; When true, displays a header row above the track with the `label` text\\n indeterminate?: boolean; // default false; When true, ignores `value` for visual width and plays a looping animation\\n}\\n\\n// Example:\\n<Progress value={65} variant=\\\"success\\\" size=\\\"md\\\" showLabel />\\n\\n// Image — An `<img>` element with built-in error handling and fallback support\\ninterface ImageProps {\\n src: string; // Image source URL\\n alt: string; // Alt text for the image\\n width?: number | string; // default '100%' (applied at render time, not on the type); Image width\\n height?: number | string; // default 'auto' (applied at render time, not on the type); Image height\\n objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'; // default 'cover'; CSS `object-fit` value controlling how the image fills its box\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // Corner radius applied to both the image and the error placeholder\\n fallback?: ReactNode; // Custom ReactNode rendered when the image fails to load\\n}\\n\\n// Example:\\n<Image src=\\\"/hero.jpg\\\" alt=\\\"Hero banner\\\" width=\\\"100%\\\" height={400} objectFit=\\\"cover\\\" radius=\\\"md\\\" />\\n\\n// Icon — a curated Lucide subset (~200 names) + emoji passthrough\\ninterface IconProps {\\n name?: string; // Lucide icon name from the curated subset (kebab-case, camelCase, or\\n size?: number; // default 24; Icon dimensions in pixels (applied to both width and height of the wrapper\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (icon uses `currentColor`); Semantic color slot\\n children?: ReactNode; // Custom SVG children\\n 'aria-label'?: string; // Accessible name for a standalone, meaning-bearing icon\\n}\\n\\n// Example:\\n<Icon name=\\\"search\\\" size={20} tone=\\\"muted\\\" />\\n<Icon name=\\\"cloud-rain\\\" size={32} />\\n<Icon name=\\\"☀️\\\" size={24} />\\n\\n// Link — Styled anchor element with external link support\\ninterface LinkProps {\\n children?: ReactNode;\\n href: string; // Destination URL\\n external?: boolean; // default false; When true, opens link in a new tab (`target=\\\"_blank\\\"`, `rel=\\\"noopener norefer...\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-primary-600)`); Semantic color slot for the link text\\n underline?: 'always' | 'hover' | 'none'; // default 'hover'; Underline behavior:\\n}\\n\\n// Example:\\n<Link href=\\\"https://docs.ggui.ai\\\" external>Documentation</Link>\\n\\n// Tooltip — Hoverable information popup positioned relative to a trigger element\\ninterface TooltipProps {\\n children: ReactNode; // Trigger element\\n content: ReactNode; // Tooltip content\\n position?: 'top' | 'bottom' | 'left' | 'right'; // default 'top'; Tooltip placement relative to the trigger element:\\n delay?: number; // default 200; Delay in milliseconds before the tooltip becomes visible after hover/focus\\n}\\n\\n// Example:\\n<Tooltip content=\\\"Copy to clipboard\\\" position=\\\"top\\\">\\n <Button variant=\\\"ghost\\\"><Icon name=\\\"copy\\\" /></Button>\\n</Tooltip>\\n\\n// Table — Data table with sortable columns, striped rows, and hover highlights\\ninterface TableProps {\\n columns: TableColumn<T>[]; // Array of column definitions controlling header labels, data keys, and rendering\\n data: T[]; // Array of row data objects\\n sortKey?: string; // The `key` of the currently sorted column\\n sortDirection?: SortDirection; // default 'asc'; Current sort direction for the column identified by `sortKey`\\n onSort?: (key: string, direction: SortDirection) => void; // Sort change handler\\n striped?: boolean; // default false; When true, alternating rows (odd index) get a\\n hoverable?: boolean; // default true; When true, rows highlight with `var(--ggui-color-container)`\\n compact?: boolean; // default false; When true, reduces cell padding:\\n bordered?: boolean; // default false; When true, adds a 1px border around the table wrapper and between cells\\n caption?: string; // Accessible table caption\\n}\\n\\n// Example:\\n<Table\\n columns={[\\n { key: 'name', header: 'Name', sortable: true },\\n { key: 'role', header: 'Role' },\\n { key: 'status', header: 'Status', render: (v) => <Badge variant={v as string}>{v as string}</Badge> },\\n ]}\\n data={users}\\n sortKey={sortKey}\\n sortDirection={sortDir}\\n onSort={(key, dir) => { setSortKey(key); setSortDir(dir); }}\\n striped\\n/>\\n\\n// Tabs — Accessible tab navigation with panels and keyboard support\\ninterface TabsProps {\\n items: TabItem[]; // Array of tab definitions\\n activeKey?: string; // Controlled active tab key\\n onChange?: (key: string) => void; // Tab change handler\\n variant?: 'line' | 'pills' | 'enclosed'; // default 'line'; Visual style of the tab bar:\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls tab button padding and font size:\\n fullWidth?: boolean; // default false; When true, tab buttons expand equally to fill the container width\\n}\\n\\n// Example:\\n<Tabs\\n variant=\\\"pills\\\"\\n items={[\\n { key: 'overview', label: 'Overview', content: <Overview /> },\\n { key: 'settings', label: 'Settings', content: <Settings /> },\\n ]}\\n activeKey={tab}\\n onChange={setTab}\\n/>\\n\\n// Toast — Notification banner with auto-dismiss and slide-in animation\\ninterface ToastProps {\\n message: ReactNode; // Message body content\\n variant?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Visual style\\n title?: string; // Optional title rendered above the message in semibold\\n duration?: number; // default 5000; Auto-dismiss delay in milliseconds\\n onClose?: () => void; // Callback fired on auto-dismiss timeout or when the close button is clicked\\n visible?: boolean; // default true; Controls rendering\\n position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center'; // Intended screen position\\n}\\n\\n// Example:\\n<Toast variant=\\\"success\\\" title=\\\"Saved\\\" message=\\\"Your changes have been saved.\\\" onClose={() => setShow(false)} />\\n\\n// Accordion — Collapsible content sections with chevron rotation animation\\ninterface AccordionProps {\\n items: AccordionItem[]; // Array of collapsible section definitions\\n expandedKeys?: string[]; // Controlled expanded state\\n onChange?: (expandedKeys: string[]) => void; // Expand/collapse handler\\n multiple?: boolean; // default false; When true, multiple items can be open simultaneously\\n variant?: 'default' | 'bordered' | 'separated'; // default 'default'; Visual style controlling borders and spacing:\\n}\\n\\n// Example:\\n<Accordion\\n variant=\\\"separated\\\"\\n items={[\\n { key: 'faq1', title: 'How do I get started?', content: 'Sign up and...' },\\n { key: 'faq2', title: 'What is the pricing?', content: 'We offer...' },\\n ]}\\n expandedKeys={expanded}\\n onChange={setExpanded}\\n multiple\\n/>\\n\\n// Row — Convenience alias for a horizontal Stack\\ninterface RowProps {\\n children?: ReactNode;\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'sm'; Gap between children\\n align?: 'start' | 'center' | 'end' | 'stretch'; // default 'stretch'; Cross-axis alignment (maps to `align-items`)\\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; // default 'start'; Main-axis content distribution (maps to `justify-content`)\\n wrap?: boolean; // default false; Whether children wrap to the next line when they overflow\\n}\\n\\n// Example:\\n<Row gap=\\\"md\\\" align=\\\"center\\\" justify=\\\"between\\\">\\n <Text size=\\\"sm\\\" weight=\\\"medium\\\">Total</Text>\\n <Text weight=\\\"bold\\\">$42.00</Text>\\n</Row>\\n\\n// Support types:\\n// ResponsiveColumns — Explicit column count per viewport breakpoint, mobile-first\\ninterface ResponsiveColumns {\\n base?: number; // default 1; Columns below the `sm` breakpoint\\n sm?: number; // Columns from 640px up\\n md?: number; // Columns from 768px up\\n lg?: number; // Columns from 1024px up\\n xl?: number; // Columns from 1280px up\\n}\\n\\n// SelectOption — An individual option within a `Select` dropdown\\ninterface SelectOption {\\n value: string; // The value submitted when this option is selected\\n label: string; // The display text shown in the dropdown\\n disabled?: boolean; // default false; When true, the option is visible but cannot be selected (grayed out by the br...\\n}\\n\\n// RadioOption — An individual option within a `RadioGroup`\\ninterface RadioOption {\\n value: string; // The value emitted via `RadioGroupProps\\n label: string; // Display text for this option\\n description?: string; // Optional secondary description rendered below the label in smaller, muted type\\n disabled?: boolean; // default false; When true, this individual option is grayed out (`opacity: 0\\n}\\n\\n// TableColumn — Column definition for the Table component\\ninterface TableColumn {\\n key: string; // Property key in the row data object\\n header: string; // Column header text\\n width?: number | string; // Fixed column width\\n align?: 'left' | 'center' | 'right'; // default 'left'; Horizontal text alignment for both the header and data cells\\n sortable?: boolean; // default false; When true, the header cell becomes clickable and shows sort direction\\n render?: (value: unknown, row: T, index: number) => ReactNode; // Custom cell renderer\\n}\\n\\n// TabItem — Definition of a single tab within a Tabs component\\ninterface TabItem {\\n key: string; // Unique identifier for this tab\\n label: ReactNode; // Tab button label\\n content: ReactNode; // Panel content rendered below the tab bar when this tab is active\\n disabled?: boolean; // default false; When true, the tab button shows `opacity: 0\\n icon?: ReactNode; // Optional icon rendered before the label inside the tab button, with\\n}\\n\\n// AccordionItem — Definition of a single collapsible section within an Accordion\\ninterface AccordionItem {\\n key: string; // Unique identifier for this item\\n title: ReactNode; // Header label rendered inside the toggle button\\n content: ReactNode; // Panel content rendered below the header when expanded\\n disabled?: boolean; // default false; When true, the header button shows `opacity: 0\\n}\\n\\n// ════════════════════════════════════════════════════════════\\n// Components (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// SearchField — A text input with a leading search icon and optional submit button\\ninterface SearchFieldProps {\\n value?: string; // Current search value\\n onChange?: (value: string) => void; // Called on every keystroke with the new input string (value directly, not\\n onSearch?: (value: string) => void; // Called when the user presses **Enter** or clicks the search button\\n placeholder?: string; // default 'Search...'; Placeholder text shown when the input is empty\\n showButton?: boolean; // default false; When `true`, renders a `Button` primitive to the right of the input\\n buttonText?: string; // default 'Search'; Label rendered inside the submit button\\n loading?: boolean; // When `true`, replaces the search icon with a `Spinner`, disables the\\n disabled?: boolean; // When `true`, the input and button are visually disabled and do not\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls input height and font size\\n}\\n\\n// Example:\\n```tsx\\n<SearchField\\n value={query}\\n onChange={setQuery}\\n onSearch={(q) => fetchResults(q)}\\n placeholder=\\\"Search products...\\\"\\n showButton\\n buttonText=\\\"Go\\\"\\n size=\\\"md\\\"\\n/>\\n```\\n\\n// FormField — A wrapper that adds a label, optional description, error message,\\ninterface FormFieldProps {\\n label: string; // Text rendered inside the `<label>` element above the input\\n children: ReactNode; // The form control (typically an `Input`, `Select`, or `Textarea` primitive) re...\\n error?: string; // Error message displayed below `children` in `colors\\n helperText?: string; // Neutral guidance text displayed below `children` in `colors\\n required?: boolean; // When `true`, appends a red asterisk (`*`) after the label text\\n description?: string; // Secondary description rendered between the label and the child control\\n}\\n\\n// Example:\\n```tsx\\n<FormField\\n label=\\\"Email address\\\"\\n required\\n description=\\\"We will never share your email.\\\"\\n error={errors.email}\\n>\\n <Input value={email} onChange={setEmail} placeholder=\\\"you@example.com\\\" />\\n</FormField>\\n```\\n\\n// MenuItem — A full-width clickable row for menus, sidebars, and action lists\\ninterface MenuItemProps {\\n label: string; // Primary text content of the menu item\\n icon?: ReactNode; // Icon or element rendered to the left of the label\\n rightElement?: ReactNode; // Element rendered to the right of the label (e\\n onClick?: () => void; // Called when the item is clicked\\n disabled?: boolean; // When `true`, the item is non-interactive: `colors\\n active?: boolean; // Marks this item as the current selection\\n danger?: boolean; // Switches the item to destructive styling: `colors\\n}\\n\\n// Example:\\n```tsx\\n<MenuItem\\n label=\\\"Delete project\\\"\\n icon={<Icon name=\\\"trash\\\" size={16} />}\\n danger\\n onClick={() => confirmDelete(projectId)}\\n/>\\n```\\n\\n// Tag — An inline label for categories, filters, statuses, or selections\\ninterface TagProps {\\n children: ReactNode; // Tag content -- typically a short text string\\n variant?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'; // default 'default'; Semantic color variant applied to background, text, and border\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding, font size, and internal gap\\n closable?: boolean; // When `true`, renders a small close (\\\"x\\\") button after the content\\n onClose?: () => void; // Called when the close button is clicked\\n icon?: ReactNode; // Icon or element rendered before the text content\\n}\\n\\n// Example:\\n```tsx\\n<Tag variant=\\\"success\\\" size=\\\"md\\\" closable onClose={() => removeFilter(id)}>\\n Active\\n</Tag>\\n```\\n\\n// Dropdown — A click-triggered menu anchored to a trigger element\\ninterface DropdownProps {\\n trigger: ReactNode; // The element the user clicks to open the menu\\n options: DropdownOption[]; // Array of selectable options rendered as `MenuItem` rows\\n value?: string; // The `value` of the currently selected option\\n onChange?: (value: string) => void; // Called with the `value` string of the selected option (not the full\\n placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'; // default 'bottom-start'; Where to anchor the menu panel relative to the trigger\\n disabled?: boolean; // When `true`, the trigger shows `cursor: not-allowed` and clicking\\n}\\n\\n// Example:\\n```tsx\\n<Dropdown\\n trigger={<Button variant=\\\"outline\\\">Sort by</Button>}\\n options={[\\n { value: 'name', label: 'Name' },\\n { value: 'date', label: 'Date created' },\\n { value: 'delete', label: 'Delete', danger: true },\\n ]}\\n value={sortBy}\\n onChange={setSortBy}\\n placement=\\\"bottom-end\\\"\\n/>\\n```\\n\\n// Autocomplete — A text input with a filterable suggestion dropdown, keyboard navigation,\\ninterface AutocompleteProps {\\n value?: string; // default ''; Current text in the input field\\n onChange?: (value: string) => void; // Called on every keystroke with the new input string (value directly,\\n onSelect?: (option: AutocompleteOption) => void; // Called when the user selects an option (click or Enter on highlighted\\n options: AutocompleteOption[]; // The full list of available options\\n placeholder?: string; // Placeholder text forwarded to the inner `Input` primitive\\n label?: string; // Label text forwarded to the inner `Input` primitive\\n loading?: boolean; // When `true`, the dropdown shows a centered `Spinner` instead of\\n disabled?: boolean; // When `true`, the inner `Input` is disabled and the dropdown\\n error?: string; // Error message forwarded to the inner `Input` primitive\\n noResultsText?: string; // default 'No results found'; Text shown in the dropdown when filtering produces zero matches\\n}\\n\\n// Example:\\n```tsx\\n<Autocomplete\\n label=\\\"Country\\\"\\n value={country}\\n onChange={setCountry}\\n onSelect={(opt) => setCountryCode(opt.value)}\\n options={countries}\\n placeholder=\\\"Type to search...\\\"\\n noResultsText=\\\"No countries found\\\"\\n/>\\n```\\n\\n// Breadcrumb — A horizontal navigation trail showing the user's location within a\\ninterface BreadcrumbProps {\\n items: BreadcrumbItem[]; // Ordered array of breadcrumb segments from root to current page\\n separator?: ReactNode; // default '/'; Separator rendered between each pair of items\\n onItemClick?: (item: BreadcrumbItem, index: number) => void; // Called when a non-last item is clicked\\n}\\n\\n// Example:\\n```tsx\\n<Breadcrumb\\n items={[\\n { label: 'Home', href: '/' },\\n { label: 'Projects', href: '/projects' },\\n { label: 'ggui' },\\n ]}\\n separator=\\\"/\\\"\\n onItemClick={(item) => router.push(item.href!)}\\n/>\\n```\\n\\n// Pagination — Page navigation controls with previous/next arrows, numbered page buttons,\\ninterface PaginationProps {\\n currentPage: number; // Current active page\\n totalPages: number; // Total number of pages\\n onPageChange?: (page: number) => void; // Called when the user clicks a page number, arrow, or first/last button\\n showFirstLast?: boolean; // default true; When `true`, renders double-chevron buttons for jumping to the first\\n maxVisible?: number; // default 5; Maximum number of page buttons visible at once (including the first\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls button dimensions and icon sizes\\n disabled?: boolean; // When `true`, all page buttons and arrows are visually dimmed\\n}\\n\\n// Example:\\n```tsx\\n<Pagination\\n currentPage={page}\\n totalPages={20}\\n onPageChange={setPage}\\n maxVisible={7}\\n size=\\\"md\\\"\\n/>\\n```\\n\\n// EmptyState — placeholder for a region with no data: empty lists,\\ninterface EmptyStateProps {\\n icon?: string | ReactNode; // A Lucide icon name (kebab-case), rendered large and subtle above\\n title: string; // The headline, e\\n description?: string; // Optional supporting line below the title\\n action?: ReactNode; // Optional call-to-action, typically a `<Button>`\\n}\\n\\n// Example:\\n{results.length === 0\\n ? <EmptyState icon=\\\"search-x\\\" title=\\\"No matches\\\" description=\\\"Try a broader query.\\\" />\\n : results.map((r) => <Row key={r.id}>…</Row>)}\\n\\n// Stat — a single KPI / metric: a label, a large value, an optional\\ninterface StatProps {\\n label: string; // The metric name, e\\n value: string | number; // The headline value — the big number\\n delta?: string; // Optional change indicator, pre-formatted, e\\n trend?: 'up' | 'down' | 'neutral'; // default 'neutral'; Direction of `delta` — colours it: `'up'` success, `'down'` error,\\n icon?: string | ReactNode; // Optional Lucide icon name (kebab-case) or custom node, shown next\\n}\\n\\n// Example:\\n<Stat label=\\\"Revenue\\\" value=\\\"$48.2k\\\" delta=\\\"+12.5%\\\" trend=\\\"up\\\" icon=\\\"trending-up\\\" />\\n\\n// Stepper — a display-only step indicator for multi-step flows\\ninterface StepperProps {\\n steps: string[]; // Ordered step labels, one per step\\n current: number; // Zero-based index of the active step\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Flow direction\\n onStepClick?: (index: number) => void; // When provided, each step becomes a real button and clicks receive\\n}\\n\\n// Example:\\nconst STEPS = ['Account', 'Profile', 'Review'];\\n// In the component: const [step, setStep] = useState(0);\\n<Stack gap=\\\"lg\\\">\\n <Stepper steps={STEPS} current={step} />\\n {step === 0 && <AccountFields />}\\n <Row gap=\\\"sm\\\" justify=\\\"end\\\">\\n <Button variant=\\\"ghost\\\" disabled={step === 0} onClick={() => setStep(step - 1)}>Back</Button>\\n <Button onClick={() => setStep(step + 1)}>Next</Button>\\n </Row>\\n</Stack>\\n\\n// Markdown — renders markdown-formatted text as styled content:\\ninterface MarkdownProps {\\n markdown: string; // Markdown source text, passed through verbatim\\n}\\n\\n// Example:\\n<Stack gap=\\\"md\\\">\\n <Markdown markdown={recipe.instructions} />\\n</Stack>\\n\\n// MarkdownInline — renders ONE line of inline-level markdown (bold,\\ninterface MarkdownInlineProps {\\n markdown: string; // Inline markdown source -- bold, italic, inline code, and\\n}\\n\\n// Example:\\n<Row gap=\\\"sm\\\">\\n <Badge variant=\\\"success\\\">stable</Badge>\\n <MarkdownInline markdown={release.summary} />\\n</Row>\\n\\n// Support types:\\n// DropdownOption — A single option inside a `Dropdown`\\ninterface DropdownOption {\\n value: string; // Unique identifier returned to `onChange` when this option is selected\\n label: string; // Human-readable text shown in the menu row\\n icon?: ReactNode; // Optional icon rendered to the left of the label via `MenuItem\\n disabled?: boolean; // When `true`, the option is visible but non-interactive\\n danger?: boolean; // When `true`, the option uses destructive (red) styling via\\n}\\n\\n// AutocompleteOption — A single option in the `Autocomplete` suggestion list\\ninterface AutocompleteOption {\\n value: string; // Unique identifier for this option\\n label: string; // Primary display text\\n description?: string; // Secondary description rendered below the label in `fontSize\\n icon?: ReactNode; // Icon rendered to the left of the label/description block\\n disabled?: boolean; // When `true`, the option is visible but non-interactive (`cursor: not-allowed`...\\n}\\n\\n// BreadcrumbItem — A single segment in a `Breadcrumb` trail\\ninterface BreadcrumbItem {\\n label: string; // Display text for this breadcrumb segment\\n href?: string; // URL for this segment\\n icon?: ReactNode; // Icon rendered immediately before the label\\n}\\n\\n// ════════════════════════════════════════════════════════════\\n// Compositions (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// Header — Props for the `Header` composition\\ninterface HeaderProps {\\n logo?: ReactNode; // Logo or brand element rendered at the start (flex-shrink: 0)\\n navigation?: ReactNode; // Navigation content rendered in a `<nav>` element with `flex: 1` and a 32 px l...\\n actions?: ReactNode; // Right-side action elements (buttons, avatar, etc\\n sticky?: boolean; // default false; When true, the header becomes `position: sticky` at the top of its scroll con...\\n background?: string; // default colors.white; Background color of the header\\n bordered?: boolean; // default true; When true, renders a 1 px bottom border in `colors\\n}\\n\\n// Example:\\n```tsx\\n<Header\\n logo={<img src=\\\"/logo.svg\\\" alt=\\\"Acme\\\" />}\\n navigation={<a href=\\\"/docs\\\">Docs</a>}\\n actions={<Button size=\\\"sm\\\">Sign In</Button>}\\n sticky\\n bordered\\n/>\\n```\\n\\n// Sidebar — Props for the `Sidebar` composition\\ninterface SidebarProps {\\n items: SidebarItem[]; // Array of navigation items to render\\n activeId?: string; // ID of the currently active item\\n onItemClick?: (item: SidebarItem) => void; // Called when any item (including parent items with children) is clicked\\n collapsed?: boolean; // default false; When true, hides labels and badges; only icons remain visible, centered in th...\\n header?: ReactNode; // Content rendered above the item list, separated by a bottom border\\n footer?: ReactNode; // Content rendered below the item list, separated by a top border\\n width?: number; // default 256; Width in pixels when expanded\\n collapsedWidth?: number; // default 64; Width in pixels when collapsed\\n}\\n\\n// Example:\\n```tsx\\n<Sidebar\\n items={[\\n { id: 'home', label: 'Home', icon: <Icon name=\\\"home\\\" /> },\\n { id: 'settings', label: 'Settings', icon: <Icon name=\\\"settings\\\" />,\\n children: [\\n { id: 'profile', label: 'Profile' },\\n { id: 'billing', label: 'Billing' },\\n ]},\\n ]}\\n activeId=\\\"home\\\"\\n onItemClick={(item) => navigate(item.href)}\\n collapsed={false}\\n width={256}\\n/>\\n```\\n\\n// CardGrid — Props for the `CardGrid` composition\\ninterface CardGridProps {\\n children: ReactNode; // Card elements to arrange in the grid\\n columns?: number | { sm?: number; md?: number; lg?: number }; // default 3; Number of columns, or a responsive breakpoint map\\n gap?: number; // default 16; Gap between grid items in pixels\\n}\\n\\n// Example:\\n```tsx\\n<CardGrid columns={3} gap={24}>\\n <Card>A</Card>\\n <Card>B</Card>\\n <Card>C</Card>\\n</CardGrid>\\n```\\n\\n// CommentThread — Props for the `CommentThread` composition\\ninterface CommentThreadProps {\\n comments: Comment[]; // Array of top-level comments to render\\n currentUser?: { /** Display name for the current user's avatar. */ name: string; /** Avatar image URL for the current user. */ avatar?: string; }; // Current user info\\n onAddComment?: (content: string, parentId?: string) => void; // Called when the user submits a new top-level comment\\n onReply?: (commentId: string, content: string) => void; // Called when the user submits a reply to an existing comment\\n onReaction?: (commentId: string, emoji: string) => void; // Called when the user clicks an emoji reaction on a comment\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the comment list\\n}\\n\\n// Example:\\n```tsx\\n<CommentThread\\n comments={[\\n { id: '1', author: { name: 'Alice' }, content: 'Great work!', timestamp: new Date(),\\n replies: [{ id: '2', author: { name: 'Bob' }, content: 'Thanks!', timestamp: new Date() }] }\\n ]}\\n currentUser={{ name: 'Alice', avatar: '/alice.jpg' }}\\n onAddComment={(content) => post(content)}\\n onReply={(commentId, content) => reply(commentId, content)}\\n onReaction={(commentId, emoji) => react(commentId, emoji)}\\n/>\\n```\\n\\n// DataTable — Props for the `DataTable` composition\\ninterface DataTableProps {\\n columns: DataTableColumn<T>[]; // Column definitions that control header, alignment, sorting, and rendering\\n data: T[]; // Row data array\\n rowKey?: string | ((row: T) => string); // default 'id'; Property name or function used to derive a unique key for each row\\n loading?: boolean; // When true, shows a centered `Spinner` instead of table body rows\\n emptyText?: string; // default 'No data'; Text shown when `data` is empty and not loading\\n onSort?: (key: string, direction: 'asc' | 'desc') => void; // Called when a sortable column header is clicked\\n sortKey?: string; // The column key currently being sorted\\n sortDirection?: 'asc' | 'desc'; // The current sort direction\\n onRowClick?: (row: T, index: number) => void; // Called when a row is clicked\\n selectable?: boolean; // When true, adds a checkbox column at the start of each row\\n selectedKeys?: string[]; // default []; Array of currently selected row keys\\n onSelectionChange?: (keys: string[]) => void; // Called when the set of selected row keys changes (via row checkbox or select-...\\n}\\n\\n// Example:\\n```tsx\\n<DataTable\\n columns={[\\n { key: 'name', header: 'Name', sortable: true },\\n { key: 'email', header: 'Email' },\\n { key: 'role', header: 'Role', render: (v) => <Badge>{v}</Badge> },\\n ]}\\n data={users}\\n rowKey=\\\"id\\\"\\n selectable\\n selectedKeys={selected}\\n onSelectionChange={setSelected}\\n onSort={(key, dir) => sort(key, dir)}\\n sortKey=\\\"name\\\"\\n sortDirection=\\\"asc\\\"\\n/>\\n```\\n\\n// ChatWindow — Props for the `ChatWindow` composition\\ninterface ChatWindowProps {\\n messages: ChatMessage[]; // Array of chat messages to display in chronological order\\n currentUserId: string; // ID of the current user\\n onSendMessage?: (content: string) => void; // Called when the user submits a message (Enter key or send button)\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the message list\\n typing?: { name: string } | null; // When non-null, displays a typing indicator below the last message (e\\n placeholder?: string; // default 'Type a message...'; Placeholder text for the message input field\\n header?: ReactNode; // Optional header content rendered above the message area, separated by a botto...\\n}\\n\\n// Example:\\n```tsx\\n<ChatWindow\\n messages={messages}\\n currentUserId=\\\"user-1\\\"\\n onSendMessage={(content) => send(content)}\\n typing={{ name: 'Alice' }}\\n placeholder=\\\"Type a message...\\\"\\n header={<h3>Chat with Alice</h3>}\\n/>\\n```\\n\\n// NavigationBar — Props for the `NavigationBar` composition\\ninterface NavigationBarProps {\\n items: NavItem[]; // Array of navigation items to render\\n activeId?: string; // ID of the currently active item\\n onItemClick?: (item: NavItem) => void; // Called when a non-disabled item is clicked\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Layout direction\\n variant?: 'default' | 'pills' | 'underline'; // default 'default'; Visual style for active/inactive items\\n}\\n\\n// Example:\\n```tsx\\n<NavigationBar\\n items={[\\n { id: 'home', label: 'Home', icon: <Icon name=\\\"home\\\" /> },\\n { id: 'about', label: 'About' },\\n { id: 'contact', label: 'Contact' },\\n ]}\\n activeId=\\\"home\\\"\\n onItemClick={(item) => navigate(item.id)}\\n orientation=\\\"horizontal\\\"\\n variant=\\\"pills\\\"\\n/>\\n```\\n\\n// FileUploader — Props for the `FileUploader` composition\\ninterface FileUploaderProps {\\n files?: UploadedFile[]; // default []; Array of files currently in the upload queue, shown below the drop zone\\n onFilesSelected?: (files: File[]) => void; // Called when the user selects files via click or drag-and-drop\\n onFileRemove?: (fileId: string) => void; // Called when the user clicks the remove button on a file entry\\n accept?: string; // Accepted file types passed to the hidden `<input type=\\\"file\\\" accept=\\\"\\n multiple?: boolean; // default true; When true, allows selecting multiple files at once\\n maxSize?: number; // Maximum file size in bytes\\n maxFiles?: number; // Maximum number of files\\n disabled?: boolean; // When true, the drop zone is visually dimmed (opacity 0\\n dragDrop?: boolean; // default true; When true, enables drag-and-drop on the drop zone\\n}\\n\\n// Example:\\n```tsx\\n<FileUploader\\n files={uploadedFiles}\\n onFilesSelected={(files) => startUpload(files)}\\n onFileRemove={(id) => removeFile(id)}\\n accept=\\\"image/*,.pdf\\\"\\n multiple\\n maxSize={5 * 1024 * 1024}\\n maxFiles={10}\\n dragDrop\\n/>\\n```\\n\\n// NotificationCenter — Props for the `NotificationCenter` composition\\ninterface NotificationCenterProps {\\n notifications: Notification[]; // Array of notifications to display, in the order provided\\n onMarkAsRead?: (id: string) => void; // Called when the user clicks \\\"Mark as read\\\" on an individual notification\\n onMarkAllAsRead?: () => void; // Called when the user clicks the \\\"Mark all read\\\" header button\\n onDismiss?: (id: string) => void; // Called when the user clicks the dismiss (X) button on an individual notification\\n onClearAll?: () => void; // Called when the user clicks the \\\"Clear all\\\" header button\\n emptyText?: string; // default 'No notifications'; Text shown when `notifications` is empty and not loading\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the notification list\\n}\\n\\n// Example:\\n```tsx\\n<NotificationCenter\\n notifications={[\\n { id: '1', title: 'Deployment complete', type: 'success', timestamp: new Date(), read: false },\\n { id: '2', title: 'Build failed', type: 'error', message: 'Lint errors', timestamp: new Date() },\\n ]}\\n onMarkAsRead={(id) => markRead(id)}\\n onMarkAllAsRead={() => markAllRead()}\\n onDismiss={(id) => dismiss(id)}\\n onClearAll={() => clearAll()}\\n/>\\n```\\n\\n// Modal — Props for the `Modal` composition\\ninterface ModalProps {\\n open: boolean; // Controls modal visibility\\n onClose: () => void; // Called to close the modal (overlay click, escape key, or close button)\\n title?: string; // Optional title rendered in the modal header via the `Heading` primitive (leve...\\n children: ReactNode; // Modal body content rendered in a scrollable area (`overflowY: auto`)\\n footer?: ReactNode; // Footer content rendered below the body, right-aligned with an 8 px gap, separ...\\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; // default 'md'; Controls the width of the modal panel\\n closeOnOverlayClick?: boolean; // default true; When true, clicking the semi-transparent overlay behind the modal calls `onCl...\\n closeOnEscape?: boolean; // default true; When true, pressing the Escape key calls `onClose`\\n showCloseButton?: boolean; // default true; When true, renders a ghost close button (X icon) in the modal header\\n}\\n\\n// Example:\\n```tsx\\n<Modal\\n open={isOpen}\\n onClose={() => setOpen(false)}\\n title=\\\"Confirm Action\\\"\\n size=\\\"md\\\"\\n footer={\\n <>\\n <Button variant=\\\"ghost\\\" onClick={() => setOpen(false)}>Cancel</Button>\\n <Button onClick={handleConfirm}>Confirm</Button>\\n </>\\n }\\n>\\n <p>Are you sure you want to proceed?</p>\\n</Modal>\\n```\\n\\n// CommandPalette — Props for the `CommandPalette` composition\\ninterface CommandPaletteProps {\\n open: boolean; // Controls palette visibility\\n onClose: () => void; // Called to close the palette (overlay click, Escape key, or after command sele...\\n commands: Command[]; // Full array of available commands\\n onSelect: (command: Command) => void; // Called when a non-disabled command is selected (Enter key or click)\\n placeholder?: string; // default 'Search commands...'; Placeholder text for the search input\\n recentIds?: string[]; // default []; IDs of recently used commands\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the command list\\n}\\n\\n// Example:\\n```tsx\\n<CommandPalette\\n open={isOpen}\\n onClose={() => setOpen(false)}\\n commands={[\\n { id: 'new', label: 'New File', shortcut: 'Ctrl+N', group: 'File' },\\n { id: 'save', label: 'Save', shortcut: 'Ctrl+S', group: 'File' },\\n { id: 'theme', label: 'Toggle Theme', group: 'Preferences' },\\n ]}\\n onSelect={(cmd) => executeCommand(cmd.id)}\\n recentIds={['save']}\\n placeholder=\\\"Search commands...\\\"\\n/>\\n```\\n\\n// Footer — Props for the `Footer` composition\\ninterface FooterProps {\\n brand?: ReactNode; // Brand element (logo, tagline) rendered in a flexible column (`flex: 1 1 280px`)\\n columns?: FooterColumn[]; // Array of link columns rendered in a flex-wrap layout with 48 px gap\\n socialLinks?: FooterSocialLink[]; // Social media icon links rendered in the bottom bar\\n bottomText?: string; // Text displayed at the start of the bottom bar (e\\n bottomLinks?: FooterLink[]; // Links displayed in the bottom bar after social icons (e\\n background?: string; // default colors.gray[50]; Background color of the footer\\n bordered?: boolean; // default true; When true, renders a 1 px top border in `colors\\n}\\n\\n// Example:\\n```tsx\\n<Footer\\n brand={<img src=\\\"/logo.svg\\\" alt=\\\"Acme\\\" />}\\n columns={[\\n { title: 'Product', links: [{ label: 'Features', href: '/features' }] },\\n { title: 'Company', links: [{ label: 'About', href: '/about' }] },\\n ]}\\n socialLinks={[\\n { label: 'Twitter', href: 'https://twitter.com/acme', icon: <TwitterIcon /> },\\n ]}\\n bottomText=\\\"© 2026 Acme Inc.\\\"\\n bottomLinks={[{ label: 'Privacy', href: '/privacy' }]}\\n bordered\\n/>\\n```\\n\\n// Hero — Props for the `Hero` composition\\ninterface HeroProps {\\n heading?: string; // Main heading text rendered as an `<h1>` with bold weight and tight line-height\\n description?: string; // Description paragraph rendered below the heading with relaxed line-height\\n primaryAction?: HeroAction; // Primary CTA button rendered with `colors\\n secondaryAction?: HeroAction; // Secondary CTA button rendered with a transparent background and a 1 px border\\n media?: ReactNode; // Media element (image, video, illustration) rendered beside or below the text ...\\n align?: 'center' | 'left'; // default 'center'; Text and layout alignment\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls vertical padding and heading/description font sizes\\n background?: string; // default colors.white (when no backgroundImage is set); Background color of the hero section\\n backgroundImage?: string; // Background image URL applied as `background-size: cover; background-position:...\\n overlay?: boolean; // default false; When true and `backgroundImage` is set, renders a semi-transparent black overlay\\n}\\n\\n// Example:\\n```tsx\\n<Hero\\n heading=\\\"Build Better UIs, Faster\\\"\\n description=\\\"The universal interface layer between AI agents and humans.\\\"\\n primaryAction={{ label: 'Get Started', onClick: () => navigate('/signup') }}\\n secondaryAction={{ label: 'Learn More', href: '/docs' }}\\n media={<img src=\\\"/hero.png\\\" alt=\\\"Hero\\\" />}\\n align=\\\"left\\\"\\n size=\\\"lg\\\"\\n/>\\n```\\n\\n// Support types:\\n// SidebarItem — A single navigation entry in a `Sidebar`\\ninterface SidebarItem {\\n id: string; // Unique identifier used for active-state matching and React keys\\n label: string; // Display label for the item\\n icon?: ReactNode; // Leading icon rendered before the label\\n href?: string; // Optional URL associated with this item (not rendered as a link by default)\\n badge?: ReactNode; // Trailing badge element (e\\n children?: SidebarItem[]; // Nested child items\\n disabled?: boolean; // When true, the item is visually dimmed and non-interactive (`cursor: not-allo...\\n}\\n\\n// Comment — A single comment entry in a `CommentThread`\\ninterface Comment {\\n id: string; // Unique identifier for this comment\\n author: { /** Display name of the author. */ name: string; /** URL for the author's avatar image. Passed to the `Avatar` primitive. */ avatar?: string; }; // Comment author metadata\\n content: string; // The comment body text\\n timestamp: string | Date; // Timestamp of the comment\\n replies?: Comment[]; // Nested reply comments\\n reactions?: { emoji: string; count: number }[]; // Emoji reactions with their aggregated counts (e\\n}\\n\\n// DataTableColumn — Column definition for a `DataTable`\\ninterface DataTableColumn {\\n key: string; // Property key on the row object used to extract cell values\\n header: string; // Column header text displayed in the `<thead>`\\n width?: number | string; // Column width as a CSS value (number for pixels, string for any CSS unit)\\n sortable?: boolean; // When true, the column header is clickable and triggers `onSort`\\n render?: (value: unknown, row: T, index: number) => ReactNode; // Custom cell renderer\\n align?: 'left' | 'center' | 'right'; // default 'left'; Text alignment for both header and body cells\\n}\\n\\n// ChatMessage — A single message in a `ChatWindow`\\ninterface ChatMessage {\\n id: string; // Unique identifier for this message\\n content: string; // The message body text\\n sender: { /** Unique ID of the sender. Compared to `currentUserId` to determine alignment. */ id: string; /** Display name of the sender. */ name: string; /** Avatar image URL. Only shown for non-current-user messages (xs size). */ avatar?: string; }; // Sender metadata used for avatar rendering and alignment\\n timestamp: string | Date; // Message timestamp\\n status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error'; // Delivery status indicator shown on the current user's messages\\n}\\n\\n// NavItem — A single navigation entry in a `NavigationBar`\\ninterface NavItem {\\n id: string; // Unique identifier used for active-state matching and React keys\\n label: string; // Display label for the navigation link\\n href?: string; // URL for the item\\n icon?: ReactNode; // Optional icon rendered before the label\\n children?: NavItem[]; // Nested child items (for sub-menu structures; rendering is consumer-defined)\\n disabled?: boolean; // When true, the item is visually dimmed (opacity 0\\n}\\n\\n// UploadedFile — A file entry in the `FileUploader` composition\\ninterface UploadedFile {\\n id: string; // Unique identifier for this file entry\\n name: string; // Original file name\\n size: number; // File size in bytes\\n type: string; // MIME type of the file (e\\n progress?: number; // Upload progress as a percentage (0-100)\\n status: 'pending' | 'uploading' | 'success' | 'error'; // Current upload lifecycle status\\n error?: string; // Error message displayed when status is `'error'`\\n url?: string; // The remote URL of the uploaded file after successful upload\\n}\\n\\n// Notification — A single notification entry in the `NotificationCenter`\\ninterface Notification {\\n id: string; // Unique identifier for this notification\\n title: string; // Notification title rendered in medium weight\\n message?: string; // Optional body text rendered below the title in a smaller font\\n timestamp: string | Date; // Timestamp of the notification\\n read?: boolean; // default false; Read state\\n type?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Semantic type that controls the status dot color on unread notifications\\n icon?: ReactNode; // Optional icon rendered alongside the notification (not used by the default im...\\n action?: { /** Button label text. */ label: string; /** Click handler for the action. */ onClick: () => void; }; // Optional inline action button rendered next to the timestamp\\n}\\n\\n// Command — A single command entry in a `CommandPalette`\\ninterface Command {\\n id: string; // Unique identifier for this command\\n label: string; // Display label for the command\\n description?: string; // Optional description text shown below the label\\n icon?: ReactNode; // Icon rendered at the start of the command row\\n shortcut?: string; // Keyboard shortcut hint displayed at the end of the row in a `<kbd>` element\\n group?: string; // Group name for visual sectioning\\n disabled?: boolean; // When true, the command is visually dimmed and cannot be selected\\n}\\n\\n// FooterLink — A single link entry in a footer column or the bottom bar\\ninterface FooterLink {\\n label: string; // Display text for the link\\n href?: string; // URL for the link\\n onClick?: () => void; // Click handler\\n}\\n\\n// FooterColumn — A named column of links in the `Footer` layout\\ninterface FooterColumn {\\n title?: string; // Column heading rendered as an `<h4>` with semibold weight\\n links: FooterLink[]; // Links displayed in this column\\n}\\n\\n// FooterSocialLink — A social media link in the `Footer` bottom bar\\ninterface FooterSocialLink {\\n label: string; // Accessible label for the social link (used as `aria-label`)\\n href: string; // URL for the social media profile or page\\n icon: ReactNode; // Icon element rendered inside the anchor (typically an SVG or `Icon` primitive)\\n}\\n\\n// HeroAction — A call-to-action button definition for the `Hero` composition\\ninterface HeroAction {\\n label: string; // Button label text\\n onClick?: () => void; // Click handler for the button\\n href?: string; // Optional URL (not used by the default implementation; available for consumer ...\\n}\\n\\n\\n// ════════════════════════════════════════════════════════════\\n// System Conventions\\n// ════════════════════════════════════════════════════════════\\n//\\n// Motion: render <MotionKeyframes /> once to enable keyframes.\\n// Entrance/exit: fadeIn, fadeOut, slideInUp, slideInDown, scaleIn, scaleOut\\n// State feedback: flash, pulse, bounce\\n// Easing: linear, easeIn, easeOut, easeInOut, spring\\n// Durations: instant(0ms), fast(100ms), normal(200ms), slow(300ms), slower(500ms)\\n//\\n// Elevation levels (shadow → z-index):\\n// 0: flat 1: sm (cards) 2: md z=1000 (dropdowns, popovers)\\n// 3: lg z=1200 4: xl z=1400 5: 2xl z=1800 (tooltips, toasts)\\n//\\n// useAnimationKey(dep) returns a key that bumps when dep changes —\\n// apply to <div key={...}> to replay CSS animations on data update.\\n//\\n// useMotion() returns { motionEnabled } — respect prefers-reduced-motion.\\n\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/tools/get-primitives-ts.ts"],"names":[],"mappings":";AAQO,IAAM,2BAAA,GAA8B","file":"get-primitives-ts.js","sourcesContent":["/**\n * AUTO-GENERATED from @ggui-ai/design JSDoc (TS-interface format).\n * Do not edit manually. Run: pnpm --filter @ggui-ai/design generate:docs-ts\n *\n * Experiment #57 (Option A) — processed/compacted doc for LLM consumption.\n * Companion to get-primitives.ts (markdown-table format). Swap between\n * them via ContextPolicy.primitiveDocFormat.\n */\nexport const PRIMITIVES_DOCUMENTATION_TS = \"// ════════════════════════════════════════════════════════════\\n// GGUI DESIGN SYSTEM — COMPONENT REFERENCE\\n// ════════════════════════════════════════════════════════════\\n//\\n// CRITICAL — Props take enum STRING LITERALS, not CSS variables:\\n// <Text size=\\\"sm\\\" weight=\\\"bold\\\" /> ✓\\n// <Text size=\\\"var(--ggui-font-size-sm)\\\" /> ✗ (breaks)\\n// The enum strings map to CSS variables internally.\\n//\\n// CRITICAL — onChange receives the VALUE directly, not an event:\\n// <Input value={x} onChange={setX} /> ✓\\n// <Input onChange={(e) => setX(e.target.value)} /> ✗\\n//\\n// Imports allowed: 'react', '@ggui-ai/design', '@ggui-ai/wire'.\\n// The WHOLE design system — primitives, components, compositions,\\n// traits — is one import: import { Card, Grid, Modal, Clickable }\\n// from '@ggui-ai/design'. No subpaths. No external libs, no\\n// fetch(), no eval().\\n//\\n// All components accept 'style' + 'className' (omitted below).\\n// Structural primitives (Box, Stack, Row, Card) also take a trait via\\n// 'as': 'as={Clickable}' adds onClick + keyboard a11y; likewise\\n// 'as={Hoverable}' and 'as={Pressable}'. Semantic components\\n// (Button/Link/Input) are already interactive and take no 'as'.\\n// ════════════════════════════════════════════════════════════\\n\\n// ════════════════════════════════════════════════════════════\\n// Primitives (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// Container — Width-constrained wrapper that centers content horizontally\\ninterface ContainerProps {\\n children?: ReactNode;\\n maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full' | string; // default 'lg'; Maximum width constraint\\n center?: boolean; // default true; Whether to center the container horizontally via `margin: 0 auto`\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no padding); Padding applied to all sides\\n}\\n\\n// Example:\\n<Container maxWidth=\\\"xl\\\" padding=\\\"var(--ggui-spacing-6)\\\">\\n <Stack gap=\\\"var(--ggui-spacing-4)\\\">\\n <Heading level={1}>Dashboard</Heading>\\n <Card shadow=\\\"md\\\" padding=\\\"var(--ggui-spacing-5)\\\">\\n <Text>Welcome back!</Text>\\n </Card>\\n </Stack>\\n</Container>\\n\\n// Card — Container with background, shadow, and optional border\\ninterface CardProps {\\n children?: ReactNode;\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'lg'; Padding applied to all sides\\n shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl'; // default 'sm'; Shadow elevation level\\n border?: boolean; // default true; Whether to render a 1px border using `var(--ggui-color-outlineVariant)`\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default 'lg'; Corner radius\\n surface?: 'default' | 'elevated' | 'sunken' | 'accent' | 'inverted' | 'hero' | 'transparent'; // default 'default'; Semantic surface slot\\n bleed?: boolean; // default false; Span the expanded frame's inset edge to edge (ggui#1083)\\n}\\n\\n// Example:\\n<Card shadow=\\\"md\\\" padding=\\\"lg\\\" radius=\\\"lg\\\">\\n <Stack gap=\\\"md\\\">\\n <Text size=\\\"sm\\\" weight=\\\"medium\\\">Settings</Text>\\n <Input label=\\\"Name\\\" value={name} onChange={setName} />\\n <Button variant=\\\"primary\\\">Save</Button>\\n </Stack>\\n</Card>\\n\\n// Stack — Flexbox layout primitive for arranging children along a single axis\\ninterface StackProps {\\n children?: ReactNode;\\n direction?: 'vertical' | 'horizontal'; // default 'vertical'; Main axis direction\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'sm'; Gap between children\\n align?: 'start' | 'center' | 'end' | 'stretch'; // default 'stretch'; Cross-axis alignment (maps to `align-items`)\\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; // default 'start'; Main-axis content distribution (maps to `justify-content`)\\n wrap?: boolean; // default false; Whether children wrap to the next line when they overflow\\n}\\n\\n// Example:\\n<Stack gap=\\\"lg\\\" align=\\\"center\\\">\\n <Heading level={2}>Profile</Heading>\\n <Text>Edit your account details below.</Text>\\n <Stack direction=\\\"horizontal\\\" gap=\\\"sm\\\" justify=\\\"end\\\">\\n <Button variant=\\\"ghost\\\">Cancel</Button>\\n <Button variant=\\\"primary\\\">Save</Button>\\n </Stack>\\n</Stack>\\n\\n// Grid — 2-D layout primitive\\ninterface GridProps {\\n children?: ReactNode;\\n columns?: number | ResponsiveColumns; // default 2; Column count\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'md'; Gap between cells\\n minColumnWidth?: number | string; // default undefined (use `columns`); When set, the grid becomes responsive — it fits as many equal\\n}\\n\\n// Example:\\n<Grid columns={3} gap=\\\"md\\\">\\n {items.map((it) => <Card key={it.id}>{it.name}</Card>)}\\n</Grid>\\n\\n// Skeleton — a pulsing placeholder for content that has not loaded\\ninterface SkeletonProps {\\n variant?: 'rect' | 'text' | 'circle'; // default 'rect'; Shape preset\\n width?: number | string;\\n height?: number | string;\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default 'sm'; Corner radius\\n}\\n\\n// Example:\\n{user === undefined\\n ? <Skeleton variant=\\\"text\\\" width=\\\"40%\\\" />\\n : <Text>{user.name}</Text>}\\n\\n// Box — Generic container with padding, margin, background, and border-radius\\ninterface BoxProps {\\n children?: ReactNode;\\n padding?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no padding); Padding applied to all four sides\\n paddingX?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined; Horizontal (left + right) padding\\n paddingY?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined; Vertical (top + bottom) padding\\n margin?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default undefined (no margin); Margin applied to all four sides\\n surface?: 'default' | 'elevated' | 'sunken' | 'accent' | 'inverted' | 'hero' | 'transparent'; // default undefined (transparent); Semantic surface slot\\n bleed?: boolean; // default false; Span the expanded frame's inset edge to edge (ggui#1083)\\n assetColor?: string; // default undefined; Asset color escape — the typed valve for legitimate non-theme\\n assetSemantic?: string; // default undefined; Human-readable semantic label that documents why\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // default undefined (no rounding); Corner radius\\n}\\n\\n// Example:\\n<Box paddingX=\\\"xl\\\" paddingY=\\\"lg\\\" surface=\\\"accent\\\" radius=\\\"lg\\\">\\n <Text size=\\\"sm\\\" tone=\\\"emphasized\\\">\\n Tip: You can customize your theme in Settings.\\n </Text>\\n</Box>\\n\\n// Divider — A 1px line to visually separate content sections\\ninterface DividerProps {\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Line direction\\n margin?: number | string; // default 16; Spacing around the divider\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-outlineVariant)`); Semantic color slot\\n}\\n\\n// Example:\\n<Stack gap={0}>\\n <Text>Section A</Text>\\n <Divider margin=\\\"var(--ggui-spacing-3)\\\" />\\n <Text>Section B</Text>\\n</Stack>\\n\\n// Spacer — Invisible spacing element, either fixed-size or flexible\\ninterface SpacerProps {\\n size?: number | 'flex'; // default 16; Spacing amount\\n}\\n\\n// Example:\\n<Stack direction=\\\"horizontal\\\" align=\\\"center\\\">\\n <Heading level={3}>Logo</Heading>\\n <Spacer size=\\\"flex\\\" />\\n <Button variant=\\\"ghost\\\">Login</Button>\\n</Stack>\\n\\n// Text — Versatile typography primitive for body copy, captions, and labels\\ninterface TextProps {\\n children?: ReactNode;\\n size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; // default 'base'; Font size\\n weight?: 'normal' | 'medium' | 'semibold' | 'bold'; // default 'normal'; Font weight\\n caps?: boolean; // default false; When true, uppercases the text and adds 0\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default 'default' (var(--ggui-color-onContainer)); Semantic color slot\\n align?: 'left' | 'center' | 'right'; // default undefined (inherits from parent); Horizontal text alignment\\n truncate?: boolean; // default false; When true, clips overflowing text with an ellipsis\\n is?: 'p' | 'span' | 'div' | 'label'; // default 'p'; HTML element to render\\n id?: string; // `id` for the rendered element — anchor an in-page link, or pair\\n htmlFor?: string; // Associates an `is=\\\"label\\\"` element with a form control by the\\n}\\n\\n// Example:\\n<Stack gap=\\\"var(--ggui-spacing-1)\\\">\\n <Text size=\\\"xs\\\" weight=\\\"semibold\\\" caps tone=\\\"muted\\\">{props.section}</Text>\\n <Text size=\\\"lg\\\">Welcome back, Jane.</Text>\\n <Text size=\\\"xs\\\" tone=\\\"muted\\\">\\n Last login: 2 hours ago\\n </Text>\\n</Stack>\\n\\n// Heading — Semantic heading element (h1-h6) with preset typography styles\\ninterface HeadingProps {\\n children?: ReactNode;\\n level?: 1 | 2 | 3 | 4 | 5 | 6; // default 2; Semantic heading level\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default 'default' (var(--ggui-color-onContainer)); Semantic color slot\\n align?: 'left' | 'center' | 'right'; // default undefined (inherits from parent); Horizontal text alignment\\n}\\n\\n// Example:\\n<Stack gap=\\\"var(--ggui-spacing-2)\\\">\\n <Heading level={1}>Page Title</Heading>\\n <Heading level={3} tone=\\\"emphasized\\\">\\n Subsection\\n </Heading>\\n <Text>Body content goes here.</Text>\\n</Stack>\\n\\n// Button — A clickable button primitive with multiple visual variants and sizes\\ninterface ButtonProps {\\n children?: ReactNode;\\n variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'; // default 'primary'; Visual style\\n size?: 'xs' | 'sm' | 'md' | 'lg'; // default 'md'; Controls padding, font size, and minimum height:\\n fullWidth?: boolean; // default false; When true, sets `width: 100%` so the button fills its container\\n loading?: boolean; // default false; When true, replaces children with a 16px `Spinner` (color: `currentColor`)\\n type?: 'button' | 'submit' | 'reset'; // default 'button'; The HTML button type\\n leftIcon?: ReactNode; // ReactNode rendered before children, inside the flex layout with `var(--ggui-s...\\n rightIcon?: ReactNode; // ReactNode rendered after children, inside the flex layout with `var(--ggui-sp...\\n onPress?: () => void; // Alias for `onClick` for cross-platform compatibility (React Native convention)\\n}\\n\\n// Example:\\n<Button variant=\\\"primary\\\" size=\\\"md\\\" leftIcon={<Icon name=\\\"save\\\" />} onClick={handleSave}>\\n Save Changes\\n</Button>\\n\\n// Input — A single-line text input with label, validation, and helper text\\ninterface InputProps {\\n label?: string; // Label rendered above the input\\n placeholder?: string; // Placeholder text shown when the input is empty\\n value?: string; // Controlled value of the input\\n onChange?: (value: string) => void; // Change handler\\n type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local'; // default 'text'; HTML input type\\n error?: string; // Error message displayed below the input in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the input in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n}\\n\\n// Example:\\n<Input label=\\\"Email\\\" type=\\\"email\\\" value={email} onChange={setEmail} error={emailError} />\\n\\n// TextArea — A multiline text input with label, validation, character count, a...\\ninterface TextAreaProps {\\n label?: string; // Label rendered above the textarea\\n placeholder?: string; // Placeholder text shown when the textarea is empty\\n value?: string; // Controlled value of the textarea\\n onChange?: (value: string) => void; // Change handler\\n rows?: number; // default 4; Number of visible text rows (native `rows` attribute on `<textarea>`)\\n error?: string; // Error message displayed below the textarea in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the textarea in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n maxLength?: number; // Maximum character length (native `maxLength` attribute)\\n showCount?: boolean; // default false; When true AND `maxLength` is set, displays a `{current}/{max}` character\\n autoResize?: boolean; // default false; When true, sets CSS `resize: none` on the textarea\\n}\\n\\n// Example:\\n<TextArea label=\\\"Bio\\\" value={bio} onChange={setBio} rows={6} maxLength={500} showCount />\\n\\n// Select — A native dropdown selection primitive with label and validation\\ninterface SelectProps {\\n label?: string; // Label rendered above the select\\n value?: string; // Controlled value\\n onChange?: (value: string) => void; // Change handler\\n options: SelectOption[]; // Array of selectable options\\n placeholder?: string; // Placeholder text rendered as a disabled `<option value=\\\"\\\">` at the top of the\\n error?: string; // Error message displayed below the select in `var(--ggui-color-error-500)`\\n helperText?: string; // Helper text displayed below the select in `var(--ggui-color-onSunken)`\\n required?: boolean; // default false; When true, appends a red asterisk (`*`) to the label and sets the native\\n disabled?: boolean; // default false; When true, sets the native `disabled` attribute\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n}\\n\\n// Example:\\n<Select\\n label=\\\"Country\\\"\\n value={country}\\n onChange={setCountry}\\n options={[\\n { value: 'us', label: 'United States' },\\n { value: 'uk', label: 'United Kingdom' },\\n ]}\\n placeholder=\\\"Select a country\\\"\\n/>\\n\\n// Checkbox — A custom-styled checkbox with label and description\\ninterface CheckboxProps {\\n label?: string; // Primary label text rendered beside the checkbox box\\n checked?: boolean; // Controlled checked state\\n onChange?: (checked: boolean) => void; // Change handler\\n disabled?: boolean; // default false; When true, sets `opacity: 0\\n description?: string; // Secondary description text rendered below the label in smaller, muted type\\n indeterminate?: boolean; // default false; When true, displays a horizontal dash instead of a checkmark\\n}\\n\\n// Example:\\n<Checkbox\\n label=\\\"Accept terms\\\"\\n description=\\\"You agree to the Terms of Service and Privacy Policy\\\"\\n checked={accepted}\\n onChange={setAccepted}\\n/>\\n\\n// Toggle — A switch/toggle input rendered as a pill-shaped track with a slidin...\\ninterface ToggleProps {\\n label?: string; // Label text rendered to the right of the toggle track\\n checked?: boolean; // Controlled checked (on/off) state\\n onChange?: (checked: boolean) => void; // Change handler\\n disabled?: boolean; // default false; When true, sets `opacity: 0\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls track and knob dimensions:\\n}\\n\\n// Example:\\n<Toggle label=\\\"Enable notifications\\\" checked={enabled} onChange={setEnabled} size=\\\"md\\\" />\\n\\n// RadioGroup — A group of mutually exclusive radio options with optional label...\\ninterface RadioGroupProps {\\n label?: string; // Group label rendered above the options\\n value?: string; // Controlled value\\n onChange?: (value: string) => void; // Change handler\\n options: RadioOption[]; // Array of radio options\\n direction?: 'vertical' | 'horizontal'; // default 'vertical'; Layout direction for the options container:\\n disabled?: boolean; // default false; When true, disables ALL options (individual `RadioOption\\n error?: string; // Error message displayed below all options in `var(--ggui-color-error-500)`\\n}\\n\\n// Example:\\n<RadioGroup\\n label=\\\"Plan\\\"\\n value={plan}\\n onChange={setPlan}\\n options={[\\n { value: 'free', label: 'Free', description: 'Up to 5 projects' },\\n { value: 'pro', label: 'Pro', description: 'Unlimited projects' },\\n ]}\\n/>\\n\\n// Slider — A range input with a custom-styled track, fill, and thumb\\ninterface SliderProps {\\n label?: string; // Label rendered above the slider track (left-aligned)\\n value?: number; // default 0; Controlled numeric value\\n onChange?: (value: number) => void; // Change handler\\n min?: number; // default 0; Minimum allowed value\\n max?: number; // default 100; Maximum allowed value\\n step?: number; // default 1; Step increment for the slider\\n disabled?: boolean; // default false; When true, sets `cursor: not-allowed` on the native input\\n showValue?: boolean; // default false; When true, displays the current numeric value right-aligned in the header row\\n}\\n\\n// Example:\\n<Slider label=\\\"Volume\\\" value={volume} onChange={setVolume} min={0} max={100} step={5} showValue />\\n\\n// Badge — Inline label for status indicators, counts, or categories\\ninterface BadgeProps {\\n children?: ReactNode;\\n variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info' | 'tertiary'; // default 'default'; Visual style\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding and font size:\\n pill?: boolean; // default true; When true, uses fully rounded corners (`border-radius: 9999px`)\\n}\\n\\n// Example:\\n<Badge variant=\\\"success\\\" size=\\\"sm\\\">Active</Badge>\\n\\n// Spinner — Animated SVG loading indicator\\ninterface SpinnerProps {\\n size?: number; // default 24; Width and height of the SVG element in pixels\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-primary-600)`); Semantic color slot for the animated foreground arc\\n}\\n\\n// Example:\\n<Spinner size={32} tone=\\\"success\\\" />\\n\\n// Avatar — User or entity representation with image or auto-generated initials\\ninterface AvatarProps {\\n src?: string; // Image URL\\n name?: string; // Name used for two purposes:\\n size?: number | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; // default 'md'; Avatar dimensions\\n shape?: 'circle' | 'square'; // default 'circle'; Container shape\\n}\\n\\n// Example:\\n<Avatar src=\\\"/photos/jane.jpg\\\" name=\\\"Jane Doe\\\" size=\\\"lg\\\" shape=\\\"circle\\\" />\\n\\n// Alert — Contextual message box for important information with icon and optio...\\ninterface AlertProps {\\n children?: ReactNode;\\n variant?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Visual style\\n title?: string; // Optional title rendered above the body in semibold (`var(--ggui-font-weight-s...\\n closable?: boolean; // default false; When true, renders a close button (X icon) in the top-right area\\n onClose?: () => void; // Callback fired when the close button is clicked\\n icon?: ReactNode; // Custom icon ReactNode to replace the default variant icon\\n}\\n\\n// Example:\\n<Alert variant=\\\"warning\\\" title=\\\"Rate limit\\\" closable onClose={() => setShow(false)}>\\n You have 3 requests remaining this minute.\\n</Alert>\\n\\n// Progress — Horizontal progress bar with determinate and indeterminate modes\\ninterface ProgressProps {\\n value: number; // Current progress value\\n max?: number; // default 100; Maximum value representing 100% progress\\n variant?: 'default' | 'success' | 'warning' | 'error'; // default 'default'; Fill bar color\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls track height in pixels:\\n label?: string; // Accessible name describing what this bar measures, e\\n showLabel?: boolean; // default false; When true, displays a header row above the track with the `label` text\\n indeterminate?: boolean; // default false; When true, ignores `value` for visual width and plays a looping animation\\n}\\n\\n// Example:\\n<Progress value={65} variant=\\\"success\\\" size=\\\"md\\\" showLabel />\\n\\n// Image — An `<img>` element with built-in error handling and fallback support\\ninterface ImageProps {\\n src: string; // Image source URL\\n alt: string; // Alt text for the image\\n width?: number | string; // default '100%' (applied at render time, not on the type); Image width\\n height?: number | string; // default 'auto' (applied at render time, not on the type); Image height\\n objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'; // default 'cover'; CSS `object-fit` value controlling how the image fills its box\\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | number | string; // Corner radius applied to both the image and the error placeholder\\n fallback?: ReactNode; // Custom ReactNode rendered when the image fails to load\\n}\\n\\n// Example:\\n<Image src=\\\"/hero.jpg\\\" alt=\\\"Hero banner\\\" width=\\\"100%\\\" height={400} objectFit=\\\"cover\\\" radius=\\\"md\\\" />\\n\\n// Icon — a curated Lucide subset (~200 names) + emoji passthrough\\ninterface IconProps {\\n name?: string; // Lucide icon name from the curated subset (kebab-case, camelCase, or\\n size?: number; // default 24; Icon dimensions in pixels (applied to both width and height of the wrapper\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (icon uses `currentColor`); Semantic color slot\\n children?: ReactNode; // Custom SVG children\\n 'aria-label'?: string; // Accessible name for a standalone, meaning-bearing icon\\n}\\n\\n// Example:\\n<Icon name=\\\"search\\\" size={20} tone=\\\"muted\\\" />\\n<Icon name=\\\"cloud-rain\\\" size={32} />\\n<Icon name=\\\"☀️\\\" size={24} />\\n\\n// Link — Styled anchor element with external link support\\ninterface LinkProps {\\n children?: ReactNode;\\n href: string; // Destination URL\\n external?: boolean; // default false; When true, opens link in a new tab (`target=\\\"_blank\\\"`, `rel=\\\"noopener norefer...\\n tone?: | 'default' | 'muted' | 'subtle' | 'emphasized' | 'loud' | 'success' | 'warning' | 'error' | 'info' | 'inverse' | 'inherit'; // default undefined (uses `var(--ggui-color-primary-600)`); Semantic color slot for the link text\\n underline?: 'always' | 'hover' | 'none'; // default 'hover'; Underline behavior:\\n}\\n\\n// Example:\\n<Link href=\\\"https://docs.ggui.ai\\\" external>Documentation</Link>\\n\\n// Tooltip — Hoverable information popup positioned relative to a trigger element\\ninterface TooltipProps {\\n children: ReactNode; // Trigger element\\n content: ReactNode; // Tooltip content\\n position?: 'top' | 'bottom' | 'left' | 'right'; // default 'top'; Tooltip placement relative to the trigger element:\\n delay?: number; // default 200; Delay in milliseconds before the tooltip becomes visible after hover/focus\\n}\\n\\n// Example:\\n<Tooltip content=\\\"Copy to clipboard\\\" position=\\\"top\\\">\\n <Button variant=\\\"ghost\\\"><Icon name=\\\"copy\\\" /></Button>\\n</Tooltip>\\n\\n// Table — Data table with sortable columns, striped rows, and hover highlights\\ninterface TableProps {\\n columns: TableColumn<T>[]; // Array of column definitions controlling header labels, data keys, and rendering\\n data: T[]; // Array of row data objects\\n sortKey?: string; // The `key` of the currently sorted column\\n sortDirection?: SortDirection; // default 'asc'; Current sort direction for the column identified by `sortKey`\\n onSort?: (key: string, direction: SortDirection) => void; // Sort change handler\\n striped?: boolean; // default false; When true, alternating rows (odd index) get a\\n hoverable?: boolean; // default true; When true, rows highlight with `var(--ggui-color-container)`\\n compact?: boolean; // default false; When true, reduces cell padding:\\n bordered?: boolean; // default false; When true, adds a 1px border around the table wrapper and between cells\\n caption?: string; // Accessible table caption\\n}\\n\\n// Example:\\n<Table\\n columns={[\\n { key: 'name', header: 'Name', sortable: true },\\n { key: 'role', header: 'Role' },\\n { key: 'status', header: 'Status', render: (v) => <Badge variant={v as string}>{v as string}</Badge> },\\n ]}\\n data={users}\\n sortKey={sortKey}\\n sortDirection={sortDir}\\n onSort={(key, dir) => { setSortKey(key); setSortDir(dir); }}\\n striped\\n/>\\n\\n// Tabs — Accessible tab navigation with panels and keyboard support\\ninterface TabsProps {\\n items: TabItem[]; // Array of tab definitions\\n activeKey?: string; // Controlled active tab key\\n onChange?: (key: string) => void; // Tab change handler\\n variant?: 'line' | 'pills' | 'enclosed'; // default 'line'; Visual style of the tab bar:\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls tab button padding and font size:\\n fullWidth?: boolean; // default false; When true, tab buttons expand equally to fill the container width\\n}\\n\\n// Example:\\n<Tabs\\n variant=\\\"pills\\\"\\n items={[\\n { key: 'overview', label: 'Overview', content: <Overview /> },\\n { key: 'settings', label: 'Settings', content: <Settings /> },\\n ]}\\n activeKey={tab}\\n onChange={setTab}\\n/>\\n\\n// Toast — Notification banner with auto-dismiss and slide-in animation\\ninterface ToastProps {\\n message: ReactNode; // Message body content\\n variant?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Visual style\\n title?: string; // Optional title rendered above the message in semibold\\n duration?: number; // default 5000; Auto-dismiss delay in milliseconds\\n onClose?: () => void; // Callback fired on auto-dismiss timeout or when the close button is clicked\\n visible?: boolean; // default true; Controls rendering\\n position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center'; // Intended screen position\\n}\\n\\n// Example:\\n<Toast variant=\\\"success\\\" title=\\\"Saved\\\" message=\\\"Your changes have been saved.\\\" onClose={() => setShow(false)} />\\n\\n// Accordion — Collapsible content sections with chevron rotation animation\\ninterface AccordionProps {\\n items: AccordionItem[]; // Array of collapsible section definitions\\n expandedKeys?: string[]; // Controlled expanded state\\n onChange?: (expandedKeys: string[]) => void; // Expand/collapse handler\\n multiple?: boolean; // default false; When true, multiple items can be open simultaneously\\n variant?: 'default' | 'bordered' | 'separated'; // default 'default'; Visual style controlling borders and spacing:\\n}\\n\\n// Example:\\n<Accordion\\n variant=\\\"separated\\\"\\n items={[\\n { key: 'faq1', title: 'How do I get started?', content: 'Sign up and...' },\\n { key: 'faq2', title: 'What is the pricing?', content: 'We offer...' },\\n ]}\\n expandedKeys={expanded}\\n onChange={setExpanded}\\n multiple\\n/>\\n\\n// Row — Convenience alias for a horizontal Stack\\ninterface RowProps {\\n children?: ReactNode;\\n gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | string; // default 'sm'; Gap between children\\n align?: 'start' | 'center' | 'end' | 'stretch'; // default 'stretch'; Cross-axis alignment (maps to `align-items`)\\n justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; // default 'start'; Main-axis content distribution (maps to `justify-content`)\\n wrap?: boolean; // default false; Whether children wrap to the next line when they overflow\\n}\\n\\n// Example:\\n<Row gap=\\\"md\\\" align=\\\"center\\\" justify=\\\"between\\\">\\n <Text size=\\\"sm\\\" weight=\\\"medium\\\">Total</Text>\\n <Text weight=\\\"bold\\\">$42.00</Text>\\n</Row>\\n\\n// Support types:\\n// ResponsiveColumns — Explicit column count per viewport breakpoint, mobile-first\\ninterface ResponsiveColumns {\\n base?: number; // default 1; Columns below the `sm` breakpoint\\n sm?: number; // Columns from 640px up\\n md?: number; // Columns from 768px up\\n lg?: number; // Columns from 1024px up\\n xl?: number; // Columns from 1280px up\\n}\\n\\n// SelectOption — An individual option within a `Select` dropdown\\ninterface SelectOption {\\n value: string; // The value submitted when this option is selected\\n label: string; // The display text shown in the dropdown\\n disabled?: boolean; // default false; When true, the option is visible but cannot be selected (grayed out by the br...\\n}\\n\\n// RadioOption — An individual option within a `RadioGroup`\\ninterface RadioOption {\\n value: string; // The value emitted via `RadioGroupProps\\n label: string; // Display text for this option\\n description?: string; // Optional secondary description rendered below the label in smaller, muted type\\n disabled?: boolean; // default false; When true, this individual option is grayed out (`opacity: 0\\n}\\n\\n// TableColumn — Column definition for the Table component\\ninterface TableColumn {\\n key: string; // Property key in the row data object\\n header: string; // Column header text\\n width?: number | string; // Fixed column width\\n align?: 'left' | 'center' | 'right'; // default 'left'; Horizontal text alignment for both the header and data cells\\n sortable?: boolean; // default false; When true, the header cell becomes clickable and shows sort direction\\n render?: (value: unknown, row: T, index: number) => ReactNode; // Custom cell renderer\\n}\\n\\n// TabItem — Definition of a single tab within a Tabs component\\ninterface TabItem {\\n key: string; // Unique identifier for this tab\\n label: ReactNode; // Tab button label\\n content: ReactNode; // Panel content rendered below the tab bar when this tab is active\\n disabled?: boolean; // default false; When true, the tab button shows `opacity: 0\\n icon?: ReactNode; // Optional icon rendered before the label inside the tab button, with\\n}\\n\\n// AccordionItem — Definition of a single collapsible section within an Accordion\\ninterface AccordionItem {\\n key: string; // Unique identifier for this item\\n title: ReactNode; // Header label rendered inside the toggle button\\n content: ReactNode; // Panel content rendered below the header when expanded\\n disabled?: boolean; // default false; When true, the header button shows `opacity: 0\\n}\\n\\n// ════════════════════════════════════════════════════════════\\n// Components (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// SearchField — A text input with a leading search icon and optional submit button\\ninterface SearchFieldProps {\\n value?: string; // Current search value\\n onChange?: (value: string) => void; // Called on every keystroke with the new input string (value directly, not\\n onSearch?: (value: string) => void; // Called when the user presses **Enter** or clicks the search button\\n placeholder?: string; // default 'Search...'; Placeholder text shown when the input is empty\\n showButton?: boolean; // default false; When `true`, renders a `Button` primitive to the right of the input\\n buttonText?: string; // default 'Search'; Label rendered inside the submit button\\n loading?: boolean; // When `true`, replaces the search icon with a `Spinner`, disables the\\n disabled?: boolean; // When `true`, the input and button are visually disabled and do not\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls input height and font size\\n}\\n\\n// Example:\\n```tsx\\n<SearchField\\n value={query}\\n onChange={setQuery}\\n onSearch={(q) => fetchResults(q)}\\n placeholder=\\\"Search products...\\\"\\n showButton\\n buttonText=\\\"Go\\\"\\n size=\\\"md\\\"\\n/>\\n```\\n\\n// FormField — A wrapper that adds a label, optional description, error message,\\ninterface FormFieldProps {\\n label: string; // Text rendered inside the `<label>` element above the input\\n children: ReactNode; // The form control (typically an `Input`, `Select`, or `Textarea` primitive) re...\\n error?: string; // Error message displayed below `children` in `colors\\n helperText?: string; // Neutral guidance text displayed below `children` in `colors\\n required?: boolean; // When `true`, appends a red asterisk (`*`) after the label text\\n description?: string; // Secondary description rendered between the label and the child control\\n}\\n\\n// Example:\\n```tsx\\n<FormField\\n label=\\\"Email address\\\"\\n required\\n description=\\\"We will never share your email.\\\"\\n error={errors.email}\\n>\\n <Input value={email} onChange={setEmail} placeholder=\\\"you@example.com\\\" />\\n</FormField>\\n```\\n\\n// MenuItem — A full-width clickable row for menus, sidebars, and action lists\\ninterface MenuItemProps {\\n label: string; // Primary text content of the menu item\\n icon?: ReactNode; // Icon or element rendered to the left of the label\\n rightElement?: ReactNode; // Element rendered to the right of the label (e\\n onClick?: () => void; // Called when the item is clicked\\n disabled?: boolean; // When `true`, the item is non-interactive: `colors\\n active?: boolean; // Marks this item as the current selection\\n danger?: boolean; // Switches the item to destructive styling: `colors\\n}\\n\\n// Example:\\n```tsx\\n<MenuItem\\n label=\\\"Delete project\\\"\\n icon={<Icon name=\\\"trash\\\" size={16} />}\\n danger\\n onClick={() => confirmDelete(projectId)}\\n/>\\n```\\n\\n// Tag — An inline label for categories, filters, statuses, or selections\\ninterface TagProps {\\n children: ReactNode; // Tag content -- typically a short text string\\n variant?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'; // default 'default'; Semantic color variant applied to background, text, and border\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls padding, font size, and internal gap\\n closable?: boolean; // When `true`, renders a small close (\\\"x\\\") button after the content\\n onClose?: () => void; // Called when the close button is clicked\\n icon?: ReactNode; // Icon or element rendered before the text content\\n}\\n\\n// Example:\\n```tsx\\n<Tag variant=\\\"success\\\" size=\\\"md\\\" closable onClose={() => removeFilter(id)}>\\n Active\\n</Tag>\\n```\\n\\n// Dropdown — A click-triggered menu anchored to a trigger element\\ninterface DropdownProps {\\n trigger: ReactNode; // The element the user clicks to open the menu\\n options: DropdownOption[]; // Array of selectable options rendered as `MenuItem` rows\\n value?: string; // The `value` of the currently selected option\\n onChange?: (value: string) => void; // Called with the `value` string of the selected option (not the full\\n placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'; // default 'bottom-start'; Where to anchor the menu panel relative to the trigger\\n disabled?: boolean; // When `true`, the trigger shows `cursor: not-allowed` and clicking\\n}\\n\\n// Example:\\n```tsx\\n<Dropdown\\n trigger={<Button variant=\\\"outline\\\">Sort by</Button>}\\n options={[\\n { value: 'name', label: 'Name' },\\n { value: 'date', label: 'Date created' },\\n { value: 'delete', label: 'Delete', danger: true },\\n ]}\\n value={sortBy}\\n onChange={setSortBy}\\n placement=\\\"bottom-end\\\"\\n/>\\n```\\n\\n// Autocomplete — A text input with a filterable suggestion dropdown, keyboard navigation,\\ninterface AutocompleteProps {\\n value?: string; // default ''; Current text in the input field\\n onChange?: (value: string) => void; // Called on every keystroke with the new input string (value directly,\\n onSelect?: (option: AutocompleteOption) => void; // Called when the user selects an option (click or Enter on highlighted\\n options: AutocompleteOption[]; // The full list of available options\\n placeholder?: string; // Placeholder text forwarded to the inner `Input` primitive\\n label?: string; // Label text forwarded to the inner `Input` primitive\\n loading?: boolean; // When `true`, the dropdown shows a centered `Spinner` instead of\\n disabled?: boolean; // When `true`, the inner `Input` is disabled and the dropdown\\n error?: string; // Error message forwarded to the inner `Input` primitive\\n noResultsText?: string; // default 'No results found'; Text shown in the dropdown when filtering produces zero matches\\n}\\n\\n// Example:\\n```tsx\\n<Autocomplete\\n label=\\\"Country\\\"\\n value={country}\\n onChange={setCountry}\\n onSelect={(opt) => setCountryCode(opt.value)}\\n options={countries}\\n placeholder=\\\"Type to search...\\\"\\n noResultsText=\\\"No countries found\\\"\\n/>\\n```\\n\\n// Breadcrumb — A horizontal navigation trail showing the user's location within a\\ninterface BreadcrumbProps {\\n items: BreadcrumbItem[]; // Ordered array of breadcrumb segments from root to current page\\n separator?: ReactNode; // default '/'; Separator rendered between each pair of items\\n onItemClick?: (item: BreadcrumbItem, index: number) => void; // Called when a non-last item is clicked\\n}\\n\\n// Example:\\n```tsx\\n<Breadcrumb\\n items={[\\n { label: 'Home', href: '/' },\\n { label: 'Projects', href: '/projects' },\\n { label: 'ggui' },\\n ]}\\n separator=\\\"/\\\"\\n onItemClick={(item) => router.push(item.href!)}\\n/>\\n```\\n\\n// Pagination — Page navigation controls with previous/next arrows, numbered page buttons,\\ninterface PaginationProps {\\n currentPage: number; // Current active page\\n totalPages: number; // Total number of pages\\n onPageChange?: (page: number) => void; // Called when the user clicks a page number, arrow, or first/last button\\n showFirstLast?: boolean; // default true; When `true`, renders double-chevron buttons for jumping to the first\\n maxVisible?: number; // default 5; Maximum number of page buttons visible at once (including the first\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls button dimensions and icon sizes\\n disabled?: boolean; // When `true`, all page buttons and arrows are visually dimmed\\n}\\n\\n// Example:\\n```tsx\\n<Pagination\\n currentPage={page}\\n totalPages={20}\\n onPageChange={setPage}\\n maxVisible={7}\\n size=\\\"md\\\"\\n/>\\n```\\n\\n// EmptyState — placeholder for a region with no data: empty lists,\\ninterface EmptyStateProps {\\n icon?: string | ReactNode; // A Lucide icon name (kebab-case), rendered large and subtle above\\n title: string; // The headline, e\\n description?: string; // Optional supporting line below the title\\n action?: ReactNode; // Optional call-to-action, typically a `<Button>`\\n}\\n\\n// Example:\\n{results.length === 0\\n ? <EmptyState icon=\\\"search-x\\\" title=\\\"No matches\\\" description=\\\"Try a broader query.\\\" />\\n : results.map((r) => <Row key={r.id}>…</Row>)}\\n\\n// Stat — a single KPI / metric: a label, a large value, an optional\\ninterface StatProps {\\n label: string; // The metric name, e\\n value: string | number; // The headline value — the big number\\n delta?: string; // Optional change indicator, pre-formatted, e\\n trend?: 'up' | 'down' | 'neutral'; // default 'neutral'; Direction of `delta` — colours it: `'up'` success, `'down'` error,\\n icon?: string | ReactNode; // Optional Lucide icon name (kebab-case) or custom node, shown next\\n}\\n\\n// Example:\\n<Stat label=\\\"Revenue\\\" value=\\\"$48.2k\\\" delta=\\\"+12.5%\\\" trend=\\\"up\\\" icon=\\\"trending-up\\\" />\\n\\n// Stepper — a display-only step indicator for multi-step flows\\ninterface StepperProps {\\n steps: string[]; // Ordered step labels, one per step\\n current: number; // Zero-based index of the active step\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Flow direction\\n onStepClick?: (index: number) => void; // When provided, each step becomes a real button and clicks receive\\n}\\n\\n// Example:\\nconst STEPS = ['Account', 'Profile', 'Review'];\\n// In the component: const [step, setStep] = useState(0);\\n<Stack gap=\\\"lg\\\">\\n <Stepper steps={STEPS} current={step} />\\n {step === 0 && <AccountFields />}\\n <Row gap=\\\"sm\\\" justify=\\\"end\\\">\\n <Button variant=\\\"ghost\\\" disabled={step === 0} onClick={() => setStep(step - 1)}>Back</Button>\\n <Button onClick={() => setStep(step + 1)}>Next</Button>\\n </Row>\\n</Stack>\\n\\n// Markdown — renders markdown-formatted text as styled content:\\ninterface MarkdownProps {\\n markdown: string; // Markdown source text, passed through verbatim\\n}\\n\\n// Example:\\n<Stack gap=\\\"md\\\">\\n <Markdown markdown={recipe.instructions} />\\n</Stack>\\n\\n// MarkdownInline — renders ONE line of inline-level markdown (bold,\\ninterface MarkdownInlineProps {\\n markdown: string; // Inline markdown source -- bold, italic, inline code, and\\n}\\n\\n// Example:\\n<Row gap=\\\"sm\\\">\\n <Badge variant=\\\"success\\\">stable</Badge>\\n <MarkdownInline markdown={release.summary} />\\n</Row>\\n\\n// Support types:\\n// DropdownOption — A single option inside a `Dropdown`\\ninterface DropdownOption {\\n value: string; // Unique identifier returned to `onChange` when this option is selected\\n label: string; // Human-readable text shown in the menu row\\n icon?: ReactNode; // Optional icon rendered to the left of the label via `MenuItem\\n disabled?: boolean; // When `true`, the option is visible but non-interactive\\n danger?: boolean; // When `true`, the option uses destructive (red) styling via\\n}\\n\\n// AutocompleteOption — A single option in the `Autocomplete` suggestion list\\ninterface AutocompleteOption {\\n value: string; // Unique identifier for this option\\n label: string; // Primary display text\\n description?: string; // Secondary description rendered below the label in `fontSize\\n icon?: ReactNode; // Icon rendered to the left of the label/description block\\n disabled?: boolean; // When `true`, the option is visible but non-interactive (`cursor: not-allowed`...\\n}\\n\\n// BreadcrumbItem — A single segment in a `Breadcrumb` trail\\ninterface BreadcrumbItem {\\n label: string; // Display text for this breadcrumb segment\\n href?: string; // URL for this segment\\n icon?: ReactNode; // Icon rendered immediately before the label\\n}\\n\\n// ════════════════════════════════════════════════════════════\\n// Compositions (import from '@ggui-ai/design')\\n// ════════════════════════════════════════════════════════════\\n\\n// Header — Props for the `Header` composition\\ninterface HeaderProps {\\n logo?: ReactNode; // Logo or brand element rendered at the start (flex-shrink: 0)\\n navigation?: ReactNode; // Navigation content rendered in a `<nav>` element with `flex: 1` and a 32 px l...\\n actions?: ReactNode; // Right-side action elements (buttons, avatar, etc\\n sticky?: boolean; // default false; When true, the header becomes `position: sticky` at the top of its scroll con...\\n background?: string; // default colors.white; Background color of the header\\n bordered?: boolean; // default true; When true, renders a 1 px bottom border in `colors\\n}\\n\\n// Example:\\n```tsx\\n<Header\\n logo={<img src=\\\"/logo.svg\\\" alt=\\\"Acme\\\" />}\\n navigation={<a href=\\\"/docs\\\">Docs</a>}\\n actions={<Button size=\\\"sm\\\">Sign In</Button>}\\n sticky\\n bordered\\n/>\\n```\\n\\n// Sidebar — Props for the `Sidebar` composition\\ninterface SidebarProps {\\n items: SidebarItem[]; // Array of navigation items to render\\n activeId?: string; // ID of the currently active item\\n onItemClick?: (item: SidebarItem) => void; // Called when any item (including parent items with children) is clicked\\n collapsed?: boolean; // default false; When true, hides labels and badges; only icons remain visible, centered in th...\\n header?: ReactNode; // Content rendered above the item list, separated by a bottom border\\n footer?: ReactNode; // Content rendered below the item list, separated by a top border\\n width?: number; // default 256; Width in pixels when expanded\\n collapsedWidth?: number; // default 64; Width in pixels when collapsed\\n}\\n\\n// Example:\\n```tsx\\n<Sidebar\\n items={[\\n { id: 'home', label: 'Home', icon: <Icon name=\\\"home\\\" /> },\\n { id: 'settings', label: 'Settings', icon: <Icon name=\\\"settings\\\" />,\\n children: [\\n { id: 'profile', label: 'Profile' },\\n { id: 'billing', label: 'Billing' },\\n ]},\\n ]}\\n activeId=\\\"home\\\"\\n onItemClick={(item) => navigate(item.href)}\\n collapsed={false}\\n width={256}\\n/>\\n```\\n\\n// CardGrid — Props for the `CardGrid` composition\\ninterface CardGridProps {\\n children: ReactNode; // Card elements to arrange in the grid\\n columns?: number | { sm?: number; md?: number; lg?: number }; // default 3; Number of columns, or a responsive breakpoint map\\n gap?: number; // default 16; Gap between grid items in pixels\\n}\\n\\n// Example:\\n```tsx\\n<CardGrid columns={3} gap={24}>\\n <Card>A</Card>\\n <Card>B</Card>\\n <Card>C</Card>\\n</CardGrid>\\n```\\n\\n// CommentThread — Props for the `CommentThread` composition\\ninterface CommentThreadProps {\\n comments: Comment[]; // Array of top-level comments to render\\n currentUser?: { /** Display name for the current user's avatar. */ name: string; /** Avatar image URL for the current user. */ avatar?: string; }; // Current user info\\n onAddComment?: (content: string, parentId?: string) => void; // Called when the user submits a new top-level comment\\n onReply?: (commentId: string, content: string) => void; // Called when the user submits a reply to an existing comment\\n onReaction?: (commentId: string, emoji: string) => void; // Called when the user clicks an emoji reaction on a comment\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the comment list\\n}\\n\\n// Example:\\n```tsx\\n<CommentThread\\n comments={[\\n { id: '1', author: { name: 'Alice' }, content: 'Great work!', timestamp: new Date(),\\n replies: [{ id: '2', author: { name: 'Bob' }, content: 'Thanks!', timestamp: new Date() }] }\\n ]}\\n currentUser={{ name: 'Alice', avatar: '/alice.jpg' }}\\n onAddComment={(content) => post(content)}\\n onReply={(commentId, content) => reply(commentId, content)}\\n onReaction={(commentId, emoji) => react(commentId, emoji)}\\n/>\\n```\\n\\n// DataTable — Props for the `DataTable` composition\\ninterface DataTableProps {\\n columns: DataTableColumn<T>[]; // Column definitions that control header, alignment, sorting, and rendering\\n data: T[]; // Row data array\\n rowKey?: string | ((row: T) => string); // default 'id'; Property name or function used to derive a unique key for each row\\n loading?: boolean; // When true, shows a centered `Spinner` instead of table body rows\\n emptyText?: string; // default 'No data'; Text shown when `data` is empty and not loading\\n onSort?: (key: string, direction: 'asc' | 'desc') => void; // Called when a sortable column header is clicked\\n sortKey?: string; // The column key currently being sorted\\n sortDirection?: 'asc' | 'desc'; // The current sort direction\\n onRowClick?: (row: T, index: number) => void; // Called when a row is clicked\\n selectable?: boolean; // When true, adds a checkbox column at the start of each row\\n selectedKeys?: string[]; // default []; Array of currently selected row keys\\n onSelectionChange?: (keys: string[]) => void; // Called when the set of selected row keys changes (via row checkbox or select-...\\n}\\n\\n// Example:\\n```tsx\\n<DataTable\\n columns={[\\n { key: 'name', header: 'Name', sortable: true },\\n { key: 'email', header: 'Email' },\\n { key: 'role', header: 'Role', render: (v) => <Badge>{v}</Badge> },\\n ]}\\n data={users}\\n rowKey=\\\"id\\\"\\n selectable\\n selectedKeys={selected}\\n onSelectionChange={setSelected}\\n onSort={(key, dir) => sort(key, dir)}\\n sortKey=\\\"name\\\"\\n sortDirection=\\\"asc\\\"\\n/>\\n```\\n\\n// ChatWindow — Props for the `ChatWindow` composition\\ninterface ChatWindowProps {\\n messages: ChatMessage[]; // Array of chat messages to display in chronological order\\n currentUserId: string; // ID of the current user\\n onSendMessage?: (content: string) => void; // Called when the user submits a message (Enter key or send button)\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the message list\\n typing?: { name: string } | null; // When non-null, displays a typing indicator below the last message (e\\n placeholder?: string; // default 'Type a message...'; Placeholder text for the message input field\\n header?: ReactNode; // Optional header content rendered above the message area, separated by a botto...\\n}\\n\\n// Example:\\n```tsx\\n<ChatWindow\\n messages={messages}\\n currentUserId=\\\"user-1\\\"\\n onSendMessage={(content) => send(content)}\\n typing={{ name: 'Alice' }}\\n placeholder=\\\"Type a message...\\\"\\n header={<h3>Chat with Alice</h3>}\\n/>\\n```\\n\\n// NavigationBar — Props for the `NavigationBar` composition\\ninterface NavigationBarProps {\\n items: NavItem[]; // Array of navigation items to render\\n activeId?: string; // ID of the currently active item\\n onItemClick?: (item: NavItem) => void; // Called when a non-disabled item is clicked\\n orientation?: 'horizontal' | 'vertical'; // default 'horizontal'; Layout direction\\n variant?: 'default' | 'pills' | 'underline'; // default 'default'; Visual style for active/inactive items\\n}\\n\\n// Example:\\n```tsx\\n<NavigationBar\\n items={[\\n { id: 'home', label: 'Home', icon: <Icon name=\\\"home\\\" /> },\\n { id: 'about', label: 'About' },\\n { id: 'contact', label: 'Contact' },\\n ]}\\n activeId=\\\"home\\\"\\n onItemClick={(item) => navigate(item.id)}\\n orientation=\\\"horizontal\\\"\\n variant=\\\"pills\\\"\\n/>\\n```\\n\\n// FileUploader — Props for the `FileUploader` composition\\ninterface FileUploaderProps {\\n files?: UploadedFile[]; // default []; Array of files currently in the upload queue, shown below the drop zone\\n onFilesSelected?: (files: File[]) => void; // Called when the user selects files via click or drag-and-drop\\n onFileRemove?: (fileId: string) => void; // Called when the user clicks the remove button on a file entry\\n accept?: string; // Accepted file types passed to the hidden `<input type=\\\"file\\\" accept=\\\"\\n multiple?: boolean; // default true; When true, allows selecting multiple files at once\\n maxSize?: number; // Maximum file size in bytes\\n maxFiles?: number; // Maximum number of files\\n disabled?: boolean; // When true, the drop zone is visually dimmed (opacity 0\\n dragDrop?: boolean; // default true; When true, enables drag-and-drop on the drop zone\\n}\\n\\n// Example:\\n```tsx\\n<FileUploader\\n files={uploadedFiles}\\n onFilesSelected={(files) => startUpload(files)}\\n onFileRemove={(id) => removeFile(id)}\\n accept=\\\"image/*,.pdf\\\"\\n multiple\\n maxSize={5 * 1024 * 1024}\\n maxFiles={10}\\n dragDrop\\n/>\\n```\\n\\n// NotificationCenter — Props for the `NotificationCenter` composition\\ninterface NotificationCenterProps {\\n notifications: Notification[]; // Array of notifications to display, in the order provided\\n onMarkAsRead?: (id: string) => void; // Called when the user clicks \\\"Mark as read\\\" on an individual notification\\n onMarkAllAsRead?: () => void; // Called when the user clicks the \\\"Mark all read\\\" header button\\n onDismiss?: (id: string) => void; // Called when the user clicks the dismiss (X) button on an individual notification\\n onClearAll?: () => void; // Called when the user clicks the \\\"Clear all\\\" header button\\n emptyText?: string; // default 'No notifications'; Text shown when `notifications` is empty and not loading\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the notification list\\n}\\n\\n// Example:\\n```tsx\\n<NotificationCenter\\n notifications={[\\n { id: '1', title: 'Deployment complete', type: 'success', timestamp: new Date(), read: false },\\n { id: '2', title: 'Build failed', type: 'error', message: 'Lint errors', timestamp: new Date() },\\n ]}\\n onMarkAsRead={(id) => markRead(id)}\\n onMarkAllAsRead={() => markAllRead()}\\n onDismiss={(id) => dismiss(id)}\\n onClearAll={() => clearAll()}\\n/>\\n```\\n\\n// Modal — Props for the `Modal` composition\\ninterface ModalProps {\\n open: boolean; // Controls modal visibility\\n onClose: () => void; // Called to close the modal (overlay click, escape key, or close button)\\n title?: string; // Optional title rendered in the modal header via the `Heading` primitive (leve...\\n children: ReactNode; // Modal body content rendered in a scrollable area (`overflowY: auto`)\\n footer?: ReactNode; // Footer content rendered below the body, right-aligned with an 8 px gap, separ...\\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; // default 'md'; Controls the width of the modal panel\\n closeOnOverlayClick?: boolean; // default true; When true, clicking the semi-transparent overlay behind the modal calls `onCl...\\n closeOnEscape?: boolean; // default true; When true, pressing the Escape key calls `onClose`\\n showCloseButton?: boolean; // default true; When true, renders a ghost close button (X icon) in the modal header\\n}\\n\\n// Example:\\n```tsx\\n<Modal\\n open={isOpen}\\n onClose={() => setOpen(false)}\\n title=\\\"Confirm Action\\\"\\n size=\\\"md\\\"\\n footer={\\n <>\\n <Button variant=\\\"ghost\\\" onClick={() => setOpen(false)}>Cancel</Button>\\n <Button onClick={handleConfirm}>Confirm</Button>\\n </>\\n }\\n>\\n <p>Are you sure you want to proceed?</p>\\n</Modal>\\n```\\n\\n// CommandPalette — Props for the `CommandPalette` composition\\ninterface CommandPaletteProps {\\n open: boolean; // Controls palette visibility\\n onClose: () => void; // Called to close the palette (overlay click, Escape key, or after command sele...\\n commands: Command[]; // Full array of available commands\\n onSelect: (command: Command) => void; // Called when a non-disabled command is selected (Enter key or click)\\n placeholder?: string; // default 'Search commands...'; Placeholder text for the search input\\n recentIds?: string[]; // default []; IDs of recently used commands\\n loading?: boolean; // When true, shows a centered `Spinner` instead of the command list\\n}\\n\\n// Example:\\n```tsx\\n<CommandPalette\\n open={isOpen}\\n onClose={() => setOpen(false)}\\n commands={[\\n { id: 'new', label: 'New File', shortcut: 'Ctrl+N', group: 'File' },\\n { id: 'save', label: 'Save', shortcut: 'Ctrl+S', group: 'File' },\\n { id: 'theme', label: 'Toggle Theme', group: 'Preferences' },\\n ]}\\n onSelect={(cmd) => executeCommand(cmd.id)}\\n recentIds={['save']}\\n placeholder=\\\"Search commands...\\\"\\n/>\\n```\\n\\n// Footer — Props for the `Footer` composition\\ninterface FooterProps {\\n brand?: ReactNode; // Brand element (logo, tagline) rendered in a flexible column (`flex: 1 1 280px`)\\n columns?: FooterColumn[]; // Array of link columns rendered in a flex-wrap layout with 48 px gap\\n socialLinks?: FooterSocialLink[]; // Social media icon links rendered in the bottom bar\\n bottomText?: string; // Text displayed at the start of the bottom bar (e\\n bottomLinks?: FooterLink[]; // Links displayed in the bottom bar after social icons (e\\n background?: string; // default colors.gray[50]; Background color of the footer\\n bordered?: boolean; // default true; When true, renders a 1 px top border in `colors\\n}\\n\\n// Example:\\n```tsx\\n<Footer\\n brand={<img src=\\\"/logo.svg\\\" alt=\\\"Acme\\\" />}\\n columns={[\\n { title: 'Product', links: [{ label: 'Features', href: '/features' }] },\\n { title: 'Company', links: [{ label: 'About', href: '/about' }] },\\n ]}\\n socialLinks={[\\n { label: 'Twitter', href: 'https://twitter.com/acme', icon: <TwitterIcon /> },\\n ]}\\n bottomText=\\\"© 2026 Acme Inc.\\\"\\n bottomLinks={[{ label: 'Privacy', href: '/privacy' }]}\\n bordered\\n/>\\n```\\n\\n// Hero — Props for the `Hero` composition\\ninterface HeroProps {\\n heading?: string; // Main heading text rendered as an `<h1>` with bold weight and tight line-height\\n description?: string; // Description paragraph rendered below the heading with relaxed line-height\\n primaryAction?: HeroAction; // Primary CTA button rendered with `colors\\n secondaryAction?: HeroAction; // Secondary CTA button rendered with a transparent background and a 1 px border\\n media?: ReactNode; // Media element (image, video, illustration) rendered beside or below the text ...\\n align?: 'center' | 'left'; // default 'center'; Text and layout alignment\\n size?: 'sm' | 'md' | 'lg'; // default 'md'; Controls vertical padding and heading/description font sizes\\n background?: string; // default colors.white (when no backgroundImage is set); Background color of the hero section\\n backgroundImage?: string; // Background image URL applied as `background-size: cover; background-position:...\\n overlay?: boolean; // default false; When true and `backgroundImage` is set, renders a semi-transparent black overlay\\n}\\n\\n// Example:\\n```tsx\\n<Hero\\n heading=\\\"Build Better UIs, Faster\\\"\\n description=\\\"The universal interface layer between AI agents and humans.\\\"\\n primaryAction={{ label: 'Get Started', onClick: () => navigate('/signup') }}\\n secondaryAction={{ label: 'Learn More', href: '/docs' }}\\n media={<img src=\\\"/hero.png\\\" alt=\\\"Hero\\\" />}\\n align=\\\"left\\\"\\n size=\\\"lg\\\"\\n/>\\n```\\n\\n// Support types:\\n// SidebarItem — A single navigation entry in a `Sidebar`\\ninterface SidebarItem {\\n id: string; // Unique identifier used for active-state matching and React keys\\n label: string; // Display label for the item\\n icon?: ReactNode; // Leading icon rendered before the label\\n href?: string; // Optional URL associated with this item (not rendered as a link by default)\\n badge?: ReactNode; // Trailing badge element (e\\n children?: SidebarItem[]; // Nested child items\\n disabled?: boolean; // When true, the item is visually dimmed and non-interactive (`cursor: not-allo...\\n}\\n\\n// Comment — A single comment entry in a `CommentThread`\\ninterface Comment {\\n id: string; // Unique identifier for this comment\\n author: { /** Display name of the author. */ name: string; /** URL for the author's avatar image. Passed to the `Avatar` primitive. */ avatar?: string; }; // Comment author metadata\\n content: string; // The comment body text\\n timestamp: string | Date; // Timestamp of the comment\\n replies?: Comment[]; // Nested reply comments\\n reactions?: { emoji: string; count: number }[]; // Emoji reactions with their aggregated counts (e\\n}\\n\\n// DataTableColumn — Column definition for a `DataTable`\\ninterface DataTableColumn {\\n key: string; // Property key on the row object used to extract cell values\\n header: string; // Column header text displayed in the `<thead>`\\n width?: number | string; // Column width as a CSS value (number for pixels, string for any CSS unit)\\n sortable?: boolean; // When true, the column header is clickable and triggers `onSort`\\n render?: (value: unknown, row: T, index: number) => ReactNode; // Custom cell renderer\\n align?: 'left' | 'center' | 'right'; // default 'left'; Text alignment for both header and body cells\\n}\\n\\n// ChatMessage — A single message in a `ChatWindow`\\ninterface ChatMessage {\\n id: string; // Unique identifier for this message\\n content: string; // The message body text\\n sender: { /** Unique ID of the sender. Compared to `currentUserId` to determine alignment. */ id: string; /** Display name of the sender. */ name: string; /** Avatar image URL. Only shown for non-current-user messages (xs size). */ avatar?: string; }; // Sender metadata used for avatar rendering and alignment\\n timestamp: string | Date; // Message timestamp\\n status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error'; // Delivery status indicator shown on the current user's messages\\n}\\n\\n// NavItem — A single navigation entry in a `NavigationBar`\\ninterface NavItem {\\n id: string; // Unique identifier used for active-state matching and React keys\\n label: string; // Display label for the navigation link\\n href?: string; // URL for the item\\n icon?: ReactNode; // Optional icon rendered before the label\\n children?: NavItem[]; // Nested child items (for sub-menu structures; rendering is consumer-defined)\\n disabled?: boolean; // When true, the item is visually dimmed (opacity 0\\n}\\n\\n// UploadedFile — A file entry in the `FileUploader` composition\\ninterface UploadedFile {\\n id: string; // Unique identifier for this file entry\\n name: string; // Original file name\\n size: number; // File size in bytes\\n type: string; // MIME type of the file (e\\n progress?: number; // Upload progress as a percentage (0-100)\\n status: 'pending' | 'uploading' | 'success' | 'error'; // Current upload lifecycle status\\n error?: string; // Error message displayed when status is `'error'`\\n url?: string; // The remote URL of the uploaded file after successful upload\\n}\\n\\n// Notification — A single notification entry in the `NotificationCenter`\\ninterface Notification {\\n id: string; // Unique identifier for this notification\\n title: string; // Notification title rendered in medium weight\\n message?: string; // Optional body text rendered below the title in a smaller font\\n timestamp: string | Date; // Timestamp of the notification\\n read?: boolean; // default false; Read state\\n type?: 'info' | 'success' | 'warning' | 'error'; // default 'info'; Semantic type that controls the status dot color on unread notifications\\n icon?: ReactNode; // Optional icon rendered alongside the notification (not used by the default im...\\n action?: { /** Button label text. */ label: string; /** Click handler for the action. */ onClick: () => void; }; // Optional inline action button rendered next to the timestamp\\n}\\n\\n// Command — A single command entry in a `CommandPalette`\\ninterface Command {\\n id: string; // Unique identifier for this command\\n label: string; // Display label for the command\\n description?: string; // Optional description text shown below the label\\n icon?: ReactNode; // Icon rendered at the start of the command row\\n shortcut?: string; // Keyboard shortcut hint displayed at the end of the row in a `<kbd>` element\\n group?: string; // Group name for visual sectioning\\n disabled?: boolean; // When true, the command is visually dimmed and cannot be selected\\n}\\n\\n// FooterLink — A single link entry in a footer column or the bottom bar\\ninterface FooterLink {\\n label: string; // Display text for the link\\n href?: string; // URL for the link\\n onClick?: () => void; // Click handler\\n}\\n\\n// FooterColumn — A named column of links in the `Footer` layout\\ninterface FooterColumn {\\n title?: string; // Column heading rendered as an `<h4>` with semibold weight\\n links: FooterLink[]; // Links displayed in this column\\n}\\n\\n// FooterSocialLink — A social media link in the `Footer` bottom bar\\ninterface FooterSocialLink {\\n label: string; // Accessible label for the social link (used as `aria-label`)\\n href: string; // URL for the social media profile or page\\n icon: ReactNode; // Icon element rendered inside the anchor (typically an SVG or `Icon` primitive)\\n}\\n\\n// HeroAction — A call-to-action button definition for the `Hero` composition\\ninterface HeroAction {\\n label: string; // Button label text\\n onClick?: () => void; // Click handler for the button\\n href?: string; // Optional URL (not used by the default implementation; available for consumer ...\\n}\\n\\n\\n// ════════════════════════════════════════════════════════════\\n// System Conventions\\n// ════════════════════════════════════════════════════════════\\n//\\n// Motion: render <MotionKeyframes /> once to enable keyframes.\\n// Entrance/exit: fadeIn, fadeOut, slideInUp, slideInDown, scaleIn, scaleOut\\n// State feedback: flash, pulse, bounce\\n// Easing: linear, easeIn, easeOut, easeInOut, spring\\n// Durations: instant(0ms), fast(100ms), normal(200ms), slow(300ms), slower(500ms)\\n//\\n// Elevation levels (shadow → z-index):\\n// 0: flat 1: sm (cards) 2: md z=1000 (dropdowns, popovers)\\n// 3: lg z=1200 4: xl z=1400 5: 2xl z=1800 (tooltips, toasts)\\n//\\n// useAnimationKey(dep) returns a key that bumps when dep changes —\\n// apply to <div key={...}> to replay CSS animations on data update.\\n//\\n// useMotion() returns { motionEnabled } — respect prefers-reduced-motion.\\n\";\n"]}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse one input document, render once, write the verdict. Whatever
|
|
3
|
+
* happens, esbuild's service is stopped before this returns (or
|
|
4
|
+
* rejects). If the service was never started, `stop()` is a no-op.
|
|
5
|
+
*/
|
|
6
|
+
declare function workerMain(raw: string): Promise<void>;
|
|
1
7
|
|
|
2
|
-
export {
|
|
8
|
+
export { workerMain };
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
+
import { realpathSync } from 'fs';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import * as esbuild from 'esbuild';
|
|
2
6
|
|
|
3
7
|
// src/tools/render-check-worker.ts
|
|
4
8
|
|
|
5
9
|
// src/internal/evaluate-component-source.ts
|
|
6
10
|
async function evaluateComponentSource(sourceCode, sandboxRequire) {
|
|
7
|
-
const
|
|
11
|
+
const esbuild2 = await import('esbuild');
|
|
8
12
|
const { Script } = await import('vm');
|
|
9
|
-
const cjsResult = await
|
|
13
|
+
const cjsResult = await esbuild2.transform(sourceCode, {
|
|
10
14
|
loader: "tsx",
|
|
11
15
|
target: "es2020",
|
|
12
16
|
format: "cjs",
|
|
@@ -33,24 +37,37 @@ function hostGlobals() {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
// src/tools/render-check-worker.ts
|
|
36
|
-
async function
|
|
37
|
-
const raw = await readAllStdin();
|
|
38
|
-
let input;
|
|
40
|
+
async function workerMain(raw) {
|
|
39
41
|
try {
|
|
40
|
-
input
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
let input;
|
|
43
|
+
try {
|
|
44
|
+
input = JSON.parse(raw);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
emit({
|
|
47
|
+
ok: false,
|
|
48
|
+
error: `worker: malformed input JSON \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (typeof input.sourceCode !== "string" || input.sourceCode.length === 0) {
|
|
53
|
+
emit({ ok: false, error: "worker: sourceCode is required" });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const result = await renderOnce(input);
|
|
57
|
+
emit(result);
|
|
58
|
+
} finally {
|
|
59
|
+
try {
|
|
60
|
+
await esbuild.stop();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
process.stderr.write(
|
|
63
|
+
`render-check worker: esbuild.stop() failed \u2014 ${err instanceof Error ? err.message : String(err)}
|
|
64
|
+
`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
51
67
|
}
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
}
|
|
69
|
+
async function main() {
|
|
70
|
+
await workerMain(await readAllStdin());
|
|
54
71
|
}
|
|
55
72
|
async function renderOnce(input) {
|
|
56
73
|
const g = hostGlobals();
|
|
@@ -158,11 +175,26 @@ async function readAllStdin() {
|
|
|
158
175
|
}
|
|
159
176
|
return buffer;
|
|
160
177
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
function isProcessEntry() {
|
|
179
|
+
const entry = process.argv[1];
|
|
180
|
+
if (entry === void 0) return false;
|
|
181
|
+
let entryReal;
|
|
182
|
+
try {
|
|
183
|
+
entryReal = realpathSync(resolve(entry));
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return entryReal === realpathSync(fileURLToPath(import.meta.url));
|
|
188
|
+
}
|
|
189
|
+
if (isProcessEntry()) {
|
|
190
|
+
void main().catch((err) => {
|
|
191
|
+
emit({
|
|
192
|
+
ok: false,
|
|
193
|
+
error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`
|
|
194
|
+
});
|
|
165
195
|
});
|
|
166
|
-
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { workerMain };
|
|
167
199
|
//# sourceMappingURL=render-check-worker.js.map
|
|
168
200
|
//# sourceMappingURL=render-check-worker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/internal/evaluate-component-source.ts","../../src/internal/open-record.ts","../../src/tools/render-check-worker.ts"],"names":[],"mappings":"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","file":"render-check-worker.js","sourcesContent":["// packages/ui-gen/src/internal/evaluate-component-source.ts\n//\n// The ONE compile-and-evaluate helper for component source: esbuild\n// TSX→CJS transform + vm.Script in-context execution behind a\n// caller-supplied `require`. Consumed by:\n//\n// - `tools/render-check-worker.ts` — the subprocess render smoke\n// test (host isolation lives at the spawn layer, in the parent's\n// `tryRender` via `@ggui-ai/sandbox`).\n// - `harness/check/runtime-render/load-component.ts` — the in-loop\n// probe (single-realm React; caller injects module resolutions).\n//\n// The module-resolution POLICY is deliberately not shared — each\n// caller owns its own `require` shim (allowed specifiers, fallbacks,\n// error wording). What is shared is the mechanical eval pipeline:\n//\n// 1. esbuild.transform(source) → CJS\n// 2. wrap in `(function(require, exports, module) { ... })`\n// 3. vm.Script(...).runInThisContext()\n// 4. call with the caller's require + a fresh module object\n// 5. return `module.exports` read AFTER execution — esbuild's CJS\n// output REPLACES `module.exports` (`module.exports =\n// __toCommonJS(...)`), so the initial reference can't be trusted.\n\nexport async function evaluateComponentSource(\n sourceCode: string,\n sandboxRequire: (id: string) => unknown,\n): Promise<Record<string, unknown>> {\n const esbuild = await import(\"esbuild\");\n const { Script } = await import(\"node:vm\");\n\n const cjsResult = await esbuild.transform(sourceCode, {\n loader: \"tsx\",\n target: \"es2020\",\n format: \"cjs\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n sourcefile: \"Component.tsx\",\n });\n\n const sandboxModule: { exports: Record<string, unknown> } = { exports: {} };\n\n const wrappedCode = `(function(require, exports, module) {\\n${cjsResult.code}\\n})`;\n const script = new Script(wrappedCode, { filename: \"Component.cjs\" });\n const fn = script.runInThisContext() as (\n require: (id: string) => unknown,\n exports: Record<string, unknown>,\n module: { exports: Record<string, unknown> },\n ) => void;\n\n fn(sandboxRequire, sandboxModule.exports, sandboxModule);\n\n return sandboxModule.exports;\n}\n","// packages/ui-gen/src/internal/open-record.ts\n//\n// Single documented narrowing seam for \"object used as an open\n// string-keyed property bag\" — the host global object and happy-dom's\n// `Window` both genuinely ARE open records at runtime (we install /\n// restore named globals like `window` / `document` on them by key),\n// but TypeScript cannot express that without polluting the global type\n// space via declaration merging. Centralizing the one structural\n// widening here keeps every call site cast-free.\n\n/**\n * View an object as an open string-keyed record.\n *\n * Single structural widening (`Record<string, unknown>` is assignable\n * to `object`, so the cast is checked in one direction by tsc — no\n * `unknown` erasure). Returns the SAME reference: property writes land\n * on the live object.\n */\nexport function openRecord(o: object): Record<string, unknown> {\n return o as Record<string, unknown>;\n}\n\n/**\n * The host global object viewed as an open property bag.\n *\n * Used by the render-check pipeline (and its subprocess worker) to\n * install and restore DOM globals by name.\n */\nexport function hostGlobals(): Record<string, unknown> {\n return openRecord(globalThis);\n}\n","/**\n * Child worker for `tryRender`. Runs the render smoke test in a\n * subprocess so the LLM-generated TSX executes outside the parent\n * Node process.\n *\n * Wire protocol (newline-delimited JSON):\n *\n * stdin: { sourceCode: string, sampleProps?: JsonObject }\n * stdout: { ok: true } | { ok: false, error: string }\n * exit: 0 on either outcome (errors are data, not status).\n *\n * Why a separate worker instead of `vm.Script.runInThisContext` in\n * the parent:\n *\n * - `runInThisContext` runs in the parent's V8 context. A\n * pathological component that attaches to a global, recurses\n * into the event loop, or allocates a large buffer poisons the\n * parent.\n * - A subprocess gives us real kill semantics on runaway code +\n * output caps + env isolation — delegated to\n * `@ggui-ai/sandbox`.\n *\n * The worker still uses `vm.Script` to run compiled CJS because the\n * component needs a tailored `require` (react / jsx-runtime /\n * @ggui-ai/design only). Host-level process isolation is the\n * sandbox; module-scoped `require` is the resolution policy. The\n * mechanical compile+vm pipeline is the shared\n * `evaluateComponentSource` helper (also used by the in-loop probe's\n * `loadComponent`); only the resolution policy lives here.\n */\nimport { createRequire } from 'node:module';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport { evaluateComponentSource } from '../internal/evaluate-component-source.js';\nimport { hostGlobals } from '../internal/open-record.js';\n\ninterface WorkerInput {\n readonly sourceCode: string;\n readonly sampleProps?: JsonObject;\n}\n\ntype WorkerOutput =\n | { readonly ok: true }\n | { readonly ok: false; readonly error: string };\n\nasync function main(): Promise<void> {\n const raw = await readAllStdin();\n let input: WorkerInput;\n try {\n input = JSON.parse(raw) as WorkerInput;\n } catch (err) {\n emit({\n ok: false,\n error: `worker: malformed input JSON — ${\n err instanceof Error ? err.message : String(err)\n }`,\n });\n return;\n }\n\n if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {\n emit({ ok: false, error: 'worker: sourceCode is required' });\n return;\n }\n\n const result = await renderOnce(input);\n emit(result);\n}\n\nasync function renderOnce(input: WorkerInput): Promise<WorkerOutput> {\n // Minimal window/document shims — mirror the in-process impl so\n // components that touch either global don't crash on import. The\n // shims only need to exist for the render; they're torn down by\n // process exit.\n const g = hostGlobals();\n if (!('window' in globalThis)) {\n g.window = {\n addEventListener: () => {},\n removeEventListener: () => {},\n };\n }\n if (!('document' in globalThis)) {\n g.document = {\n getElementById: () => null,\n createElement: () => ({}),\n addEventListener: () => {},\n };\n }\n\n try {\n const React = await import('react');\n const ReactDOMServer = await import('react-dom/server');\n\n const require_ = createRequire(import.meta.url);\n\n const sandboxRequire = (id: string): unknown => {\n if (id === 'react/jsx-runtime' || id === 'react/jsx-dev-runtime') {\n return require_('react/jsx-runtime');\n }\n if (id === 'react') return require_('react');\n if (id.startsWith('@ggui-ai/design')) {\n try {\n return require_(id);\n } catch {\n // Fallback proxy — used in environments where the real\n // design package isn't resolvable. Each primitive becomes\n // a pass-through `div` so the render still exercises the\n // component's own logic.\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === '__esModule') return true;\n return ({\n children,\n ...props\n }: {\n children?: React.ReactNode;\n [key: string]: unknown;\n }) =>\n React.createElement(\n 'div',\n { 'data-component': String(prop), ...props },\n children,\n );\n },\n },\n );\n }\n }\n throw new Error(`Import not allowed: ${id}`);\n };\n\n const exportsRecord = await evaluateComponentSource(\n input.sourceCode,\n sandboxRequire,\n );\n\n const Component = exportsRecord.default;\n if (typeof Component !== 'function') {\n return {\n ok: false,\n error: 'No default export function found in compiled code',\n };\n }\n\n const html = ReactDOMServer.renderToString(\n React.createElement(\n Component as React.ComponentType<JsonObject>,\n input.sampleProps ?? {},\n ),\n );\n\n if (!html || html.length < 5) {\n return { ok: false, error: 'Component rendered empty output' };\n }\n\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('Cannot read properties of undefined')) {\n return {\n ok: false,\n error: `Render error: ${message}. A prop is likely undefined — add a default value or null check.`,\n };\n }\n if (message.includes('Cannot read properties of null')) {\n return {\n ok: false,\n error: `Render error: ${message}. A value is null — add a null check or fallback.`,\n };\n }\n if (message.includes('is not a function')) {\n return {\n ok: false,\n error: `Render error: ${message}. Check that all imported functions exist and are called correctly.`,\n };\n }\n return { ok: false, error: `Render error: ${message}` };\n }\n}\n\nfunction emit(output: WorkerOutput): void {\n process.stdout.write(JSON.stringify(output));\n}\n\nasync function readAllStdin(): Promise<string> {\n let buffer = '';\n process.stdin.setEncoding('utf-8');\n for await (const chunk of process.stdin) {\n buffer += chunk as string;\n }\n return buffer;\n}\n\nvoid main().catch((err: unknown) => {\n emit({\n ok: false,\n error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,\n });\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/internal/evaluate-component-source.ts","../../src/internal/open-record.ts","../../src/tools/render-check-worker.ts"],"names":["esbuild","resolvePath"],"mappings":"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","file":"render-check-worker.js","sourcesContent":["// packages/ui-gen/src/internal/evaluate-component-source.ts\n//\n// The ONE compile-and-evaluate helper for component source: esbuild\n// TSX→CJS transform + vm.Script in-context execution behind a\n// caller-supplied `require`. Consumed by:\n//\n// - `tools/render-check-worker.ts` — the subprocess render smoke\n// test (host isolation lives at the spawn layer, in the parent's\n// `tryRender` via `@ggui-ai/sandbox`).\n// - `harness/check/runtime-render/load-component.ts` — the in-loop\n// probe (single-realm React; caller injects module resolutions).\n//\n// The module-resolution POLICY is deliberately not shared — each\n// caller owns its own `require` shim (allowed specifiers, fallbacks,\n// error wording). What is shared is the mechanical eval pipeline:\n//\n// 1. esbuild.transform(source) → CJS\n// 2. wrap in `(function(require, exports, module) { ... })`\n// 3. vm.Script(...).runInThisContext()\n// 4. call with the caller's require + a fresh module object\n// 5. return `module.exports` read AFTER execution — esbuild's CJS\n// output REPLACES `module.exports` (`module.exports =\n// __toCommonJS(...)`), so the initial reference can't be trusted.\n\nexport async function evaluateComponentSource(\n sourceCode: string,\n sandboxRequire: (id: string) => unknown,\n): Promise<Record<string, unknown>> {\n const esbuild = await import(\"esbuild\");\n const { Script } = await import(\"node:vm\");\n\n const cjsResult = await esbuild.transform(sourceCode, {\n loader: \"tsx\",\n target: \"es2020\",\n format: \"cjs\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n sourcefile: \"Component.tsx\",\n });\n\n const sandboxModule: { exports: Record<string, unknown> } = { exports: {} };\n\n const wrappedCode = `(function(require, exports, module) {\\n${cjsResult.code}\\n})`;\n const script = new Script(wrappedCode, { filename: \"Component.cjs\" });\n const fn = script.runInThisContext() as (\n require: (id: string) => unknown,\n exports: Record<string, unknown>,\n module: { exports: Record<string, unknown> },\n ) => void;\n\n fn(sandboxRequire, sandboxModule.exports, sandboxModule);\n\n return sandboxModule.exports;\n}\n","// packages/ui-gen/src/internal/open-record.ts\n//\n// Single documented narrowing seam for \"object used as an open\n// string-keyed property bag\" — the host global object and happy-dom's\n// `Window` both genuinely ARE open records at runtime (we install /\n// restore named globals like `window` / `document` on them by key),\n// but TypeScript cannot express that without polluting the global type\n// space via declaration merging. Centralizing the one structural\n// widening here keeps every call site cast-free.\n\n/**\n * View an object as an open string-keyed record.\n *\n * Single structural widening (`Record<string, unknown>` is assignable\n * to `object`, so the cast is checked in one direction by tsc — no\n * `unknown` erasure). Returns the SAME reference: property writes land\n * on the live object.\n */\nexport function openRecord(o: object): Record<string, unknown> {\n return o as Record<string, unknown>;\n}\n\n/**\n * The host global object viewed as an open property bag.\n *\n * Used by the render-check pipeline (and its subprocess worker) to\n * install and restore DOM globals by name.\n */\nexport function hostGlobals(): Record<string, unknown> {\n return openRecord(globalThis);\n}\n","/**\n * Child worker for `tryRender`. Runs the render smoke test in a\n * subprocess so the LLM-generated TSX executes outside the parent\n * Node process.\n *\n * Wire protocol (newline-delimited JSON):\n *\n * stdin: { sourceCode: string, sampleProps?: JsonObject }\n * stdout: { ok: true } | { ok: false, error: string }\n * exit: 0 on either outcome (errors are data, not status).\n *\n * Why a separate worker instead of `vm.Script.runInThisContext` in\n * the parent:\n *\n * - `runInThisContext` runs in the parent's V8 context. A\n * pathological component that attaches to a global, recurses\n * into the event loop, or allocates a large buffer poisons the\n * parent.\n * - A subprocess gives us real kill semantics on runaway code +\n * output caps + env isolation — delegated to\n * `@ggui-ai/sandbox`.\n *\n * The worker still uses `vm.Script` to run compiled CJS because the\n * component needs a tailored `require` (react / jsx-runtime /\n * @ggui-ai/design only). Host-level process isolation is the\n * sandbox; module-scoped `require` is the resolution policy. The\n * mechanical compile+vm pipeline is the shared\n * `evaluateComponentSource` helper (also used by the in-loop probe's\n * `loadComponent`); only the resolution policy lives here.\n *\n * Shutdown (ggui#1380): the compile runs on esbuild's service, a child\n * process esbuild keeps alive for the life of this worker. After the\n * verdict is written the worker calls `esbuild.stop()` so that child is\n * gone before the worker is. `workerMain` is exported for the\n * process-shape pins; `main()` runs only when this file is the process\n * entry, so importing it never reads stdin.\n */\nimport { createRequire } from 'node:module';\nimport { realpathSync } from 'node:fs';\nimport { resolve as resolvePath } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as esbuild from 'esbuild';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport { evaluateComponentSource } from '../internal/evaluate-component-source.js';\nimport { hostGlobals } from '../internal/open-record.js';\n\ninterface WorkerInput {\n readonly sourceCode: string;\n readonly sampleProps?: JsonObject;\n}\n\ntype WorkerOutput =\n | { readonly ok: true }\n | { readonly ok: false; readonly error: string };\n\n/**\n * Parse one input document, render once, write the verdict. Whatever\n * happens, esbuild's service is stopped before this returns (or\n * rejects). If the service was never started, `stop()` is a no-op.\n */\nexport async function workerMain(raw: string): Promise<void> {\n try {\n let input: WorkerInput;\n try {\n input = JSON.parse(raw) as WorkerInput;\n } catch (err) {\n emit({\n ok: false,\n error: `worker: malformed input JSON — ${\n err instanceof Error ? err.message : String(err)\n }`,\n });\n return;\n }\n\n if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {\n emit({ ok: false, error: 'worker: sourceCode is required' });\n return;\n }\n\n const result = await renderOnce(input);\n emit(result);\n } finally {\n // Post-verdict work must never turn a written verdict into a failure:\n // a rejecting `stop()` would otherwise become an unhandled rejection,\n // exit code 1, and the host discarding the verdict on stdout.\n try {\n await esbuild.stop();\n } catch (err) {\n process.stderr.write(\n `render-check worker: esbuild.stop() failed — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n }\n}\n\nasync function main(): Promise<void> {\n await workerMain(await readAllStdin());\n}\n\nasync function renderOnce(input: WorkerInput): Promise<WorkerOutput> {\n // Minimal window/document shims — mirror the in-process impl so\n // components that touch either global don't crash on import. The\n // shims only need to exist for the render; they're torn down by\n // process exit.\n const g = hostGlobals();\n if (!('window' in globalThis)) {\n g.window = {\n addEventListener: () => {},\n removeEventListener: () => {},\n };\n }\n if (!('document' in globalThis)) {\n g.document = {\n getElementById: () => null,\n createElement: () => ({}),\n addEventListener: () => {},\n };\n }\n\n try {\n const React = await import('react');\n const ReactDOMServer = await import('react-dom/server');\n\n const require_ = createRequire(import.meta.url);\n\n const sandboxRequire = (id: string): unknown => {\n if (id === 'react/jsx-runtime' || id === 'react/jsx-dev-runtime') {\n return require_('react/jsx-runtime');\n }\n if (id === 'react') return require_('react');\n if (id.startsWith('@ggui-ai/design')) {\n try {\n return require_(id);\n } catch {\n // Fallback proxy — used in environments where the real\n // design package isn't resolvable. Each primitive becomes\n // a pass-through `div` so the render still exercises the\n // component's own logic.\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === '__esModule') return true;\n return ({\n children,\n ...props\n }: {\n children?: React.ReactNode;\n [key: string]: unknown;\n }) =>\n React.createElement(\n 'div',\n { 'data-component': String(prop), ...props },\n children,\n );\n },\n },\n );\n }\n }\n throw new Error(`Import not allowed: ${id}`);\n };\n\n const exportsRecord = await evaluateComponentSource(\n input.sourceCode,\n sandboxRequire,\n );\n\n const Component = exportsRecord.default;\n if (typeof Component !== 'function') {\n return {\n ok: false,\n error: 'No default export function found in compiled code',\n };\n }\n\n const html = ReactDOMServer.renderToString(\n React.createElement(\n Component as React.ComponentType<JsonObject>,\n input.sampleProps ?? {},\n ),\n );\n\n if (!html || html.length < 5) {\n return { ok: false, error: 'Component rendered empty output' };\n }\n\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('Cannot read properties of undefined')) {\n return {\n ok: false,\n error: `Render error: ${message}. A prop is likely undefined — add a default value or null check.`,\n };\n }\n if (message.includes('Cannot read properties of null')) {\n return {\n ok: false,\n error: `Render error: ${message}. A value is null — add a null check or fallback.`,\n };\n }\n if (message.includes('is not a function')) {\n return {\n ok: false,\n error: `Render error: ${message}. Check that all imported functions exist and are called correctly.`,\n };\n }\n return { ok: false, error: `Render error: ${message}` };\n }\n}\n\nfunction emit(output: WorkerOutput): void {\n process.stdout.write(JSON.stringify(output));\n}\n\nasync function readAllStdin(): Promise<string> {\n let buffer = '';\n process.stdin.setEncoding('utf-8');\n for await (const chunk of process.stdin) {\n buffer += chunk as string;\n }\n return buffer;\n}\n\n/** True when this file is the process entry (`node <this file>`), not an import. */\nfunction isProcessEntry(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n // Resolve BOTH sides through the filesystem: `import.meta.url` is this\n // file's realpath while argv[1] is whatever the spawner passed — a\n // symlinked spawn path must still read as the entry, or the worker would\n // exit with no verdict and the host would read a silent `unverified`.\n let entryReal: string;\n try {\n entryReal = realpathSync(resolvePath(entry));\n } catch {\n return false;\n }\n return entryReal === realpathSync(fileURLToPath(import.meta.url));\n}\n\nif (isProcessEntry()) {\n void main().catch((err: unknown) => {\n emit({\n ok: false,\n error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,\n });\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AxisVector } from './axes-
|
|
1
|
+
import { A as AxisVector } from './axes-CKVCzR_x.js';
|
|
2
2
|
|
|
3
3
|
type CacheTier = "stable" | "axisDelta" | "volatile";
|
|
4
4
|
type AxisKey = keyof Pick<AxisVector, "render" | "state" | "writes" | "writeTrigger" | "realtime" | "fetch" | "layout" | "tooling">;
|