@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifyUi, MODEL_REGISTRY, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1,
|
|
1
|
+
import { listContractGadgets, HOOK_NAME_RE, classifyUi, MODEL_REGISTRY, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1, STDLIB_GADGETS_PACKAGE, resolveAppGadgets, APP_GENERATION_PROFILE_BOUNDS, anthropicRejectsForcedToolChoice, normalizeAnthropicModelId, anthropicRejectsSamplingParams, isRecord } from '@ggui-ai/protocol';
|
|
2
2
|
import Anthropic from '@anthropic-ai/sdk';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { getCssTokens, fillFitRule, FORBIDDEN_IMPORT_SPECIFIERS } from '@ggui-ai/design/rendering';
|
|
@@ -2812,6 +2812,14 @@ function resolveRunPolicyForProfile(profile, harness, runtimeCtx) {
|
|
|
2812
2812
|
}
|
|
2813
2813
|
};
|
|
2814
2814
|
}
|
|
2815
|
+
if (profile === "ctx-slice-primitives-v2") {
|
|
2816
|
+
return {
|
|
2817
|
+
context: {
|
|
2818
|
+
...harness.policy.context,
|
|
2819
|
+
primitiveDocSlice: "axis-keyed+contract"
|
|
2820
|
+
}
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2815
2823
|
return resolveRunPolicy2(harness, runtimeCtx);
|
|
2816
2824
|
}
|
|
2817
2825
|
var init_policy2 = __esm({
|
|
@@ -3360,9 +3368,25 @@ var init_validation = __esm({
|
|
|
3360
3368
|
// src/harness/check/runtime-render/render-check-host.ts
|
|
3361
3369
|
var render_check_host_exports = {};
|
|
3362
3370
|
__export(render_check_host_exports, {
|
|
3371
|
+
DEFAULT_RENDER_CHECK_HOST_BOUNDS: () => DEFAULT_RENDER_CHECK_HOST_BOUNDS,
|
|
3363
3372
|
mapSandboxResultToCheckResult: () => mapSandboxResultToCheckResult,
|
|
3373
|
+
resolveRenderCheckHostBounds: () => resolveRenderCheckHostBounds,
|
|
3374
|
+
resolveWorkerSpawn: () => resolveWorkerSpawn,
|
|
3364
3375
|
runRenderCheckViaWorker: () => runRenderCheckViaWorker
|
|
3365
3376
|
});
|
|
3377
|
+
function resolveRenderCheckHostBounds(bounds = {}) {
|
|
3378
|
+
const timeoutMs = bounds.timeoutMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs;
|
|
3379
|
+
const heapMb = bounds.heapMb ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.heapMb;
|
|
3380
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 2) {
|
|
3381
|
+
throw new RangeError(`render-check: timeoutMs must be an integer \u2265 2, got ${timeoutMs}`);
|
|
3382
|
+
}
|
|
3383
|
+
if (!Number.isInteger(heapMb) || heapMb < 1) {
|
|
3384
|
+
throw new RangeError(`render-check: heapMb must be a positive integer, got ${heapMb}`);
|
|
3385
|
+
}
|
|
3386
|
+
const requestedGrace = bounds.gracePeriodMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.gracePeriodMs;
|
|
3387
|
+
const gracePeriodMs = Math.max(1, Math.min(requestedGrace, Math.floor(timeoutMs / 2)));
|
|
3388
|
+
return { timeoutMs, heapMb, gracePeriodMs };
|
|
3389
|
+
}
|
|
3366
3390
|
function resolveWorkerSpawn() {
|
|
3367
3391
|
const jsCandidates = [
|
|
3368
3392
|
// Sibling — when import.meta.url is already .../harness/check/runtime-render/.
|
|
@@ -3411,12 +3435,12 @@ function unverified(reason, t0) {
|
|
|
3411
3435
|
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
3412
3436
|
};
|
|
3413
3437
|
}
|
|
3414
|
-
function mapSandboxResultToCheckResult(result, t0) {
|
|
3438
|
+
function mapSandboxResultToCheckResult(result, t0, boundMs = DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs) {
|
|
3415
3439
|
if (result.outcome === "timeout") {
|
|
3416
3440
|
return {
|
|
3417
3441
|
ok: false,
|
|
3418
3442
|
issues: [],
|
|
3419
|
-
incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs
|
|
3443
|
+
incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs },
|
|
3420
3444
|
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
3421
3445
|
};
|
|
3422
3446
|
}
|
|
@@ -3461,8 +3485,9 @@ function mapSandboxResultToCheckResult(result, t0) {
|
|
|
3461
3485
|
);
|
|
3462
3486
|
}
|
|
3463
3487
|
}
|
|
3464
|
-
async function runRenderCheckViaWorker(input) {
|
|
3488
|
+
async function runRenderCheckViaWorker(input, options = {}) {
|
|
3465
3489
|
const t0 = Date.now();
|
|
3490
|
+
const bounds = resolveRenderCheckHostBounds(options.bounds);
|
|
3466
3491
|
let spawn;
|
|
3467
3492
|
try {
|
|
3468
3493
|
spawn = resolveWorkerSpawn();
|
|
@@ -3473,9 +3498,10 @@ async function runRenderCheckViaWorker(input) {
|
|
|
3473
3498
|
const result = await runSandboxed({
|
|
3474
3499
|
command: spawn.command,
|
|
3475
3500
|
args: spawn.args,
|
|
3476
|
-
timeoutMs:
|
|
3501
|
+
timeoutMs: bounds.timeoutMs,
|
|
3502
|
+
gracePeriodMs: bounds.gracePeriodMs,
|
|
3477
3503
|
maxStdoutBytes: CHECK_STDOUT_CAP,
|
|
3478
|
-
nodeHeapMb:
|
|
3504
|
+
nodeHeapMb: bounds.heapMb,
|
|
3479
3505
|
stdin: JSON.stringify(input),
|
|
3480
3506
|
// NODE_ENV steers React's production vs development build — keep
|
|
3481
3507
|
// parity with the caller; everything else stays on the sandbox's
|
|
@@ -3487,15 +3513,18 @@ async function runRenderCheckViaWorker(input) {
|
|
|
3487
3513
|
end: loadavg()[0] ?? 0,
|
|
3488
3514
|
cores: availableParallelism()
|
|
3489
3515
|
};
|
|
3490
|
-
const mapped = mapSandboxResultToCheckResult(result, t0);
|
|
3516
|
+
const mapped = mapSandboxResultToCheckResult(result, t0, bounds.timeoutMs);
|
|
3491
3517
|
return { ...mapped, stats: { ...mapped.stats, hostLoad } };
|
|
3492
3518
|
}
|
|
3493
|
-
var
|
|
3519
|
+
var DEFAULT_RENDER_CHECK_HOST_BOUNDS, CHECK_STDOUT_CAP, WORKER_BASENAME;
|
|
3494
3520
|
var init_render_check_host = __esm({
|
|
3495
3521
|
"src/harness/check/runtime-render/render-check-host.ts"() {
|
|
3496
|
-
|
|
3522
|
+
DEFAULT_RENDER_CHECK_HOST_BOUNDS = {
|
|
3523
|
+
timeoutMs: 3e4,
|
|
3524
|
+
heapMb: 512,
|
|
3525
|
+
gracePeriodMs: 2e3
|
|
3526
|
+
};
|
|
3497
3527
|
CHECK_STDOUT_CAP = 2 * 1024 * 1024;
|
|
3498
|
-
CHECK_NODE_HEAP_MB = 512;
|
|
3499
3528
|
WORKER_BASENAME = "render-check-worker";
|
|
3500
3529
|
}
|
|
3501
3530
|
});
|
|
@@ -4429,6 +4458,46 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode
|
|
|
4429
4458
|
visit(sf);
|
|
4430
4459
|
return issues;
|
|
4431
4460
|
}
|
|
4461
|
+
function suggestManifestTokens(name) {
|
|
4462
|
+
const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
|
|
4463
|
+
if (successor !== void 0) return [successor];
|
|
4464
|
+
const segments = name.split("-").filter((segment) => segment.length > 0);
|
|
4465
|
+
const own = name.endsWith("-") ? segments.length : segments.length - 1;
|
|
4466
|
+
for (let n = own; n >= 2; n--) {
|
|
4467
|
+
const prefix = `--${segments.slice(0, n).join("-")}-`;
|
|
4468
|
+
const family = consumedTokenManifest.filter((token) => token.startsWith(prefix));
|
|
4469
|
+
if (family.length > 0) return wordHitsFirst(name, rolesFirst(family));
|
|
4470
|
+
}
|
|
4471
|
+
const words = new Set(
|
|
4472
|
+
segments.slice(1).filter((segment) => segment.length >= 4).map((segment) => segment.toLowerCase())
|
|
4473
|
+
);
|
|
4474
|
+
return rolesFirst(
|
|
4475
|
+
consumedTokenManifest.filter(
|
|
4476
|
+
(token) => token.split("-").some((segment) => words.has(segment.toLowerCase()))
|
|
4477
|
+
)
|
|
4478
|
+
);
|
|
4479
|
+
}
|
|
4480
|
+
function wordHitsFirst(name, family) {
|
|
4481
|
+
const word = name.slice(name.lastIndexOf("-") + 1).toLowerCase();
|
|
4482
|
+
if (word.length < 4) return family;
|
|
4483
|
+
const hit = (token) => token.slice(token.lastIndexOf("-") + 1).toLowerCase().includes(word);
|
|
4484
|
+
return [...family.filter(hit), ...family.filter((token) => !hit(token))];
|
|
4485
|
+
}
|
|
4486
|
+
function rolesFirst(tokens) {
|
|
4487
|
+
const isRole = (token) => !/^\d+$/.test(token.slice(token.lastIndexOf("-") + 1));
|
|
4488
|
+
return [...tokens.filter(isRole), ...tokens.filter((token) => !isRole(token))];
|
|
4489
|
+
}
|
|
4490
|
+
function offManifestHint(name) {
|
|
4491
|
+
const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
|
|
4492
|
+
if (successor !== void 0) {
|
|
4493
|
+
return `"${name}" was retired (ggui#987); the current name is var(${successor}). `;
|
|
4494
|
+
}
|
|
4495
|
+
const near = suggestManifestTokens(name);
|
|
4496
|
+
if (near.length === 0) return "";
|
|
4497
|
+
const shown = near.slice(0, 8).map((token) => `var(${token})`).join(", ");
|
|
4498
|
+
const more = near.length > 8 ? ` (and ${near.length - 8} more in that family)` : "";
|
|
4499
|
+
return `Nearest names in the manifest: ${shown}${more}. `;
|
|
4500
|
+
}
|
|
4432
4501
|
function isGadgetExportImported(source, pkg, exportName) {
|
|
4433
4502
|
const esc = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4434
4503
|
const re = new RegExp(
|
|
@@ -4651,7 +4720,7 @@ ${errorDetail}`,
|
|
|
4651
4720
|
subcategory: "off-manifest-token",
|
|
4652
4721
|
severity: "critical",
|
|
4653
4722
|
description: `Token "${name}" is not in the design system's token manifest \u2014 no theme defines it, so it renders unset.`,
|
|
4654
|
-
fix:
|
|
4723
|
+
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.`,
|
|
4655
4724
|
line: lineNum
|
|
4656
4725
|
});
|
|
4657
4726
|
}
|
|
@@ -5168,7 +5237,7 @@ async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE)
|
|
|
5168
5237
|
}
|
|
5169
5238
|
return { issues, pass };
|
|
5170
5239
|
}
|
|
5171
|
-
var MANIFEST_TOKENS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
|
|
5240
|
+
var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
|
|
5172
5241
|
var init_run_tier0 = __esm({
|
|
5173
5242
|
"src/check/run-tier0.ts"() {
|
|
5174
5243
|
init_design_mode();
|
|
@@ -5179,6 +5248,12 @@ var init_run_tier0 = __esm({
|
|
|
5179
5248
|
init_extract_wire_calls();
|
|
5180
5249
|
init_types_public();
|
|
5181
5250
|
MANIFEST_TOKENS = new Set(consumedTokenManifest);
|
|
5251
|
+
RETIRED_TOKEN_SUCCESSORS = /* @__PURE__ */ new Map([
|
|
5252
|
+
["--ggui-color-surface", "--ggui-color-container"],
|
|
5253
|
+
["--ggui-color-onSurface", "--ggui-color-onContainer"],
|
|
5254
|
+
["--ggui-color-surfaceVariant", "--ggui-color-sunken"],
|
|
5255
|
+
["--ggui-color-onSurfaceVariant", "--ggui-color-onSunken"]
|
|
5256
|
+
]);
|
|
5182
5257
|
TOKEN_REF_RE = /var\(\s*(--ggui-[a-zA-Z0-9-]+)/g;
|
|
5183
5258
|
HOOK_NAME_FOR = {
|
|
5184
5259
|
action: "useAction",
|
|
@@ -7020,11 +7095,25 @@ Respond with ONLY a JSON object (no markdown, no explanation):
|
|
|
7020
7095
|
}
|
|
7021
7096
|
});
|
|
7022
7097
|
|
|
7098
|
+
// src/tools/get-primitives-ts.ts
|
|
7099
|
+
var get_primitives_ts_exports = {};
|
|
7100
|
+
__export(get_primitives_ts_exports, {
|
|
7101
|
+
PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
|
|
7102
|
+
});
|
|
7103
|
+
var PRIMITIVES_DOCUMENTATION_TS;
|
|
7104
|
+
var init_get_primitives_ts = __esm({
|
|
7105
|
+
"src/tools/get-primitives-ts.ts"() {
|
|
7106
|
+
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";
|
|
7107
|
+
}
|
|
7108
|
+
});
|
|
7109
|
+
|
|
7023
7110
|
// src/harness/primitive-slice.ts
|
|
7024
7111
|
var primitive_slice_exports = {};
|
|
7025
7112
|
__export(primitive_slice_exports, {
|
|
7113
|
+
computeContractPrimitives: () => computeContractPrimitives,
|
|
7026
7114
|
computePrimitiveAllowlist: () => computePrimitiveAllowlist,
|
|
7027
|
-
slicePrimitiveDocumentation: () => slicePrimitiveDocumentation
|
|
7115
|
+
slicePrimitiveDocumentation: () => slicePrimitiveDocumentation,
|
|
7116
|
+
slicePrimitiveDocumentationTs: () => slicePrimitiveDocumentationTs
|
|
7028
7117
|
});
|
|
7029
7118
|
function computePrimitiveAllowlist(classification) {
|
|
7030
7119
|
const set = new Set(CORE_PRIMITIVES);
|
|
@@ -7044,6 +7133,42 @@ function computePrimitiveAllowlist(classification) {
|
|
|
7044
7133
|
}
|
|
7045
7134
|
return [...set].sort();
|
|
7046
7135
|
}
|
|
7136
|
+
function computeContractPrimitives(contract) {
|
|
7137
|
+
const out = /* @__PURE__ */ new Set();
|
|
7138
|
+
if (!contract) return [];
|
|
7139
|
+
for (const slot of Object.values(contract.contextSpec ?? {})) addLeaves(slot.schema, out);
|
|
7140
|
+
for (const action of Object.values(contract.actionSpec ?? {})) {
|
|
7141
|
+
if (action.schema) addLeaves(action.schema, out);
|
|
7142
|
+
}
|
|
7143
|
+
if (Object.keys(contract.streamSpec ?? {}).length > 0) {
|
|
7144
|
+
out.add("Spinner");
|
|
7145
|
+
out.add("Badge");
|
|
7146
|
+
}
|
|
7147
|
+
return [...out].sort();
|
|
7148
|
+
}
|
|
7149
|
+
function addLeaves(schema, out) {
|
|
7150
|
+
for (const child of Object.values(schema.properties ?? {})) addLeaves(child, out);
|
|
7151
|
+
if (schema.items) addLeaves(schema.items, out);
|
|
7152
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
7153
|
+
out.add("Select");
|
|
7154
|
+
out.add("RadioGroup");
|
|
7155
|
+
return;
|
|
7156
|
+
}
|
|
7157
|
+
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
7158
|
+
for (const t of types) {
|
|
7159
|
+
if (t === "string") {
|
|
7160
|
+
out.add("Input");
|
|
7161
|
+
out.add("TextArea");
|
|
7162
|
+
out.add("FormField");
|
|
7163
|
+
} else if (t === "number" || t === "integer") {
|
|
7164
|
+
out.add("Input");
|
|
7165
|
+
out.add("Slider");
|
|
7166
|
+
} else if (t === "boolean") {
|
|
7167
|
+
out.add("Checkbox");
|
|
7168
|
+
out.add("Toggle");
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7047
7172
|
function slicePrimitiveDocumentation(fullDoc, allowlist) {
|
|
7048
7173
|
const allow = new Set(allowlist);
|
|
7049
7174
|
const firstSection = fullDoc.search(/^### \S/m);
|
|
@@ -7067,7 +7192,26 @@ function slicePrimitiveDocumentation(fullDoc, allowlist) {
|
|
|
7067
7192
|
}
|
|
7068
7193
|
return preamble + kept.join("");
|
|
7069
7194
|
}
|
|
7070
|
-
|
|
7195
|
+
function slicePrimitiveDocumentationTs(fullDoc, allowlist) {
|
|
7196
|
+
const allow = new Set(allowlist);
|
|
7197
|
+
const lines = fullDoc.split("\n");
|
|
7198
|
+
let sawHeader = false;
|
|
7199
|
+
let keep = true;
|
|
7200
|
+
const kept = [];
|
|
7201
|
+
for (const line of lines) {
|
|
7202
|
+
const header = TS_SECTION_HEADER.exec(line);
|
|
7203
|
+
if (header !== null) {
|
|
7204
|
+
sawHeader = true;
|
|
7205
|
+
const name = header[1];
|
|
7206
|
+
keep = !KNOWN_PRIMITIVE_SECTIONS.has(name) || allow.has(name);
|
|
7207
|
+
} else if (TS_SECTION_BANNER.test(line)) {
|
|
7208
|
+
keep = true;
|
|
7209
|
+
}
|
|
7210
|
+
if (keep) kept.push(line);
|
|
7211
|
+
}
|
|
7212
|
+
return sawHeader ? kept.join("\n") : fullDoc;
|
|
7213
|
+
}
|
|
7214
|
+
var CORE_PRIMITIVES, KNOWN_PRIMITIVE_SECTIONS, AXIS_PRIMITIVES, TS_SECTION_HEADER, TS_SECTION_BANNER;
|
|
7071
7215
|
var init_primitive_slice = __esm({
|
|
7072
7216
|
"src/harness/primitive-slice.ts"() {
|
|
7073
7217
|
CORE_PRIMITIVES = [
|
|
@@ -7200,6 +7344,8 @@ var init_primitive_slice = __esm({
|
|
|
7200
7344
|
"tooling:both": ["Alert"],
|
|
7201
7345
|
"tooling:none": []
|
|
7202
7346
|
};
|
|
7347
|
+
TS_SECTION_HEADER = /^\/\/ ([A-Za-z]+) — /;
|
|
7348
|
+
TS_SECTION_BANNER = /^\/\/ ═/;
|
|
7203
7349
|
}
|
|
7204
7350
|
});
|
|
7205
7351
|
|
|
@@ -7338,18 +7484,6 @@ var init_primitive_index = __esm({
|
|
|
7338
7484
|
}
|
|
7339
7485
|
});
|
|
7340
7486
|
|
|
7341
|
-
// src/tools/get-primitives-ts.ts
|
|
7342
|
-
var get_primitives_ts_exports = {};
|
|
7343
|
-
__export(get_primitives_ts_exports, {
|
|
7344
|
-
PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
|
|
7345
|
-
});
|
|
7346
|
-
var PRIMITIVES_DOCUMENTATION_TS;
|
|
7347
|
-
var init_get_primitives_ts = __esm({
|
|
7348
|
-
"src/tools/get-primitives-ts.ts"() {
|
|
7349
|
-
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";
|
|
7350
|
-
}
|
|
7351
|
-
});
|
|
7352
|
-
|
|
7353
7487
|
// src/classifier/inspect.ts
|
|
7354
7488
|
function resolveSchema(node) {
|
|
7355
7489
|
if (!node || typeof node !== "object") return void 0;
|
|
@@ -13103,13 +13237,22 @@ function editableSurface(el) {
|
|
|
13103
13237
|
function parentLink(node) {
|
|
13104
13238
|
return node;
|
|
13105
13239
|
}
|
|
13106
|
-
|
|
13240
|
+
var RENDER_CHECK_KINDS = [
|
|
13241
|
+
"render-no-throw",
|
|
13242
|
+
"prop-sensitivity",
|
|
13243
|
+
"action-wiring",
|
|
13244
|
+
"selection-identity",
|
|
13245
|
+
"prop-coverage",
|
|
13246
|
+
"optional-props-omitted",
|
|
13247
|
+
"stream-rerender"
|
|
13248
|
+
];
|
|
13249
|
+
async function runRenderCheck(input, options = {}) {
|
|
13107
13250
|
const g = hostGlobals();
|
|
13108
13251
|
if ("window" in g && "document" in g) {
|
|
13109
13252
|
return runRenderCheckInProcess(input);
|
|
13110
13253
|
}
|
|
13111
13254
|
const { runRenderCheckViaWorker: runRenderCheckViaWorker2 } = await Promise.resolve().then(() => (init_render_check_host(), render_check_host_exports));
|
|
13112
|
-
return runRenderCheckViaWorker2(input);
|
|
13255
|
+
return runRenderCheckViaWorker2(input, options);
|
|
13113
13256
|
}
|
|
13114
13257
|
async function runRenderCheckInProcess(input) {
|
|
13115
13258
|
const t0 = Date.now();
|
|
@@ -13123,7 +13266,7 @@ async function runRenderCheckInProcess(input) {
|
|
|
13123
13266
|
const Wire = await import('@ggui-ai/wire');
|
|
13124
13267
|
const { GguiWireProvider } = Wire;
|
|
13125
13268
|
const moduleResolutions = {
|
|
13126
|
-
|
|
13269
|
+
react: React,
|
|
13127
13270
|
"react/jsx-runtime": ReactJsxRuntime,
|
|
13128
13271
|
"@ggui-ai/wire": Wire
|
|
13129
13272
|
};
|
|
@@ -13239,14 +13382,23 @@ async function runRenderCheckInProcess(input) {
|
|
|
13239
13382
|
)
|
|
13240
13383
|
);
|
|
13241
13384
|
const timeoutPromise = new Promise((_, reject) => {
|
|
13242
|
-
setTimeout(
|
|
13385
|
+
setTimeout(
|
|
13386
|
+
() => reject(
|
|
13387
|
+
new Error(
|
|
13388
|
+
`[runtime-render] render exceeded ${RENDER_TIMEOUT_MS}ms \u2014 classified as runtime-hang`
|
|
13389
|
+
)
|
|
13390
|
+
),
|
|
13391
|
+
RENDER_TIMEOUT_MS
|
|
13392
|
+
).unref?.();
|
|
13243
13393
|
});
|
|
13244
13394
|
renderResult = await Promise.race([renderPromise, timeoutPromise]);
|
|
13245
13395
|
} catch (e) {
|
|
13246
13396
|
const msg = e instanceof Error ? e.message : String(e);
|
|
13247
13397
|
const errStack = e instanceof Error ? e.stack ?? "" : "";
|
|
13248
13398
|
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_MS}ms \u2014 likely async effect loop or unresolved promise` : `Render threw: ${msg}`;
|
|
13249
|
-
const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter(
|
|
13399
|
+
const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter(
|
|
13400
|
+
(l) => !/(node_modules|happy-dom|@testing-library|react-dom|node:internal|node:async)/.test(l)
|
|
13401
|
+
).slice(0, 5);
|
|
13250
13402
|
const stackSource = boundaryRef.stack ?? capturedComponentStack ?? "";
|
|
13251
13403
|
const componentFrames = stackSource.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).slice(0, 4);
|
|
13252
13404
|
const stackBlock = userFrames.length ? `
|
|
@@ -13711,9 +13863,7 @@ function idLikeRequiredStringField(schema) {
|
|
|
13711
13863
|
const required = schema["required"];
|
|
13712
13864
|
if (properties === null || typeof properties !== "object") return null;
|
|
13713
13865
|
if (!Array.isArray(required)) return null;
|
|
13714
|
-
for (const [propName, propSchema] of Object.entries(
|
|
13715
|
-
properties
|
|
13716
|
-
)) {
|
|
13866
|
+
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
13717
13867
|
if (!/id$/i.test(propName)) continue;
|
|
13718
13868
|
if (!required.includes(propName)) continue;
|
|
13719
13869
|
const t = propSchema !== null && typeof propSchema === "object" ? propSchema["type"] : void 0;
|
|
@@ -13744,9 +13894,7 @@ async function checkSelectionIdentity(input) {
|
|
|
13744
13894
|
return void 0;
|
|
13745
13895
|
}
|
|
13746
13896
|
await flushPromises();
|
|
13747
|
-
const events = probe.getFireLog().slice(before).filter(
|
|
13748
|
-
(e) => e.kind === "action.fired" && e.name === actionName
|
|
13749
|
-
);
|
|
13897
|
+
const events = probe.getFireLog().slice(before).filter((e) => e.kind === "action.fired" && e.name === actionName);
|
|
13750
13898
|
return events.length > 0 ? events[events.length - 1].payload : void 0;
|
|
13751
13899
|
};
|
|
13752
13900
|
const first = await payloadFor(group[0]);
|
|
@@ -13807,7 +13955,10 @@ async function dispatchTrigger(el, kind, user) {
|
|
|
13807
13955
|
case "submit": {
|
|
13808
13956
|
const form = closestForm(el);
|
|
13809
13957
|
if (!form) return;
|
|
13810
|
-
const ev = new globalThis.Event("submit", {
|
|
13958
|
+
const ev = new globalThis.Event("submit", {
|
|
13959
|
+
bubbles: true,
|
|
13960
|
+
cancelable: true
|
|
13961
|
+
});
|
|
13811
13962
|
toEventDispatcher(form).dispatchEvent(ev);
|
|
13812
13963
|
return;
|
|
13813
13964
|
}
|
|
@@ -14012,6 +14163,9 @@ function flushPromises() {
|
|
|
14012
14163
|
return new Promise((resolve4) => setTimeout(resolve4, 0));
|
|
14013
14164
|
}
|
|
14014
14165
|
|
|
14166
|
+
// src/harness/check/runtime-render/adapter.ts
|
|
14167
|
+
init_render_check_host();
|
|
14168
|
+
|
|
14015
14169
|
// src/harness/check/runtime-render/prepare-mockup.ts
|
|
14016
14170
|
function prepareMockupProps(input) {
|
|
14017
14171
|
const { contract, fixtureProps } = input;
|
|
@@ -14144,47 +14298,111 @@ function synthesizeFromSchema(schema, hint, warnings, depth, ordinal) {
|
|
|
14144
14298
|
}
|
|
14145
14299
|
|
|
14146
14300
|
// src/harness/check/runtime-render/adapter.ts
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14301
|
+
function createSlotLimiter(maxConcurrent) {
|
|
14302
|
+
if (maxConcurrent === void 0) return (task) => task(0);
|
|
14303
|
+
if (!Number.isInteger(maxConcurrent) || maxConcurrent < 1) {
|
|
14304
|
+
throw new RangeError(
|
|
14305
|
+
`createRuntimeRenderCheck: maxConcurrent must be a positive integer, got ${maxConcurrent}`
|
|
14306
|
+
);
|
|
14307
|
+
}
|
|
14308
|
+
let active = 0;
|
|
14309
|
+
const waiting = [];
|
|
14310
|
+
const acquire = () => {
|
|
14311
|
+
if (active < maxConcurrent) {
|
|
14312
|
+
active += 1;
|
|
14313
|
+
return Promise.resolve(0);
|
|
14314
|
+
}
|
|
14315
|
+
const enqueuedAt = Date.now();
|
|
14316
|
+
return new Promise((resolve4) => {
|
|
14317
|
+
waiting.push(() => {
|
|
14318
|
+
active += 1;
|
|
14319
|
+
resolve4(Date.now() - enqueuedAt);
|
|
14164
14320
|
});
|
|
14165
|
-
}
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14321
|
+
});
|
|
14322
|
+
};
|
|
14323
|
+
const release = () => {
|
|
14324
|
+
active -= 1;
|
|
14325
|
+
const next = waiting.shift();
|
|
14326
|
+
if (next !== void 0) next();
|
|
14327
|
+
};
|
|
14328
|
+
return async (task) => {
|
|
14329
|
+
const waitedMs = await acquire();
|
|
14330
|
+
try {
|
|
14331
|
+
return await task(waitedMs);
|
|
14332
|
+
} finally {
|
|
14333
|
+
release();
|
|
14171
14334
|
}
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14335
|
+
};
|
|
14336
|
+
}
|
|
14337
|
+
function queuedField(queuedMs) {
|
|
14338
|
+
return queuedMs > 0 ? { queuedMs } : {};
|
|
14339
|
+
}
|
|
14340
|
+
function toRenderCheckOptions(config) {
|
|
14341
|
+
if (config.timeoutMs === void 0 && config.heapMb === void 0) return void 0;
|
|
14342
|
+
return {
|
|
14343
|
+
bounds: {
|
|
14344
|
+
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {},
|
|
14345
|
+
...config.heapMb !== void 0 ? { heapMb: config.heapMb } : {}
|
|
14180
14346
|
}
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14347
|
+
};
|
|
14348
|
+
}
|
|
14349
|
+
function createRuntimeRenderCheck(config = {}) {
|
|
14350
|
+
const limit = createSlotLimiter(config.maxConcurrent);
|
|
14351
|
+
const options = toRenderCheckOptions(config);
|
|
14352
|
+
if (options !== void 0) resolveRenderCheckHostBounds(options.bounds);
|
|
14353
|
+
return {
|
|
14354
|
+
id: "runtime-render",
|
|
14355
|
+
run: async (input) => {
|
|
14356
|
+
const { sourceCode, compiledCode, contract, fixtureProps } = input;
|
|
14357
|
+
if (compiledCode === null) {
|
|
14358
|
+
return { status: "not-applicable", issues: [], reason: "no compiled code" };
|
|
14359
|
+
}
|
|
14360
|
+
if (!contract) {
|
|
14361
|
+
return { status: "not-applicable", issues: [], reason: "no contract surface" };
|
|
14362
|
+
}
|
|
14363
|
+
const mockup = prepareMockupProps({ contract, fixtureProps });
|
|
14364
|
+
let queuedMs = 0;
|
|
14365
|
+
let t0 = Date.now();
|
|
14366
|
+
let result;
|
|
14367
|
+
try {
|
|
14368
|
+
result = await limit((waitedMs) => {
|
|
14369
|
+
queuedMs = waitedMs;
|
|
14370
|
+
t0 = Date.now();
|
|
14371
|
+
return runRenderCheck(
|
|
14372
|
+
{
|
|
14373
|
+
sourceCode,
|
|
14374
|
+
mockupProps: mockup.props,
|
|
14375
|
+
contract
|
|
14376
|
+
},
|
|
14377
|
+
options
|
|
14378
|
+
);
|
|
14379
|
+
});
|
|
14380
|
+
} catch (e) {
|
|
14381
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
14382
|
+
console.warn(`[runtime-render] probe skipped \u2014 infra failure: ${message}`);
|
|
14383
|
+
return { status: "infra-skipped", issues: [], reason: message, elapsedMs: Date.now() - t0, ...queuedField(queuedMs) };
|
|
14384
|
+
}
|
|
14385
|
+
const hostLoad = result.stats.hostLoad;
|
|
14386
|
+
const load = hostLoad !== void 0 ? { hostLoad } : {};
|
|
14387
|
+
if (result.incomplete !== void 0) {
|
|
14388
|
+
const { elapsedMs, boundMs } = result.incomplete;
|
|
14389
|
+
const loadNote = hostLoad !== void 0 ? `; host load ${hostLoad.start.toFixed(1)} \u2192 ${hostLoad.end.toFixed(1)} on ${hostLoad.cores} CPUs` : "";
|
|
14390
|
+
const reason = `render check did not finish within ${boundMs} ms (stopped at ${elapsedMs} ms)${loadNote}`;
|
|
14391
|
+
console.warn(`[runtime-render] probe timed out \u2014 ${reason}`);
|
|
14392
|
+
return { status: "timed-out", issues: [], reason, elapsedMs: Date.now() - t0, ...queuedField(queuedMs), ...load };
|
|
14393
|
+
}
|
|
14394
|
+
return {
|
|
14395
|
+
status: "ran",
|
|
14396
|
+
issues: result.issues.map(toEvalIssue).filter((x) => x !== null),
|
|
14397
|
+
elapsedMs: Date.now() - t0,
|
|
14398
|
+
...queuedField(queuedMs),
|
|
14399
|
+
renderMs: result.stats.renderMs,
|
|
14400
|
+
...load
|
|
14401
|
+
};
|
|
14402
|
+
}
|
|
14403
|
+
};
|
|
14404
|
+
}
|
|
14405
|
+
var DEFAULT_RUNTIME_RENDER_CHECK = createRuntimeRenderCheck();
|
|
14188
14406
|
var CLASS_SPECIFIC_PATTERNS = [
|
|
14189
14407
|
/too many re-renders/i,
|
|
14190
14408
|
/maximum update depth/i,
|
|
@@ -14223,6 +14441,14 @@ function classifyRenderCrashFix(reason) {
|
|
|
14223
14441
|
}
|
|
14224
14442
|
return "Add null guards on optional props, handle empty arrays/strings, and verify all hook outputs before destructuring.";
|
|
14225
14443
|
}
|
|
14444
|
+
var RUNTIME_SUBCATEGORY_PREFIX = "runtime:";
|
|
14445
|
+
function parseRuntimeSubcategory(subcategory) {
|
|
14446
|
+
if (!subcategory.startsWith(RUNTIME_SUBCATEGORY_PREFIX)) return void 0;
|
|
14447
|
+
const rest = subcategory.slice(RUNTIME_SUBCATEGORY_PREFIX.length);
|
|
14448
|
+
const separator = rest.indexOf(":");
|
|
14449
|
+
const check = separator === -1 ? rest : rest.slice(0, separator);
|
|
14450
|
+
return RENDER_CHECK_KINDS.find((kind) => kind === check);
|
|
14451
|
+
}
|
|
14226
14452
|
function toEvalIssue(issue) {
|
|
14227
14453
|
if (issue.outcome === "verified" || issue.outcome === "skipped") return null;
|
|
14228
14454
|
const result = issue.outcome === "failed" ? "fail" : "warn";
|
|
@@ -15472,6 +15698,14 @@ function formatFitFeedback(issue) {
|
|
|
15472
15698
|
Fix: ${issue.fix ?? ""}`;
|
|
15473
15699
|
}
|
|
15474
15700
|
var EVAL_ROUND_THREW_REASON = "eval round threw";
|
|
15701
|
+
var PROBE_ONLY_CRITERIA_REASON = "probe-only round: no evaluator configured";
|
|
15702
|
+
var PROBE_ONLY_VISUAL_REASON = "probe-only round: no visual evaluator configured";
|
|
15703
|
+
var PROBE_ONLY_PASS = "probe-only";
|
|
15704
|
+
var COST_TRACKER_ABSENT_REASON = "cost tracker absent on an evaluation round";
|
|
15705
|
+
function requireCostTracker(costTracker) {
|
|
15706
|
+
if (costTracker === null) throw new Error(COST_TRACKER_ABSENT_REASON);
|
|
15707
|
+
return costTracker;
|
|
15708
|
+
}
|
|
15475
15709
|
function runVisualLeg(visualEvalAgent, run, measureFit) {
|
|
15476
15710
|
const provider = visionJudgeProvider(visualEvalAgent.provider);
|
|
15477
15711
|
if (provider === void 0) {
|
|
@@ -15514,6 +15748,36 @@ function fingerprintFail(issue) {
|
|
|
15514
15748
|
const desc = (issue.description ?? "").toLowerCase().replace(/line\s+\d+/g, "line N").replace(/\s+/g, " ").trim().slice(0, 40);
|
|
15515
15749
|
return `${cat}|${sub}|${desc}`;
|
|
15516
15750
|
}
|
|
15751
|
+
function failedCheckKinds(probeIssues) {
|
|
15752
|
+
const failed2 = /* @__PURE__ */ new Set();
|
|
15753
|
+
for (const issue of probeIssues) {
|
|
15754
|
+
if (issue.result !== "fail" || issue.subcategory === void 0) continue;
|
|
15755
|
+
const kind = parseRuntimeSubcategory(issue.subcategory);
|
|
15756
|
+
if (kind !== void 0) failed2.add(kind);
|
|
15757
|
+
}
|
|
15758
|
+
return RENDER_CHECK_KINDS.filter((kind) => failed2.has(kind));
|
|
15759
|
+
}
|
|
15760
|
+
function probeMetaOf(outcome, probeIssues) {
|
|
15761
|
+
const detail = {
|
|
15762
|
+
...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
|
|
15763
|
+
...outcome.renderMs !== void 0 ? { renderMs: outcome.renderMs } : {},
|
|
15764
|
+
...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {},
|
|
15765
|
+
...outcome.queuedMs !== void 0 ? { queuedMs: outcome.queuedMs } : {}
|
|
15766
|
+
};
|
|
15767
|
+
const elapsed = outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {};
|
|
15768
|
+
if (outcome.status !== "ran") {
|
|
15769
|
+
return { status: outcome.status, ...elapsed, ...detail };
|
|
15770
|
+
}
|
|
15771
|
+
const [first, ...rest] = failedCheckKinds(probeIssues);
|
|
15772
|
+
if (first === void 0) {
|
|
15773
|
+
return { status: "ran", verdict: "pass", ...elapsed, ...detail };
|
|
15774
|
+
}
|
|
15775
|
+
return { status: "ran", verdict: "fail", failChecks: [first, ...rest], ...elapsed, ...detail };
|
|
15776
|
+
}
|
|
15777
|
+
function describeProbeMeta(meta) {
|
|
15778
|
+
if (meta.verdict === void 0) return meta.status;
|
|
15779
|
+
return meta.verdict === "pass" ? "ran, verdict pass" : `ran, verdict fail (${meta.failChecks.join(", ")})`;
|
|
15780
|
+
}
|
|
15517
15781
|
async function runProbeAtExit(input) {
|
|
15518
15782
|
const { harness, sourceCode, compiledCode, contract, fixtureProps } = input;
|
|
15519
15783
|
const probe = harness.check.runtimeRender;
|
|
@@ -15546,12 +15810,7 @@ async function runProbeAtExit(input) {
|
|
|
15546
15810
|
meta: { status: "infra-skipped", reason: message }
|
|
15547
15811
|
};
|
|
15548
15812
|
}
|
|
15549
|
-
const meta =
|
|
15550
|
-
status: outcome.status,
|
|
15551
|
-
...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
|
|
15552
|
-
...outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {},
|
|
15553
|
-
...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {}
|
|
15554
|
-
};
|
|
15813
|
+
const meta = probeMetaOf(outcome, outcome.issues);
|
|
15555
15814
|
if (outcome.status !== "ran") {
|
|
15556
15815
|
return { fired: false, recoverableFail: false, probeIssues: [], meta };
|
|
15557
15816
|
}
|
|
@@ -15594,11 +15853,13 @@ async function runEvalRound(ctx, input) {
|
|
|
15594
15853
|
visualThreshold,
|
|
15595
15854
|
qualityMode,
|
|
15596
15855
|
maxEvalRounds,
|
|
15597
|
-
costTracker,
|
|
15598
15856
|
llmEvalMod,
|
|
15599
15857
|
visualMod,
|
|
15600
15858
|
preWarmPromise,
|
|
15601
|
-
onProgress
|
|
15859
|
+
onProgress,
|
|
15860
|
+
probeOnly,
|
|
15861
|
+
probeRepairUsed,
|
|
15862
|
+
probeRepairTrigger
|
|
15602
15863
|
} = ctx;
|
|
15603
15864
|
const { compiledCode, prevModeSubcats, prevFailFingerprints } = input;
|
|
15604
15865
|
let { evalRoundsUsed, preWarmedContext } = input;
|
|
@@ -15609,6 +15870,88 @@ async function runEvalRound(ctx, input) {
|
|
|
15609
15870
|
let evalTokens = { input: 0, output: 0 };
|
|
15610
15871
|
let evalLlmMs = 0;
|
|
15611
15872
|
try {
|
|
15873
|
+
if (probeOnly) {
|
|
15874
|
+
const exitProbe = await runProbeAtExit({
|
|
15875
|
+
harness,
|
|
15876
|
+
sourceCode: currentSource,
|
|
15877
|
+
compiledCode,
|
|
15878
|
+
contract,
|
|
15879
|
+
fixtureProps
|
|
15880
|
+
});
|
|
15881
|
+
const probeOnlyResult = {
|
|
15882
|
+
issues: [...exitProbe.probeIssues],
|
|
15883
|
+
pass: [PROBE_ONLY_PASS],
|
|
15884
|
+
runtimeProbe: exitProbe.meta,
|
|
15885
|
+
criteriaCoverage: notApplicableCoverage(PROBE_ONLY_CRITERIA_REASON),
|
|
15886
|
+
visualCoverage: { status: "not-applicable", reason: PROBE_ONLY_VISUAL_REASON }
|
|
15887
|
+
};
|
|
15888
|
+
const recoverableFail = exitProbe.fired && exitProbe.recoverableFail;
|
|
15889
|
+
if (probeRepairUsed) {
|
|
15890
|
+
if (probeRepairTrigger === void 0) {
|
|
15891
|
+
throw new Error("harness defect: a probe-only re-probe round without the probe that bought the repair turn");
|
|
15892
|
+
}
|
|
15893
|
+
const repair = { attempted: true, compiled: true, trigger: probeRepairTrigger };
|
|
15894
|
+
evalResult = { ...probeOnlyResult, runtimeProbeRepair: repair };
|
|
15895
|
+
console.log(
|
|
15896
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only re-probe after the repair turn \u2014 ${describeProbeMeta(exitProbe.meta)}${recoverableFail ? ", recoverable crash still present" : ""} \u2014 serving`
|
|
15897
|
+
);
|
|
15898
|
+
return {
|
|
15899
|
+
control: "break",
|
|
15900
|
+
evalDone: true,
|
|
15901
|
+
evalResult,
|
|
15902
|
+
evalRoundsUsed,
|
|
15903
|
+
prevModeSubcats,
|
|
15904
|
+
prevFailFingerprints,
|
|
15905
|
+
preWarmedContext,
|
|
15906
|
+
evalTokens,
|
|
15907
|
+
evalLlmMs,
|
|
15908
|
+
lastResultText: "",
|
|
15909
|
+
isEvalFeedback: false,
|
|
15910
|
+
lastDiffFailed: false
|
|
15911
|
+
};
|
|
15912
|
+
}
|
|
15913
|
+
evalResult = probeOnlyResult;
|
|
15914
|
+
if (recoverableFail) {
|
|
15915
|
+
const runtimeFails = exitProbe.probeIssues.filter(
|
|
15916
|
+
(i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
|
|
15917
|
+
);
|
|
15918
|
+
const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
|
|
15919
|
+
console.log(
|
|
15920
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 runtime probe fail (recoverable) \u2014 granting the one repair turn`
|
|
15921
|
+
);
|
|
15922
|
+
return {
|
|
15923
|
+
control: "feedback",
|
|
15924
|
+
evalDone: false,
|
|
15925
|
+
evalResult,
|
|
15926
|
+
evalRoundsUsed,
|
|
15927
|
+
prevModeSubcats,
|
|
15928
|
+
prevFailFingerprints,
|
|
15929
|
+
preWarmedContext,
|
|
15930
|
+
evalTokens,
|
|
15931
|
+
evalLlmMs,
|
|
15932
|
+
lastResultText: lines.join("\n\n"),
|
|
15933
|
+
isEvalFeedback: true,
|
|
15934
|
+
lastDiffFailed: false
|
|
15935
|
+
};
|
|
15936
|
+
}
|
|
15937
|
+
console.log(
|
|
15938
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 probe ${describeProbeMeta(exitProbe.meta)} \u2014 serving`
|
|
15939
|
+
);
|
|
15940
|
+
return {
|
|
15941
|
+
control: "break",
|
|
15942
|
+
evalDone: true,
|
|
15943
|
+
evalResult,
|
|
15944
|
+
evalRoundsUsed,
|
|
15945
|
+
prevModeSubcats,
|
|
15946
|
+
prevFailFingerprints,
|
|
15947
|
+
preWarmedContext,
|
|
15948
|
+
evalTokens,
|
|
15949
|
+
evalLlmMs,
|
|
15950
|
+
lastResultText: "",
|
|
15951
|
+
isEvalFeedback: false,
|
|
15952
|
+
lastDiffFailed: false
|
|
15953
|
+
};
|
|
15954
|
+
}
|
|
15612
15955
|
const checkResult = await runCheck({
|
|
15613
15956
|
harness,
|
|
15614
15957
|
sourceCode: currentSource,
|
|
@@ -15683,6 +16026,7 @@ async function runEvalRound(ctx, input) {
|
|
|
15683
16026
|
lastDiffFailed: false
|
|
15684
16027
|
};
|
|
15685
16028
|
}
|
|
16029
|
+
const costTracker = requireCostTracker(ctx.costTracker);
|
|
15686
16030
|
if (evalRoundsUsed === 1 && !preWarmedContext && preWarmPromise) {
|
|
15687
16031
|
preWarmedContext = await preWarmPromise;
|
|
15688
16032
|
}
|
|
@@ -16168,6 +16512,8 @@ function createTelemetry() {
|
|
|
16168
16512
|
totalIn: 0,
|
|
16169
16513
|
totalOut: 0,
|
|
16170
16514
|
evalResult: void 0,
|
|
16515
|
+
probeRepairUsed: false,
|
|
16516
|
+
probeRepairTrigger: void 0,
|
|
16171
16517
|
contractFeedback: void 0,
|
|
16172
16518
|
sameExchangeBreak: void 0,
|
|
16173
16519
|
compiledCode: "",
|
|
@@ -16267,6 +16613,15 @@ function createGenerateTaskRunner(input) {
|
|
|
16267
16613
|
absorbTurn(telemetry, turn);
|
|
16268
16614
|
iconNamesCache = turn.iconNamesCache;
|
|
16269
16615
|
preWarmedContext = turn.preWarmedContext;
|
|
16616
|
+
if (turn.control !== "proceed" && session.probeOnlyEnabled && telemetry.probeRepairUsed) {
|
|
16617
|
+
if (telemetry.evalResult !== void 0) {
|
|
16618
|
+
telemetry.evalResult = { ...telemetry.evalResult, runtimeProbeRepair: { attempted: true, compiled: false } };
|
|
16619
|
+
}
|
|
16620
|
+
console.log(
|
|
16621
|
+
`[simple] probe-only repair turn did not compile (${turn.outcome ?? "no tool call"}) \u2014 serving the pre-repair card`
|
|
16622
|
+
);
|
|
16623
|
+
break;
|
|
16624
|
+
}
|
|
16270
16625
|
if (turn.control === "break") break;
|
|
16271
16626
|
if (turn.control === "continue") {
|
|
16272
16627
|
lastResultText = turn.lastResultText;
|
|
@@ -16305,7 +16660,7 @@ function createGenerateTaskRunner(input) {
|
|
|
16305
16660
|
`[simple] initial result delivered | ${codingDoneMs}ms | turns=${telemetry.turnsUsed} | tokens=${telemetry.totalIn + telemetry.totalOut} | compiled=${telemetry.compiledCode.length}B`
|
|
16306
16661
|
);
|
|
16307
16662
|
}
|
|
16308
|
-
if (!evalDone && telemetry.compiledCode && session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled)) {
|
|
16663
|
+
if (!evalDone && telemetry.compiledCode && (session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled) || session.probeOnlyEnabled)) {
|
|
16309
16664
|
const evalRoundStart = Date.now();
|
|
16310
16665
|
const round = await runEvalRound(
|
|
16311
16666
|
{
|
|
@@ -16325,7 +16680,10 @@ function createGenerateTaskRunner(input) {
|
|
|
16325
16680
|
llmEvalMod: session.llmEvalMod,
|
|
16326
16681
|
visualMod: session.visualMod,
|
|
16327
16682
|
preWarmPromise: session.preWarmPromise,
|
|
16328
|
-
onProgress: params.onProgress
|
|
16683
|
+
onProgress: params.onProgress,
|
|
16684
|
+
probeOnly: session.probeOnlyEnabled,
|
|
16685
|
+
probeRepairUsed: telemetry.probeRepairUsed,
|
|
16686
|
+
...telemetry.probeRepairTrigger !== void 0 ? { probeRepairTrigger: telemetry.probeRepairTrigger } : {}
|
|
16329
16687
|
},
|
|
16330
16688
|
{
|
|
16331
16689
|
compiledCode: telemetry.compiledCode,
|
|
@@ -16346,6 +16704,10 @@ function createGenerateTaskRunner(input) {
|
|
|
16346
16704
|
evalDone = round.evalDone;
|
|
16347
16705
|
absorbTokens(telemetry, round.evalTokens);
|
|
16348
16706
|
if (round.control === "break") break;
|
|
16707
|
+
if (session.probeOnlyEnabled) {
|
|
16708
|
+
telemetry.probeRepairUsed = true;
|
|
16709
|
+
telemetry.probeRepairTrigger = round.evalResult?.runtimeProbe;
|
|
16710
|
+
}
|
|
16349
16711
|
lastResultText = round.lastResultText;
|
|
16350
16712
|
isEvalFeedback = round.isEvalFeedback;
|
|
16351
16713
|
lastDiffFailed = round.lastDiffFailed;
|
|
@@ -16353,6 +16715,9 @@ function createGenerateTaskRunner(input) {
|
|
|
16353
16715
|
break;
|
|
16354
16716
|
}
|
|
16355
16717
|
} while (telemetry.turnsUsed < maxTurns && !evalDone);
|
|
16718
|
+
if (session.probeOnlyEnabled && telemetry.probeRepairUsed && telemetry.evalResult !== void 0 && telemetry.evalResult.runtimeProbeRepair === void 0) {
|
|
16719
|
+
console.log(`[simple] probe-only repair turn not taken \u2014 turn cap (${maxTurns}) reached before it`);
|
|
16720
|
+
}
|
|
16356
16721
|
telemetry.codingMs = Date.now() - telemetry.codingStartedAtMs;
|
|
16357
16722
|
if (!telemetry.compiledCode) {
|
|
16358
16723
|
for (const [, meta] of session.commitMeta) {
|
|
@@ -16579,6 +16944,7 @@ async function initSession(input) {
|
|
|
16579
16944
|
const qualityMode = qualityConfig?.quality ?? "fast";
|
|
16580
16945
|
const codeEvalEnabled = !!params.evaluation?.enabled;
|
|
16581
16946
|
const visualEvalEnabled = !!(params.visualEvaluation?.enabled || qualityConfig?.visualEval);
|
|
16947
|
+
const probeOnlyEnabled = harness.check.runtimeRender !== void 0 && !codeEvalEnabled && !visualEvalEnabled;
|
|
16582
16948
|
const maxEvalRounds = qualityMode === "high-quality" ? params.evaluation?.maxRounds ?? 3 : params.evaluation?.maxRounds ?? 2;
|
|
16583
16949
|
const visualThreshold = params.visualEvaluation?.passThreshold ?? 70;
|
|
16584
16950
|
let tiersMod = null;
|
|
@@ -16609,6 +16975,7 @@ async function initSession(input) {
|
|
|
16609
16975
|
systemPrompt,
|
|
16610
16976
|
codeEvalEnabled,
|
|
16611
16977
|
visualEvalEnabled,
|
|
16978
|
+
probeOnlyEnabled,
|
|
16612
16979
|
maxEvalRounds,
|
|
16613
16980
|
visualThreshold,
|
|
16614
16981
|
qualityMode,
|
|
@@ -16713,7 +17080,15 @@ async function dispatchGeneration(params) {
|
|
|
16713
17080
|
// prompt's gadget catalog renders a `Type:` line per third-party
|
|
16714
17081
|
// gadget (the LLM sees a wrapper's real call shape it otherwise
|
|
16715
17082
|
// can't know).
|
|
16716
|
-
systemPromptBuilder: ({
|
|
17083
|
+
systemPromptBuilder: ({
|
|
17084
|
+
userRequest,
|
|
17085
|
+
shellType,
|
|
17086
|
+
screen,
|
|
17087
|
+
axisDelta,
|
|
17088
|
+
designMode,
|
|
17089
|
+
canvas,
|
|
17090
|
+
profile
|
|
17091
|
+
}) => buildSystemPrompt2(
|
|
16717
17092
|
userRequest,
|
|
16718
17093
|
shellType,
|
|
16719
17094
|
screen,
|
|
@@ -16729,14 +17104,14 @@ async function dispatchGeneration(params) {
|
|
|
16729
17104
|
axisChecks: REGISTRY.filter(
|
|
16730
17105
|
(check) => matches(classification.vector, check)
|
|
16731
17106
|
),
|
|
16732
|
-
// The
|
|
16733
|
-
// design) stay on the dispatch side of
|
|
16734
|
-
// createHarness defaults this to undefined.
|
|
17107
|
+
// The runtime-render check, built under the caller's probe config.
|
|
17108
|
+
// Heavy deps (happy-dom + wire + design) stay on the dispatch side of
|
|
17109
|
+
// the boundary; the skeleton createHarness defaults this to undefined.
|
|
16735
17110
|
//
|
|
16736
17111
|
// Gated behind `enableRuntimeRender` (default true) so
|
|
16737
17112
|
// benchmark/dev callers can opt out of in-loop probe execution and
|
|
16738
17113
|
// run the probe externally as a final post-generation check.
|
|
16739
|
-
runtimeRender: params.enableRuntimeRender ?? true ? DEFAULT_RUNTIME_RENDER_CHECK : void 0
|
|
17114
|
+
runtimeRender: params.enableRuntimeRender ?? true ? params.runtimeRender ?? DEFAULT_RUNTIME_RENDER_CHECK : void 0
|
|
16740
17115
|
});
|
|
16741
17116
|
console.log(
|
|
16742
17117
|
`[harness] id=${harness.id} name=${harness.name} workflow=${harness.process.workflow.name} overrides=${harness.meta.overrides.join(",") || "none"}`
|
|
@@ -16754,67 +17129,70 @@ async function dispatchGeneration(params) {
|
|
|
16754
17129
|
evaluationAgent
|
|
16755
17130
|
});
|
|
16756
17131
|
const { resolveRunPolicyForProfile: resolveRunPolicyForProfile2 } = await Promise.resolve().then(() => (init_policy2(), policy_exports));
|
|
16757
|
-
const resolvedPolicy = resolveRunPolicyForProfile2(
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
);
|
|
17132
|
+
const resolvedPolicy = resolveRunPolicyForProfile2(process.env.GGUI_POLICY_PROFILE, harness, {
|
|
17133
|
+
provider: routerProvider,
|
|
17134
|
+
modelId: resolvedModel
|
|
17135
|
+
});
|
|
16762
17136
|
let systemPromptOverride;
|
|
16763
17137
|
{
|
|
16764
17138
|
const { PRIMITIVES_DOCUMENTATION: PRIMITIVES_DOCUMENTATION2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
|
|
16765
17139
|
const fullBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION2, "utf8");
|
|
17140
|
+
const docFormat = resolvedPolicy.context.primitiveDocFormat ?? "markdown";
|
|
17141
|
+
const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
|
|
16766
17142
|
const mode = resolvedPolicy.context.primitiveDocSlice ?? "full";
|
|
16767
|
-
|
|
16768
|
-
|
|
17143
|
+
let doc = PRIMITIVES_DOCUMENTATION2;
|
|
17144
|
+
if (docFormat === "ts" && indexMode === "off") {
|
|
17145
|
+
const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
|
|
17146
|
+
doc = PRIMITIVES_DOCUMENTATION_TS2;
|
|
17147
|
+
const tsBytes = Buffer.byteLength(doc, "utf8");
|
|
17148
|
+
console.log(
|
|
17149
|
+
`[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
|
|
17150
|
+
);
|
|
17151
|
+
}
|
|
17152
|
+
const formatBytes = Buffer.byteLength(doc, "utf8");
|
|
17153
|
+
if (mode === "axis-keyed" || mode === "axis-keyed+contract") {
|
|
17154
|
+
const {
|
|
17155
|
+
computePrimitiveAllowlist: computePrimitiveAllowlist2,
|
|
17156
|
+
computeContractPrimitives: computeContractPrimitives2,
|
|
17157
|
+
slicePrimitiveDocumentation: slicePrimitiveDocumentation2,
|
|
17158
|
+
slicePrimitiveDocumentationTs: slicePrimitiveDocumentationTs2
|
|
17159
|
+
} = await Promise.resolve().then(() => (init_primitive_slice(), primitive_slice_exports));
|
|
16769
17160
|
let allowlist = computePrimitiveAllowlist2(classification);
|
|
17161
|
+
if (mode === "axis-keyed+contract") {
|
|
17162
|
+
const fromContract = computeContractPrimitives2(effectiveContract);
|
|
17163
|
+
allowlist = [.../* @__PURE__ */ new Set([...allowlist, ...fromContract])].sort();
|
|
17164
|
+
}
|
|
16770
17165
|
const excludes = resolvedPolicy.context.primitiveDocExcludes ?? [];
|
|
16771
17166
|
if (excludes.length > 0) {
|
|
16772
17167
|
const excludeSet = new Set(excludes);
|
|
16773
17168
|
allowlist = allowlist.filter((p) => !excludeSet.has(p));
|
|
16774
17169
|
}
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
allowlist
|
|
16778
|
-
);
|
|
16779
|
-
const slicedBytes = Buffer.byteLength(sliced, "utf8");
|
|
17170
|
+
doc = docFormat === "ts" && indexMode === "off" ? slicePrimitiveDocumentationTs2(doc, allowlist) : slicePrimitiveDocumentation2(doc, allowlist);
|
|
17171
|
+
const slicedBytes = Buffer.byteLength(doc, "utf8");
|
|
16780
17172
|
const excludeTag = excludes.length ? ` excludes=${excludes.join(",")}` : "";
|
|
16781
17173
|
console.log(
|
|
16782
|
-
`[simple] primitive-doc-bytes=${slicedBytes} mode
|
|
16783
|
-
);
|
|
16784
|
-
systemPromptOverride = harness.how.systemPrompt.replace(
|
|
16785
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
16786
|
-
sliced
|
|
17174
|
+
`[simple] primitive-doc-bytes=${slicedBytes} mode=${mode} baseline=${formatBytes} drop=${((1 - slicedBytes / formatBytes) * 100).toFixed(1)}% allowlist=${allowlist.length}/${allowlist.join(",")}${excludeTag}`
|
|
16787
17175
|
);
|
|
16788
17176
|
} else {
|
|
16789
17177
|
console.log(
|
|
16790
|
-
`[simple] primitive-doc-bytes=${
|
|
17178
|
+
`[simple] primitive-doc-bytes=${formatBytes} mode=full baseline=${formatBytes} drop=0.0%`
|
|
16791
17179
|
);
|
|
16792
17180
|
}
|
|
16793
|
-
const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
|
|
16794
17181
|
if (indexMode !== "off") {
|
|
16795
17182
|
const { buildPrimitiveIndex: buildPrimitiveIndex2 } = await Promise.resolve().then(() => (init_primitive_index(), primitive_index_exports));
|
|
16796
|
-
|
|
16797
|
-
const indexBytes = Buffer.byteLength(
|
|
17183
|
+
doc = buildPrimitiveIndex2(PRIMITIVES_DOCUMENTATION2, indexMode);
|
|
17184
|
+
const indexBytes = Buffer.byteLength(doc, "utf8");
|
|
16798
17185
|
console.log(
|
|
16799
17186
|
`[simple] primitive-index-bytes=${indexBytes} mode=${indexMode} baseline=${fullBytes} drop=${((1 - indexBytes / fullBytes) * 100).toFixed(1)}%`
|
|
16800
17187
|
);
|
|
16801
|
-
systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
|
|
16802
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
16803
|
-
index
|
|
16804
|
-
);
|
|
16805
17188
|
}
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
|
|
16809
|
-
const tsBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION_TS2, "utf8");
|
|
16810
|
-
console.log(
|
|
16811
|
-
`[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
|
|
16812
|
-
);
|
|
16813
|
-
systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
|
|
16814
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
16815
|
-
PRIMITIVES_DOCUMENTATION_TS2
|
|
16816
|
-
);
|
|
17189
|
+
if (doc !== PRIMITIVES_DOCUMENTATION2) {
|
|
17190
|
+
systemPromptOverride = harness.how.systemPrompt.replace(PRIMITIVES_DOCUMENTATION2, doc);
|
|
16817
17191
|
}
|
|
17192
|
+
const sentPrompt = systemPromptOverride ?? harness.how.systemPrompt;
|
|
17193
|
+
console.log(
|
|
17194
|
+
`[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}`
|
|
17195
|
+
);
|
|
16818
17196
|
if (resolvedPolicy.context.planFirstTurn) {
|
|
16819
17197
|
const phaseGuidance = `
|
|
16820
17198
|
|
|
@@ -16866,7 +17244,11 @@ Your plan is a commitment, not a design doc. Keep it tight. Do not over-plan; ju
|
|
|
16866
17244
|
);
|
|
16867
17245
|
}
|
|
16868
17246
|
const finalSource = runResult.finalSource ?? "";
|
|
16869
|
-
const result = await assembleGenerationResult({
|
|
17247
|
+
const result = await assembleGenerationResult({
|
|
17248
|
+
session,
|
|
17249
|
+
telemetry,
|
|
17250
|
+
source: finalSource
|
|
17251
|
+
});
|
|
16870
17252
|
return withArmRecord(result, params);
|
|
16871
17253
|
}
|
|
16872
17254
|
function withArmRecord(result, params) {
|