@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.
- package/dist/adapter-Du7P_5Fx.d.ts +201 -0
- package/dist/adapters/generation-dispatch.d.ts +33 -12
- package/dist/adapters/generation-dispatch.js +510 -128
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +5 -3
- package/dist/advanced/index.js +551 -142
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +153 -59
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +2 -1
- package/dist/check/index.js +47 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-GIhzx08x.d.ts} +34 -10
- package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-BnAH56p4.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +4 -3
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -1
- package/dist/evaluation/index.d.ts +4 -3
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +29 -10
- package/dist/harness/check/runtime-render/index.js +203 -60
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +2 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +2 -1
- package/dist/harness/runtime.d.ts +2 -1
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +11 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -1
- package/dist/index.d.ts +38 -3
- package/dist/index.js +553 -143
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +2 -1
- package/dist/policy.d.ts +7 -4
- package/dist/policy.js.map +1 -1
- package/dist/tools/render-check-worker.d.ts +7 -1
- package/dist/tools/render-check-worker.js +55 -23
- package/dist/tools/render-check-worker.js.map +1 -1
- package/dist/{types-public-Ow-tNtFE.d.ts → types-public-Bk6d1WLN.d.ts} +77 -11
- package/dist/workflows.d.ts +2 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +137 -96
- package/src/check/run-tier0.ts +80 -1
- package/src/create-ui-generator.ts +185 -95
- package/src/evaluation/types-public.ts +73 -11
- package/src/harness/check/runtime-render/adapter.ts +221 -75
- package/src/harness/check/runtime-render/index.ts +75 -2
- package/src/harness/check/runtime-render/render-check-host.ts +82 -19
- package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
- package/src/harness/check/runtime-render/render-check.ts +154 -117
- package/src/harness/coding/generate-task-runner.ts +57 -4
- package/src/harness/coding/init-session.ts +13 -0
- package/src/harness/coding/run-eval-round.ts +238 -15
- package/src/harness/policy.ts +11 -0
- package/src/harness/primitive-slice.ts +104 -0
- package/src/harness/types-public.ts +9 -1
- package/src/index.ts +4 -0
- package/src/policy.ts +7 -4
- package/src/tools/render-check-worker.ts +74 -23
- 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 {
|
|
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:
|
|
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
|
|
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:
|
|
6603
|
+
timeoutMs: bounds.timeoutMs,
|
|
6604
|
+
gracePeriodMs: bounds.gracePeriodMs,
|
|
6533
6605
|
maxStdoutBytes: CHECK_STDOUT_CAP,
|
|
6534
|
-
nodeHeapMb:
|
|
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
|
|
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
|
-
|
|
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=\"© 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
|
-
|
|
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=\"© 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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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", {
|
|
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
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
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
|
-
}
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
|
|
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
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
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
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
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: ({
|
|
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
|
|
18739
|
-
// design) stay on the dispatch side of
|
|
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
|
-
|
|
18765
|
-
|
|
18766
|
-
|
|
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
|
-
|
|
18774
|
-
|
|
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
|
-
|
|
18782
|
-
|
|
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
|
|
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=${
|
|
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
|
-
|
|
18803
|
-
const indexBytes = Buffer.byteLength(
|
|
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
|
-
|
|
18813
|
-
|
|
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({
|
|
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
|
-
|
|
19387
|
-
|
|
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
|