@ggui-ai/ui-gen 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/{adapter-BJwn74rx.d.ts → adapter-h4_V69LI.d.ts} +1 -1
  2. package/dist/adapters/base.d.ts +6 -1
  3. package/dist/adapters/base.js.map +1 -1
  4. package/dist/adapters/generation-dispatch.d.ts +2 -2
  5. package/dist/adapters/generation-dispatch.js +61 -10
  6. package/dist/adapters/generation-dispatch.js.map +1 -1
  7. package/dist/adapters/index.js +36 -14
  8. package/dist/adapters/index.js.map +1 -1
  9. package/dist/advanced/index.d.ts +4 -4
  10. package/dist/advanced/index.js +94 -19
  11. package/dist/advanced/index.js.map +1 -1
  12. package/dist/boilerplate.js +4 -6
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +1 -7
  15. package/dist/check/index.js +50 -4
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/{create-ui-generator-N4gRXpcL.d.ts → create-ui-generator-BrZHF0cf.d.ts} +2 -2
  18. package/dist/{dispatch-BUoXRj_r.d.ts → dispatch-B07TI30d.d.ts} +1 -1
  19. package/dist/evaluation/axis-checks/index.d.ts +3 -3
  20. package/dist/evaluation/axis-checks/index.js.map +1 -1
  21. package/dist/evaluation/axis-checks/registry.d.ts +1 -1
  22. package/dist/evaluation/index.d.ts +22 -12
  23. package/dist/evaluation/index.js +36 -10
  24. package/dist/evaluation/index.js.map +1 -1
  25. package/dist/evaluation/types.d.ts +1 -1
  26. package/dist/harness/check/runtime-render/index.d.ts +3 -3
  27. package/dist/harness/index.d.ts +1 -1
  28. package/dist/harness/index.js +4 -6
  29. package/dist/harness/index.js.map +1 -1
  30. package/dist/harness/prompts.js +1 -4
  31. package/dist/harness/prompts.js.map +1 -1
  32. package/dist/harness/result-types.d.ts +2 -2
  33. package/dist/harness/runtime.d.ts +1 -1
  34. package/dist/harness/runtime.js +6 -7
  35. package/dist/harness/runtime.js.map +1 -1
  36. package/dist/harness/types-public.d.ts +1 -1
  37. package/dist/harness/types-public.js +4 -6
  38. package/dist/harness/types-public.js.map +1 -1
  39. package/dist/harness/validator-trace-sink.d.ts +1 -1
  40. package/dist/index.d.ts +5 -5
  41. package/dist/index.js +94 -19
  42. package/dist/index.js.map +1 -1
  43. package/dist/patch.d.ts +1 -1
  44. package/dist/{types-public-B7wn4ce5.d.ts → types-public-h8IMnYuh.d.ts} +4 -0
  45. package/dist/{visual-evaluator-C9L-o6ap.d.ts → visual-evaluator-C2strxqD.d.ts} +1 -1
  46. package/dist/workflows.d.ts +1 -1
  47. package/package.json +12 -12
  48. package/src/adapters/base.ts +6 -1
  49. package/src/adapters/claude/message-parser.ts +5 -6
  50. package/src/adapters/claude/sdk-tool-pins.ts +44 -0
  51. package/src/adapters/claude/sdk.ts +3 -3
  52. package/src/adapters/claude/source-capture.ts +20 -0
  53. package/src/boilerplate/system-prompt.ts +3 -2
  54. package/src/check/run-tier0.ts +61 -0
  55. package/src/design-system-docs.ts +2 -1
  56. package/src/evaluation/criteria/resolve.ts +19 -1
  57. package/src/evaluation/loop.ts +25 -4
  58. package/src/evaluation/message-parsing.ts +11 -12
  59. package/src/evaluation/types-public.ts +4 -0
  60. package/src/taught-tokens.ts +5 -6
@@ -1,5 +1,5 @@
1
1
  import { C as Classification } from '../axes-CKVCzR_x.js';
2
- import { E as EvalIssue } from '../types-public-B7wn4ce5.js';
2
+ import { E as EvalIssue } from '../types-public-h8IMnYuh.js';
3
3
  import '@ggui-ai/protocol';
4
4
  import '../llm.js';
5
5
  import '@ggui-ai/mcp-server-core';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { C as CreateUiGeneratorOptions, c as createUiGenerator, e as extractComponentCode } from './create-ui-generator-N4gRXpcL.js';
2
- export { R as RuntimeRenderProbeConfig } from './adapter-BJwn74rx.js';
3
- import { Q as QualityConfig } from './types-public-B7wn4ce5.js';
4
- export { P as ProviderRetryInfo } from './types-public-B7wn4ce5.js';
1
+ export { C as CreateUiGeneratorOptions, c as createUiGenerator, e as extractComponentCode } from './create-ui-generator-BrZHF0cf.js';
2
+ export { R as RuntimeRenderProbeConfig } from './adapter-h4_V69LI.js';
3
+ import { Q as QualityConfig } from './types-public-h8IMnYuh.js';
4
+ export { P as ProviderRetryInfo } from './types-public-h8IMnYuh.js';
5
5
  export { GenerationResult, SameExchangeBreak } from './harness/result-types.js';
6
6
  import { GeneratorBuild, UiGenerator } from '@ggui-ai/mcp-server-core';
7
7
  import { D as DesignMode } from './design-mode-DGWN_Gkm.js';
@@ -24,7 +24,7 @@ import './evaluation/types.js';
24
24
  import './harness/types-public.js';
25
25
  import './policy.js';
26
26
  import './adapters/types.js';
27
- import './visual-evaluator-C9L-o6ap.js';
27
+ import './visual-evaluator-C2strxqD.js';
28
28
  import 'puppeteer-core';
29
29
 
30
30
  /**
package/dist/index.js CHANGED
@@ -338,6 +338,21 @@ var init_raw = __esm({
338
338
  }
339
339
  });
340
340
 
341
+ // src/adapters/claude/source-capture.ts
342
+ function sourceCodeFromToolUse(name, input) {
343
+ if (typeof name !== "string") return void 0;
344
+ const field2 = name === "Write" ? "content" : name.endsWith(COMPILE_TOOL_SUFFIX) ? "code" : void 0;
345
+ if (field2 === void 0 || typeof input !== "object" || input === null) return void 0;
346
+ const value = Reflect.get(input, field2);
347
+ return typeof value === "string" && value.length > 0 ? value : void 0;
348
+ }
349
+ var COMPILE_TOOL_SUFFIX;
350
+ var init_source_capture = __esm({
351
+ "src/adapters/claude/source-capture.ts"() {
352
+ COMPILE_TOOL_SUFFIX = "__compile_component";
353
+ }
354
+ });
355
+
341
356
  // src/adapters/claude/message-parser.ts
342
357
  function createArtifacts() {
343
358
  return {
@@ -457,12 +472,9 @@ function extractSourceCode(message, artifacts) {
457
472
  const content = assistantMsg.message?.content;
458
473
  if (!Array.isArray(content)) return;
459
474
  for (const block of content) {
460
- if (block.type === "tool_use" && block.name === "Write") {
461
- const input = block.input;
462
- if (input?.content) {
463
- artifacts.sourceCode = input.content;
464
- }
465
- }
475
+ if (block.type !== "tool_use") continue;
476
+ const value = sourceCodeFromToolUse(block.name, block.input);
477
+ if (value !== void 0) artifacts.sourceCode = value;
466
478
  }
467
479
  }
468
480
  function unescapeJsonString(s) {
@@ -500,6 +512,7 @@ function extractGeneratorMeta(message, artifacts) {
500
512
  }
501
513
  var init_message_parser = __esm({
502
514
  "src/adapters/claude/message-parser.ts"() {
515
+ init_source_capture();
503
516
  }
504
517
  });
505
518
 
@@ -595,6 +608,17 @@ var init_claude_code_login = __esm({
595
608
  }
596
609
  });
597
610
 
611
+ // src/adapters/claude/sdk-tool-pins.ts
612
+ function sdkToolPins(input) {
613
+ const builtins = [...input.builtinTools ?? []];
614
+ const approved = input.allowedTools !== void 0 ? [...input.allowedTools] : Object.keys(input.mcpServers ?? {}).map((name) => `mcp__${name}`);
615
+ return { tools: builtins, settingSources: [], allowedTools: [...approved, ...builtins] };
616
+ }
617
+ var init_sdk_tool_pins = __esm({
618
+ "src/adapters/claude/sdk-tool-pins.ts"() {
619
+ }
620
+ });
621
+
598
622
  // src/adapters/claude/sdk.ts
599
623
  var sdk_exports = {};
600
624
  __export(sdk_exports, {
@@ -621,6 +645,7 @@ var init_sdk = __esm({
621
645
  init_message_parser();
622
646
  init_tool_bridge2();
623
647
  init_claude_code_login();
648
+ init_sdk_tool_pins();
624
649
  ClaudeSdkAdapter = class extends GeneratorAdapter {
625
650
  provider = "claude";
626
651
  mode = "sdk";
@@ -660,9 +685,8 @@ var init_sdk = __esm({
660
685
  systemPrompt: params.systemPrompt,
661
686
  ...mcpServers && { mcpServers },
662
687
  ...cfg.agents && { agents: cfg.agents },
663
- ...allowedTools && { allowedTools },
664
- permissionMode: "bypassPermissions",
665
- allowDangerouslySkipPermissions: true,
688
+ // ggui#1714 — no CLI built-ins unless asked for, no host settings, no permission bypass.
689
+ ...sdkToolPins({ mcpServers, allowedTools, builtinTools: cfg.builtinTools }),
666
690
  ...cfg.cliPath && { pathToClaudeCodeExecutable: cfg.cliPath },
667
691
  ...cfg.stderr && { stderr: cfg.stderr },
668
692
  // ggui#1185 — tool-less, config-less, non-bare; the model is pinned above.
@@ -2952,10 +2976,7 @@ var init_design_mode = __esm({
2952
2976
  var NOT_YET_TAUGHT_TOKENS, taughtTokenManifest;
2953
2977
  var init_taught_tokens = __esm({
2954
2978
  "src/taught-tokens.ts"() {
2955
- NOT_YET_TAUGHT_TOKENS = /* @__PURE__ */ new Set([
2956
- "--ggui-color-controlAccentOutline",
2957
- "--ggui-color-controlOutline"
2958
- ]);
2979
+ NOT_YET_TAUGHT_TOKENS = /* @__PURE__ */ new Set([]);
2959
2980
  taughtTokenManifest = consumedTokenManifest.filter(
2960
2981
  (token) => !NOT_YET_TAUGHT_TOKENS.has(token)
2961
2982
  );
@@ -3475,6 +3496,40 @@ function detectComponentNeverReturns(sourceCode) {
3475
3496
  for (const st of body.statements) if (!isFunctionLike(st)) visit(st);
3476
3497
  return returnsValue ? null : { jsxStatementLine };
3477
3498
  }
3499
+ function kitControlTags(source) {
3500
+ const out = [];
3501
+ const open = new RegExp(`<(${KIT_CONTROLS.join("|")})\\b`, "g");
3502
+ for (const m of source.matchAll(open)) {
3503
+ const start = (m.index ?? 0) + m[0].length;
3504
+ let depth = 0;
3505
+ let quote = null;
3506
+ let i = start;
3507
+ for (; i < source.length; i += 1) {
3508
+ const c = source[i];
3509
+ if (quote !== null) {
3510
+ if (c === quote && source[i - 1] !== "\\") quote = null;
3511
+ } else if (c === '"' || c === "'" || c === "`") quote = c;
3512
+ else if (c === "{") depth += 1;
3513
+ else if (c === "}") depth -= 1;
3514
+ else if (c === ">" && depth === 0) break;
3515
+ }
3516
+ out.push({ name: m[1] ?? "", attrs: source.slice(start, i), line: source.slice(0, m.index ?? 0).split("\n").length });
3517
+ }
3518
+ return out;
3519
+ }
3520
+ function inlineBorderKey(attrs, source) {
3521
+ const BORDER = /\b(border(?:Top|Right|Bottom|Left)?(?:Color|Width|Style)?)\s*:/;
3522
+ const style = attrs.match(/\bstyle=\{\{([\s\S]*)\}\}/);
3523
+ if (style !== null) {
3524
+ const hit2 = (style[1] ?? "").match(BORDER);
3525
+ if (hit2 !== null) return hit2[1] ?? null;
3526
+ }
3527
+ const named = attrs.match(/\bstyle=\{\s*([A-Za-z_$][\w$]*)\s*\}/);
3528
+ if (named === null) return null;
3529
+ const decl = source.match(new RegExp(`\\b(?:const|let)\\s+${named[1]}\\b[^=]*=\\s*\\{([^{}]*)\\}`));
3530
+ const hit = decl !== null ? (decl[1] ?? "").match(BORDER) : null;
3531
+ return hit !== null ? hit[1] ?? null : null;
3532
+ }
3478
3533
  async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes, designMode = DEFAULT_DESIGN_MODE) {
3479
3534
  const issues = [];
3480
3535
  const lines = sourceCode.split("\n");
@@ -3872,6 +3927,20 @@ ${errorDetail}`,
3872
3927
  });
3873
3928
  }
3874
3929
  }
3930
+ for (const tag of enforceDesignVocabulary ? kitControlTags(sourceCode) : []) {
3931
+ const key = inlineBorderKey(tag.attrs, sourceCode);
3932
+ if (key === null) continue;
3933
+ issues.push({
3934
+ tier: 0,
3935
+ result: "fail",
3936
+ category: "tokens",
3937
+ subcategory: "control-inline-border",
3938
+ severity: "critical",
3939
+ description: `control-inline-border: <${tag.name}> sets an inline \`${key}\` \u2014 a kit control draws its own boundary, and an inline border paints over it`,
3940
+ fix: `Remove the border from <${tag.name}>'s style (keep borderRadius if you need it). An outline chip is <Button variant="outline"> exactly as it comes; a control-like edge on your own element uses var(--ggui-color-controlOutline).`,
3941
+ line: tag.line
3942
+ });
3943
+ }
3875
3944
  if (!sourceCode.includes("interface Props") && !sourceCode.includes("type Props")) {
3876
3945
  issues.push({
3877
3946
  tier: 0,
@@ -4143,7 +4212,7 @@ async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE)
4143
4212
  }
4144
4213
  return { issues, pass };
4145
4214
  }
4146
- var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR;
4215
+ var MANIFEST_TOKENS, RETIRED_TOKEN_SUCCESSORS, TOKEN_REF_RE, HOOK_NAME_FOR, CONTRACT_FIELD_FOR, KIT_CONTROLS;
4147
4216
  var init_run_tier0 = __esm({
4148
4217
  "src/check/run-tier0.ts"() {
4149
4218
  init_design_mode();
@@ -4172,6 +4241,7 @@ var init_run_tier0 = __esm({
4172
4241
  stream: "streamSpec",
4173
4242
  context: "contextSpec"
4174
4243
  };
4244
+ KIT_CONTROLS = ["Button", "Input", "Select", "TextArea", "Checkbox", "RadioGroup", "Toggle", "SearchField"];
4175
4245
  }
4176
4246
  });
4177
4247
 
@@ -7899,6 +7969,7 @@ function instrumentRead(kind, m) {
7899
7969
  return { verdict: "n/a", evidence: `fill ${m.inkRatio.toFixed(3)} reported; no floor ruled` };
7900
7970
  }
7901
7971
  function readRow(row, answers, m, unansweredReason) {
7972
+ if (row.evaluation === "judge" && REQUIRES_POST_ACTION_CAPTURE.has(row.id) && m.postActionCapture !== true) return { verdict: "n/a", evidence: NO_POST_ACTION };
7902
7973
  if (row.evaluation === "judge") return majority(row.id, answers, unansweredReason);
7903
7974
  if (row.evaluation === "human") return { verdict: "n/a", evidence: `reader's column (status ${row.status})` };
7904
7975
  if (row.status !== "live") return { verdict: "n/a", evidence: `not implemented (status ${row.status})` };
@@ -7957,14 +8028,14 @@ ${text}
7957
8028
  }
7958
8029
  function buildCriteriaJudgeBlock(bank, selected, inputs = {}) {
7959
8030
  const byId = new Map(bankRows(bank).map((r) => [r.id, r]));
7960
- const asked = selected.selection.map((s) => byId.get(s.id)).filter((r) => r !== void 0 && r.evaluation === "judge");
8031
+ const asked = selected.selection.map((s) => byId.get(s.id)).filter((r) => r !== void 0 && r.evaluation === "judge").filter((r) => inputs.postActionCapture === true || !REQUIRES_POST_ACTION_CAPTURE.has(r.id));
7961
8032
  if (asked.length === 0) return "";
7962
8033
  const lines = asked.map((r) => `- ${r.id} (${r.severity}): ${r.text} Evidence must name: ${r.evidence}`);
7963
8034
  return "## Criteria \u2014 answer each one from the FRAME only\nRead these off the screenshot. Never infer them from the request, the styling profile or any direction text above.\n" + (inputs.frame !== void 0 ? `${criteriaFrameLine(inputs.frame)}
7964
8035
  ` : "") + (inputs.propsJson !== void 0 ? criteriaPropsSection(inputs.propsJson) : "") + `${lines.join("\n")}
7965
8036
  Add to your JSON a top-level "criteria" array with exactly these ids: [{ "id": "<id>", "verdict": "pass" | "fail" | "n/a", "evidence": "<one sentence naming the region or element you read it from>" }]. Every row here was selected as applicable to this card, so "n/a" never means "does not apply": use it ONLY when this frame cannot show the criterion \u2014 it is then recorded as not evaluated.`;
7966
8037
  }
7967
- var INSTRUMENT_BY_ID, INSTRUMENT_BESIDE_JUDGE, CRITERIA_PROPS_MAX_CHARS;
8038
+ var INSTRUMENT_BY_ID, INSTRUMENT_BESIDE_JUDGE, REQUIRES_POST_ACTION_CAPTURE, NO_POST_ACTION, CRITERIA_PROPS_MAX_CHARS;
7968
8039
  var init_resolve = __esm({
7969
8040
  "src/evaluation/criteria/resolve.ts"() {
7970
8041
  init_bank();
@@ -7977,6 +8048,8 @@ var init_resolve = __esm({
7977
8048
  INSTRUMENT_BESIDE_JUDGE = {
7978
8049
  "space.edge": "edge"
7979
8050
  };
8051
+ REQUIRES_POST_ACTION_CAPTURE = /* @__PURE__ */ new Set(["state.feedback"]);
8052
+ NO_POST_ACTION = "not evaluated: no post-action capture";
7980
8053
  CRITERIA_PROPS_MAX_CHARS = 2e3;
7981
8054
  }
7982
8055
  });
@@ -10148,6 +10221,7 @@ These tokens adapt automatically to any theme (light, dark, branded). **ALWAYS u
10148
10221
  | link | var(--ggui-color-link) | Accent TEXT \u2014 eyebrows, taglines, links, labels, inline arrows: the brand's readable ink, derived per mode to clear 4.5:1 on the container and the ground |
10149
10222
  | outline | var(--ggui-color-outline) | Borders, dividers |
10150
10223
  | outlineVariant | var(--ggui-color-outlineVariant) | Subtle borders |
10224
+ | controlOutline | var(--ggui-color-controlOutline) | A control's edge \u2014 clears 3:1 on its surface; kit controls draw it themselves |
10151
10225
 
10152
10226
  **Usage pattern:**
10153
10227
  - Page/section background \u2192 \`var(--ggui-color-ground)\`
@@ -10272,7 +10346,7 @@ Use \`var(--ggui-spacing-N)\` for all padding, gap, and margin values. **Never u
10272
10346
  ### Color usage guide
10273
10347
  - **Layering roles** (ground, container, elevated, sunken + their on* inks; outline): one kind of AREA each \u2014 use them for every background/text/border decision; they adapt to any theme
10274
10348
  - **primary-50/100**: Section backgrounds, highlight strips, card headers
10275
- - **primary-200/300**: Borders, dividers, focus rings, input outlines
10349
+ - **primary-200/300**: Dividers and section rules \u2014 never a control's border: kit controls draw their own edge (\`controlOutline\` / \`controlAccentOutline\`), so set no inline border on them
10276
10350
  - **primary-500/600**: Filled buttons, CTAs, badges, icon fills, active indicators \u2014 fills and borders, NOT a text color
10277
10351
  - **link**: Accent TEXT \u2014 eyebrows, taglines, links, labels, inline arrows; the brand's readable ink per mode (a bare \`primary-300\u2026600\` as a text color fails tier-0: \`universal.accent_text_ink\`)
10278
10352
  - **primary-700/800/900**: Headings and text on light primary backgrounds
@@ -15275,13 +15349,14 @@ MANDATORY:
15275
15349
  2. NEVER use CSS gradients with custom colors. If you need a gradient: \`linear-gradient(to bottom, var(--ggui-color-primary-500), var(--ggui-color-primary-700))\`.
15276
15350
  3. NEVER invent your own palette. The system provides primary, neutral, success, warning, error, and info \u2014 use ONLY these.
15277
15351
  4. NEVER include literal fallback values in ANY token reference \u2014 colors, spacing, typography, radius, shadows alike. Write \`var(--ggui-spacing-4)\` bare, never \`var(--ggui-spacing-4, 16px)\`; \`var(--ggui-color-primary-600)\` bare, never \`var(--ggui-color-primary-600, #0284c7)\`. The runtime injects every token; a literal fallback paints the wrong value exactly when the operator's theme matters most.
15352
+ 5. NEVER set an inline \`border\`, \`borderColor\`, \`borderWidth\` or \`borderStyle\` on a kit control \u2014 \`Button\`, \`Input\`, \`Select\`, \`TextArea\`, \`Checkbox\`, \`RadioGroup\`, \`Toggle\`, \`SearchField\`. The control draws its own boundary, and it clears 3:1 on its surface in every theme: an outline chip is \`<Button variant="outline">\` exactly as it comes (its edge is \`controlAccentOutline\`). Where you draw a control-like edge on your own element, use \`var(--ggui-color-controlOutline)\`. A ramp step such as \`primary-200\` as a control's border is nearly invisible (about 1.1:1).
15278
15353
 
15279
15354
  Token categories:
15280
15355
  - Brand fills: \`var(--ggui-color-primary-600)\`, \`var(--ggui-color-primary-50)\`
15281
15356
  - Accent text: \`var(--ggui-color-link)\` \u2014 eyebrows, taglines, links, labels; readable per mode (never a bare \`primary-<stop>\` as a text color)
15282
15357
  - Text: \`var(--ggui-color-onContainer)\`, \`var(--ggui-color-onSunken)\`
15283
15358
  - Backgrounds: \`var(--ggui-color-container)\`, \`var(--ggui-color-sunken)\`
15284
- - Borders: \`var(--ggui-color-outline)\`
15359
+ - Borders: \`var(--ggui-color-outline)\` \u2014 dividers and the edges of non-interactive boxes; a control's edge is the kit's (rule 5)
15285
15360
  - Spacing: \`var(--ggui-spacing-4)\`, \`var(--ggui-spacing-6)\`
15286
15361
  - Typography: \`var(--ggui-font-size-sm)\`, \`var(--ggui-font-weight-semibold)\`
15287
15362
  - Shadows: \`var(--ggui-shape-shadow-sm)\`, \`var(--ggui-shape-shadow-md)\`, \`var(--ggui-shape-shadow-lg)\`
@@ -15296,7 +15371,7 @@ Use the FULL primary palette throughout the component \u2014 NOT only on submit
15296
15371
  | Element | Token | Purpose |
15297
15372
  |---------|-------|---------|
15298
15373
  | Section headers, hero areas, highlight strips | \`primary-50\` / \`primary-100\` | Subtle branded backgrounds |
15299
- | Borders, dividers, focus rings, input focus | \`primary-200\` / \`primary-300\` | Branded structure |
15374
+ | Dividers, section rules, edges of non-interactive boxes | \`primary-200\` / \`primary-300\` | Branded structure \u2014 never a control's border (rule 5) |
15300
15375
  | Accent TEXT \u2014 eyebrows, taglines, links, labels, inline arrows | \`link\` (\`var(--ggui-color-link)\`) or \`<Text tone="emphasized">\` | The brand's READABLE ink, derived per mode to clear 4.5:1 \u2014 a bare \`primary-300\u2026600\` as a text color fails tier-0 |
15301
15376
  | Icon fills, badges, active indicators | \`primary-500\` / \`primary-600\` | Core accent fill |
15302
15377
  | Buttons, CTAs, filled interactive elements | \`primary-600\` / \`primary-700\` | Primary actions |