@ggui-ai/ui-gen 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-Du7P_5Fx.d.ts +201 -0
- package/dist/adapters/generation-dispatch.d.ts +33 -12
- package/dist/adapters/generation-dispatch.js +510 -128
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +5 -3
- package/dist/advanced/index.js +551 -142
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +153 -59
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +2 -1
- package/dist/check/index.js +47 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-GIhzx08x.d.ts} +34 -10
- package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-BnAH56p4.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +4 -3
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -1
- package/dist/evaluation/index.d.ts +4 -3
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +29 -10
- package/dist/harness/check/runtime-render/index.js +203 -60
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +2 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +2 -1
- package/dist/harness/runtime.d.ts +2 -1
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +11 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -1
- package/dist/index.d.ts +38 -3
- package/dist/index.js +553 -143
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +2 -1
- package/dist/policy.d.ts +7 -4
- package/dist/policy.js.map +1 -1
- package/dist/tools/render-check-worker.d.ts +7 -1
- package/dist/tools/render-check-worker.js +55 -23
- package/dist/tools/render-check-worker.js.map +1 -1
- package/dist/{types-public-Ow-tNtFE.d.ts → types-public-Bk6d1WLN.d.ts} +77 -11
- package/dist/workflows.d.ts +2 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +137 -96
- package/src/check/run-tier0.ts +80 -1
- package/src/create-ui-generator.ts +185 -95
- package/src/evaluation/types-public.ts +73 -11
- package/src/harness/check/runtime-render/adapter.ts +221 -75
- package/src/harness/check/runtime-render/index.ts +75 -2
- package/src/harness/check/runtime-render/render-check-host.ts +82 -19
- package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
- package/src/harness/check/runtime-render/render-check.ts +154 -117
- package/src/harness/coding/generate-task-runner.ts +57 -4
- package/src/harness/coding/init-session.ts +13 -0
- package/src/harness/coding/run-eval-round.ts +238 -15
- package/src/harness/policy.ts +11 -0
- package/src/harness/primitive-slice.ts +104 -0
- package/src/harness/types-public.ts +9 -1
- package/src/index.ts +4 -0
- package/src/policy.ts +7 -4
- package/src/tools/render-check-worker.ts +74 -23
- package/dist/render-check-Blwj8gB6.d.ts +0 -105
package/dist/advanced/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as esbuild2 from 'esbuild';
|
|
|
3
3
|
import { build } from 'esbuild';
|
|
4
4
|
import Anthropic from '@anthropic-ai/sdk';
|
|
5
5
|
import { getCssTokens, fillFitRule, FORBIDDEN_IMPORT_SPECIFIERS } from '@ggui-ai/design/rendering';
|
|
6
|
-
import { classifyUi, MODEL_REGISTRY, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1,
|
|
6
|
+
import { listContractGadgets, HOOK_NAME_RE, classifyUi, MODEL_REGISTRY, DANGEROUS_PATTERNS as DANGEROUS_PATTERNS$1, modelRefOfRoute, isGeneratorId, APP_GENERATION_PROFILE_BOUNDS, STDLIB_GADGETS_PACKAGE, resolveAppGadgets, anthropicRejectsForcedToolChoice, normalizeAnthropicModelId, anthropicRejectsSamplingParams, isRecord } from '@ggui-ai/protocol';
|
|
7
7
|
import fs, { existsSync, mkdtempSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
8
8
|
import { contentHash } from '@ggui-ai/protocol/content-hash';
|
|
9
9
|
import ts4 from 'typescript';
|
|
@@ -3343,6 +3343,46 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode
|
|
|
3343
3343
|
visit(sf);
|
|
3344
3344
|
return issues;
|
|
3345
3345
|
}
|
|
3346
|
+
function suggestManifestTokens(name) {
|
|
3347
|
+
const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
|
|
3348
|
+
if (successor !== void 0) return [successor];
|
|
3349
|
+
const segments = name.split("-").filter((segment) => segment.length > 0);
|
|
3350
|
+
const own = name.endsWith("-") ? segments.length : segments.length - 1;
|
|
3351
|
+
for (let n = own; n >= 2; n--) {
|
|
3352
|
+
const prefix = `--${segments.slice(0, n).join("-")}-`;
|
|
3353
|
+
const family = consumedTokenManifest.filter((token) => token.startsWith(prefix));
|
|
3354
|
+
if (family.length > 0) return wordHitsFirst(name, rolesFirst(family));
|
|
3355
|
+
}
|
|
3356
|
+
const words = new Set(
|
|
3357
|
+
segments.slice(1).filter((segment) => segment.length >= 4).map((segment) => segment.toLowerCase())
|
|
3358
|
+
);
|
|
3359
|
+
return rolesFirst(
|
|
3360
|
+
consumedTokenManifest.filter(
|
|
3361
|
+
(token) => token.split("-").some((segment) => words.has(segment.toLowerCase()))
|
|
3362
|
+
)
|
|
3363
|
+
);
|
|
3364
|
+
}
|
|
3365
|
+
function wordHitsFirst(name, family) {
|
|
3366
|
+
const word = name.slice(name.lastIndexOf("-") + 1).toLowerCase();
|
|
3367
|
+
if (word.length < 4) return family;
|
|
3368
|
+
const hit = (token) => token.slice(token.lastIndexOf("-") + 1).toLowerCase().includes(word);
|
|
3369
|
+
return [...family.filter(hit), ...family.filter((token) => !hit(token))];
|
|
3370
|
+
}
|
|
3371
|
+
function rolesFirst(tokens) {
|
|
3372
|
+
const isRole = (token) => !/^\d+$/.test(token.slice(token.lastIndexOf("-") + 1));
|
|
3373
|
+
return [...tokens.filter(isRole), ...tokens.filter((token) => !isRole(token))];
|
|
3374
|
+
}
|
|
3375
|
+
function offManifestHint(name) {
|
|
3376
|
+
const successor = RETIRED_TOKEN_SUCCESSORS.get(name);
|
|
3377
|
+
if (successor !== void 0) {
|
|
3378
|
+
return `"${name}" was retired (ggui#987); the current name is var(${successor}). `;
|
|
3379
|
+
}
|
|
3380
|
+
const near = suggestManifestTokens(name);
|
|
3381
|
+
if (near.length === 0) return "";
|
|
3382
|
+
const shown = near.slice(0, 8).map((token) => `var(${token})`).join(", ");
|
|
3383
|
+
const more = near.length > 8 ? ` (and ${near.length - 8} more in that family)` : "";
|
|
3384
|
+
return `Nearest names in the manifest: ${shown}${more}. `;
|
|
3385
|
+
}
|
|
3346
3386
|
function isGadgetExportImported(source, pkg, exportName) {
|
|
3347
3387
|
const esc = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3348
3388
|
const re = new RegExp(
|
|
@@ -3565,7 +3605,7 @@ ${errorDetail}`,
|
|
|
3565
3605
|
subcategory: "off-manifest-token",
|
|
3566
3606
|
severity: "critical",
|
|
3567
3607
|
description: `Token "${name}" is not in the design system's token manifest \u2014 no theme defines it, so it renders unset.`,
|
|
3568
|
-
fix:
|
|
3608
|
+
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.`,
|
|
3569
3609
|
line: lineNum
|
|
3570
3610
|
});
|
|
3571
3611
|
}
|
|
@@ -4082,7 +4122,7 @@ async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE)
|
|
|
4082
4122
|
}
|
|
4083
4123
|
return { issues, pass };
|
|
4084
4124
|
}
|
|
4085
|
-
var MANIFEST_TOKENS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
|
|
4125
|
+
var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
|
|
4086
4126
|
var init_run_tier0 = __esm({
|
|
4087
4127
|
"src/check/run-tier0.ts"() {
|
|
4088
4128
|
init_design_mode();
|
|
@@ -4093,6 +4133,12 @@ var init_run_tier0 = __esm({
|
|
|
4093
4133
|
init_extract_wire_calls();
|
|
4094
4134
|
init_types_public();
|
|
4095
4135
|
MANIFEST_TOKENS = new Set(consumedTokenManifest);
|
|
4136
|
+
RETIRED_TOKEN_SUCCESSORS = /* @__PURE__ */ new Map([
|
|
4137
|
+
["--ggui-color-surface", "--ggui-color-container"],
|
|
4138
|
+
["--ggui-color-onSurface", "--ggui-color-onContainer"],
|
|
4139
|
+
["--ggui-color-surfaceVariant", "--ggui-color-sunken"],
|
|
4140
|
+
["--ggui-color-onSurfaceVariant", "--ggui-color-onSunken"]
|
|
4141
|
+
]);
|
|
4096
4142
|
TOKEN_REF_RE = /var\(\s*(--ggui-[a-zA-Z0-9-]+)/g;
|
|
4097
4143
|
HOOK_NAME_FOR = {
|
|
4098
4144
|
action: "useAction",
|
|
@@ -6395,6 +6441,14 @@ function resolveRunPolicyForProfile(profile, harness, runtimeCtx) {
|
|
|
6395
6441
|
}
|
|
6396
6442
|
};
|
|
6397
6443
|
}
|
|
6444
|
+
if (profile === "ctx-slice-primitives-v2") {
|
|
6445
|
+
return {
|
|
6446
|
+
context: {
|
|
6447
|
+
...harness.policy.context,
|
|
6448
|
+
primitiveDocSlice: "axis-keyed+contract"
|
|
6449
|
+
}
|
|
6450
|
+
};
|
|
6451
|
+
}
|
|
6398
6452
|
return resolveRunPolicy2(harness, runtimeCtx);
|
|
6399
6453
|
}
|
|
6400
6454
|
var init_policy2 = __esm({
|
|
@@ -6408,9 +6462,25 @@ var init_policy2 = __esm({
|
|
|
6408
6462
|
// src/harness/check/runtime-render/render-check-host.ts
|
|
6409
6463
|
var render_check_host_exports = {};
|
|
6410
6464
|
__export(render_check_host_exports, {
|
|
6465
|
+
DEFAULT_RENDER_CHECK_HOST_BOUNDS: () => DEFAULT_RENDER_CHECK_HOST_BOUNDS,
|
|
6411
6466
|
mapSandboxResultToCheckResult: () => mapSandboxResultToCheckResult,
|
|
6467
|
+
resolveRenderCheckHostBounds: () => resolveRenderCheckHostBounds,
|
|
6468
|
+
resolveWorkerSpawn: () => resolveWorkerSpawn2,
|
|
6412
6469
|
runRenderCheckViaWorker: () => runRenderCheckViaWorker
|
|
6413
6470
|
});
|
|
6471
|
+
function resolveRenderCheckHostBounds(bounds = {}) {
|
|
6472
|
+
const timeoutMs = bounds.timeoutMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs;
|
|
6473
|
+
const heapMb = bounds.heapMb ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.heapMb;
|
|
6474
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 2) {
|
|
6475
|
+
throw new RangeError(`render-check: timeoutMs must be an integer \u2265 2, got ${timeoutMs}`);
|
|
6476
|
+
}
|
|
6477
|
+
if (!Number.isInteger(heapMb) || heapMb < 1) {
|
|
6478
|
+
throw new RangeError(`render-check: heapMb must be a positive integer, got ${heapMb}`);
|
|
6479
|
+
}
|
|
6480
|
+
const requestedGrace = bounds.gracePeriodMs ?? DEFAULT_RENDER_CHECK_HOST_BOUNDS.gracePeriodMs;
|
|
6481
|
+
const gracePeriodMs = Math.max(1, Math.min(requestedGrace, Math.floor(timeoutMs / 2)));
|
|
6482
|
+
return { timeoutMs, heapMb, gracePeriodMs };
|
|
6483
|
+
}
|
|
6414
6484
|
function resolveWorkerSpawn2() {
|
|
6415
6485
|
const jsCandidates = [
|
|
6416
6486
|
// Sibling — when import.meta.url is already .../harness/check/runtime-render/.
|
|
@@ -6459,12 +6529,12 @@ function unverified(reason, t0) {
|
|
|
6459
6529
|
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
6460
6530
|
};
|
|
6461
6531
|
}
|
|
6462
|
-
function mapSandboxResultToCheckResult(result, t0) {
|
|
6532
|
+
function mapSandboxResultToCheckResult(result, t0, boundMs = DEFAULT_RENDER_CHECK_HOST_BOUNDS.timeoutMs) {
|
|
6463
6533
|
if (result.outcome === "timeout") {
|
|
6464
6534
|
return {
|
|
6465
6535
|
ok: false,
|
|
6466
6536
|
issues: [],
|
|
6467
|
-
incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs
|
|
6537
|
+
incomplete: { kind: "timeout", elapsedMs: result.durationMs, boundMs },
|
|
6468
6538
|
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
6469
6539
|
};
|
|
6470
6540
|
}
|
|
@@ -6509,8 +6579,9 @@ function mapSandboxResultToCheckResult(result, t0) {
|
|
|
6509
6579
|
);
|
|
6510
6580
|
}
|
|
6511
6581
|
}
|
|
6512
|
-
async function runRenderCheckViaWorker(input) {
|
|
6582
|
+
async function runRenderCheckViaWorker(input, options = {}) {
|
|
6513
6583
|
const t0 = Date.now();
|
|
6584
|
+
const bounds = resolveRenderCheckHostBounds(options.bounds);
|
|
6514
6585
|
let spawn;
|
|
6515
6586
|
try {
|
|
6516
6587
|
spawn = resolveWorkerSpawn2();
|
|
@@ -6521,9 +6592,10 @@ async function runRenderCheckViaWorker(input) {
|
|
|
6521
6592
|
const result = await runSandboxed({
|
|
6522
6593
|
command: spawn.command,
|
|
6523
6594
|
args: spawn.args,
|
|
6524
|
-
timeoutMs:
|
|
6595
|
+
timeoutMs: bounds.timeoutMs,
|
|
6596
|
+
gracePeriodMs: bounds.gracePeriodMs,
|
|
6525
6597
|
maxStdoutBytes: CHECK_STDOUT_CAP,
|
|
6526
|
-
nodeHeapMb:
|
|
6598
|
+
nodeHeapMb: bounds.heapMb,
|
|
6527
6599
|
stdin: JSON.stringify(input),
|
|
6528
6600
|
// NODE_ENV steers React's production vs development build — keep
|
|
6529
6601
|
// parity with the caller; everything else stays on the sandbox's
|
|
@@ -6535,15 +6607,18 @@ async function runRenderCheckViaWorker(input) {
|
|
|
6535
6607
|
end: loadavg()[0] ?? 0,
|
|
6536
6608
|
cores: availableParallelism()
|
|
6537
6609
|
};
|
|
6538
|
-
const mapped = mapSandboxResultToCheckResult(result, t0);
|
|
6610
|
+
const mapped = mapSandboxResultToCheckResult(result, t0, bounds.timeoutMs);
|
|
6539
6611
|
return { ...mapped, stats: { ...mapped.stats, hostLoad } };
|
|
6540
6612
|
}
|
|
6541
|
-
var
|
|
6613
|
+
var DEFAULT_RENDER_CHECK_HOST_BOUNDS, CHECK_STDOUT_CAP, WORKER_BASENAME;
|
|
6542
6614
|
var init_render_check_host = __esm({
|
|
6543
6615
|
"src/harness/check/runtime-render/render-check-host.ts"() {
|
|
6544
|
-
|
|
6616
|
+
DEFAULT_RENDER_CHECK_HOST_BOUNDS = {
|
|
6617
|
+
timeoutMs: 3e4,
|
|
6618
|
+
heapMb: 512,
|
|
6619
|
+
gracePeriodMs: 2e3
|
|
6620
|
+
};
|
|
6545
6621
|
CHECK_STDOUT_CAP = 2 * 1024 * 1024;
|
|
6546
|
-
CHECK_NODE_HEAP_MB = 512;
|
|
6547
6622
|
WORKER_BASENAME = "render-check-worker";
|
|
6548
6623
|
}
|
|
6549
6624
|
});
|
|
@@ -8344,11 +8419,25 @@ Respond with ONLY a JSON object (no markdown, no explanation):
|
|
|
8344
8419
|
}
|
|
8345
8420
|
});
|
|
8346
8421
|
|
|
8422
|
+
// src/tools/get-primitives-ts.ts
|
|
8423
|
+
var get_primitives_ts_exports = {};
|
|
8424
|
+
__export(get_primitives_ts_exports, {
|
|
8425
|
+
PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
|
|
8426
|
+
});
|
|
8427
|
+
var PRIMITIVES_DOCUMENTATION_TS;
|
|
8428
|
+
var init_get_primitives_ts = __esm({
|
|
8429
|
+
"src/tools/get-primitives-ts.ts"() {
|
|
8430
|
+
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";
|
|
8431
|
+
}
|
|
8432
|
+
});
|
|
8433
|
+
|
|
8347
8434
|
// src/harness/primitive-slice.ts
|
|
8348
8435
|
var primitive_slice_exports = {};
|
|
8349
8436
|
__export(primitive_slice_exports, {
|
|
8437
|
+
computeContractPrimitives: () => computeContractPrimitives,
|
|
8350
8438
|
computePrimitiveAllowlist: () => computePrimitiveAllowlist,
|
|
8351
|
-
slicePrimitiveDocumentation: () => slicePrimitiveDocumentation
|
|
8439
|
+
slicePrimitiveDocumentation: () => slicePrimitiveDocumentation,
|
|
8440
|
+
slicePrimitiveDocumentationTs: () => slicePrimitiveDocumentationTs
|
|
8352
8441
|
});
|
|
8353
8442
|
function computePrimitiveAllowlist(classification) {
|
|
8354
8443
|
const set = new Set(CORE_PRIMITIVES);
|
|
@@ -8368,6 +8457,42 @@ function computePrimitiveAllowlist(classification) {
|
|
|
8368
8457
|
}
|
|
8369
8458
|
return [...set].sort();
|
|
8370
8459
|
}
|
|
8460
|
+
function computeContractPrimitives(contract) {
|
|
8461
|
+
const out = /* @__PURE__ */ new Set();
|
|
8462
|
+
if (!contract) return [];
|
|
8463
|
+
for (const slot of Object.values(contract.contextSpec ?? {})) addLeaves(slot.schema, out);
|
|
8464
|
+
for (const action of Object.values(contract.actionSpec ?? {})) {
|
|
8465
|
+
if (action.schema) addLeaves(action.schema, out);
|
|
8466
|
+
}
|
|
8467
|
+
if (Object.keys(contract.streamSpec ?? {}).length > 0) {
|
|
8468
|
+
out.add("Spinner");
|
|
8469
|
+
out.add("Badge");
|
|
8470
|
+
}
|
|
8471
|
+
return [...out].sort();
|
|
8472
|
+
}
|
|
8473
|
+
function addLeaves(schema, out) {
|
|
8474
|
+
for (const child of Object.values(schema.properties ?? {})) addLeaves(child, out);
|
|
8475
|
+
if (schema.items) addLeaves(schema.items, out);
|
|
8476
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
8477
|
+
out.add("Select");
|
|
8478
|
+
out.add("RadioGroup");
|
|
8479
|
+
return;
|
|
8480
|
+
}
|
|
8481
|
+
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
8482
|
+
for (const t of types) {
|
|
8483
|
+
if (t === "string") {
|
|
8484
|
+
out.add("Input");
|
|
8485
|
+
out.add("TextArea");
|
|
8486
|
+
out.add("FormField");
|
|
8487
|
+
} else if (t === "number" || t === "integer") {
|
|
8488
|
+
out.add("Input");
|
|
8489
|
+
out.add("Slider");
|
|
8490
|
+
} else if (t === "boolean") {
|
|
8491
|
+
out.add("Checkbox");
|
|
8492
|
+
out.add("Toggle");
|
|
8493
|
+
}
|
|
8494
|
+
}
|
|
8495
|
+
}
|
|
8371
8496
|
function slicePrimitiveDocumentation(fullDoc, allowlist) {
|
|
8372
8497
|
const allow = new Set(allowlist);
|
|
8373
8498
|
const firstSection = fullDoc.search(/^### \S/m);
|
|
@@ -8391,7 +8516,26 @@ function slicePrimitiveDocumentation(fullDoc, allowlist) {
|
|
|
8391
8516
|
}
|
|
8392
8517
|
return preamble + kept.join("");
|
|
8393
8518
|
}
|
|
8394
|
-
|
|
8519
|
+
function slicePrimitiveDocumentationTs(fullDoc, allowlist) {
|
|
8520
|
+
const allow = new Set(allowlist);
|
|
8521
|
+
const lines = fullDoc.split("\n");
|
|
8522
|
+
let sawHeader = false;
|
|
8523
|
+
let keep = true;
|
|
8524
|
+
const kept = [];
|
|
8525
|
+
for (const line of lines) {
|
|
8526
|
+
const header = TS_SECTION_HEADER.exec(line);
|
|
8527
|
+
if (header !== null) {
|
|
8528
|
+
sawHeader = true;
|
|
8529
|
+
const name = header[1];
|
|
8530
|
+
keep = !KNOWN_PRIMITIVE_SECTIONS.has(name) || allow.has(name);
|
|
8531
|
+
} else if (TS_SECTION_BANNER.test(line)) {
|
|
8532
|
+
keep = true;
|
|
8533
|
+
}
|
|
8534
|
+
if (keep) kept.push(line);
|
|
8535
|
+
}
|
|
8536
|
+
return sawHeader ? kept.join("\n") : fullDoc;
|
|
8537
|
+
}
|
|
8538
|
+
var CORE_PRIMITIVES, KNOWN_PRIMITIVE_SECTIONS, AXIS_PRIMITIVES, TS_SECTION_HEADER, TS_SECTION_BANNER;
|
|
8395
8539
|
var init_primitive_slice = __esm({
|
|
8396
8540
|
"src/harness/primitive-slice.ts"() {
|
|
8397
8541
|
CORE_PRIMITIVES = [
|
|
@@ -8524,6 +8668,8 @@ var init_primitive_slice = __esm({
|
|
|
8524
8668
|
"tooling:both": ["Alert"],
|
|
8525
8669
|
"tooling:none": []
|
|
8526
8670
|
};
|
|
8671
|
+
TS_SECTION_HEADER = /^\/\/ ([A-Za-z]+) — /;
|
|
8672
|
+
TS_SECTION_BANNER = /^\/\/ ═/;
|
|
8527
8673
|
}
|
|
8528
8674
|
});
|
|
8529
8675
|
|
|
@@ -8662,18 +8808,6 @@ var init_primitive_index = __esm({
|
|
|
8662
8808
|
}
|
|
8663
8809
|
});
|
|
8664
8810
|
|
|
8665
|
-
// src/tools/get-primitives-ts.ts
|
|
8666
|
-
var get_primitives_ts_exports = {};
|
|
8667
|
-
__export(get_primitives_ts_exports, {
|
|
8668
|
-
PRIMITIVES_DOCUMENTATION_TS: () => PRIMITIVES_DOCUMENTATION_TS
|
|
8669
|
-
});
|
|
8670
|
-
var PRIMITIVES_DOCUMENTATION_TS;
|
|
8671
|
-
var init_get_primitives_ts = __esm({
|
|
8672
|
-
"src/tools/get-primitives-ts.ts"() {
|
|
8673
|
-
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";
|
|
8674
|
-
}
|
|
8675
|
-
});
|
|
8676
|
-
|
|
8677
8811
|
// src/adapters/index.ts
|
|
8678
8812
|
init_base();
|
|
8679
8813
|
|
|
@@ -15109,13 +15243,22 @@ function editableSurface(el) {
|
|
|
15109
15243
|
function parentLink(node) {
|
|
15110
15244
|
return node;
|
|
15111
15245
|
}
|
|
15112
|
-
|
|
15246
|
+
var RENDER_CHECK_KINDS = [
|
|
15247
|
+
"render-no-throw",
|
|
15248
|
+
"prop-sensitivity",
|
|
15249
|
+
"action-wiring",
|
|
15250
|
+
"selection-identity",
|
|
15251
|
+
"prop-coverage",
|
|
15252
|
+
"optional-props-omitted",
|
|
15253
|
+
"stream-rerender"
|
|
15254
|
+
];
|
|
15255
|
+
async function runRenderCheck(input, options = {}) {
|
|
15113
15256
|
const g = hostGlobals();
|
|
15114
15257
|
if ("window" in g && "document" in g) {
|
|
15115
15258
|
return runRenderCheckInProcess(input);
|
|
15116
15259
|
}
|
|
15117
15260
|
const { runRenderCheckViaWorker: runRenderCheckViaWorker2 } = await Promise.resolve().then(() => (init_render_check_host(), render_check_host_exports));
|
|
15118
|
-
return runRenderCheckViaWorker2(input);
|
|
15261
|
+
return runRenderCheckViaWorker2(input, options);
|
|
15119
15262
|
}
|
|
15120
15263
|
async function runRenderCheckInProcess(input) {
|
|
15121
15264
|
const t0 = Date.now();
|
|
@@ -15129,7 +15272,7 @@ async function runRenderCheckInProcess(input) {
|
|
|
15129
15272
|
const Wire = await import('@ggui-ai/wire');
|
|
15130
15273
|
const { GguiWireProvider } = Wire;
|
|
15131
15274
|
const moduleResolutions = {
|
|
15132
|
-
|
|
15275
|
+
react: React,
|
|
15133
15276
|
"react/jsx-runtime": ReactJsxRuntime,
|
|
15134
15277
|
"@ggui-ai/wire": Wire
|
|
15135
15278
|
};
|
|
@@ -15245,14 +15388,23 @@ async function runRenderCheckInProcess(input) {
|
|
|
15245
15388
|
)
|
|
15246
15389
|
);
|
|
15247
15390
|
const timeoutPromise = new Promise((_, reject) => {
|
|
15248
|
-
setTimeout(
|
|
15391
|
+
setTimeout(
|
|
15392
|
+
() => reject(
|
|
15393
|
+
new Error(
|
|
15394
|
+
`[runtime-render] render exceeded ${RENDER_TIMEOUT_MS2}ms \u2014 classified as runtime-hang`
|
|
15395
|
+
)
|
|
15396
|
+
),
|
|
15397
|
+
RENDER_TIMEOUT_MS2
|
|
15398
|
+
).unref?.();
|
|
15249
15399
|
});
|
|
15250
15400
|
renderResult = await Promise.race([renderPromise, timeoutPromise]);
|
|
15251
15401
|
} catch (e) {
|
|
15252
15402
|
const msg = e instanceof Error ? e.message : String(e);
|
|
15253
15403
|
const errStack = e instanceof Error ? e.stack ?? "" : "";
|
|
15254
15404
|
const baseReason = loopSignature ? `Infinite render loop (${loopSignature}) \u2014 likely setState/dispatch inside useEffect with missing or unstable dependency` : /runtime-hang/.test(msg) ? `Render wall-clock exceeded ${RENDER_TIMEOUT_MS2}ms \u2014 likely async effect loop or unresolved promise` : `Render threw: ${msg}`;
|
|
15255
|
-
const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter(
|
|
15405
|
+
const userFrames = errStack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).filter(
|
|
15406
|
+
(l) => !/(node_modules|happy-dom|@testing-library|react-dom|node:internal|node:async)/.test(l)
|
|
15407
|
+
).slice(0, 5);
|
|
15256
15408
|
const stackSource = boundaryRef.stack ?? capturedComponentStack ?? "";
|
|
15257
15409
|
const componentFrames = stackSource.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).slice(0, 4);
|
|
15258
15410
|
const stackBlock = userFrames.length ? `
|
|
@@ -15717,9 +15869,7 @@ function idLikeRequiredStringField(schema) {
|
|
|
15717
15869
|
const required = schema["required"];
|
|
15718
15870
|
if (properties === null || typeof properties !== "object") return null;
|
|
15719
15871
|
if (!Array.isArray(required)) return null;
|
|
15720
|
-
for (const [propName, propSchema] of Object.entries(
|
|
15721
|
-
properties
|
|
15722
|
-
)) {
|
|
15872
|
+
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
15723
15873
|
if (!/id$/i.test(propName)) continue;
|
|
15724
15874
|
if (!required.includes(propName)) continue;
|
|
15725
15875
|
const t = propSchema !== null && typeof propSchema === "object" ? propSchema["type"] : void 0;
|
|
@@ -15750,9 +15900,7 @@ async function checkSelectionIdentity(input) {
|
|
|
15750
15900
|
return void 0;
|
|
15751
15901
|
}
|
|
15752
15902
|
await flushPromises();
|
|
15753
|
-
const events = probe.getFireLog().slice(before).filter(
|
|
15754
|
-
(e) => e.kind === "action.fired" && e.name === actionName
|
|
15755
|
-
);
|
|
15903
|
+
const events = probe.getFireLog().slice(before).filter((e) => e.kind === "action.fired" && e.name === actionName);
|
|
15756
15904
|
return events.length > 0 ? events[events.length - 1].payload : void 0;
|
|
15757
15905
|
};
|
|
15758
15906
|
const first = await payloadFor(group[0]);
|
|
@@ -15813,7 +15961,10 @@ async function dispatchTrigger(el, kind, user) {
|
|
|
15813
15961
|
case "submit": {
|
|
15814
15962
|
const form = closestForm(el);
|
|
15815
15963
|
if (!form) return;
|
|
15816
|
-
const ev = new globalThis.Event("submit", {
|
|
15964
|
+
const ev = new globalThis.Event("submit", {
|
|
15965
|
+
bubbles: true,
|
|
15966
|
+
cancelable: true
|
|
15967
|
+
});
|
|
15817
15968
|
toEventDispatcher(form).dispatchEvent(ev);
|
|
15818
15969
|
return;
|
|
15819
15970
|
}
|
|
@@ -16018,6 +16169,9 @@ function flushPromises() {
|
|
|
16018
16169
|
return new Promise((resolve4) => setTimeout(resolve4, 0));
|
|
16019
16170
|
}
|
|
16020
16171
|
|
|
16172
|
+
// src/harness/check/runtime-render/adapter.ts
|
|
16173
|
+
init_render_check_host();
|
|
16174
|
+
|
|
16021
16175
|
// src/harness/check/runtime-render/prepare-mockup.ts
|
|
16022
16176
|
function prepareMockupProps(input) {
|
|
16023
16177
|
const { contract, fixtureProps } = input;
|
|
@@ -16150,47 +16304,111 @@ function synthesizeFromSchema2(schema, hint, warnings, depth, ordinal) {
|
|
|
16150
16304
|
}
|
|
16151
16305
|
|
|
16152
16306
|
// src/harness/check/runtime-render/adapter.ts
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16307
|
+
function createSlotLimiter(maxConcurrent) {
|
|
16308
|
+
if (maxConcurrent === void 0) return (task) => task(0);
|
|
16309
|
+
if (!Number.isInteger(maxConcurrent) || maxConcurrent < 1) {
|
|
16310
|
+
throw new RangeError(
|
|
16311
|
+
`createRuntimeRenderCheck: maxConcurrent must be a positive integer, got ${maxConcurrent}`
|
|
16312
|
+
);
|
|
16313
|
+
}
|
|
16314
|
+
let active = 0;
|
|
16315
|
+
const waiting = [];
|
|
16316
|
+
const acquire = () => {
|
|
16317
|
+
if (active < maxConcurrent) {
|
|
16318
|
+
active += 1;
|
|
16319
|
+
return Promise.resolve(0);
|
|
16320
|
+
}
|
|
16321
|
+
const enqueuedAt = Date.now();
|
|
16322
|
+
return new Promise((resolve4) => {
|
|
16323
|
+
waiting.push(() => {
|
|
16324
|
+
active += 1;
|
|
16325
|
+
resolve4(Date.now() - enqueuedAt);
|
|
16170
16326
|
});
|
|
16171
|
-
}
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16327
|
+
});
|
|
16328
|
+
};
|
|
16329
|
+
const release = () => {
|
|
16330
|
+
active -= 1;
|
|
16331
|
+
const next = waiting.shift();
|
|
16332
|
+
if (next !== void 0) next();
|
|
16333
|
+
};
|
|
16334
|
+
return async (task) => {
|
|
16335
|
+
const waitedMs = await acquire();
|
|
16336
|
+
try {
|
|
16337
|
+
return await task(waitedMs);
|
|
16338
|
+
} finally {
|
|
16339
|
+
release();
|
|
16177
16340
|
}
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
|
|
16185
|
-
|
|
16341
|
+
};
|
|
16342
|
+
}
|
|
16343
|
+
function queuedField(queuedMs) {
|
|
16344
|
+
return queuedMs > 0 ? { queuedMs } : {};
|
|
16345
|
+
}
|
|
16346
|
+
function toRenderCheckOptions(config) {
|
|
16347
|
+
if (config.timeoutMs === void 0 && config.heapMb === void 0) return void 0;
|
|
16348
|
+
return {
|
|
16349
|
+
bounds: {
|
|
16350
|
+
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {},
|
|
16351
|
+
...config.heapMb !== void 0 ? { heapMb: config.heapMb } : {}
|
|
16186
16352
|
}
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16353
|
+
};
|
|
16354
|
+
}
|
|
16355
|
+
function createRuntimeRenderCheck(config = {}) {
|
|
16356
|
+
const limit = createSlotLimiter(config.maxConcurrent);
|
|
16357
|
+
const options = toRenderCheckOptions(config);
|
|
16358
|
+
if (options !== void 0) resolveRenderCheckHostBounds(options.bounds);
|
|
16359
|
+
return {
|
|
16360
|
+
id: "runtime-render",
|
|
16361
|
+
run: async (input) => {
|
|
16362
|
+
const { sourceCode, compiledCode, contract, fixtureProps } = input;
|
|
16363
|
+
if (compiledCode === null) {
|
|
16364
|
+
return { status: "not-applicable", issues: [], reason: "no compiled code" };
|
|
16365
|
+
}
|
|
16366
|
+
if (!contract) {
|
|
16367
|
+
return { status: "not-applicable", issues: [], reason: "no contract surface" };
|
|
16368
|
+
}
|
|
16369
|
+
const mockup = prepareMockupProps({ contract, fixtureProps });
|
|
16370
|
+
let queuedMs = 0;
|
|
16371
|
+
let t0 = Date.now();
|
|
16372
|
+
let result;
|
|
16373
|
+
try {
|
|
16374
|
+
result = await limit((waitedMs) => {
|
|
16375
|
+
queuedMs = waitedMs;
|
|
16376
|
+
t0 = Date.now();
|
|
16377
|
+
return runRenderCheck(
|
|
16378
|
+
{
|
|
16379
|
+
sourceCode,
|
|
16380
|
+
mockupProps: mockup.props,
|
|
16381
|
+
contract
|
|
16382
|
+
},
|
|
16383
|
+
options
|
|
16384
|
+
);
|
|
16385
|
+
});
|
|
16386
|
+
} catch (e) {
|
|
16387
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
16388
|
+
console.warn(`[runtime-render] probe skipped \u2014 infra failure: ${message}`);
|
|
16389
|
+
return { status: "infra-skipped", issues: [], reason: message, elapsedMs: Date.now() - t0, ...queuedField(queuedMs) };
|
|
16390
|
+
}
|
|
16391
|
+
const hostLoad = result.stats.hostLoad;
|
|
16392
|
+
const load = hostLoad !== void 0 ? { hostLoad } : {};
|
|
16393
|
+
if (result.incomplete !== void 0) {
|
|
16394
|
+
const { elapsedMs, boundMs } = result.incomplete;
|
|
16395
|
+
const loadNote = hostLoad !== void 0 ? `; host load ${hostLoad.start.toFixed(1)} \u2192 ${hostLoad.end.toFixed(1)} on ${hostLoad.cores} CPUs` : "";
|
|
16396
|
+
const reason = `render check did not finish within ${boundMs} ms (stopped at ${elapsedMs} ms)${loadNote}`;
|
|
16397
|
+
console.warn(`[runtime-render] probe timed out \u2014 ${reason}`);
|
|
16398
|
+
return { status: "timed-out", issues: [], reason, elapsedMs: Date.now() - t0, ...queuedField(queuedMs), ...load };
|
|
16399
|
+
}
|
|
16400
|
+
return {
|
|
16401
|
+
status: "ran",
|
|
16402
|
+
issues: result.issues.map(toEvalIssue).filter((x) => x !== null),
|
|
16403
|
+
elapsedMs: Date.now() - t0,
|
|
16404
|
+
...queuedField(queuedMs),
|
|
16405
|
+
renderMs: result.stats.renderMs,
|
|
16406
|
+
...load
|
|
16407
|
+
};
|
|
16408
|
+
}
|
|
16409
|
+
};
|
|
16410
|
+
}
|
|
16411
|
+
var DEFAULT_RUNTIME_RENDER_CHECK = createRuntimeRenderCheck();
|
|
16194
16412
|
var CLASS_SPECIFIC_PATTERNS = [
|
|
16195
16413
|
/too many re-renders/i,
|
|
16196
16414
|
/maximum update depth/i,
|
|
@@ -16229,6 +16447,14 @@ function classifyRenderCrashFix(reason) {
|
|
|
16229
16447
|
}
|
|
16230
16448
|
return "Add null guards on optional props, handle empty arrays/strings, and verify all hook outputs before destructuring.";
|
|
16231
16449
|
}
|
|
16450
|
+
var RUNTIME_SUBCATEGORY_PREFIX = "runtime:";
|
|
16451
|
+
function parseRuntimeSubcategory(subcategory) {
|
|
16452
|
+
if (!subcategory.startsWith(RUNTIME_SUBCATEGORY_PREFIX)) return void 0;
|
|
16453
|
+
const rest = subcategory.slice(RUNTIME_SUBCATEGORY_PREFIX.length);
|
|
16454
|
+
const separator = rest.indexOf(":");
|
|
16455
|
+
const check = separator === -1 ? rest : rest.slice(0, separator);
|
|
16456
|
+
return RENDER_CHECK_KINDS.find((kind) => kind === check);
|
|
16457
|
+
}
|
|
16232
16458
|
function toEvalIssue(issue) {
|
|
16233
16459
|
if (issue.outcome === "verified" || issue.outcome === "skipped") return null;
|
|
16234
16460
|
const result = issue.outcome === "failed" ? "fail" : "warn";
|
|
@@ -17470,6 +17696,14 @@ function formatFitFeedback(issue) {
|
|
|
17470
17696
|
Fix: ${issue.fix ?? ""}`;
|
|
17471
17697
|
}
|
|
17472
17698
|
var EVAL_ROUND_THREW_REASON = "eval round threw";
|
|
17699
|
+
var PROBE_ONLY_CRITERIA_REASON = "probe-only round: no evaluator configured";
|
|
17700
|
+
var PROBE_ONLY_VISUAL_REASON = "probe-only round: no visual evaluator configured";
|
|
17701
|
+
var PROBE_ONLY_PASS = "probe-only";
|
|
17702
|
+
var COST_TRACKER_ABSENT_REASON = "cost tracker absent on an evaluation round";
|
|
17703
|
+
function requireCostTracker(costTracker) {
|
|
17704
|
+
if (costTracker === null) throw new Error(COST_TRACKER_ABSENT_REASON);
|
|
17705
|
+
return costTracker;
|
|
17706
|
+
}
|
|
17473
17707
|
function runVisualLeg(visualEvalAgent, run, measureFit) {
|
|
17474
17708
|
const provider = visionJudgeProvider(visualEvalAgent.provider);
|
|
17475
17709
|
if (provider === void 0) {
|
|
@@ -17512,6 +17746,36 @@ function fingerprintFail(issue) {
|
|
|
17512
17746
|
const desc = (issue.description ?? "").toLowerCase().replace(/line\s+\d+/g, "line N").replace(/\s+/g, " ").trim().slice(0, 40);
|
|
17513
17747
|
return `${cat}|${sub}|${desc}`;
|
|
17514
17748
|
}
|
|
17749
|
+
function failedCheckKinds(probeIssues) {
|
|
17750
|
+
const failed2 = /* @__PURE__ */ new Set();
|
|
17751
|
+
for (const issue of probeIssues) {
|
|
17752
|
+
if (issue.result !== "fail" || issue.subcategory === void 0) continue;
|
|
17753
|
+
const kind = parseRuntimeSubcategory(issue.subcategory);
|
|
17754
|
+
if (kind !== void 0) failed2.add(kind);
|
|
17755
|
+
}
|
|
17756
|
+
return RENDER_CHECK_KINDS.filter((kind) => failed2.has(kind));
|
|
17757
|
+
}
|
|
17758
|
+
function probeMetaOf(outcome, probeIssues) {
|
|
17759
|
+
const detail = {
|
|
17760
|
+
...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
|
|
17761
|
+
...outcome.renderMs !== void 0 ? { renderMs: outcome.renderMs } : {},
|
|
17762
|
+
...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {},
|
|
17763
|
+
...outcome.queuedMs !== void 0 ? { queuedMs: outcome.queuedMs } : {}
|
|
17764
|
+
};
|
|
17765
|
+
const elapsed = outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {};
|
|
17766
|
+
if (outcome.status !== "ran") {
|
|
17767
|
+
return { status: outcome.status, ...elapsed, ...detail };
|
|
17768
|
+
}
|
|
17769
|
+
const [first, ...rest] = failedCheckKinds(probeIssues);
|
|
17770
|
+
if (first === void 0) {
|
|
17771
|
+
return { status: "ran", verdict: "pass", ...elapsed, ...detail };
|
|
17772
|
+
}
|
|
17773
|
+
return { status: "ran", verdict: "fail", failChecks: [first, ...rest], ...elapsed, ...detail };
|
|
17774
|
+
}
|
|
17775
|
+
function describeProbeMeta(meta) {
|
|
17776
|
+
if (meta.verdict === void 0) return meta.status;
|
|
17777
|
+
return meta.verdict === "pass" ? "ran, verdict pass" : `ran, verdict fail (${meta.failChecks.join(", ")})`;
|
|
17778
|
+
}
|
|
17515
17779
|
async function runProbeAtExit(input) {
|
|
17516
17780
|
const { harness, sourceCode, compiledCode, contract, fixtureProps } = input;
|
|
17517
17781
|
const probe = harness.check.runtimeRender;
|
|
@@ -17544,12 +17808,7 @@ async function runProbeAtExit(input) {
|
|
|
17544
17808
|
meta: { status: "infra-skipped", reason: message }
|
|
17545
17809
|
};
|
|
17546
17810
|
}
|
|
17547
|
-
const meta =
|
|
17548
|
-
status: outcome.status,
|
|
17549
|
-
...outcome.reason !== void 0 ? { reason: outcome.reason } : {},
|
|
17550
|
-
...outcome.elapsedMs !== void 0 ? { elapsedMs: outcome.elapsedMs } : {},
|
|
17551
|
-
...outcome.hostLoad !== void 0 ? { hostLoad: outcome.hostLoad } : {}
|
|
17552
|
-
};
|
|
17811
|
+
const meta = probeMetaOf(outcome, outcome.issues);
|
|
17553
17812
|
if (outcome.status !== "ran") {
|
|
17554
17813
|
return { fired: false, recoverableFail: false, probeIssues: [], meta };
|
|
17555
17814
|
}
|
|
@@ -17592,11 +17851,13 @@ async function runEvalRound(ctx, input) {
|
|
|
17592
17851
|
visualThreshold,
|
|
17593
17852
|
qualityMode,
|
|
17594
17853
|
maxEvalRounds,
|
|
17595
|
-
costTracker,
|
|
17596
17854
|
llmEvalMod,
|
|
17597
17855
|
visualMod,
|
|
17598
17856
|
preWarmPromise,
|
|
17599
|
-
onProgress
|
|
17857
|
+
onProgress,
|
|
17858
|
+
probeOnly,
|
|
17859
|
+
probeRepairUsed,
|
|
17860
|
+
probeRepairTrigger
|
|
17600
17861
|
} = ctx;
|
|
17601
17862
|
const { compiledCode, prevModeSubcats, prevFailFingerprints } = input;
|
|
17602
17863
|
let { evalRoundsUsed, preWarmedContext } = input;
|
|
@@ -17607,6 +17868,88 @@ async function runEvalRound(ctx, input) {
|
|
|
17607
17868
|
let evalTokens = { input: 0, output: 0 };
|
|
17608
17869
|
let evalLlmMs = 0;
|
|
17609
17870
|
try {
|
|
17871
|
+
if (probeOnly) {
|
|
17872
|
+
const exitProbe = await runProbeAtExit({
|
|
17873
|
+
harness,
|
|
17874
|
+
sourceCode: currentSource,
|
|
17875
|
+
compiledCode,
|
|
17876
|
+
contract,
|
|
17877
|
+
fixtureProps
|
|
17878
|
+
});
|
|
17879
|
+
const probeOnlyResult = {
|
|
17880
|
+
issues: [...exitProbe.probeIssues],
|
|
17881
|
+
pass: [PROBE_ONLY_PASS],
|
|
17882
|
+
runtimeProbe: exitProbe.meta,
|
|
17883
|
+
criteriaCoverage: notApplicableCoverage(PROBE_ONLY_CRITERIA_REASON),
|
|
17884
|
+
visualCoverage: { status: "not-applicable", reason: PROBE_ONLY_VISUAL_REASON }
|
|
17885
|
+
};
|
|
17886
|
+
const recoverableFail = exitProbe.fired && exitProbe.recoverableFail;
|
|
17887
|
+
if (probeRepairUsed) {
|
|
17888
|
+
if (probeRepairTrigger === void 0) {
|
|
17889
|
+
throw new Error("harness defect: a probe-only re-probe round without the probe that bought the repair turn");
|
|
17890
|
+
}
|
|
17891
|
+
const repair = { attempted: true, compiled: true, trigger: probeRepairTrigger };
|
|
17892
|
+
evalResult = { ...probeOnlyResult, runtimeProbeRepair: repair };
|
|
17893
|
+
console.log(
|
|
17894
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only re-probe after the repair turn \u2014 ${describeProbeMeta(exitProbe.meta)}${recoverableFail ? ", recoverable crash still present" : ""} \u2014 serving`
|
|
17895
|
+
);
|
|
17896
|
+
return {
|
|
17897
|
+
control: "break",
|
|
17898
|
+
evalDone: true,
|
|
17899
|
+
evalResult,
|
|
17900
|
+
evalRoundsUsed,
|
|
17901
|
+
prevModeSubcats,
|
|
17902
|
+
prevFailFingerprints,
|
|
17903
|
+
preWarmedContext,
|
|
17904
|
+
evalTokens,
|
|
17905
|
+
evalLlmMs,
|
|
17906
|
+
lastResultText: "",
|
|
17907
|
+
isEvalFeedback: false,
|
|
17908
|
+
lastDiffFailed: false
|
|
17909
|
+
};
|
|
17910
|
+
}
|
|
17911
|
+
evalResult = probeOnlyResult;
|
|
17912
|
+
if (recoverableFail) {
|
|
17913
|
+
const runtimeFails = exitProbe.probeIssues.filter(
|
|
17914
|
+
(i) => i.result === "fail" && i.subcategory?.startsWith("runtime:")
|
|
17915
|
+
);
|
|
17916
|
+
const lines = runtimeFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatRuntimeProbeFeedback);
|
|
17917
|
+
console.log(
|
|
17918
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 runtime probe fail (recoverable) \u2014 granting the one repair turn`
|
|
17919
|
+
);
|
|
17920
|
+
return {
|
|
17921
|
+
control: "feedback",
|
|
17922
|
+
evalDone: false,
|
|
17923
|
+
evalResult,
|
|
17924
|
+
evalRoundsUsed,
|
|
17925
|
+
prevModeSubcats,
|
|
17926
|
+
prevFailFingerprints,
|
|
17927
|
+
preWarmedContext,
|
|
17928
|
+
evalTokens,
|
|
17929
|
+
evalLlmMs,
|
|
17930
|
+
lastResultText: lines.join("\n\n"),
|
|
17931
|
+
isEvalFeedback: true,
|
|
17932
|
+
lastDiffFailed: false
|
|
17933
|
+
};
|
|
17934
|
+
}
|
|
17935
|
+
console.log(
|
|
17936
|
+
`[simple] eval round ${evalRoundsUsed}: probe-only round \u2014 probe ${describeProbeMeta(exitProbe.meta)} \u2014 serving`
|
|
17937
|
+
);
|
|
17938
|
+
return {
|
|
17939
|
+
control: "break",
|
|
17940
|
+
evalDone: true,
|
|
17941
|
+
evalResult,
|
|
17942
|
+
evalRoundsUsed,
|
|
17943
|
+
prevModeSubcats,
|
|
17944
|
+
prevFailFingerprints,
|
|
17945
|
+
preWarmedContext,
|
|
17946
|
+
evalTokens,
|
|
17947
|
+
evalLlmMs,
|
|
17948
|
+
lastResultText: "",
|
|
17949
|
+
isEvalFeedback: false,
|
|
17950
|
+
lastDiffFailed: false
|
|
17951
|
+
};
|
|
17952
|
+
}
|
|
17610
17953
|
const checkResult = await runCheck({
|
|
17611
17954
|
harness,
|
|
17612
17955
|
sourceCode: currentSource,
|
|
@@ -17681,6 +18024,7 @@ async function runEvalRound(ctx, input) {
|
|
|
17681
18024
|
lastDiffFailed: false
|
|
17682
18025
|
};
|
|
17683
18026
|
}
|
|
18027
|
+
const costTracker = requireCostTracker(ctx.costTracker);
|
|
17684
18028
|
if (evalRoundsUsed === 1 && !preWarmedContext && preWarmPromise) {
|
|
17685
18029
|
preWarmedContext = await preWarmPromise;
|
|
17686
18030
|
}
|
|
@@ -18166,6 +18510,8 @@ function createTelemetry() {
|
|
|
18166
18510
|
totalIn: 0,
|
|
18167
18511
|
totalOut: 0,
|
|
18168
18512
|
evalResult: void 0,
|
|
18513
|
+
probeRepairUsed: false,
|
|
18514
|
+
probeRepairTrigger: void 0,
|
|
18169
18515
|
contractFeedback: void 0,
|
|
18170
18516
|
sameExchangeBreak: void 0,
|
|
18171
18517
|
compiledCode: "",
|
|
@@ -18265,6 +18611,15 @@ function createGenerateTaskRunner(input) {
|
|
|
18265
18611
|
absorbTurn(telemetry, turn);
|
|
18266
18612
|
iconNamesCache = turn.iconNamesCache;
|
|
18267
18613
|
preWarmedContext = turn.preWarmedContext;
|
|
18614
|
+
if (turn.control !== "proceed" && session.probeOnlyEnabled && telemetry.probeRepairUsed) {
|
|
18615
|
+
if (telemetry.evalResult !== void 0) {
|
|
18616
|
+
telemetry.evalResult = { ...telemetry.evalResult, runtimeProbeRepair: { attempted: true, compiled: false } };
|
|
18617
|
+
}
|
|
18618
|
+
console.log(
|
|
18619
|
+
`[simple] probe-only repair turn did not compile (${turn.outcome ?? "no tool call"}) \u2014 serving the pre-repair card`
|
|
18620
|
+
);
|
|
18621
|
+
break;
|
|
18622
|
+
}
|
|
18268
18623
|
if (turn.control === "break") break;
|
|
18269
18624
|
if (turn.control === "continue") {
|
|
18270
18625
|
lastResultText = turn.lastResultText;
|
|
@@ -18303,7 +18658,7 @@ function createGenerateTaskRunner(input) {
|
|
|
18303
18658
|
`[simple] initial result delivered | ${codingDoneMs}ms | turns=${telemetry.turnsUsed} | tokens=${telemetry.totalIn + telemetry.totalOut} | compiled=${telemetry.compiledCode.length}B`
|
|
18304
18659
|
);
|
|
18305
18660
|
}
|
|
18306
|
-
if (!evalDone && telemetry.compiledCode && session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled)) {
|
|
18661
|
+
if (!evalDone && telemetry.compiledCode && (session.tiersMod && (session.codeEvalEnabled || session.visualEvalEnabled) || session.probeOnlyEnabled)) {
|
|
18307
18662
|
const evalRoundStart = Date.now();
|
|
18308
18663
|
const round = await runEvalRound(
|
|
18309
18664
|
{
|
|
@@ -18323,7 +18678,10 @@ function createGenerateTaskRunner(input) {
|
|
|
18323
18678
|
llmEvalMod: session.llmEvalMod,
|
|
18324
18679
|
visualMod: session.visualMod,
|
|
18325
18680
|
preWarmPromise: session.preWarmPromise,
|
|
18326
|
-
onProgress: params.onProgress
|
|
18681
|
+
onProgress: params.onProgress,
|
|
18682
|
+
probeOnly: session.probeOnlyEnabled,
|
|
18683
|
+
probeRepairUsed: telemetry.probeRepairUsed,
|
|
18684
|
+
...telemetry.probeRepairTrigger !== void 0 ? { probeRepairTrigger: telemetry.probeRepairTrigger } : {}
|
|
18327
18685
|
},
|
|
18328
18686
|
{
|
|
18329
18687
|
compiledCode: telemetry.compiledCode,
|
|
@@ -18344,6 +18702,10 @@ function createGenerateTaskRunner(input) {
|
|
|
18344
18702
|
evalDone = round.evalDone;
|
|
18345
18703
|
absorbTokens(telemetry, round.evalTokens);
|
|
18346
18704
|
if (round.control === "break") break;
|
|
18705
|
+
if (session.probeOnlyEnabled) {
|
|
18706
|
+
telemetry.probeRepairUsed = true;
|
|
18707
|
+
telemetry.probeRepairTrigger = round.evalResult?.runtimeProbe;
|
|
18708
|
+
}
|
|
18347
18709
|
lastResultText = round.lastResultText;
|
|
18348
18710
|
isEvalFeedback = round.isEvalFeedback;
|
|
18349
18711
|
lastDiffFailed = round.lastDiffFailed;
|
|
@@ -18351,6 +18713,9 @@ function createGenerateTaskRunner(input) {
|
|
|
18351
18713
|
break;
|
|
18352
18714
|
}
|
|
18353
18715
|
} while (telemetry.turnsUsed < maxTurns && !evalDone);
|
|
18716
|
+
if (session.probeOnlyEnabled && telemetry.probeRepairUsed && telemetry.evalResult !== void 0 && telemetry.evalResult.runtimeProbeRepair === void 0) {
|
|
18717
|
+
console.log(`[simple] probe-only repair turn not taken \u2014 turn cap (${maxTurns}) reached before it`);
|
|
18718
|
+
}
|
|
18354
18719
|
telemetry.codingMs = Date.now() - telemetry.codingStartedAtMs;
|
|
18355
18720
|
if (!telemetry.compiledCode) {
|
|
18356
18721
|
for (const [, meta] of session.commitMeta) {
|
|
@@ -18577,6 +18942,7 @@ async function initSession(input) {
|
|
|
18577
18942
|
const qualityMode = qualityConfig?.quality ?? "fast";
|
|
18578
18943
|
const codeEvalEnabled = !!params.evaluation?.enabled;
|
|
18579
18944
|
const visualEvalEnabled = !!(params.visualEvaluation?.enabled || qualityConfig?.visualEval);
|
|
18945
|
+
const probeOnlyEnabled = harness.check.runtimeRender !== void 0 && !codeEvalEnabled && !visualEvalEnabled;
|
|
18580
18946
|
const maxEvalRounds = qualityMode === "high-quality" ? params.evaluation?.maxRounds ?? 3 : params.evaluation?.maxRounds ?? 2;
|
|
18581
18947
|
const visualThreshold = params.visualEvaluation?.passThreshold ?? 70;
|
|
18582
18948
|
let tiersMod = null;
|
|
@@ -18607,6 +18973,7 @@ async function initSession(input) {
|
|
|
18607
18973
|
systemPrompt,
|
|
18608
18974
|
codeEvalEnabled,
|
|
18609
18975
|
visualEvalEnabled,
|
|
18976
|
+
probeOnlyEnabled,
|
|
18610
18977
|
maxEvalRounds,
|
|
18611
18978
|
visualThreshold,
|
|
18612
18979
|
qualityMode,
|
|
@@ -18711,7 +19078,15 @@ async function dispatchGeneration(params) {
|
|
|
18711
19078
|
// prompt's gadget catalog renders a `Type:` line per third-party
|
|
18712
19079
|
// gadget (the LLM sees a wrapper's real call shape it otherwise
|
|
18713
19080
|
// can't know).
|
|
18714
|
-
systemPromptBuilder: ({
|
|
19081
|
+
systemPromptBuilder: ({
|
|
19082
|
+
userRequest,
|
|
19083
|
+
shellType,
|
|
19084
|
+
screen,
|
|
19085
|
+
axisDelta,
|
|
19086
|
+
designMode,
|
|
19087
|
+
canvas,
|
|
19088
|
+
profile
|
|
19089
|
+
}) => buildSystemPrompt2(
|
|
18715
19090
|
userRequest,
|
|
18716
19091
|
shellType,
|
|
18717
19092
|
screen,
|
|
@@ -18727,14 +19102,14 @@ async function dispatchGeneration(params) {
|
|
|
18727
19102
|
axisChecks: REGISTRY.filter(
|
|
18728
19103
|
(check) => matches(classification.vector, check)
|
|
18729
19104
|
),
|
|
18730
|
-
// The
|
|
18731
|
-
// design) stay on the dispatch side of
|
|
18732
|
-
// createHarness defaults this to undefined.
|
|
19105
|
+
// The runtime-render check, built under the caller's probe config.
|
|
19106
|
+
// Heavy deps (happy-dom + wire + design) stay on the dispatch side of
|
|
19107
|
+
// the boundary; the skeleton createHarness defaults this to undefined.
|
|
18733
19108
|
//
|
|
18734
19109
|
// Gated behind `enableRuntimeRender` (default true) so
|
|
18735
19110
|
// benchmark/dev callers can opt out of in-loop probe execution and
|
|
18736
19111
|
// run the probe externally as a final post-generation check.
|
|
18737
|
-
runtimeRender: params.enableRuntimeRender ?? true ? DEFAULT_RUNTIME_RENDER_CHECK : void 0
|
|
19112
|
+
runtimeRender: params.enableRuntimeRender ?? true ? params.runtimeRender ?? DEFAULT_RUNTIME_RENDER_CHECK : void 0
|
|
18738
19113
|
});
|
|
18739
19114
|
console.log(
|
|
18740
19115
|
`[harness] id=${harness.id} name=${harness.name} workflow=${harness.process.workflow.name} overrides=${harness.meta.overrides.join(",") || "none"}`
|
|
@@ -18752,67 +19127,70 @@ async function dispatchGeneration(params) {
|
|
|
18752
19127
|
evaluationAgent
|
|
18753
19128
|
});
|
|
18754
19129
|
const { resolveRunPolicyForProfile: resolveRunPolicyForProfile2 } = await Promise.resolve().then(() => (init_policy2(), policy_exports));
|
|
18755
|
-
const resolvedPolicy = resolveRunPolicyForProfile2(
|
|
18756
|
-
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
);
|
|
19130
|
+
const resolvedPolicy = resolveRunPolicyForProfile2(process.env.GGUI_POLICY_PROFILE, harness, {
|
|
19131
|
+
provider: routerProvider,
|
|
19132
|
+
modelId: resolvedModel
|
|
19133
|
+
});
|
|
18760
19134
|
let systemPromptOverride;
|
|
18761
19135
|
{
|
|
18762
19136
|
const { PRIMITIVES_DOCUMENTATION: PRIMITIVES_DOCUMENTATION2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
|
|
18763
19137
|
const fullBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION2, "utf8");
|
|
19138
|
+
const docFormat = resolvedPolicy.context.primitiveDocFormat ?? "markdown";
|
|
19139
|
+
const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
|
|
18764
19140
|
const mode = resolvedPolicy.context.primitiveDocSlice ?? "full";
|
|
18765
|
-
|
|
18766
|
-
|
|
19141
|
+
let doc = PRIMITIVES_DOCUMENTATION2;
|
|
19142
|
+
if (docFormat === "ts" && indexMode === "off") {
|
|
19143
|
+
const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
|
|
19144
|
+
doc = PRIMITIVES_DOCUMENTATION_TS2;
|
|
19145
|
+
const tsBytes = Buffer.byteLength(doc, "utf8");
|
|
19146
|
+
console.log(
|
|
19147
|
+
`[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
|
|
19148
|
+
);
|
|
19149
|
+
}
|
|
19150
|
+
const formatBytes = Buffer.byteLength(doc, "utf8");
|
|
19151
|
+
if (mode === "axis-keyed" || mode === "axis-keyed+contract") {
|
|
19152
|
+
const {
|
|
19153
|
+
computePrimitiveAllowlist: computePrimitiveAllowlist2,
|
|
19154
|
+
computeContractPrimitives: computeContractPrimitives2,
|
|
19155
|
+
slicePrimitiveDocumentation: slicePrimitiveDocumentation2,
|
|
19156
|
+
slicePrimitiveDocumentationTs: slicePrimitiveDocumentationTs2
|
|
19157
|
+
} = await Promise.resolve().then(() => (init_primitive_slice(), primitive_slice_exports));
|
|
18767
19158
|
let allowlist = computePrimitiveAllowlist2(classification);
|
|
19159
|
+
if (mode === "axis-keyed+contract") {
|
|
19160
|
+
const fromContract = computeContractPrimitives2(effectiveContract);
|
|
19161
|
+
allowlist = [.../* @__PURE__ */ new Set([...allowlist, ...fromContract])].sort();
|
|
19162
|
+
}
|
|
18768
19163
|
const excludes = resolvedPolicy.context.primitiveDocExcludes ?? [];
|
|
18769
19164
|
if (excludes.length > 0) {
|
|
18770
19165
|
const excludeSet = new Set(excludes);
|
|
18771
19166
|
allowlist = allowlist.filter((p) => !excludeSet.has(p));
|
|
18772
19167
|
}
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
allowlist
|
|
18776
|
-
);
|
|
18777
|
-
const slicedBytes = Buffer.byteLength(sliced, "utf8");
|
|
19168
|
+
doc = docFormat === "ts" && indexMode === "off" ? slicePrimitiveDocumentationTs2(doc, allowlist) : slicePrimitiveDocumentation2(doc, allowlist);
|
|
19169
|
+
const slicedBytes = Buffer.byteLength(doc, "utf8");
|
|
18778
19170
|
const excludeTag = excludes.length ? ` excludes=${excludes.join(",")}` : "";
|
|
18779
19171
|
console.log(
|
|
18780
|
-
`[simple] primitive-doc-bytes=${slicedBytes} mode
|
|
18781
|
-
);
|
|
18782
|
-
systemPromptOverride = harness.how.systemPrompt.replace(
|
|
18783
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
18784
|
-
sliced
|
|
19172
|
+
`[simple] primitive-doc-bytes=${slicedBytes} mode=${mode} baseline=${formatBytes} drop=${((1 - slicedBytes / formatBytes) * 100).toFixed(1)}% allowlist=${allowlist.length}/${allowlist.join(",")}${excludeTag}`
|
|
18785
19173
|
);
|
|
18786
19174
|
} else {
|
|
18787
19175
|
console.log(
|
|
18788
|
-
`[simple] primitive-doc-bytes=${
|
|
19176
|
+
`[simple] primitive-doc-bytes=${formatBytes} mode=full baseline=${formatBytes} drop=0.0%`
|
|
18789
19177
|
);
|
|
18790
19178
|
}
|
|
18791
|
-
const indexMode = resolvedPolicy.context.primitiveIndex ?? "off";
|
|
18792
19179
|
if (indexMode !== "off") {
|
|
18793
19180
|
const { buildPrimitiveIndex: buildPrimitiveIndex2 } = await Promise.resolve().then(() => (init_primitive_index(), primitive_index_exports));
|
|
18794
|
-
|
|
18795
|
-
const indexBytes = Buffer.byteLength(
|
|
19181
|
+
doc = buildPrimitiveIndex2(PRIMITIVES_DOCUMENTATION2, indexMode);
|
|
19182
|
+
const indexBytes = Buffer.byteLength(doc, "utf8");
|
|
18796
19183
|
console.log(
|
|
18797
19184
|
`[simple] primitive-index-bytes=${indexBytes} mode=${indexMode} baseline=${fullBytes} drop=${((1 - indexBytes / fullBytes) * 100).toFixed(1)}%`
|
|
18798
19185
|
);
|
|
18799
|
-
systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
|
|
18800
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
18801
|
-
index
|
|
18802
|
-
);
|
|
18803
19186
|
}
|
|
18804
|
-
|
|
18805
|
-
|
|
18806
|
-
const { PRIMITIVES_DOCUMENTATION_TS: PRIMITIVES_DOCUMENTATION_TS2 } = await Promise.resolve().then(() => (init_get_primitives_ts(), get_primitives_ts_exports));
|
|
18807
|
-
const tsBytes = Buffer.byteLength(PRIMITIVES_DOCUMENTATION_TS2, "utf8");
|
|
18808
|
-
console.log(
|
|
18809
|
-
`[simple] primitive-doc-ts-bytes=${tsBytes} baseline=${fullBytes} drop=${((1 - tsBytes / fullBytes) * 100).toFixed(1)}%`
|
|
18810
|
-
);
|
|
18811
|
-
systemPromptOverride = (systemPromptOverride ?? harness.how.systemPrompt).replace(
|
|
18812
|
-
PRIMITIVES_DOCUMENTATION2,
|
|
18813
|
-
PRIMITIVES_DOCUMENTATION_TS2
|
|
18814
|
-
);
|
|
19187
|
+
if (doc !== PRIMITIVES_DOCUMENTATION2) {
|
|
19188
|
+
systemPromptOverride = harness.how.systemPrompt.replace(PRIMITIVES_DOCUMENTATION2, doc);
|
|
18815
19189
|
}
|
|
19190
|
+
const sentPrompt = systemPromptOverride ?? harness.how.systemPrompt;
|
|
19191
|
+
console.log(
|
|
19192
|
+
`[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}`
|
|
19193
|
+
);
|
|
18816
19194
|
if (resolvedPolicy.context.planFirstTurn) {
|
|
18817
19195
|
const phaseGuidance = `
|
|
18818
19196
|
|
|
@@ -18864,7 +19242,11 @@ Your plan is a commitment, not a design doc. Keep it tight. Do not over-plan; ju
|
|
|
18864
19242
|
);
|
|
18865
19243
|
}
|
|
18866
19244
|
const finalSource = runResult.finalSource ?? "";
|
|
18867
|
-
const result = await assembleGenerationResult({
|
|
19245
|
+
const result = await assembleGenerationResult({
|
|
19246
|
+
session,
|
|
19247
|
+
telemetry,
|
|
19248
|
+
source: finalSource
|
|
19249
|
+
});
|
|
18868
19250
|
return withArmRecord(result, params);
|
|
18869
19251
|
}
|
|
18870
19252
|
function withArmRecord(result, params) {
|
|
@@ -19311,6 +19693,7 @@ var DEFAULT_MODEL = "anthropic/claude-haiku-4-5";
|
|
|
19311
19693
|
function createUiGenerator(options = {}) {
|
|
19312
19694
|
const {
|
|
19313
19695
|
enableRuntimeRender = false,
|
|
19696
|
+
runtimeRenderProbe,
|
|
19314
19697
|
maxAttempts,
|
|
19315
19698
|
maxEvalRounds,
|
|
19316
19699
|
evaluation,
|
|
@@ -19322,6 +19705,7 @@ function createUiGenerator(options = {}) {
|
|
|
19322
19705
|
onGenerated,
|
|
19323
19706
|
designMode
|
|
19324
19707
|
} = options;
|
|
19708
|
+
const runtimeRender = enableRuntimeRender ? createRuntimeRenderCheck(runtimeRenderProbe) : void 0;
|
|
19325
19709
|
const identity = resolveIdentity(options);
|
|
19326
19710
|
return {
|
|
19327
19711
|
slug: identity.slug,
|
|
@@ -19373,17 +19757,11 @@ function createUiGenerator(options = {}) {
|
|
|
19373
19757
|
const promptWithRendering = rendering ? injectRenderingContext(input.request.prompt, rendering) : input.request.prompt;
|
|
19374
19758
|
const canvas = rendering ? canvasForRenderingContext(rendering) : void 0;
|
|
19375
19759
|
const shellType = harnessShellForRendering(rendering);
|
|
19376
|
-
const promptWithVariance = injectVariance(
|
|
19377
|
-
|
|
19378
|
-
|
|
19379
|
-
{ profileDeclared: hasProfile(input.profile) }
|
|
19380
|
-
);
|
|
19760
|
+
const promptWithVariance = injectVariance(promptWithRendering, input.variance, {
|
|
19761
|
+
profileDeclared: hasProfile(input.profile)
|
|
19762
|
+
});
|
|
19381
19763
|
const resolvedAppGadgets = input.appGadgets !== void 0 ? input.appGadgets : gadgetCatalog !== void 0 && input.appId !== void 0 ? await gadgetCatalog.list(input.appId) : void 0;
|
|
19382
|
-
const userPrompt = injectContracts(
|
|
19383
|
-
promptWithVariance,
|
|
19384
|
-
input.contract,
|
|
19385
|
-
resolvedAppGadgets
|
|
19386
|
-
);
|
|
19764
|
+
const userPrompt = injectContracts(promptWithVariance, input.contract, resolvedAppGadgets);
|
|
19387
19765
|
const dials = effortDials(input.profile?.effort);
|
|
19388
19766
|
const effectiveMaxAttempts = dials?.maxAttempts ?? maxAttempts;
|
|
19389
19767
|
const effectiveMaxEvalRounds = dials?.maxEvalRounds ?? maxEvalRounds;
|
|
@@ -19408,6 +19786,7 @@ function createUiGenerator(options = {}) {
|
|
|
19408
19786
|
// `dispatchGeneration`.
|
|
19409
19787
|
...input.gadgetTypes !== void 0 ? { gadgetTypes: input.gadgetTypes } : {},
|
|
19410
19788
|
enableRuntimeRender,
|
|
19789
|
+
...runtimeRender !== void 0 ? { runtimeRender } : {},
|
|
19411
19790
|
...routeOverride !== void 0 ? { routeOverride } : {},
|
|
19412
19791
|
...onRetry !== void 0 ? { onRetry } : {},
|
|
19413
19792
|
...designMode !== void 0 ? { designMode } : {},
|
|
@@ -19419,6 +19798,8 @@ function createUiGenerator(options = {}) {
|
|
|
19419
19798
|
...input.fixtureProps !== void 0 ? { fixtureProps: input.fixtureProps } : {}
|
|
19420
19799
|
});
|
|
19421
19800
|
onGenerated?.(result);
|
|
19801
|
+
const runtimeProbe = metadataRuntimeProbe(result);
|
|
19802
|
+
const evalMs = metadataEvalMs(result);
|
|
19422
19803
|
const metadata = {
|
|
19423
19804
|
provider: input.llm.provider,
|
|
19424
19805
|
generator: identity.slug,
|
|
@@ -19434,7 +19815,11 @@ function createUiGenerator(options = {}) {
|
|
|
19434
19815
|
// them; others omit). Pass through truthfully — absent on the
|
|
19435
19816
|
// adapter result stays absent here, never defaulted to 0.
|
|
19436
19817
|
...result.cacheReadTokens !== void 0 ? { cacheReadTokens: result.cacheReadTokens } : {},
|
|
19437
|
-
...result.cacheCreationTokens !== void 0 ? { cacheCreationTokens: result.cacheCreationTokens } : {}
|
|
19818
|
+
...result.cacheCreationTokens !== void 0 ? { cacheCreationTokens: result.cacheCreationTokens } : {},
|
|
19819
|
+
// ggui#1380 — the runtime probe's record and the eval rounds' wall,
|
|
19820
|
+
// when the harness ran a round; absent stays absent.
|
|
19821
|
+
...runtimeProbe !== void 0 ? { runtimeProbe } : {},
|
|
19822
|
+
...evalMs !== void 0 ? { evalMs } : {}
|
|
19438
19823
|
};
|
|
19439
19824
|
return {
|
|
19440
19825
|
ok: true,
|
|
@@ -19481,9 +19866,7 @@ function createUiGenerator(options = {}) {
|
|
|
19481
19866
|
function resolveIdentity(opts) {
|
|
19482
19867
|
const hasSlug = typeof opts.slug === "string" && opts.slug.length > 0;
|
|
19483
19868
|
if (hasSlug && opts.tier !== void 0) {
|
|
19484
|
-
throw new Error(
|
|
19485
|
-
"createUiGenerator: pass either { slug } or { tier } \u2014 not both."
|
|
19486
|
-
);
|
|
19869
|
+
throw new Error("createUiGenerator: pass either { slug } or { tier } \u2014 not both.");
|
|
19487
19870
|
}
|
|
19488
19871
|
const model = opts.model ?? DEFAULT_MODEL;
|
|
19489
19872
|
if (hasSlug) {
|
|
@@ -19505,6 +19888,32 @@ function resolveIdentity(opts) {
|
|
|
19505
19888
|
}
|
|
19506
19889
|
return { slug, tier, model };
|
|
19507
19890
|
}
|
|
19891
|
+
function metadataProbeOutcome(meta) {
|
|
19892
|
+
const elapsed = {
|
|
19893
|
+
...meta.elapsedMs !== void 0 ? { elapsedMs: meta.elapsedMs } : {},
|
|
19894
|
+
...meta.queuedMs !== void 0 ? { queuedMs: meta.queuedMs } : {}
|
|
19895
|
+
};
|
|
19896
|
+
if (meta.status !== "ran") return { status: meta.status, ...elapsed };
|
|
19897
|
+
if (meta.verdict === "pass") return { status: "ran", verdict: "pass", ...elapsed };
|
|
19898
|
+
return { status: "ran", verdict: "fail", failChecks: meta.failChecks, ...elapsed };
|
|
19899
|
+
}
|
|
19900
|
+
function metadataProbeRepair(repair) {
|
|
19901
|
+
return repair.compiled ? { attempted: true, compiled: true, trigger: metadataProbeOutcome(repair.trigger) } : { attempted: true, compiled: false };
|
|
19902
|
+
}
|
|
19903
|
+
function metadataRuntimeProbe(result) {
|
|
19904
|
+
const probe = result.evalResult?.runtimeProbe;
|
|
19905
|
+
if (probe === void 0) return void 0;
|
|
19906
|
+
const repair = result.evalResult?.runtimeProbeRepair;
|
|
19907
|
+
return {
|
|
19908
|
+
...metadataProbeOutcome(probe),
|
|
19909
|
+
...repair !== void 0 ? { repair: metadataProbeRepair(repair) } : {}
|
|
19910
|
+
};
|
|
19911
|
+
}
|
|
19912
|
+
function metadataEvalMs(result) {
|
|
19913
|
+
const breakdown = result.breakdown;
|
|
19914
|
+
if (breakdown === void 0 || breakdown.evalRounds === 0) return void 0;
|
|
19915
|
+
return breakdown.evalMs;
|
|
19916
|
+
}
|
|
19508
19917
|
function mapLlmProviderToDispatchProvider(provider) {
|
|
19509
19918
|
switch (provider) {
|
|
19510
19919
|
case "anthropic":
|