@ggui-ai/ui-gen 0.23.0 → 0.24.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.
Files changed (67) hide show
  1. package/dist/adapter-Du7P_5Fx.d.ts +201 -0
  2. package/dist/adapters/generation-dispatch.d.ts +33 -12
  3. package/dist/adapters/generation-dispatch.js +510 -128
  4. package/dist/adapters/generation-dispatch.js.map +1 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/advanced/index.d.ts +5 -3
  7. package/dist/advanced/index.js +551 -142
  8. package/dist/advanced/index.js.map +1 -1
  9. package/dist/blueprint-validator.js +153 -59
  10. package/dist/blueprint-validator.js.map +1 -1
  11. package/dist/boilerplate.js.map +1 -1
  12. package/dist/check/index.d.ts +2 -1
  13. package/dist/check/index.js +47 -1
  14. package/dist/check/index.js.map +1 -1
  15. package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-GIhzx08x.d.ts} +34 -10
  16. package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-BnAH56p4.d.ts} +1 -1
  17. package/dist/evaluation/axis-checks/index.d.ts +4 -3
  18. package/dist/evaluation/axis-checks/index.js.map +1 -1
  19. package/dist/evaluation/axis-checks/registry.d.ts +2 -1
  20. package/dist/evaluation/index.d.ts +4 -3
  21. package/dist/evaluation/index.js.map +1 -1
  22. package/dist/harness/check/runtime-render/index.d.ts +29 -10
  23. package/dist/harness/check/runtime-render/index.js +203 -60
  24. package/dist/harness/check/runtime-render/index.js.map +1 -1
  25. package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
  26. package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
  27. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  28. package/dist/harness/index.d.ts +2 -1
  29. package/dist/harness/index.js.map +1 -1
  30. package/dist/harness/prompts.js.map +1 -1
  31. package/dist/harness/result-types.d.ts +2 -1
  32. package/dist/harness/runtime.d.ts +2 -1
  33. package/dist/harness/runtime.js.map +1 -1
  34. package/dist/harness/types-public.d.ts +11 -2
  35. package/dist/harness/types-public.js.map +1 -1
  36. package/dist/harness/validator-trace-sink.d.ts +2 -1
  37. package/dist/index.d.ts +38 -3
  38. package/dist/index.js +553 -143
  39. package/dist/index.js.map +1 -1
  40. package/dist/patch.d.ts +2 -1
  41. package/dist/policy.d.ts +7 -4
  42. package/dist/policy.js.map +1 -1
  43. package/dist/tools/render-check-worker.d.ts +7 -1
  44. package/dist/tools/render-check-worker.js +55 -23
  45. package/dist/tools/render-check-worker.js.map +1 -1
  46. package/dist/{types-public-Ow-tNtFE.d.ts → types-public-Bk6d1WLN.d.ts} +77 -11
  47. package/dist/workflows.d.ts +2 -1
  48. package/package.json +12 -12
  49. package/src/adapters/generation-dispatch.ts +137 -96
  50. package/src/check/run-tier0.ts +80 -1
  51. package/src/create-ui-generator.ts +185 -95
  52. package/src/evaluation/types-public.ts +73 -11
  53. package/src/harness/check/runtime-render/adapter.ts +221 -75
  54. package/src/harness/check/runtime-render/index.ts +75 -2
  55. package/src/harness/check/runtime-render/render-check-host.ts +82 -19
  56. package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
  57. package/src/harness/check/runtime-render/render-check.ts +154 -117
  58. package/src/harness/coding/generate-task-runner.ts +57 -4
  59. package/src/harness/coding/init-session.ts +13 -0
  60. package/src/harness/coding/run-eval-round.ts +238 -15
  61. package/src/harness/policy.ts +11 -0
  62. package/src/harness/primitive-slice.ts +104 -0
  63. package/src/harness/types-public.ts +9 -1
  64. package/src/index.ts +4 -0
  65. package/src/policy.ts +7 -4
  66. package/src/tools/render-check-worker.ts +74 -23
  67. package/dist/render-check-Blwj8gB6.d.ts +0 -105
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import * as esbuild2 from 'esbuild';
3
3
  import { build } from 'esbuild';
4
4
  import Anthropic from '@anthropic-ai/sdk';
5
5
  import { getCssTokens, fillFitRule, FORBIDDEN_IMPORT_SPECIFIERS } from '@ggui-ai/design/rendering';
6
- import { classifyUi, MODEL_REGISTRY, listContractGadgets, HOOK_NAME_RE, modelRefOfRoute, isGeneratorId, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1, APP_GENERATION_PROFILE_BOUNDS, STDLIB_GADGETS_PACKAGE, resolveAppGadgets, anthropicRejectsForcedToolChoice, normalizeAnthropicModelId, anthropicRejectsSamplingParams, isRecord } from '@ggui-ai/protocol';
6
+ import { listContractGadgets, HOOK_NAME_RE, classifyUi, MODEL_REGISTRY, modelRefOfRoute, isGeneratorId, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1, APP_GENERATION_PROFILE_BOUNDS, STDLIB_GADGETS_PACKAGE, resolveAppGadgets, anthropicRejectsForcedToolChoice, normalizeAnthropicModelId, anthropicRejectsSamplingParams, isRecord } from '@ggui-ai/protocol';
7
7
  import fs, { existsSync, mkdtempSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync } from 'fs';
8
8
  import { contentHash } from '@ggui-ai/protocol/content-hash';
9
9
  import ts4 from 'typescript';
@@ -3351,6 +3351,46 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode
3351
3351
  visit(sf);
3352
3352
  return issues;
3353
3353
  }
3354
+ function suggestManifestTokens(name) {
3355
+ const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
3356
+ if (successor !== void 0) return [successor];
3357
+ const segments = name.split("-").filter((segment) => segment.length > 0);
3358
+ const own = name.endsWith("-") ? segments.length : segments.length - 1;
3359
+ for (let n = own; n >= 2; n--) {
3360
+ const prefix = `--${segments.slice(0, n).join("-")}-`;
3361
+ const family = consumedTokenManifest.filter((token) => token.startsWith(prefix));
3362
+ if (family.length > 0) return wordHitsFirst(name, rolesFirst(family));
3363
+ }
3364
+ const words = new Set(
3365
+ segments.slice(1).filter((segment) => segment.length >= 4).map((segment) => segment.toLowerCase())
3366
+ );
3367
+ return rolesFirst(
3368
+ consumedTokenManifest.filter(
3369
+ (token) => token.split("-").some((segment) => words.has(segment.toLowerCase()))
3370
+ )
3371
+ );
3372
+ }
3373
+ function wordHitsFirst(name, family) {
3374
+ const word = name.slice(name.lastIndexOf("-") + 1).toLowerCase();
3375
+ if (word.length < 4) return family;
3376
+ const hit = (token) => token.slice(token.lastIndexOf("-") + 1).toLowerCase().includes(word);
3377
+ return [...family.filter(hit), ...family.filter((token) => !hit(token))];
3378
+ }
3379
+ function rolesFirst(tokens) {
3380
+ const isRole = (token) => !/^\d+$/.test(token.slice(token.lastIndexOf("-") + 1));
3381
+ return [...tokens.filter(isRole), ...tokens.filter((token) => !isRole(token))];
3382
+ }
3383
+ function offManifestHint(name) {
3384
+ const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
3385
+ if (successor !== void 0) {
3386
+ return `"${name}" was retired (ggui#987); the current name is var(${successor}). `;
3387
+ }
3388
+ const near = suggestManifestTokens(name);
3389
+ if (near.length === 0) return "";
3390
+ const shown = near.slice(0, 8).map((token) => `var(${token})`).join(", ");
3391
+ const more = near.length > 8 ? ` (and ${near.length - 8} more in that family)` : "";
3392
+ return `Nearest names in the manifest: ${shown}${more}. `;
3393
+ }
3354
3394
  function isGadgetExportImported(source, pkg, exportName) {
3355
3395
  const esc = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3356
3396
  const re = new RegExp(
@@ -3573,7 +3613,7 @@ ${errorDetail}`,
3573
3613
  subcategory: "off-manifest-token",
3574
3614
  severity: "critical",
3575
3615
  description: `Token "${name}" is not in the design system's token manifest \u2014 no theme defines it, so it renders unset.`,
3576
- fix: `Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-onContainer), \u2026; spacing: var(--ggui-spacing-4); type: var(--ggui-font-size-sm)). Never invent a name and never add a literal fallback.`,
3616
+ fix: `${offManifestHint(name)}Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-onContainer), \u2026; spacing: var(--ggui-spacing-4); type: var(--ggui-font-size-sm)). Never invent a name and never add a literal fallback.`,
3577
3617
  line: lineNum
3578
3618
  });
3579
3619
  }
@@ -4090,7 +4130,7 @@ async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE)
4090
4130
  }
4091
4131
  return { issues, pass };
4092
4132
  }
4093
- var MANIFEST_TOKENS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
4133
+ var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
4094
4134
  var init_run_tier0 = __esm({
4095
4135
  "src/check/run-tier0.ts"() {
4096
4136
  init_design_mode();
@@ -4101,6 +4141,12 @@ var init_run_tier0 = __esm({
4101
4141
  init_extract_wire_calls();
4102
4142
  init_types_public();
4103
4143
  MANIFEST_TOKENS = new Set(consumedTokenManifest);
4144
+ RETIRED_TOKEN_SUCCESSORS = /* @__PURE__ */ new Map([
4145
+ ["--ggui-color-surface", "--ggui-color-container"],
4146
+ ["--ggui-color-onSurface", "--ggui-color-onContainer"],
4147
+ ["--ggui-color-surfaceVariant", "--ggui-color-sunken"],
4148
+ ["--ggui-color-onSurfaceVariant", "--ggui-color-onSunken"]
4149
+ ]);
4104
4150
  TOKEN_REF_RE = /var\(\s*(--ggui-[a-zA-Z0-9-]+)/g;
4105
4151
  HOOK_NAME_FOR = {
4106
4152
  action: "useAction",
@@ -6403,6 +6449,14 @@ function resolveRunPolicyForProfile(profile, harness, runtimeCtx) {
6403
6449
  }
6404
6450
  };
6405
6451
  }
6452
+ if (profile === "ctx-slice-primitives-v2") {
6453
+ return {
6454
+ context: {
6455
+ ...harness.policy.context,
6456
+ primitiveDocSlice: "axis-keyed+contract"
6457
+ }
6458
+ };
6459
+ }
6406
6460
  return resolveRunPolicy2(harness, runtimeCtx);
6407
6461
  }
6408
6462
  var init_policy2 = __esm({
@@ -6416,9 +6470,25 @@ var init_policy2 = __esm({
6416
6470
  // src/harness/check/runtime-render/render-check-host.ts
6417
6471
  var render_check_host_exports = {};
6418
6472
  __export(render_check_host_exports, {
6473
+ DEFAULT_RENDER_CHECK_HOST_BOUNDS: () => DEFAULT_RENDER_CHECK_HOST_BOUNDS,
6419
6474
  mapSandboxResultToCheckResult: () => mapSandboxResultToCheckResult,
6475
+ resolveRenderCheckHostBounds: () => resolveRenderCheckHostBounds,
6476
+ resolveWorkerSpawn: () => resolveWorkerSpawn2,
6420
6477
  runRenderCheckViaWorker: () => runRenderCheckViaWorker
6421
6478
  });
6479
+ function resolveRenderCheckHostBounds(bounds = {}) {
6480
+ const timeoutMs = bounds.timeoutMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs;
6481
+ const heapMb = bounds.heapMb ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.heapMb;
6482
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 2) {
6483
+ throw new RangeError(`render-check: timeoutMs must be an integer \u2265 2, got ${timeoutMs}`);
6484
+ }
6485
+ if (!Number.isInteger(heapMb) || heapMb < 1) {
6486
+ throw new RangeError(`render-check: heapMb must be a positive integer, got ${heapMb}`);
6487
+ }
6488
+ const requestedGrace = bounds.gracePeriodMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.gracePeriodMs;
6489
+ const gracePeriodMs = Math.max(1, Math.min(requestedGrace, Math.floor(timeoutMs / 2)));
6490
+ return { timeoutMs, heapMb, gracePeriodMs };
6491
+ }
6422
6492
  function resolveWorkerSpawn2() {
6423
6493
  const jsCandidates = [
6424
6494
  // Sibling — when import.meta.url is already .../harness/check/runtime-render/.
@@ -6467,12 +6537,12 @@ function unverified(reason, t0) {
6467
6537
  stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
6468
6538
  };
6469
6539
  }
6470
- function mapSandboxResultToCheckResult(result, t0) {
6540
+ function mapSandboxResultToCheckResult(result, t0, boundMs = DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs) {
6471
6541
  if (result.outcome === "timeout") {
6472
6542
  return {
6473
6543
  ok: false,
6474
6544
  issues: [],
6475
- incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs: CHECK_TIMEOUT_MS },
6545
+ incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs },
6476
6546
  stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
6477
6547
  };
6478
6548
  }
@@ -6517,8 +6587,9 @@ function mapSandboxResultToCheckResult(result, t0) {
6517
6587
  );
6518
6588
  }
6519
6589
  }
6520
- async function runRenderCheckViaWorker(input) {
6590
+ async function runRenderCheckViaWorker(input, options = {}) {
6521
6591
  const t0 = Date.now();
6592
+ const bounds = resolveRenderCheckHostBounds(options.bounds);
6522
6593
  let spawn;
6523
6594
  try {
6524
6595
  spawn = resolveWorkerSpawn2();
@@ -6529,9 +6600,10 @@ async function runRenderCheckViaWorker(input) {
6529
6600
  const result = await runSandboxed({
6530
6601
  command: spawn.command,
6531
6602
  args: spawn.args,
6532
- timeoutMs: CHECK_TIMEOUT_MS,
6603
+ timeoutMs: bounds.timeoutMs,
6604
+ gracePeriodMs: bounds.gracePeriodMs,
6533
6605
  maxStdoutBytes: CHECK_STDOUT_CAP,
6534
- nodeHeapMb: CHECK_NODE_HEAP_MB,
6606
+ nodeHeapMb: bounds.heapMb,
6535
6607
  stdin: JSON.stringify(input),
6536
6608
  // NODE_ENV steers React's production vs development build — keep
6537
6609
  // parity with the caller; everything else stays on the sandbox's
@@ -6543,15 +6615,18 @@ async function runRenderCheckViaWorker(input) {
6543
6615
  end: loadavg()[0] ?? 0,
6544
6616
  cores: availableParallelism()
6545
6617
  };
6546
- const mapped = mapSandboxResultToCheckResult(result, t0);
6618
+ const mapped = mapSandboxResultToCheckResult(result, t0, bounds.timeoutMs);
6547
6619
  return { ...mapped, stats: { ...mapped.stats, hostLoad } };
6548
6620
  }
6549
- var CHECK_TIMEOUT_MS, CHECK_STDOUT_CAP, CHECK_NODE_HEAP_MB, WORKER_BASENAME;
6621
+ var DEFAULT_RENDER_CHECK_HOST_BOUNDS, CHECK_STDOUT_CAP, WORKER_BASENAME;
6550
6622
  var init_render_check_host = __esm({
6551
6623
  "src/harness/check/runtime-render/render-check-host.ts"() {
6552
- CHECK_TIMEOUT_MS = 3e4;
6624
+ DEFAULT_RENDER_CHECK_HOST_BOUNDS = {
6625
+ timeoutMs: 3e4,
6626
+ heapMb: 512,
6627
+ gracePeriodMs: 2e3
6628
+ };
6553
6629
  CHECK_STDOUT_CAP = 2 * 1024 * 1024;
6554
- CHECK_NODE_HEAP_MB = 512;
6555
6630
  WORKER_BASENAME = "render-check-worker";
6556
6631
  }
6557
6632
  });
@@ -8352,11 +8427,25 @@ Respond with ONLY a JSON object (no markdown, no explanation):
8352
8427
  }
8353
8428
  });
8354
8429
 
8430
+ // src/tools/get-primitives-ts.ts
8431
+ var get_primitives_ts_exports = {};
8432
+ __export(get_primitives_ts_exports, {
8433
+ PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
8434
+ });
8435
+ var PRIMITIVES_DOCUMENTATION_TS;
8436
+ var init_get_primitives_ts = __esm({
8437
+ "src/tools/get-primitives-ts.ts"() {
8438
+ PRIMITIVES_DOCUMENTATION_TS = "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// GGUI DESIGN SYSTEM \u2014 COMPONENT REFERENCE\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n//\n// CRITICAL \u2014 Props take enum STRING LITERALS, not CSS variables:\n// <Text size=\"sm\" weight=\"bold\" /> \u2713\n// <Text size=\"var(--ggui-font-size-sm)\" /> \u2717 (breaks)\n// The enum strings map to CSS variables internally.\n//\n// CRITICAL \u2014 onChange receives the VALUE directly, not an event:\n// <Input value={x} onChange={setX} /> \u2713\n// <Input onChange={(e) => setX(e.target.value)} /> \u2717\n//\n// Imports allowed: 'react', '@ggui-ai/design', '@ggui-ai/wire'.\n// The WHOLE design system \u2014 primitives, components, compositions,\n// traits \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Primitives (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Container \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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=\"\u2600\uFE0F\" size={24} />\n\n// Link \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Components (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// SearchField \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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}>\u2026</Row>)}\n\n// Stat \u2014 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 \u2014 the big number\n delta?: string; // Optional change indicator, pre-formatted, e\n trend?: 'up' | 'down' | 'neutral'; // default 'neutral'; Direction of `delta` \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Compositions (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Header \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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=\"&copy; 2026 Acme Inc.\"\n bottomLinks={[{ label: 'Privacy', href: '/privacy' }]}\n bordered\n/>\n```\n\n// Hero \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// System Conventions\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 \u2192 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 \u2014\n// apply to <div key={...}> to replay CSS animations on data update.\n//\n// useMotion() returns { motionEnabled } \u2014 respect prefers-reduced-motion.\n";
8439
+ }
8440
+ });
8441
+
8355
8442
  // src/harness/primitive-slice.ts
8356
8443
  var primitive_slice_exports = {};
8357
8444
  __export(primitive_slice_exports, {
8445
+ computeContractPrimitives: () => computeContractPrimitives,
8358
8446
  computePrimitiveAllowlist: () => computePrimitiveAllowlist,
8359
- slicePrimitiveDocumentation: () => slicePrimitiveDocumentation
8447
+ slicePrimitiveDocumentation: () => slicePrimitiveDocumentation,
8448
+ slicePrimitiveDocumentationTs: () => slicePrimitiveDocumentationTs
8360
8449
  });
8361
8450
  function computePrimitiveAllowlist(classification) {
8362
8451
  const set = new Set(CORE_PRIMITIVES);
@@ -8376,6 +8465,42 @@ function computePrimitiveAllowlist(classification) {
8376
8465
  }
8377
8466
  return [...set].sort();
8378
8467
  }
8468
+ function computeContractPrimitives(contract) {
8469
+ const out = /* @__PURE__ */ new Set();
8470
+ if (!contract) return [];
8471
+ for (const slot of Object.values(contract.contextSpec ?? {})) addLeaves(slot.schema, out);
8472
+ for (const action of Object.values(contract.actionSpec ?? {})) {
8473
+ if (action.schema) addLeaves(action.schema, out);
8474
+ }
8475
+ if (Object.keys(contract.streamSpec ?? {}).length > 0) {
8476
+ out.add("Spinner");
8477
+ out.add("Badge");
8478
+ }
8479
+ return [...out].sort();
8480
+ }
8481
+ function addLeaves(schema, out) {
8482
+ for (const child of Object.values(schema.properties ?? {})) addLeaves(child, out);
8483
+ if (schema.items) addLeaves(schema.items, out);
8484
+ if (schema.enum && schema.enum.length > 0) {
8485
+ out.add("Select");
8486
+ out.add("RadioGroup");
8487
+ return;
8488
+ }
8489
+ const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
8490
+ for (const t of types) {
8491
+ if (t === "string") {
8492
+ out.add("Input");
8493
+ out.add("TextArea");
8494
+ out.add("FormField");
8495
+ } else if (t === "number" || t === "integer") {
8496
+ out.add("Input");
8497
+ out.add("Slider");
8498
+ } else if (t === "boolean") {
8499
+ out.add("Checkbox");
8500
+ out.add("Toggle");
8501
+ }
8502
+ }
8503
+ }
8379
8504
  function slicePrimitiveDocumentation(fullDoc, allowlist) {
8380
8505
  const allow = new Set(allowlist);
8381
8506
  const firstSection = fullDoc.search(/^### \S/m);
@@ -8399,7 +8524,26 @@ function slicePrimitiveDocumentation(fullDoc, allowlist) {
8399
8524
  }
8400
8525
  return preamble + kept.join("");
8401
8526
  }
8402
- var CORE_PRIMITIVES, KNOWN_PRIMITIVE_SECTIONS, AXIS_PRIMITIVES;
8527
+ function slicePrimitiveDocumentationTs(fullDoc, allowlist) {
8528
+ const allow = new Set(allowlist);
8529
+ const lines = fullDoc.split("\n");
8530
+ let sawHeader = false;
8531
+ let keep = true;
8532
+ const kept = [];
8533
+ for (const line of lines) {
8534
+ const header = TS_SECTION_HEADER.exec(line);
8535
+ if (header !== null) {
8536
+ sawHeader = true;
8537
+ const name = header[1];
8538
+ keep = !KNOWN_PRIMITIVE_SECTIONS.has(name) || allow.has(name);
8539
+ } else if (TS_SECTION_BANNER.test(line)) {
8540
+ keep = true;
8541
+ }
8542
+ if (keep) kept.push(line);
8543
+ }
8544
+ return sawHeader ? kept.join("\n") : fullDoc;
8545
+ }
8546
+ var CORE_PRIMITIVES, KNOWN_PRIMITIVE_SECTIONS, AXIS_PRIMITIVES, TS_SECTION_HEADER, TS_SECTION_BANNER;
8403
8547
  var init_primitive_slice = __esm({
8404
8548
  "src/harness/primitive-slice.ts"() {
8405
8549
  CORE_PRIMITIVES = [
@@ -8532,6 +8676,8 @@ var init_primitive_slice = __esm({
8532
8676
  "tooling:both": ["Alert"],
8533
8677
  "tooling:none": []
8534
8678
  };
8679
+ TS_SECTION_HEADER = /^\/\/ ([A-Za-z]+) — /;
8680
+ TS_SECTION_BANNER = /^\/\/ ═/;
8535
8681
  }
8536
8682
  });
8537
8683
 
@@ -8670,18 +8816,6 @@ var init_primitive_index = __esm({
8670
8816
  }
8671
8817
  });
8672
8818
 
8673
- // src/tools/get-primitives-ts.ts
8674
- var get_primitives_ts_exports = {};
8675
- __export(get_primitives_ts_exports, {
8676
- PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
8677
- });
8678
- var PRIMITIVES_DOCUMENTATION_TS;
8679
- var init_get_primitives_ts = __esm({
8680
- "src/tools/get-primitives-ts.ts"() {
8681
- PRIMITIVES_DOCUMENTATION_TS = "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// GGUI DESIGN SYSTEM \u2014 COMPONENT REFERENCE\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n//\n// CRITICAL \u2014 Props take enum STRING LITERALS, not CSS variables:\n// <Text size=\"sm\" weight=\"bold\" /> \u2713\n// <Text size=\"var(--ggui-font-size-sm)\" /> \u2717 (breaks)\n// The enum strings map to CSS variables internally.\n//\n// CRITICAL \u2014 onChange receives the VALUE directly, not an event:\n// <Input value={x} onChange={setX} /> \u2713\n// <Input onChange={(e) => setX(e.target.value)} /> \u2717\n//\n// Imports allowed: 'react', '@ggui-ai/design', '@ggui-ai/wire'.\n// The WHOLE design system \u2014 primitives, components, compositions,\n// traits \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Primitives (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Container \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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=\"\u2600\uFE0F\" size={24} />\n\n// Link \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Components (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// SearchField \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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}>\u2026</Row>)}\n\n// Stat \u2014 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 \u2014 the big number\n delta?: string; // Optional change indicator, pre-formatted, e\n trend?: 'up' | 'down' | 'neutral'; // default 'neutral'; Direction of `delta` \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Compositions (import from '@ggui-ai/design')\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Header \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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=\"&copy; 2026 Acme Inc.\"\n bottomLinks={[{ label: 'Privacy', href: '/privacy' }]}\n bordered\n/>\n```\n\n// Hero \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// System Conventions\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 \u2192 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 \u2014\n// apply to <div key={...}> to replay CSS animations on data update.\n//\n// useMotion() returns { motionEnabled } \u2014 respect prefers-reduced-motion.\n";
8682
- }
8683
- });
8684
-
8685
8819
  // src/adapters/index.ts
8686
8820
  init_base();
8687
8821
 
@@ -15117,13 +15251,22 @@ function editableSurface(el) {
15117
15251
  function parentLink(node) {
15118
15252
  return node;
15119
15253
  }
15120
- async function runRenderCheck(input) {
15254
+ var RENDER_CHECK_KINDS = [
15255
+ "render-no-throw",
15256
+ "prop-sensitivity",
15257
+ "action-wiring",
15258
+ "selection-identity",
15259
+ "prop-coverage",
15260
+ "optional-props-omitted",
15261
+ "stream-rerender"
15262
+ ];
15263
+ async function runRenderCheck(input, options = {}) {
15121
15264
  const g = hostGlobals();
15122
15265
  if ("window" in g && "document" in g) {
15123
15266
  return runRenderCheckInProcess(input);
15124
15267
  }
15125
15268
  const { runRenderCheckViaWorker: runRenderCheckViaWorker2 } = await Promise.resolve().then(() => (init_render_check_host(), render_check_host_exports));
15126
- return runRenderCheckViaWorker2(input);
15269
+ return runRenderCheckViaWorker2(input, options);
15127
15270
  }
15128
15271
  async function runRenderCheckInProcess(input) {
15129
15272
  const t0 = Date.now();
@@ -15137,7 +15280,7 @@ async function runRenderCheckInProcess(input) {
15137
15280
  const Wire = await import('@ggui-ai/wire');
15138
15281
  const { GguiWireProvider } = Wire;
15139
15282
  const moduleResolutions = {
15140
- "react": React,
15283
+ react: React,
15141
15284
  "react/jsx-runtime": ReactJsxRuntime,
15142
15285
  "@ggui-ai/wire": Wire
15143
15286
  };
@@ -15253,14 +15396,23 @@ async function runRenderCheckInProcess(input) {
15253
15396
  )
15254
15397
  );
15255
15398
  const timeoutPromise = new Promise((_, reject) => {
15256
- setTimeout(() => reject(new Error(`[runtime-render] render exceeded ${RENDER_TIMEOUT_MS2}ms \u2014 classified as runtime-hang`)), RENDER_TIMEOUT_MS2).unref?.();
15399
+ setTimeout(
15400
+ () => reject(
15401
+ new Error(
15402
+ `[runtime-render] render exceeded ${RENDER_TIMEOUT_MS2}ms \u2014 classified as runtime-hang`
15403
+ )
15404
+ ),
15405
+ RENDER_TIMEOUT_MS2
15406
+ ).unref?.();
15257
15407
  });
15258
15408
  renderResult = await Promise.race([renderPromise, timeoutPromise]);
15259
15409
  } catch (e) {
15260
15410
  const msg = e instanceof Error ? e.message : String(e);
15261
15411
  const errStack = e instanceof Error ? e.stack ?? "" : "";
15262
15412
  const baseReason = loopSignature ? `Infinite render loop (${loopSignature}) \u2014 likely setState/dispatch inside useEffect with missing or unstable dependency` : /runtime-hang/.test(msg) ? `Render wall-clock exceeded ${RENDER_TIMEOUT_MS2}ms \u2014 likely async effect loop or unresolved promise` : `Render threw: ${msg}`;
15263
- const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter((l) => !/(node_modules|happy-dom|@testing-library|react-dom|node:internal|node:async)/.test(l)).slice(0, 5);
15413
+ const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter(
15414
+ (l) => !/(node_modules|happy-dom|@testing-library|react-dom|node:internal|node:async)/.test(l)
15415
+ ).slice(0, 5);
15264
15416
  const stackSource = boundaryRef.stack ?? capturedComponentStack ?? "";
15265
15417
  const componentFrames = stackSource.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).slice(0, 4);
15266
15418
  const stackBlock = userFrames.length ? `
@@ -15725,9 +15877,7 @@ function idLikeRequiredStringField(schema) {
15725
15877
  const required = schema["required"];
15726
15878
  if (properties === null || typeof properties !== "object") return null;
15727
15879
  if (!Array.isArray(required)) return null;
15728
- for (const [propName, propSchema] of Object.entries(
15729
- properties
15730
- )) {
15880
+ for (const [propName, propSchema] of Object.entries(properties)) {
15731
15881
  if (!/id$/i.test(propName)) continue;
15732
15882
  if (!required.includes(propName)) continue;
15733
15883
  const t = propSchema !== null && typeof propSchema === "object" ? propSchema["type"] : void 0;
@@ -15758,9 +15908,7 @@ async function checkSelectionIdentity(input) {
15758
15908
  return void 0;
15759
15909
  }
15760
15910
  await flushPromises();
15761
- const events = probe.getFireLog().slice(before).filter(
15762
- (e) => e.kind === "action.fired" && e.name === actionName
15763
- );
15911
+ const events = probe.getFireLog().slice(before).filter((e) => e.kind === "action.fired" && e.name === actionName);
15764
15912
  return events.length > 0 ? events[events.length - 1].payload : void 0;
15765
15913
  };
15766
15914
  const first = await payloadFor(group[0]);
@@ -15821,7 +15969,10 @@ async function dispatchTrigger(el, kind, user) {
15821
15969
  case "submit": {
15822
15970
  const form = closestForm(el);
15823
15971
  if (!form) return;
15824
- const ev = new globalThis.Event("submit", { bubbles: true, cancelable: true });
15972
+ const ev = new globalThis.Event("submit", {
15973
+ bubbles: true,
15974
+ cancelable: true
15975
+ });
15825
15976
  toEventDispatcher(form).dispatchEvent(ev);
15826
15977
  return;
15827
15978
  }
@@ -16026,6 +16177,9 @@ function flushPromises() {
16026
16177
  return new Promise((resolve4) => setTimeout(resolve4, 0));
16027
16178
  }
16028
16179
 
16180
+ // src/harness/check/runtime-render/adapter.ts
16181
+ init_render_check_host();
16182
+
16029
16183
  // src/harness/check/runtime-render/prepare-mockup.ts
16030
16184
  function prepareMockupProps(input) {
16031
16185
  const { contract, fixtureProps } = input;
@@ -16158,47 +16312,111 @@ function synthesizeFromSchema2(schema, hint, warnings, depth, ordinal) {
16158
16312
  }
16159
16313
 
16160
16314
  // src/harness/check/runtime-render/adapter.ts
16161
- var DEFAULT_RUNTIME_RENDER_CHECK = {
16162
- id: "runtime-render",
16163
- run: async (input) => {
16164
- const { sourceCode, compiledCode, contract, fixtureProps } = input;
16165
- if (compiledCode === null) {
16166
- return { status: "not-applicable", issues: [], reason: "no compiled code" };
16167
- }
16168
- if (!contract) {
16169
- return { status: "not-applicable", issues: [], reason: "no contract surface" };
16170
- }
16171
- const mockup = prepareMockupProps({ contract, fixtureProps });
16172
- let result;
16173
- try {
16174
- result = await runRenderCheck({
16175
- sourceCode,
16176
- mockupProps: mockup.props,
16177
- contract
16315
+ function createSlotLimiter(maxConcurrent) {
16316
+ if (maxConcurrent === void 0) return (task) => task(0);
16317
+ if (!Number.isInteger(maxConcurrent) || maxConcurrent < 1) {
16318
+ throw new RangeError(
16319
+ `createRuntimeRenderCheck: maxConcurrent must be a positive integer, got ${maxConcurrent}`
16320
+ );
16321
+ }
16322
+ let active = 0;
16323
+ const waiting = [];
16324
+ const acquire = () => {
16325
+ if (active < maxConcurrent) {
16326
+ active += 1;
16327
+ return Promise.resolve(0);
16328
+ }
16329
+ const enqueuedAt = Date.now();
16330
+ return new Promise((resolve4) => {
16331
+ waiting.push(() => {
16332
+ active += 1;
16333
+ resolve4(Date.now() - enqueuedAt);
16178
16334
  });
16179
- } catch (e) {
16180
- const message = e instanceof Error ? e.message : String(e);
16181
- console.warn(
16182
- `[runtime-render] probe skipped \u2014 infra failure: ${message}`
16183
- );
16184
- return { status: "infra-skipped", issues: [], reason: message };
16335
+ });
16336
+ };
16337
+ const release = () => {
16338
+ active -= 1;
16339
+ const next = waiting.shift();
16340
+ if (next !== void 0) next();
16341
+ };
16342
+ return async (task) => {
16343
+ const waitedMs = await acquire();
16344
+ try {
16345
+ return await task(waitedMs);
16346
+ } finally {
16347
+ release();
16185
16348
  }
16186
- const hostLoad = result.stats.hostLoad;
16187
- const load = hostLoad !== void 0 ? { hostLoad } : {};
16188
- if (result.incomplete !== void 0) {
16189
- const { elapsedMs, boundMs } = result.incomplete;
16190
- const loadNote = hostLoad !== void 0 ? `; host load ${hostLoad.start.toFixed(1)} \u2192 ${hostLoad.end.toFixed(1)} on ${hostLoad.cores} CPUs` : "";
16191
- const reason = `render check did not finish within ${boundMs} ms (stopped at ${elapsedMs} ms)${loadNote}`;
16192
- console.warn(`[runtime-render] probe timed out \u2014 ${reason}`);
16193
- return { status: "timed-out", issues: [], reason, elapsedMs, ...load };
16349
+ };
16350
+ }
16351
+ function queuedField(queuedMs) {
16352
+ return queuedMs > 0 ? { queuedMs } : {};
16353
+ }
16354
+ function toRenderCheckOptions(config) {
16355
+ if (config.timeoutMs === void 0 && config.heapMb === void 0) return void 0;
16356
+ return {
16357
+ bounds: {
16358
+ ...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {},
16359
+ ...config.heapMb !== void 0 ? { heapMb: config.heapMb } : {}
16194
16360
  }
16195
- return {
16196
- status: "ran",
16197
- issues: result.issues.map(toEvalIssue).filter((x) => x !== null),
16198
- ...load
16199
- };
16200
- }
16201
- };
16361
+ };
16362
+ }
16363
+ function createRuntimeRenderCheck(config = {}) {
16364
+ const limit = createSlotLimiter(config.maxConcurrent);
16365
+ const options = toRenderCheckOptions(config);
16366
+ if (options !== void 0) resolveRenderCheckHostBounds(options.bounds);
16367
+ return {
16368
+ id: "runtime-render",
16369
+ run: async (input) => {
16370
+ const { sourceCode, compiledCode, contract, fixtureProps } = input;
16371
+ if (compiledCode === null) {
16372
+ return { status: "not-applicable", issues: [], reason: "no compiled code" };
16373
+ }
16374
+ if (!contract) {
16375
+ return { status: "not-applicable", issues: [], reason: "no contract surface" };
16376
+ }
16377
+ const mockup = prepareMockupProps({ contract, fixtureProps });
16378
+ let queuedMs = 0;
16379
+ let t0 = Date.now();
16380
+ let result;
16381
+ try {
16382
+ result = await limit((waitedMs) => {
16383
+ queuedMs = waitedMs;
16384
+ t0 = Date.now();
16385
+ return runRenderCheck(
16386
+ {
16387
+ sourceCode,
16388
+ mockupProps: mockup.props,
16389
+ contract
16390
+ },
16391
+ options
16392
+ );
16393
+ });
16394
+ } catch (e) {
16395
+ const message = e instanceof Error ? e.message : String(e);
16396
+ console.warn(`[runtime-render] probe skipped \u2014 infra failure: ${message}`);
16397
+ return { status: "infra-skipped", issues: [], reason: message, elapsedMs: Date.now() - t0, ...queuedField(queuedMs) };
16398
+ }
16399
+ const hostLoad = result.stats.hostLoad;
16400
+ const load = hostLoad !== void 0 ? { hostLoad } : {};
16401
+ if (result.incomplete !== void 0) {
16402
+ const { elapsedMs, boundMs } = result.incomplete;
16403
+ const loadNote = hostLoad !== void 0 ? `; host load ${hostLoad.start.toFixed(1)} \u2192 ${hostLoad.end.toFixed(1)} on ${hostLoad.cores} CPUs` : "";
16404
+ const reason = `render check did not finish within ${boundMs} ms (stopped at ${elapsedMs} ms)${loadNote}`;
16405
+ console.warn(`[runtime-render] probe timed out \u2014 ${reason}`);
16406
+ return { status: "timed-out", issues: [], reason, elapsedMs: Date.now() - t0, ...queuedField(queuedMs), ...load };
16407
+ }
16408
+ return {
16409
+ status: "ran",
16410
+ issues: result.issues.map(toEvalIssue).filter((x) => x !== null),
16411
+ elapsedMs: Date.now() - t0,
16412
+ ...queuedField(queuedMs),
16413
+ renderMs: result.stats.renderMs,
16414
+ ...load
16415
+ };
16416
+ }
16417
+ };
16418
+ }
16419
+ var DEFAULT_RUNTIME_RENDER_CHECK = createRuntimeRenderCheck();
16202
16420
  var CLASS_SPECIFIC_PATTERNS = [
16203
16421
  /too many re-renders/i,
16204
16422
  /maximum update depth/i,
@@ -16237,6 +16455,14 @@ function classifyRenderCrashFix(reason) {
16237
16455
  }
16238
16456
  return "Add null guards on optional props, handle empty arrays/strings, and verify all hook outputs before destructuring.";
16239
16457
  }
16458
+ var RUNTIME_SUBCATEGORY_PREFIX = "runtime:";
16459
+ function parseRuntimeSubcategory(subcategory) {
16460
+ if (!subcategory.startsWith(RUNTIME_SUBCATEGORY_PREFIX)) return void 0;
16461
+ const rest = subcategory.slice(RUNTIME_SUBCATEGORY_PREFIX.length);
16462
+ const separator = rest.indexOf(":");
16463
+ const check = separator === -1 ? rest : rest.slice(0, separator);
16464
+ return RENDER_CHECK_KINDS.find((kind) => kind === check);
16465
+ }
16240
16466
  function toEvalIssue(issue) {
16241
16467
  if (issue.outcome === "verified" || issue.outcome === "skipped") return null;
16242
16468
  const result = issue.outcome === "failed" ? "fail" : "warn";
@@ -17478,6 +17704,14 @@ function formatFitFeedback(issue) {
17478
17704
  Fix: ${issue.fix ?? ""}`;
17479
17705
  }
17480
17706
  var EVAL_ROUND_THREW_REASON = "eval round threw";
17707
+ var PROBE_ONLY_CRITERIA_REASON = "probe-only round: no evaluator configured";
17708
+ var PROBE_ONLY_VISUAL_REASON = "probe-only round: no visual evaluator configured";
17709
+ var PROBE_ONLY_PASS = "probe-only";
17710
+ var COST_TRACKER_ABSENT_REASON = "cost tracker absent on an evaluation round";
17711
+ function requireCostTracker(costTracker) {
17712
+ if (costTracker === null) throw new Error(COST_TRACKER_ABSENT_REASON);
17713
+ return costTracker;
17714
+ }
17481
17715
  function runVisualLeg(visualEvalAgent, run, measureFit) {
17482
17716
  const provider = visionJudgeProvider(visualEvalAgent.provider);
17483
17717
  if (provider === void 0) {
@@ -17520,6 +17754,36 @@ function fingerprintFail(issue) {
17520
17754
  const desc = (issue.description ?? "").toLowerCase().replace(/line\s+\d+/g, "line N").replace(/\s+/g, " ").trim().slice(0, 40);
17521
17755
  return `${cat}|${sub}|${desc}`;
17522
17756
  }
17757
+ function failedCheckKinds(probeIssues) {
17758
+ const failed2 = /* @__PURE__ */ new Set();
17759
+ for (const issue of probeIssues) {
17760
+ if (issue.result !== "fail" || issue.subcategory === void 0) continue;
17761
+ const kind = parseRuntimeSubcategory(issue.subcategory);
17762
+ if (kind !== void 0) failed2.add(kind);
17763
+ }
17764
+ return RENDER_CHECK_KINDS.filter((kind) => failed2.has(kind));
17765
+ }
17766
+ function probeMetaOf(outcome, probeIssues) {
17767
+ const detail = {
17768
+ ...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
17769
+ ...outcome.renderMs !== void 0 ? { renderMs: outcome.renderMs } : {},
17770
+ ...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {},
17771
+ ...outcome.queuedMs !== void 0 ? { queuedMs: outcome.queuedMs } : {}
17772
+ };
17773
+ const elapsed = outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {};
17774
+ if (outcome.status !== "ran") {
17775
+ return { status: outcome.status, ...elapsed, ...detail };
17776
+ }
17777
+ const [first, ...rest] = failedCheckKinds(probeIssues);
17778
+ if (first === void 0) {
17779
+ return { status: "ran", verdict: "pass", ...elapsed, ...detail };
17780
+ }
17781
+ return { status: "ran", verdict: "fail", failChecks: [first, ...rest], ...elapsed, ...detail };
17782
+ }
17783
+ function describeProbeMeta(meta) {
17784
+ if (meta.verdict === void 0) return meta.status;
17785
+ return meta.verdict === "pass" ? "ran, verdict pass" : `ran, verdict fail (${meta.failChecks.join(", ")})`;
17786
+ }
17523
17787
  async function runProbeAtExit(input) {
17524
17788
  const { harness, sourceCode, compiledCode, contract, fixtureProps } = input;
17525
17789
  const probe = harness.check.runtimeRender;
@@ -17552,12 +17816,7 @@ async function runProbeAtExit(input) {
17552
17816
  meta: { status: "infra-skipped", reason: message }
17553
17817
  };
17554
17818
  }
17555
- const meta = {
17556
- status: outcome.status,
17557
- ...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
17558
- ...outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {},
17559
- ...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {}
17560
- };
17819
+ const meta = probeMetaOf(outcome, outcome.issues);
17561
17820
  if (outcome.status !== "ran") {
17562
17821
  return { fired: false, recoverableFail: false, probeIssues: [], meta };
17563
17822
  }
@@ -17600,11 +17859,13 @@ async function runEvalRound(ctx, input) {
17600
17859
  visualThreshold,
17601
17860
  qualityMode,
17602
17861
  maxEvalRounds,
17603
- costTracker,
17604
17862
  llmEvalMod,
17605
17863
  visualMod,
17606
17864
  preWarmPromise,
17607
- onProgress
17865
+ onProgress,
17866
+ probeOnly,
17867
+ probeRepairUsed,
17868
+ probeRepairTrigger
17608
17869
  } = ctx;
17609
17870
  const { compiledCode, prevModeSubcats, prevFailFingerprints } = input;
17610
17871
  let { evalRoundsUsed, preWarmedContext } = input;
@@ -17615,6 +17876,88 @@ async function runEvalRound(ctx, input) {
17615
17876
  let evalTokens = { input: 0, output: 0 };
17616
17877
  let evalLlmMs = 0;
17617
17878
  try {
17879
+ if (probeOnly) {
17880
+ const exitProbe = await runProbeAtExit({
17881
+ harness,
17882
+ sourceCode: currentSource,
17883
+ compiledCode,
17884
+ contract,
17885
+ fixtureProps
17886
+ });
17887
+ const probeOnlyResult = {
17888
+ issues: [...exitProbe.probeIssues],
17889
+ pass: [PROBE_ONLY_PASS],
17890
+ runtimeProbe: exitProbe.meta,
17891
+ criteriaCoverage: notApplicableCoverage(PROBE_ONLY_CRITERIA_REASON),
17892
+ visualCoverage: { status: "not-applicable", reason: PROBE_ONLY_VISUAL_REASON }
17893
+ };
17894
+ const recoverableFail = exitProbe.fired && exitProbe.recoverableFail;
17895
+ if (probeRepairUsed) {
17896
+ if (probeRepairTrigger === void 0) {
17897
+ throw new Error("harness defect: a probe-only re-probe round without the probe that bought the repair turn");
17898
+ }
17899
+ const repair = { attempted: true, compiled: true, trigger: probeRepairTrigger };
17900
+ evalResult = { ...probeOnlyResult, runtimeProbeRepair: repair };
17901
+ console.log(
17902
+ `[simple] eval round ${evalRoundsUsed}: probe-only re-probe after the repair turn \u2014 ${describeProbeMeta(exitProbe.meta)}${recoverableFail ? ", recoverable crash still present" : ""} \u2014 serving`
17903
+ );
17904
+ return {
17905
+ control: "break",
17906
+ evalDone: true,
17907
+ evalResult,
17908
+ evalRoundsUsed,
17909
+ prevModeSubcats,
17910
+ prevFailFingerprints,
17911
+ preWarmedContext,
17912
+ evalTokens,
17913
+ evalLlmMs,
17914
+ lastResultText: "",
17915
+ isEvalFeedback: false,
17916
+ lastDiffFailed: false
17917
+ };
17918
+ }
17919
+ evalResult = probeOnlyResult;
17920
+ if (recoverableFail) {
17921
+ const runtimeFails = exitProbe.probeIssues.filter(
17922
+ (i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
17923
+ );
17924
+ const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
17925
+ console.log(
17926
+ `[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 runtime probe fail (recoverable) \u2014 granting the one repair turn`
17927
+ );
17928
+ return {
17929
+ control: "feedback",
17930
+ evalDone: false,
17931
+ evalResult,
17932
+ evalRoundsUsed,
17933
+ prevModeSubcats,
17934
+ prevFailFingerprints,
17935
+ preWarmedContext,
17936
+ evalTokens,
17937
+ evalLlmMs,
17938
+ lastResultText: lines.join("\n\n"),
17939
+ isEvalFeedback: true,
17940
+ lastDiffFailed: false
17941
+ };
17942
+ }
17943
+ console.log(
17944
+ `[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 probe ${describeProbeMeta(exitProbe.meta)} \u2014 serving`
17945
+ );
17946
+ return {
17947
+ control: "break",
17948
+ evalDone: true,
17949
+ evalResult,
17950
+ evalRoundsUsed,
17951
+ prevModeSubcats,
17952
+ prevFailFingerprints,
17953
+ preWarmedContext,
17954
+ evalTokens,
17955
+ evalLlmMs,
17956
+ lastResultText: "",
17957
+ isEvalFeedback: false,
17958
+ lastDiffFailed: false
17959
+ };
17960
+ }
17618
17961
  const checkResult = await runCheck({
17619
17962
  harness,
17620
17963
  sourceCode: currentSource,
@@ -17689,6 +18032,7 @@ async function runEvalRound(ctx, input) {
17689
18032
  lastDiffFailed: false
17690
18033
  };
17691
18034
  }
18035
+ const costTracker = requireCostTracker(ctx.costTracker);
17692
18036
  if (evalRoundsUsed === 1 && !preWarmedContext && preWarmPromise) {
17693
18037
  preWarmedContext = await preWarmPromise;
17694
18038
  }
@@ -18174,6 +18518,8 @@ function createTelemetry() {
18174
18518
  totalIn: 0,
18175
18519
  totalOut: 0,
18176
18520
  evalResult: void 0,
18521
+ probeRepairUsed: false,
18522
+ probeRepairTrigger: void 0,
18177
18523
  contractFeedback: void 0,
18178
18524
  sameExchangeBreak: void 0,
18179
18525
  compiledCode: "",
@@ -18273,6 +18619,15 @@ function createGenerateTaskRunner(input) {
18273
18619
  absorbTurn(telemetry, turn);
18274
18620
  iconNamesCache = turn.iconNamesCache;
18275
18621
  preWarmedContext = turn.preWarmedContext;
18622
+ if (turn.control !== "proceed" && session.probeOnlyEnabled && telemetry.probeRepairUsed) {
18623
+ if (telemetry.evalResult !== void 0) {
18624
+ telemetry.evalResult = { ...telemetry.evalResult, runtimeProbeRepair: { attempted: true, compiled: false } };
18625
+ }
18626
+ console.log(
18627
+ `[simple] probe-only repair turn did not compile (${turn.outcome ?? "no tool call"}) \u2014 serving the pre-repair card`
18628
+ );
18629
+ break;
18630
+ }
18276
18631
  if (turn.control === "break") break;
18277
18632
  if (turn.control === "continue") {
18278
18633
  lastResultText = turn.lastResultText;
@@ -18311,7 +18666,7 @@ function createGenerateTaskRunner(input) {
18311
18666
  `[simple] initial result delivered | ${codingDoneMs}ms | turns=${telemetry.turnsUsed} | tokens=${telemetry.totalIn + telemetry.totalOut} | compiled=${telemetry.compiledCode.length}B`
18312
18667
  );
18313
18668
  }
18314
- if (!evalDone && telemetry.compiledCode && session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled)) {
18669
+ if (!evalDone && telemetry.compiledCode && (session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled) || session.probeOnlyEnabled)) {
18315
18670
  const evalRoundStart = Date.now();
18316
18671
  const round = await runEvalRound(
18317
18672
  {
@@ -18331,7 +18686,10 @@ function createGenerateTaskRunner(input) {
18331
18686
  llmEvalMod: session.llmEvalMod,
18332
18687
  visualMod: session.visualMod,
18333
18688
  preWarmPromise: session.preWarmPromise,
18334
- onProgress: params.onProgress
18689
+ onProgress: params.onProgress,
18690
+ probeOnly: session.probeOnlyEnabled,
18691
+ probeRepairUsed: telemetry.probeRepairUsed,
18692
+ ...telemetry.probeRepairTrigger !== void 0 ? { probeRepairTrigger: telemetry.probeRepairTrigger } : {}
18335
18693
  },
18336
18694
  {
18337
18695
  compiledCode: telemetry.compiledCode,
@@ -18352,6 +18710,10 @@ function createGenerateTaskRunner(input) {
18352
18710
  evalDone = round.evalDone;
18353
18711
  absorbTokens(telemetry, round.evalTokens);
18354
18712
  if (round.control === "break") break;
18713
+ if (session.probeOnlyEnabled) {
18714
+ telemetry.probeRepairUsed = true;
18715
+ telemetry.probeRepairTrigger = round.evalResult?.runtimeProbe;
18716
+ }
18355
18717
  lastResultText = round.lastResultText;
18356
18718
  isEvalFeedback = round.isEvalFeedback;
18357
18719
  lastDiffFailed = round.lastDiffFailed;
@@ -18359,6 +18721,9 @@ function createGenerateTaskRunner(input) {
18359
18721
  break;
18360
18722
  }
18361
18723
  } while (telemetry.turnsUsed < maxTurns && !evalDone);
18724
+ if (session.probeOnlyEnabled && telemetry.probeRepairUsed && telemetry.evalResult !== void 0 && telemetry.evalResult.runtimeProbeRepair === void 0) {
18725
+ console.log(`[simple] probe-only repair turn not taken \u2014 turn cap (${maxTurns}) reached before it`);
18726
+ }
18362
18727
  telemetry.codingMs = Date.now() - telemetry.codingStartedAtMs;
18363
18728
  if (!telemetry.compiledCode) {
18364
18729
  for (const [, meta] of session.commitMeta) {
@@ -18585,6 +18950,7 @@ async function initSession(input) {
18585
18950
  const qualityMode = qualityConfig?.quality ?? "fast";
18586
18951
  const codeEvalEnabled = !!params.evaluation?.enabled;
18587
18952
  const visualEvalEnabled = !!(params.visualEvaluation?.enabled || qualityConfig?.visualEval);
18953
+ const probeOnlyEnabled = harness.check.runtimeRender !== void 0 && !codeEvalEnabled && !visualEvalEnabled;
18588
18954
  const maxEvalRounds = qualityMode === "high-quality" ? params.evaluation?.maxRounds ?? 3 : params.evaluation?.maxRounds ?? 2;
18589
18955
  const visualThreshold = params.visualEvaluation?.passThreshold ?? 70;
18590
18956
  let tiersMod = null;
@@ -18615,6 +18981,7 @@ async function initSession(input) {
18615
18981
  systemPrompt,
18616
18982
  codeEvalEnabled,
18617
18983
  visualEvalEnabled,
18984
+ probeOnlyEnabled,
18618
18985
  maxEvalRounds,
18619
18986
  visualThreshold,
18620
18987
  qualityMode,
@@ -18719,7 +19086,15 @@ async function dispatchGeneration(params) {
18719
19086
  // prompt's gadget catalog renders a `Type:` line per third-party
18720
19087
  // gadget (the LLM sees a wrapper's real call shape it otherwise
18721
19088
  // can't know).
18722
- systemPromptBuilder: ({ userRequest, shellType, screen, axisDelta, designMode, canvas, profile }) => buildSystemPrompt2(
19089
+ systemPromptBuilder: ({
19090
+ userRequest,
19091
+ shellType,
19092
+ screen,
19093
+ axisDelta,
19094
+ designMode,
19095
+ canvas,
19096
+ profile
19097
+ }) => buildSystemPrompt2(
18723
19098
  userRequest,
18724
19099
  shellType,
18725
19100
  screen,
@@ -18735,14 +19110,14 @@ async function dispatchGeneration(params) {
18735
19110
  axisChecks: REGISTRY.filter(
18736
19111
  (check) => matches(classification.vector, check)
18737
19112
  ),
18738
- // The default runtime-render check. Heavy deps (happy-dom + wire +
18739
- // design) stay on the dispatch side of the boundary; the skeleton
18740
- // createHarness defaults this to undefined.
19113
+ // The runtime-render check, built under the caller's probe config.
19114
+ // Heavy deps (happy-dom + wire + design) stay on the dispatch side of
19115
+ // the boundary; the skeleton createHarness defaults this to undefined.
18741
19116
  //
18742
19117
  // Gated behind `enableRuntimeRender` (default true) so
18743
19118
  // benchmark/dev callers can opt out of in-loop probe execution and
18744
19119
  // run the probe externally as a final post-generation check.
18745
- runtimeRender: params.enableRuntimeRender ?? true ? DEFAULT_RUNTIME_RENDER_CHECK : void 0
19120
+ runtimeRender: params.enableRuntimeRender ?? true ? params.runtimeRender ?? DEFAULT_RUNTIME_RENDER_CHECK : void 0
18746
19121
  });
18747
19122
  console.log(
18748
19123
  `[harness] id=${harness.id} name=${harness.name} workflow=${harness.process.workflow.name} overrides=${harness.meta.overrides.join(",") || "none"}`
@@ -18760,67 +19135,70 @@ async function dispatchGeneration(params) {
18760
19135
  evaluationAgent
18761
19136
  });
18762
19137
  const { resolveRunPolicyForProfile: resolveRunPolicyForProfile2 } = await Promise.resolve().then(() => (init_policy2(), policy_exports));
18763
- const resolvedPolicy = resolveRunPolicyForProfile2(
18764
- process.env.GGUI_POLICY_PROFILE,
18765
- harness,
18766
- { provider: routerProvider, modelId: resolvedModel }
18767
- );
19138
+ const resolvedPolicy = resolveRunPolicyForProfile2(process.env.GGUI_POLICY_PROFILE, harness, {
19139
+ provider: routerProvider,
19140
+ modelId: resolvedModel
19141
+ });
18768
19142
  let systemPromptOverride;
18769
19143
  {
18770
19144
  const { PRIMITIVES_DOCUMENTATION: PRIMITIVES_DOCUMENTATION2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
18771
19145
  const fullBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION2, "utf8");
19146
+ const docFormat = resolvedPolicy.context.primitiveDocFormat ?? "markdown";
19147
+ const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
18772
19148
  const mode = resolvedPolicy.context.primitiveDocSlice ?? "full";
18773
- if (mode === "axis-keyed") {
18774
- const { computePrimitiveAllowlist: computePrimitiveAllowlist2, slicePrimitiveDocumentation: slicePrimitiveDocumentation2 } = await Promise.resolve().then(() => (init_primitive_slice(), primitive_slice_exports));
19149
+ let doc = PRIMITIVES_DOCUMENTATION2;
19150
+ if (docFormat === "ts" && indexMode === "off") {
19151
+ const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
19152
+ doc = PRIMITIVES_DOCUMENTATION_TS2;
19153
+ const tsBytes = Buffer.byteLength(doc, "utf8");
19154
+ console.log(
19155
+ `[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
19156
+ );
19157
+ }
19158
+ const formatBytes = Buffer.byteLength(doc, "utf8");
19159
+ if (mode === "axis-keyed" || mode === "axis-keyed+contract") {
19160
+ const {
19161
+ computePrimitiveAllowlist: computePrimitiveAllowlist2,
19162
+ computeContractPrimitives: computeContractPrimitives2,
19163
+ slicePrimitiveDocumentation: slicePrimitiveDocumentation2,
19164
+ slicePrimitiveDocumentationTs: slicePrimitiveDocumentationTs2
19165
+ } = await Promise.resolve().then(() => (init_primitive_slice(), primitive_slice_exports));
18775
19166
  let allowlist = computePrimitiveAllowlist2(classification);
19167
+ if (mode === "axis-keyed+contract") {
19168
+ const fromContract = computeContractPrimitives2(effectiveContract);
19169
+ allowlist = [.../* @__PURE__ */ new Set([...allowlist, ...fromContract])].sort();
19170
+ }
18776
19171
  const excludes = resolvedPolicy.context.primitiveDocExcludes ?? [];
18777
19172
  if (excludes.length > 0) {
18778
19173
  const excludeSet = new Set(excludes);
18779
19174
  allowlist = allowlist.filter((p) => !excludeSet.has(p));
18780
19175
  }
18781
- const sliced = slicePrimitiveDocumentation2(
18782
- PRIMITIVES_DOCUMENTATION2,
18783
- allowlist
18784
- );
18785
- const slicedBytes = Buffer.byteLength(sliced, "utf8");
19176
+ doc = docFormat === "ts" && indexMode === "off" ? slicePrimitiveDocumentationTs2(doc, allowlist) : slicePrimitiveDocumentation2(doc, allowlist);
19177
+ const slicedBytes = Buffer.byteLength(doc, "utf8");
18786
19178
  const excludeTag = excludes.length ? ` excludes=${excludes.join(",")}` : "";
18787
19179
  console.log(
18788
- `[simple] primitive-doc-bytes=${slicedBytes} mode=axis-keyed baseline=${fullBytes} drop=${((1 - slicedBytes / fullBytes) * 100).toFixed(1)}% allowlist=${allowlist.length}/${allowlist.join(",")}${excludeTag}`
18789
- );
18790
- systemPromptOverride = harness.how.systemPrompt.replace(
18791
- PRIMITIVES_DOCUMENTATION2,
18792
- sliced
19180
+ `[simple] primitive-doc-bytes=${slicedBytes} mode=${mode} baseline=${formatBytes} drop=${((1 - slicedBytes / formatBytes) * 100).toFixed(1)}% allowlist=${allowlist.length}/${allowlist.join(",")}${excludeTag}`
18793
19181
  );
18794
19182
  } else {
18795
19183
  console.log(
18796
- `[simple] primitive-doc-bytes=${fullBytes} mode=full baseline=${fullBytes} drop=0.0%`
19184
+ `[simple] primitive-doc-bytes=${formatBytes} mode=full baseline=${formatBytes} drop=0.0%`
18797
19185
  );
18798
19186
  }
18799
- const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
18800
19187
  if (indexMode !== "off") {
18801
19188
  const { buildPrimitiveIndex: buildPrimitiveIndex2 } = await Promise.resolve().then(() => (init_primitive_index(), primitive_index_exports));
18802
- const index = buildPrimitiveIndex2(PRIMITIVES_DOCUMENTATION2, indexMode);
18803
- const indexBytes = Buffer.byteLength(index, "utf8");
19189
+ doc = buildPrimitiveIndex2(PRIMITIVES_DOCUMENTATION2, indexMode);
19190
+ const indexBytes = Buffer.byteLength(doc, "utf8");
18804
19191
  console.log(
18805
19192
  `[simple] primitive-index-bytes=${indexBytes} mode=${indexMode} baseline=${fullBytes} drop=${((1 - indexBytes / fullBytes) * 100).toFixed(1)}%`
18806
19193
  );
18807
- systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
18808
- PRIMITIVES_DOCUMENTATION2,
18809
- index
18810
- );
18811
19194
  }
18812
- const docFormat = resolvedPolicy.context.primitiveDocFormat ?? "markdown";
18813
- if (docFormat === "ts" && indexMode === "off") {
18814
- const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
18815
- const tsBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION_TS2, "utf8");
18816
- console.log(
18817
- `[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
18818
- );
18819
- systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
18820
- PRIMITIVES_DOCUMENTATION2,
18821
- PRIMITIVES_DOCUMENTATION_TS2
18822
- );
19195
+ if (doc !== PRIMITIVES_DOCUMENTATION2) {
19196
+ systemPromptOverride = harness.how.systemPrompt.replace(PRIMITIVES_DOCUMENTATION2, doc);
18823
19197
  }
19198
+ const sentPrompt = systemPromptOverride ?? harness.how.systemPrompt;
19199
+ console.log(
19200
+ `[simple] system-prompt-bytes=${Buffer.byteLength(sentPrompt, "utf8")} doc-format=${docFormat === "ts" && indexMode === "off" ? "ts" : docFormat} doc-bytes=${Buffer.byteLength(doc, "utf8")} mode=${indexMode !== "off" ? `index:${indexMode}` : mode}`
19201
+ );
18824
19202
  if (resolvedPolicy.context.planFirstTurn) {
18825
19203
  const phaseGuidance = `
18826
19204
 
@@ -18872,7 +19250,11 @@ Your plan is a commitment, not a design doc. Keep it tight. Do not over-plan; ju
18872
19250
  );
18873
19251
  }
18874
19252
  const finalSource = runResult.finalSource ?? "";
18875
- const result = await assembleGenerationResult({ session, telemetry, source: finalSource });
19253
+ const result = await assembleGenerationResult({
19254
+ session,
19255
+ telemetry,
19256
+ source: finalSource
19257
+ });
18876
19258
  return withArmRecord(result, params);
18877
19259
  }
18878
19260
  function withArmRecord(result, params) {
@@ -19320,6 +19702,7 @@ var DEFAULT_MODEL = "anthropic/claude-haiku-4-5";
19320
19702
  function createUiGenerator(options = {}) {
19321
19703
  const {
19322
19704
  enableRuntimeRender = false,
19705
+ runtimeRenderProbe,
19323
19706
  maxAttempts,
19324
19707
  maxEvalRounds,
19325
19708
  evaluation,
@@ -19331,6 +19714,7 @@ function createUiGenerator(options = {}) {
19331
19714
  onGenerated,
19332
19715
  designMode
19333
19716
  } = options;
19717
+ const runtimeRender = enableRuntimeRender ? createRuntimeRenderCheck(runtimeRenderProbe) : void 0;
19334
19718
  const identity = resolveIdentity(options);
19335
19719
  return {
19336
19720
  slug: identity.slug,
@@ -19382,17 +19766,11 @@ function createUiGenerator(options = {}) {
19382
19766
  const promptWithRendering = rendering ? injectRenderingContext(input.request.prompt, rendering) : input.request.prompt;
19383
19767
  const canvas = rendering ? canvasForRenderingContext(rendering) : void 0;
19384
19768
  const shellType = harnessShellForRendering(rendering);
19385
- const promptWithVariance = injectVariance(
19386
- promptWithRendering,
19387
- input.variance,
19388
- { profileDeclared: hasProfile(input.profile) }
19389
- );
19769
+ const promptWithVariance = injectVariance(promptWithRendering, input.variance, {
19770
+ profileDeclared: hasProfile(input.profile)
19771
+ });
19390
19772
  const resolvedAppGadgets = input.appGadgets !== void 0 ? input.appGadgets : gadgetCatalog !== void 0 && input.appId !== void 0 ? await gadgetCatalog.list(input.appId) : void 0;
19391
- const userPrompt = injectContracts(
19392
- promptWithVariance,
19393
- input.contract,
19394
- resolvedAppGadgets
19395
- );
19773
+ const userPrompt = injectContracts(promptWithVariance, input.contract, resolvedAppGadgets);
19396
19774
  const dials = effortDials(input.profile?.effort);
19397
19775
  const effectiveMaxAttempts = dials?.maxAttempts ?? maxAttempts;
19398
19776
  const effectiveMaxEvalRounds = dials?.maxEvalRounds ?? maxEvalRounds;
@@ -19417,6 +19795,7 @@ function createUiGenerator(options = {}) {
19417
19795
  // `dispatchGeneration`.
19418
19796
  ...input.gadgetTypes !== void 0 ? { gadgetTypes: input.gadgetTypes } : {},
19419
19797
  enableRuntimeRender,
19798
+ ...runtimeRender !== void 0 ? { runtimeRender } : {},
19420
19799
  ...routeOverride !== void 0 ? { routeOverride } : {},
19421
19800
  ...onRetry !== void 0 ? { onRetry } : {},
19422
19801
  ...designMode !== void 0 ? { designMode } : {},
@@ -19428,6 +19807,8 @@ function createUiGenerator(options = {}) {
19428
19807
  ...input.fixtureProps !== void 0 ? { fixtureProps: input.fixtureProps } : {}
19429
19808
  });
19430
19809
  onGenerated?.(result);
19810
+ const runtimeProbe = metadataRuntimeProbe(result);
19811
+ const evalMs = metadataEvalMs(result);
19431
19812
  const metadata = {
19432
19813
  provider: input.llm.provider,
19433
19814
  generator: identity.slug,
@@ -19443,7 +19824,11 @@ function createUiGenerator(options = {}) {
19443
19824
  // them; others omit). Pass through truthfully — absent on the
19444
19825
  // adapter result stays absent here, never defaulted to 0.
19445
19826
  ...result.cacheReadTokens !== void 0 ? { cacheReadTokens: result.cacheReadTokens } : {},
19446
- ...result.cacheCreationTokens !== void 0 ? { cacheCreationTokens: result.cacheCreationTokens } : {}
19827
+ ...result.cacheCreationTokens !== void 0 ? { cacheCreationTokens: result.cacheCreationTokens } : {},
19828
+ // ggui#1380 — the runtime probe's record and the eval rounds' wall,
19829
+ // when the harness ran a round; absent stays absent.
19830
+ ...runtimeProbe !== void 0 ? { runtimeProbe } : {},
19831
+ ...evalMs !== void 0 ? { evalMs } : {}
19447
19832
  };
19448
19833
  return {
19449
19834
  ok: true,
@@ -19490,9 +19875,7 @@ function createUiGenerator(options = {}) {
19490
19875
  function resolveIdentity(opts) {
19491
19876
  const hasSlug = typeof opts.slug === "string" && opts.slug.length > 0;
19492
19877
  if (hasSlug && opts.tier !== void 0) {
19493
- throw new Error(
19494
- "createUiGenerator: pass either { slug } or { tier } \u2014 not both."
19495
- );
19878
+ throw new Error("createUiGenerator: pass either { slug } or { tier } \u2014 not both.");
19496
19879
  }
19497
19880
  const model = opts.model ?? DEFAULT_MODEL;
19498
19881
  if (hasSlug) {
@@ -19514,6 +19897,32 @@ function resolveIdentity(opts) {
19514
19897
  }
19515
19898
  return { slug, tier, model };
19516
19899
  }
19900
+ function metadataProbeOutcome(meta) {
19901
+ const elapsed = {
19902
+ ...meta.elapsedMs !== void 0 ? { elapsedMs: meta.elapsedMs } : {},
19903
+ ...meta.queuedMs !== void 0 ? { queuedMs: meta.queuedMs } : {}
19904
+ };
19905
+ if (meta.status !== "ran") return { status: meta.status, ...elapsed };
19906
+ if (meta.verdict === "pass") return { status: "ran", verdict: "pass", ...elapsed };
19907
+ return { status: "ran", verdict: "fail", failChecks: meta.failChecks, ...elapsed };
19908
+ }
19909
+ function metadataProbeRepair(repair) {
19910
+ return repair.compiled ? { attempted: true, compiled: true, trigger: metadataProbeOutcome(repair.trigger) } : { attempted: true, compiled: false };
19911
+ }
19912
+ function metadataRuntimeProbe(result) {
19913
+ const probe = result.evalResult?.runtimeProbe;
19914
+ if (probe === void 0) return void 0;
19915
+ const repair = result.evalResult?.runtimeProbeRepair;
19916
+ return {
19917
+ ...metadataProbeOutcome(probe),
19918
+ ...repair !== void 0 ? { repair: metadataProbeRepair(repair) } : {}
19919
+ };
19920
+ }
19921
+ function metadataEvalMs(result) {
19922
+ const breakdown = result.breakdown;
19923
+ if (breakdown === void 0 || breakdown.evalRounds === 0) return void 0;
19924
+ return breakdown.evalMs;
19925
+ }
19517
19926
  function mapLlmProviderToDispatchProvider(provider) {
19518
19927
  switch (provider) {
19519
19928
  case "anthropic":
@@ -19708,8 +20117,9 @@ function stringifyError(err) {
19708
20117
  // src/index.ts
19709
20118
  init_design_mode();
19710
20119
  init_client();
20120
+ init_claude_code_login();
19711
20121
  init_styling_profile();
19712
20122
 
19713
- export { AESTHETIC_BRIEF_MAX_CHARS, CANVAS_BREAKPOINTS, CANVAS_CLASSES, CANVAS_DESCRIPTORS, CANVAS_VIEWPORTS, CompileComponentCodeError, DEFAULT_DESIGN_MODE, DESIGN_MODES, EFFORT_DIALS, EFFORT_TABLE_VERSION, buildContractsContext, buildRenderingContext, buildVarianceContext, canvasDisplayModeForShell, canvasForRendering, canvasForRenderingContext, canvasForViewportWidth, compileComponentCode, createAnthropicClient, createUiGenerator, describeCanvas, effortDials, extractComponentCode, injectContracts, injectRenderingContext, injectVariance, withBrowserCompile };
20123
+ export { AESTHETIC_BRIEF_MAX_CHARS, CANVAS_BREAKPOINTS, CANVAS_CLASSES, CANVAS_DESCRIPTORS, CANVAS_VIEWPORTS, CLAUDE_CODE_LOGIN_CREDENTIAL, CompileComponentCodeError, DEFAULT_DESIGN_MODE, DESIGN_MODES, EFFORT_DIALS, EFFORT_TABLE_VERSION, buildContractsContext, buildRenderingContext, buildVarianceContext, canvasDisplayModeForShell, canvasForRendering, canvasForRenderingContext, canvasForViewportWidth, compileComponentCode, createAnthropicClient, createUiGenerator, describeCanvas, effortDials, extractComponentCode, injectContracts, injectRenderingContext, injectVariance, withBrowserCompile };
19714
20124
  //# sourceMappingURL=index.js.map
19715
20125
  //# sourceMappingURL=index.js.map