@ggui-ai/ui-gen 0.16.0 → 0.18.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 (105) hide show
  1. package/dist/adapters/generation-dispatch.d.ts +6 -3
  2. package/dist/adapters/generation-dispatch.js +5161 -4312
  3. package/dist/adapters/generation-dispatch.js.map +1 -1
  4. package/dist/adapters/index.js +8 -5
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/advanced/index.d.ts +12 -8
  7. package/dist/advanced/index.js +4685 -3801
  8. package/dist/advanced/index.js.map +1 -1
  9. package/dist/blueprint-validator.js +8 -12
  10. package/dist/blueprint-validator.js.map +1 -1
  11. package/dist/boilerplate.d.ts +5 -2
  12. package/dist/boilerplate.js +120 -13
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +2 -2
  15. package/dist/check/index.js +72 -0
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/compose.js +1 -1
  18. package/dist/compose.js.map +1 -1
  19. package/dist/{create-ui-generator-CCIeQaEe.d.ts → create-ui-generator-C8Ki9Wz1.d.ts} +18 -2
  20. package/dist/{design-mode-BHEE7flj.d.ts → design-mode-DLufhrNp.d.ts} +0 -6
  21. package/dist/{dispatch-BA5dJcg-.d.ts → dispatch-D2htzi9h.d.ts} +2 -2
  22. package/dist/evaluation/axis-checks/index.d.ts +4 -4
  23. package/dist/evaluation/axis-checks/index.js +347 -13
  24. package/dist/evaluation/axis-checks/index.js.map +1 -1
  25. package/dist/evaluation/axis-checks/registry.d.ts +2 -2
  26. package/dist/evaluation/axis-checks/registry.js +302 -1
  27. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  28. package/dist/evaluation/index.d.ts +160 -8
  29. package/dist/evaluation/index.js +752 -81
  30. package/dist/evaluation/index.js.map +1 -1
  31. package/dist/fragments/index.js +1 -1
  32. package/dist/fragments/index.js.map +1 -1
  33. package/dist/{generate-8L_bc2iP.d.ts → generate-9IeNT4ZY.d.ts} +1 -1
  34. package/dist/harness/check/runtime-render/index.d.ts +3 -2
  35. package/dist/harness/check/runtime-render/index.js +8 -12
  36. package/dist/harness/check/runtime-render/index.js.map +1 -1
  37. package/dist/harness/index.d.ts +3 -2
  38. package/dist/harness/index.js +410 -255
  39. package/dist/harness/index.js.map +1 -1
  40. package/dist/harness/prompts.d.ts +2 -2
  41. package/dist/harness/prompts.js +7 -3
  42. package/dist/harness/prompts.js.map +1 -1
  43. package/dist/harness/result-types.d.ts +3 -3
  44. package/dist/harness/runtime.d.ts +13 -4
  45. package/dist/harness/runtime.js +131 -20
  46. package/dist/harness/runtime.js.map +1 -1
  47. package/dist/harness/types-public.d.ts +13 -2
  48. package/dist/harness/types-public.js +410 -255
  49. package/dist/harness/types-public.js.map +1 -1
  50. package/dist/harness/validator-trace-sink.d.ts +2 -2
  51. package/dist/index.d.ts +29 -7
  52. package/dist/index.js +4691 -3805
  53. package/dist/index.js.map +1 -1
  54. package/dist/patch.d.ts +3 -2
  55. package/dist/{prompts-k6m6reqe.d.ts → prompts-YU9piOgs.d.ts} +12 -46
  56. package/dist/styling-profile-CxfvV48g.d.ts +35 -0
  57. package/dist/tools/get-primitives-ts.d.ts +1 -1
  58. package/dist/tools/get-primitives-ts.js +1 -1
  59. package/dist/tools/get-primitives-ts.js.map +1 -1
  60. package/dist/{types-public-D24JLJgl.d.ts → types-public-DVSssdc-.d.ts} +39 -3
  61. package/dist/validation/index.d.ts +1 -1
  62. package/dist/validation/index.js +1 -1
  63. package/dist/validation/index.js.map +1 -1
  64. package/dist/workflows.d.ts +3 -2
  65. package/package.json +12 -12
  66. package/src/adapters/generation-dispatch.ts +6 -1
  67. package/src/boilerplate/free-design-prompt.ts +9 -0
  68. package/src/boilerplate/generate.ts +8 -1
  69. package/src/boilerplate/hard-sections.ts +45 -1
  70. package/src/boilerplate/styling-profile.ts +166 -0
  71. package/src/boilerplate/system-prompt.ts +22 -8
  72. package/src/boilerplate/templates/layouts/fullscreen-desktop.tsx.tmpl +5 -3
  73. package/src/boilerplate/templates/layouts/fullscreen-mobile.tsx.tmpl +3 -3
  74. package/src/boilerplate/templates/layouts/fullscreen-universal.tsx.tmpl +5 -3
  75. package/src/check/run-tier0.ts +154 -0
  76. package/src/contract-context.ts +28 -14
  77. package/src/create-harness.ts +4 -0
  78. package/src/create-ui-generator.ts +41 -2
  79. package/src/design-mode.ts +11 -1
  80. package/src/design-system-docs.ts +7 -4
  81. package/src/effort.ts +48 -0
  82. package/src/evaluation/axis-checks/checks/grid.ts +148 -0
  83. package/src/evaluation/axis-checks/checks/universal.ts +308 -0
  84. package/src/evaluation/axis-checks/dispatch.ts +78 -13
  85. package/src/evaluation/axis-checks/helpers.ts +12 -0
  86. package/src/evaluation/axis-checks/registry.ts +2 -0
  87. package/src/evaluation/design-identity.ts +96 -0
  88. package/src/evaluation/index.ts +17 -0
  89. package/src/evaluation/llm-evaluator.ts +13 -1
  90. package/src/evaluation/theme-css.ts +14 -0
  91. package/src/evaluation/types-public.ts +35 -1
  92. package/src/evaluation/visual-evaluator.ts +478 -45
  93. package/src/fragments/render.ts +1 -1
  94. package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
  95. package/src/harness/coding/init-session.ts +1 -0
  96. package/src/harness/coding/run-coding-turn.ts +6 -14
  97. package/src/harness/coding/run-eval-round.ts +31 -3
  98. package/src/harness/runtime.ts +10 -0
  99. package/src/harness/types-public.ts +11 -0
  100. package/src/index.ts +2 -0
  101. package/src/pin-fixtures.ts +98 -0
  102. package/src/run-check.ts +10 -10
  103. package/src/tools/get-primitives-ts.ts +1 -1
  104. package/src/tools.ts +2 -1
  105. package/src/validation/primitives.ts +1 -1
@@ -1,4 +1,4 @@
1
- import { mkdirSync, writeFileSync, unlinkSync, existsSync, rm, createReadStream, createWriteStream } from 'fs';
1
+ import { readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, unlinkSync, existsSync, rm, createReadStream, createWriteStream } from 'fs';
2
2
  import 'fs/promises';
3
3
  import { tmpdir } from 'os';
4
4
  import { resolve, dirname, join, basename } from 'path';
@@ -9,9 +9,12 @@ import { fileURLToPath } from 'url';
9
9
  import { query, createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
10
10
  import Anthropic from '@anthropic-ai/sdk';
11
11
  import { z } from 'zod';
12
- import { listContractGadgets, HOOK_NAME_RE, STDLIB_GADGETS_PACKAGE } from '@ggui-ai/protocol';
12
+ import { LUCIDE_ICON_NAMES } from '@ggui-ai/design';
13
+ import { listContractGadgets, HOOK_NAME_RE, STDLIB_GADGETS_PACKAGE, APP_GENERATION_PROFILE_BOUNDS } from '@ggui-ai/protocol';
14
+ import { createHash } from 'crypto';
13
15
  import { build } from 'esbuild';
14
- import { getCssTokens } from '@ggui-ai/design/rendering';
16
+ import { getCssTokens, composeThemeCss, fillFitRule } from '@ggui-ai/design/rendering';
17
+ import { createRequire } from 'module';
15
18
 
16
19
  var __create = Object.create;
17
20
  var __defProp = Object.defineProperty;
@@ -1404,8 +1407,8 @@ var require_streamx = __commonJS({
1404
1407
  return this;
1405
1408
  },
1406
1409
  next() {
1407
- return new Promise(function(resolve2, reject) {
1408
- promiseResolve = resolve2;
1410
+ return new Promise(function(resolve3, reject) {
1411
+ promiseResolve = resolve3;
1409
1412
  promiseReject = reject;
1410
1413
  const data = stream.read();
1411
1414
  if (data !== null) ondata(data);
@@ -1438,11 +1441,11 @@ var require_streamx = __commonJS({
1438
1441
  }
1439
1442
  function destroy(err) {
1440
1443
  stream.destroy(err);
1441
- return new Promise((resolve2, reject) => {
1442
- if (stream._duplexState & DESTROYED) return resolve2({ value: void 0, done: true });
1444
+ return new Promise((resolve3, reject) => {
1445
+ if (stream._duplexState & DESTROYED) return resolve3({ value: void 0, done: true });
1443
1446
  stream.once("close", function() {
1444
1447
  if (err) reject(err);
1445
- else resolve2({ value: void 0, done: true });
1448
+ else resolve3({ value: void 0, done: true });
1446
1449
  });
1447
1450
  });
1448
1451
  }
@@ -1486,8 +1489,8 @@ var require_streamx = __commonJS({
1486
1489
  const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0);
1487
1490
  if (writes === 0) return Promise.resolve(true);
1488
1491
  if (state.drains === null) state.drains = [];
1489
- return new Promise((resolve2) => {
1490
- state.drains.push({ writes, resolve: resolve2 });
1492
+ return new Promise((resolve3) => {
1493
+ state.drains.push({ writes, resolve: resolve3 });
1491
1494
  });
1492
1495
  }
1493
1496
  write(data) {
@@ -1592,10 +1595,10 @@ var require_streamx = __commonJS({
1592
1595
  cb(null);
1593
1596
  }
1594
1597
  function pipelinePromise(...streams) {
1595
- return new Promise((resolve2, reject) => {
1598
+ return new Promise((resolve3, reject) => {
1596
1599
  return pipeline2(...streams, (err) => {
1597
1600
  if (err) return reject(err);
1598
- resolve2();
1601
+ resolve3();
1599
1602
  });
1600
1603
  });
1601
1604
  }
@@ -2253,16 +2256,16 @@ var require_extract = __commonJS({
2253
2256
  entryCallback = null;
2254
2257
  cb(err);
2255
2258
  }
2256
- function onnext(resolve2, reject) {
2259
+ function onnext(resolve3, reject) {
2257
2260
  if (error) {
2258
2261
  return reject(error);
2259
2262
  }
2260
2263
  if (entryStream) {
2261
- resolve2({ value: entryStream, done: false });
2264
+ resolve3({ value: entryStream, done: false });
2262
2265
  entryStream = null;
2263
2266
  return;
2264
2267
  }
2265
- promiseResolve = resolve2;
2268
+ promiseResolve = resolve3;
2266
2269
  promiseReject = reject;
2267
2270
  consumeCallback(null);
2268
2271
  if (extract3._finished && promiseResolve) {
@@ -2290,11 +2293,11 @@ var require_extract = __commonJS({
2290
2293
  function destroy(err) {
2291
2294
  extract3.destroy(err);
2292
2295
  consumeCallback(err);
2293
- return new Promise((resolve2, reject) => {
2294
- if (extract3.destroyed) return resolve2({ value: void 0, done: true });
2296
+ return new Promise((resolve3, reject) => {
2297
+ if (extract3.destroyed) return resolve3({ value: void 0, done: true });
2295
2298
  extract3.once("close", function() {
2296
2299
  if (err) reject(err);
2297
- else resolve2({ value: void 0, done: true });
2300
+ else resolve3({ value: void 0, done: true });
2298
2301
  });
2299
2302
  });
2300
2303
  }
@@ -3190,9 +3193,9 @@ var init_helper = __esm({
3190
3193
  try {
3191
3194
  await pipeline(Readable.fromWeb(response.body), (0, import_tar_fs.extract)(destDir));
3192
3195
  } catch (error) {
3193
- await new Promise((resolve2) => {
3196
+ await new Promise((resolve3) => {
3194
3197
  rm(destDir, { force: true, recursive: true }, () => {
3195
- resolve2();
3198
+ resolve3();
3196
3199
  });
3197
3200
  });
3198
3201
  throw error;
@@ -3211,14 +3214,14 @@ var init_lambdafs = __esm({
3211
3214
  GZIP_EXTENSION_REGEX = /gz$/i;
3212
3215
  inflate = (filePath) => {
3213
3216
  const output = filePath.includes("swiftshader") ? tmpdir() : join(tmpdir(), basename(filePath).replace(ARCHIVE_EXTENSION_REGEX, ""));
3214
- return new Promise((resolve2, reject) => {
3217
+ return new Promise((resolve3, reject) => {
3215
3218
  if (filePath.includes("swiftshader")) {
3216
3219
  if (existsSync(`${output}/libGLESv2.so`)) {
3217
- resolve2(output);
3220
+ resolve3(output);
3218
3221
  return;
3219
3222
  }
3220
3223
  } else if (existsSync(output)) {
3221
- resolve2(output);
3224
+ resolve3(output);
3222
3225
  return;
3223
3226
  }
3224
3227
  const isBrotli = BROTLI_EXTENSION_REGEX.test(filePath);
@@ -3234,12 +3237,12 @@ var init_lambdafs = __esm({
3234
3237
  if (isTar) {
3235
3238
  target = (0, import_tar_fs2.extract)(output);
3236
3239
  target.once("finish", () => {
3237
- resolve2(output);
3240
+ resolve3(output);
3238
3241
  });
3239
3242
  } else {
3240
3243
  target = createWriteStream(output, { mode: 448 });
3241
3244
  target.once("close", () => {
3242
- resolve2(output);
3245
+ resolve3(output);
3243
3246
  });
3244
3247
  }
3245
3248
  target.once("error", handleError);
@@ -3818,7 +3821,7 @@ function extractRetryAfterSec(e) {
3818
3821
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : void 0;
3819
3822
  }
3820
3823
  function sleep(ms) {
3821
- return new Promise((resolve2) => setTimeout(resolve2, ms));
3824
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
3822
3825
  }
3823
3826
  var MAX_RETRY_ATTEMPTS = 2;
3824
3827
  var RETRY_AFTER_CAP_SEC = 15;
@@ -5555,6 +5558,9 @@ function mkIssue(subcategory, description, fix, result = "fail") {
5555
5558
  function cap(s) {
5556
5559
  return s.charAt(0).toUpperCase() + s.slice(1);
5557
5560
  }
5561
+ function axisCheckTraceEnabled() {
5562
+ return process.env["GGUI_AXIS_CHECK_TRACE"] === "1";
5563
+ }
5558
5564
 
5559
5565
  // src/evaluation/axis-checks/checks/universal.ts
5560
5566
  var ALL_RENDER_VALUES = [
@@ -5658,7 +5664,222 @@ function runPropSeedNoResync(input) {
5658
5664
  }
5659
5665
  return issues;
5660
5666
  }
5667
+ var KNOWN_ICON_KEYS = new Set(
5668
+ LUCIDE_ICON_NAMES.map((n) => n.replace(/[-_]/g, "").toLowerCase())
5669
+ );
5670
+ function isKnownIconName(name) {
5671
+ return KNOWN_ICON_KEYS.has(name.replace(/[-_]/g, "").toLowerCase());
5672
+ }
5673
+ var ICON_NAME_LITERAL_RX = /<Icon\b[^>]*?\bname\s*=\s*(?:"([^"]*)"|'([^']*)'|\{\s*"([^"]*)"\s*\}|\{\s*'([^']*)'\s*\})/g;
5674
+ function findUnknownIconNames(sourceCode) {
5675
+ const unknown = /* @__PURE__ */ new Set();
5676
+ for (const m of sourceCode.matchAll(ICON_NAME_LITERAL_RX)) {
5677
+ const name = m[1] ?? m[2] ?? m[3] ?? m[4] ?? "";
5678
+ if (name.length === 0) continue;
5679
+ if (/[^\x20-\x7e]/.test(name)) continue;
5680
+ if (!isKnownIconName(name)) unknown.add(name);
5681
+ }
5682
+ return [...unknown];
5683
+ }
5684
+ function runIconNameKnown(input) {
5685
+ if (input.compiledCode === null) return [];
5686
+ const unknown = findUnknownIconNames(input.sourceCode);
5687
+ if (unknown.length === 0) return [];
5688
+ const list = unknown.map((n) => `"${n}"`).join(", ");
5689
+ return [
5690
+ mkIssue(
5691
+ "universal.icon_name_known",
5692
+ `<Icon name=\u2026> uses ${unknown.length} name(s) outside the design system's Lucide subset: ${list} \u2014 an unknown name renders an empty box.`,
5693
+ "Call get_available_icons and pick a listed name (kebab-case), or use an emoji directly."
5694
+ )
5695
+ ];
5696
+ }
5697
+ var LADDER_STOP_AS_TEXT_RX = /(?<![\w-])color\s*(?:=\s*\{?\s*|:\s*)["'`]?\s*var\(--ggui-color-primary-(\d{2,3})\s*[,)]/g;
5698
+ var TEXT_INK_STOP_MIN = 300;
5699
+ var TEXT_INK_STOP_MAX = 600;
5700
+ function findLadderStopsAsText(sourceCode) {
5701
+ const found = /* @__PURE__ */ new Set();
5702
+ for (const m of sourceCode.matchAll(LADDER_STOP_AS_TEXT_RX)) {
5703
+ const digits = m[1] ?? "";
5704
+ const stop = Number(digits);
5705
+ if (stop >= TEXT_INK_STOP_MIN && stop <= TEXT_INK_STOP_MAX) found.add(`primary-${digits}`);
5706
+ }
5707
+ return [...found];
5708
+ }
5709
+ function runAccentTextInk(input) {
5710
+ if (input.compiledCode === null) return [];
5711
+ const stops = findLadderStopsAsText(input.sourceCode);
5712
+ if (stops.length === 0) return [];
5713
+ const list = stops.map((s) => `\`${s}\``).join(", ");
5714
+ return [
5715
+ mkIssue(
5716
+ "universal.accent_text_ink",
5717
+ `Text is painted with a brand-ladder fill stop as its color (${list}) \u2014 on a low-saturation brand these stops are mid-grey on the surface and the text fails readability.`,
5718
+ 'Accent text (eyebrows, taglines, links, labels, inline arrows) takes var(--ggui-color-link) or <Text tone="emphasized">; body text takes var(--ggui-color-onContainer); keep primary-* stops on fills and borders.'
5719
+ )
5720
+ ];
5721
+ }
5722
+ var SCOPED_SURFACE_TAG_RX = /<(Card|Box)\b([^<>]*?)surface\s*=\s*["'](inverted|hero)["']([^<>]*?)>/g;
5723
+ var STYLE_BACKGROUND_RX = /\bstyle\s*=\s*\{\{[^}]*\b(background|backgroundColor|backgroundImage)\s*:/;
5724
+ function findScopedSurfaceBackgrounds(sourceCode) {
5725
+ const hits = [];
5726
+ for (const m of sourceCode.matchAll(SCOPED_SURFACE_TAG_RX)) {
5727
+ const attrs = `${m[2] ?? ""} ${m[4] ?? ""}`;
5728
+ if (STYLE_BACKGROUND_RX.test(attrs)) hits.push([m[1] ?? "", m[3] ?? ""]);
5729
+ }
5730
+ return hits;
5731
+ }
5732
+ function runScopedSurfaceOwnsGround(input) {
5733
+ if (input.compiledCode === null) return [];
5734
+ if (input.designMode === "free") return [];
5735
+ const hits = findScopedSurfaceBackgrounds(input.sourceCode);
5736
+ if (hits.length === 0) return [];
5737
+ const [tag, surface] = hits[0];
5738
+ return [
5739
+ mkIssue(
5740
+ "universal.scoped_surface_owns_ground",
5741
+ `<${tag} surface="${surface}"> sets its own background in style \u2014 a scoped surface owns its ground; repainting it puts the scope's swapped inks over the wrong ground (ink on ink).`,
5742
+ `Remove the background from that ${tag}'s style \u2014 surface="${surface}" paints the ground; pick another surface if a different ground is wanted.`
5743
+ )
5744
+ ];
5745
+ }
5746
+ var CAPS_TEXT_RX = /<Text\b(?=[^>]*\bcaps\b)[^>]*>([\s\S]*?)<\/Text>/g;
5747
+ function findInventedCapsLabels(sourceCode) {
5748
+ const out = [];
5749
+ for (const m of sourceCode.matchAll(CAPS_TEXT_RX)) {
5750
+ const inner = (m[1] ?? "").trim();
5751
+ if (inner.length === 0) continue;
5752
+ if (inner.includes("{")) continue;
5753
+ out.push(inner.replace(/\s+/g, " ").slice(0, 60));
5754
+ }
5755
+ return out;
5756
+ }
5757
+ function runCapsLabelInvented(input) {
5758
+ if (input.compiledCode === null) return [];
5759
+ const invented = findInventedCapsLabels(input.sourceCode);
5760
+ if (invented.length === 0) return [];
5761
+ const list = invented.map((t) => `"${t}"`).join(", ");
5762
+ return [
5763
+ mkIssue(
5764
+ "universal.caps_label_invented",
5765
+ `${invented.length} caps label(s) carry invented copy the props do not supply: ${list} \u2014 an eyebrow, kicker or overline the agent cannot change is chatter on every render.`,
5766
+ "Remove the label, or bind its text to a prop (`<Text caps>{props.section}</Text>`); copy comes from the contract, never from the component."
5767
+ )
5768
+ ];
5769
+ }
5770
+ var VIEWPORT_UNIT = String.raw`(?:calc\(\s*)?100(?:vh|dvh|svh|lvh)\b(?:[^)"'\x60;\n]*\))?`;
5771
+ var VIEWPORT_STYLE_OBJECT_RX = new RegExp(
5772
+ String.raw`\b(?:minHeight|height|minBlockSize|blockSize)\s*:\s*["'\x60]\s*${VIEWPORT_UNIT}["'\x60]?`,
5773
+ "g"
5774
+ );
5775
+ var VIEWPORT_CSS_RX = new RegExp(
5776
+ String.raw`(?<![\w-])(?:min-)?(?:height|block-size)\s*:\s*${VIEWPORT_UNIT}`,
5777
+ "g"
5778
+ );
5779
+ var VIEWPORT_CLASS_RX = /(?<![\w-])(?:min-)?h-(?:screen|dvh|svh|lvh)\b/g;
5780
+ function findViewportSizing(sourceCode) {
5781
+ const hits = [];
5782
+ for (const rx of [VIEWPORT_STYLE_OBJECT_RX, VIEWPORT_CSS_RX, VIEWPORT_CLASS_RX]) {
5783
+ for (const m of sourceCode.matchAll(rx)) {
5784
+ hits.push({ at: m.index ?? 0, text: m[0].replace(/\s+/g, " ").trim().slice(0, 60) });
5785
+ }
5786
+ }
5787
+ return hits.sort((a, b) => a.at - b.at).map((h) => h.text);
5788
+ }
5789
+ function runViewportSized(input) {
5790
+ if (input.compiledCode === null) return [];
5791
+ const sized = findViewportSizing(input.sourceCode);
5792
+ if (sized.length === 0) return [];
5793
+ const list = sized.map((t) => "`" + t + "`").join(", ");
5794
+ return [
5795
+ mkIssue(
5796
+ "universal.viewport_sized",
5797
+ `${sized.length} element(s) are sized to the viewport: ${list} \u2014 the frame owns the height; a content-sized frame is measured from the content, so a viewport-sized root never shrinks and grows the frame on every re-measure.`,
5798
+ "Remove the viewport height \u2014 size to content, never to the viewport. Under fullscreen the frame stretches your root already (centre inside it with `display: flex; flex-direction: column; justify-content: center`); to fill a parent that has a height use `height: 100%` or `flex: 1`."
5799
+ )
5800
+ ];
5801
+ }
5802
+ var TERMINAL_WORDS = /* @__PURE__ */ new Set([
5803
+ "submit",
5804
+ "confirm",
5805
+ "approve",
5806
+ "schedule",
5807
+ "reserve",
5808
+ "book",
5809
+ "pay",
5810
+ "checkout",
5811
+ "purchase",
5812
+ "order"
5813
+ ]);
5814
+ var TERMINAL_LABEL_RX = /\b(submit|confirm|approve|schedule|reserve|book|pay|checkout|purchase|place\s+order)\b/i;
5815
+ var GUARD_RX = /\b(disabled|aria-disabled)\s*=/;
5816
+ function nameWords(name) {
5817
+ return name.split(/(?=[A-Z])|[_\-\s]+/).map((w) => w.toLowerCase()).filter((w) => w.length > 0);
5818
+ }
5819
+ function findUnguardedTerminalControls(sourceCode) {
5820
+ if (GUARD_RX.test(sourceCode)) return [];
5821
+ const names = /* @__PURE__ */ new Set();
5822
+ for (const m of sourceCode.matchAll(/useAction(?:<[^>]*>)?\(\s*['"`]([^'"`]+)['"`]/g)) {
5823
+ const name = m[1] ?? "";
5824
+ if (nameWords(name).some((w) => TERMINAL_WORDS.has(w))) names.add(name);
5825
+ }
5826
+ for (const m of sourceCode.matchAll(/>([^<>{}]{1,60})<\/Button>/g)) {
5827
+ const label = (m[1] ?? "").replace(/\s+/g, " ").trim();
5828
+ if (label.length > 0 && TERMINAL_LABEL_RX.test(label)) names.add(label.slice(0, 40));
5829
+ }
5830
+ return [...names];
5831
+ }
5832
+ function runTerminalActionUnguarded(input) {
5833
+ if (input.compiledCode === null) return [];
5834
+ const found = findUnguardedTerminalControls(input.sourceCode);
5835
+ if (found.length === 0) return [];
5836
+ const list = found.map((t) => `"${t}"`).join(", ");
5837
+ return [
5838
+ mkIssue(
5839
+ "universal.terminal_action_unguarded",
5840
+ `${list} reads as an action the user means once, and nothing in this component disables the control after it fires \u2014 a double-click sends it twice.`,
5841
+ 'Hold the fired state (`const [submitted, setSubmitted] = useState(false)`), set it in the handler, and pass `disabled={submitted}` to that control with a word that says so ("Submitted"). If the action IS meant to repeat \u2014 sending a message, adding an item \u2014 leave it armed and ignore this.',
5842
+ "warn"
5843
+ )
5844
+ ];
5845
+ }
5661
5846
  var UNIVERSAL_CHECKS = [
5847
+ {
5848
+ id: "universal.icon_name_known",
5849
+ axis: "render",
5850
+ values: ALL_RENDER_VALUES,
5851
+ run: runIconNameKnown
5852
+ },
5853
+ {
5854
+ id: "universal.caps_label_invented",
5855
+ axis: "render",
5856
+ values: ALL_RENDER_VALUES,
5857
+ run: runCapsLabelInvented
5858
+ },
5859
+ {
5860
+ id: "universal.viewport_sized",
5861
+ axis: "render",
5862
+ values: ALL_RENDER_VALUES,
5863
+ run: runViewportSized
5864
+ },
5865
+ {
5866
+ id: "universal.terminal_action_unguarded",
5867
+ axis: "render",
5868
+ values: ALL_RENDER_VALUES,
5869
+ run: runTerminalActionUnguarded
5870
+ },
5871
+ {
5872
+ id: "universal.accent_text_ink",
5873
+ axis: "render",
5874
+ values: ALL_RENDER_VALUES,
5875
+ run: runAccentTextInk
5876
+ },
5877
+ {
5878
+ id: "universal.scoped_surface_owns_ground",
5879
+ axis: "render",
5880
+ values: ALL_RENDER_VALUES,
5881
+ run: runScopedSurfaceOwnsGround
5882
+ },
5662
5883
  {
5663
5884
  id: "universal.prop_coverage",
5664
5885
  axis: "render",
@@ -6461,6 +6682,87 @@ var TOOLING_CHECKS = [
6461
6682
  }
6462
6683
  ];
6463
6684
 
6685
+ // src/evaluation/axis-checks/checks/grid.ts
6686
+ var BOARD_PROP_RX = /^(columns?|lanes?|stages?|lists?|buckets?|swimlanes?)$/i;
6687
+ var BOARD_PROMPT_RX = /\b(kanban|board)\b/i;
6688
+ var BOARD_NAMES = ["columns", "lanes", "stages", "lists", "buckets", "swimlanes"];
6689
+ var LAYOUT_TAG_RX = /<(\/?)(Grid|Row|Stack|Card|Box|Container|ScrollArea|div|section)\b([^<>]*?)>/g;
6690
+ var SIDE_BY_SIDE = /* @__PURE__ */ new Set(["Grid", "Row"]);
6691
+ function boardPropNames(contract) {
6692
+ const propsField = contract?.propsSpec;
6693
+ const properties = propsField?.properties ?? {};
6694
+ return Object.entries(properties).filter(([name, p]) => BOARD_PROP_RX.test(name) && (p?.type === "array" || p?.schema?.type === "array")).map(([name]) => name);
6695
+ }
6696
+ function jsxMapPositions(sourceCode, name) {
6697
+ const rx = new RegExp(
6698
+ `\\b${name}(?:\\?\\.)?\\.map\\(\\s*\\(?\\s*\\w+(?:\\s*,\\s*\\w+)?\\s*\\)?\\s*=>\\s*(?:\\(?\\s*<|\\{[^{}]{0,600}?\\breturn\\s*\\(?\\s*<)`,
6699
+ "g"
6700
+ );
6701
+ return [...sourceCode.matchAll(rx)].map((m) => m.index ?? 0);
6702
+ }
6703
+ function enclosingLayoutTag(sourceCode, pos) {
6704
+ const stack = [];
6705
+ for (const m of sourceCode.slice(0, pos).matchAll(LAYOUT_TAG_RX)) {
6706
+ const closing = m[1] === "/";
6707
+ const name = m[2] ?? "";
6708
+ const attrs = m[3] ?? "";
6709
+ if (closing) {
6710
+ if (stack[stack.length - 1] === name) stack.pop();
6711
+ continue;
6712
+ }
6713
+ if (attrs.trimEnd().endsWith("/")) continue;
6714
+ stack.push(name);
6715
+ }
6716
+ return stack[stack.length - 1];
6717
+ }
6718
+ function describeBoardRead(input) {
6719
+ const declared = boardPropNames(input.contract);
6720
+ const names = declared.length > 0 ? declared : BOARD_PROMPT_RX.test(input.originalPrompt) ? [...BOARD_NAMES] : BOARD_NAMES.filter((n) => jsxMapPositions(input.sourceCode, n).length > 0);
6721
+ const perName = names.map((name) => {
6722
+ const positions = jsxMapPositions(input.sourceCode, name);
6723
+ return { name, maps: positions.length, enclosing: positions.map((pos) => enclosingLayoutTag(input.sourceCode, pos) ?? "nothing") };
6724
+ });
6725
+ return { declared, names, perName };
6726
+ }
6727
+ function findStackedBoardColumns(input) {
6728
+ const stacked = [];
6729
+ for (const { name, enclosing } of describeBoardRead(input).perName) {
6730
+ for (const tag of enclosing) if (!SIDE_BY_SIDE.has(tag)) stacked.push([name, tag]);
6731
+ }
6732
+ return stacked;
6733
+ }
6734
+ function runBoardColumnsSideBySide(input) {
6735
+ if (input.compiledCode === null) return [];
6736
+ const standDown = input.designMode === "free";
6737
+ const stacked = standDown ? [] : findStackedBoardColumns(input);
6738
+ if (axisCheckTraceEnabled()) {
6739
+ const line = {
6740
+ ...describeBoardRead(input),
6741
+ designMode: input.designMode ?? "constrained",
6742
+ standDown,
6743
+ result: standDown ? "stand-down" : stacked.length > 0 ? "fail" : "pass"
6744
+ };
6745
+ console.log(JSON.stringify({ boardColumnsTrace: line }));
6746
+ }
6747
+ if (stacked.length === 0) return [];
6748
+ const [name, tag] = stacked[0];
6749
+ return [
6750
+ mkIssue(
6751
+ "grid.board_columns_side_by_side",
6752
+ `The board's \`${name}\` are composed under <${tag}> \u2014 a vertical stack of column sections; at md and wider the columns must sit side by side.`,
6753
+ `Wrap the ${name} map in <Grid columns={{ base: 1, md: ${name}.length }} gap="md"> (each column its own <Stack> of cards); never a <Stack> of column sections.`
6754
+ )
6755
+ ];
6756
+ }
6757
+ var GRID_CHECKS = [
6758
+ {
6759
+ id: "grid.board_columns_side_by_side",
6760
+ axis: "render",
6761
+ values: ["grid"],
6762
+ run: runBoardColumnsSideBySide
6763
+ }
6764
+ ];
6765
+
6464
6766
  // src/evaluation/axis-checks/extras.ts
6465
6767
  function mkIssue2(subcategory, description, fix, result = "warn") {
6466
6768
  return { tier: 0, result, category: "mode", subcategory, description, fix };
@@ -6600,29 +6902,164 @@ var REGISTRY = [
6600
6902
  ...WRITES_CHECKS,
6601
6903
  ...STATE_PAYLOAD_CHECKS,
6602
6904
  ...TOOLING_CHECKS,
6905
+ ...GRID_CHECKS,
6603
6906
  ...EXTRA_CHECKS
6604
6907
  ];
6605
-
6606
- // src/evaluation/axis-checks/dispatch.ts
6607
6908
  function runAxisChecks(classification, input) {
6608
- if (input.compiledCode === null) return [];
6609
- const axisInput = {
6909
+ const facts = {
6610
6910
  sourceCode: input.sourceCode,
6611
- compiledCode: input.compiledCode,
6612
6911
  ...input.contract !== void 0 ? { contract: input.contract } : {},
6613
6912
  originalPrompt: input.originalPrompt,
6614
6913
  classification,
6615
6914
  ...input.designMode !== void 0 ? { designMode: input.designMode } : {}
6616
6915
  };
6916
+ if (input.compiledCode === null) {
6917
+ traceAxisChecksSkipped(facts, "compiledCode null \u2014 no check ran");
6918
+ return [];
6919
+ }
6920
+ const axisInput = { ...facts, compiledCode: input.compiledCode };
6921
+ return runGatedAxisChecks(
6922
+ REGISTRY.filter((check) => matches(classification.vector, check)),
6923
+ axisInput
6924
+ ).issues;
6925
+ }
6926
+ function runGatedAxisChecks(checks, input) {
6617
6927
  const issues = [];
6618
- const firedIds = /* @__PURE__ */ new Set();
6619
- for (const check of REGISTRY) {
6620
- if (!matches(classification.vector, check)) continue;
6621
- if (firedIds.has(check.id)) continue;
6622
- firedIds.add(check.id);
6623
- issues.push(...check.run(axisInput));
6928
+ const firedIds = [];
6929
+ const seen = /* @__PURE__ */ new Set();
6930
+ for (const check of checks) {
6931
+ if (seen.has(check.id)) continue;
6932
+ seen.add(check.id);
6933
+ firedIds.push(check.id);
6934
+ issues.push(...check.run(input));
6935
+ }
6936
+ if (axisCheckTraceEnabled()) {
6937
+ const line = {
6938
+ ...traceFacts(input),
6939
+ matched: [...firedIds],
6940
+ issues: issues.map((i) => ({ id: i.subcategory ?? i.category, result: i.result }))
6941
+ };
6942
+ console.log(JSON.stringify({ axisCheckTrace: line }));
6624
6943
  }
6625
- return issues;
6944
+ return { issues, firedIds };
6945
+ }
6946
+ function traceAxisChecksSkipped(facts, note) {
6947
+ if (!axisCheckTraceEnabled()) return;
6948
+ const line = { ...traceFacts(facts), matched: [], issues: [], note };
6949
+ console.log(JSON.stringify({ axisCheckTrace: line }));
6950
+ }
6951
+ function traceFacts(input) {
6952
+ const propsSpec = input.contract?.propsSpec;
6953
+ return {
6954
+ sourceSha256: createHash("sha256").update(input.sourceCode, "utf8").digest("hex"),
6955
+ originalPrompt: input.originalPrompt,
6956
+ designMode: input.designMode ?? "constrained",
6957
+ classification: input.classification.vector,
6958
+ propsSpecKeys: Object.keys(propsSpec ?? {}),
6959
+ propsSpecPropertyKeys: Object.keys(propsSpec?.properties ?? {})
6960
+ };
6961
+ }
6962
+ var STYLING_PROFILE_TRUNCATION_MARKER = " \u2026[truncated at the profile bound]";
6963
+ var STYLING_PROFILE_JUDGE_FRAME = `The app's operator declared the styling profile below. Judge visual treatment RELATIVE to it \u2014 a dense profile is not "cramped", a flat one is not "unpolished", a sparse one is not "empty", and a directed composition is not missing what its Direction leaves out; completeness, hierarchy, accessibility and correctness are unchanged by it.`;
6964
+ var AESTHETIC_BRIEF_MAX_CHARS = 2e3;
6965
+ function sanitizeProfileMember(text, member) {
6966
+ return sanitizeProfileText(text, APP_GENERATION_PROFILE_BOUNDS[member]);
6967
+ }
6968
+ function sanitizeProfileText(text, bound) {
6969
+ if (typeof text !== "string") return "";
6970
+ const lines = [];
6971
+ let inFence = false;
6972
+ for (const raw of text.split(/\r?\n/)) {
6973
+ const line = raw.replace(/[ \t]+/g, " ").trim();
6974
+ if (line.startsWith("```")) {
6975
+ inFence = !inFence;
6976
+ continue;
6977
+ }
6978
+ if (inFence || line.length === 0 || line.startsWith("#")) continue;
6979
+ lines.push(line);
6980
+ }
6981
+ let out = lines.join("\n").replace(/`/g, "");
6982
+ if (out.length > bound) out = out.slice(0, bound) + STYLING_PROFILE_TRUNCATION_MARKER;
6983
+ return out;
6984
+ }
6985
+ function aestheticRefLabel(profile) {
6986
+ const ref = profile?.aesthetic;
6987
+ if (ref === void 0 || sanitizeProfileText(profile?.aestheticBrief, AESTHETIC_BRIEF_MAX_CHARS).length === 0) return "";
6988
+ return ref.version !== void 0 ? `${ref.id}@${ref.version}` : ref.id;
6989
+ }
6990
+ function sanitizeProfile(profile) {
6991
+ const aestheticRef = aestheticRefLabel(profile);
6992
+ return {
6993
+ styling: sanitizeProfileMember(profile?.styling, "styling"),
6994
+ density: sanitizeProfileMember(profile?.density, "density"),
6995
+ layout: sanitizeProfileMember(profile?.layout, "layout"),
6996
+ direction: sanitizeProfileMember(profile?.direction, "direction"),
6997
+ aesthetic: aestheticRef.length > 0 ? sanitizeProfileText(profile?.aestheticBrief, AESTHETIC_BRIEF_MAX_CHARS) : "",
6998
+ aestheticRef
6999
+ };
7000
+ }
7001
+ function renderProfileQuote(profile) {
7002
+ const p = sanitizeProfile(profile);
7003
+ const parts = [];
7004
+ if (p.styling.length > 0) parts.push(p.styling.split("\n").map((line) => `> ${line}`).join("\n"));
7005
+ if (p.density.length > 0) parts.push(`> **Density**: ${p.density}`);
7006
+ if (p.layout.length > 0) parts.push(`> **Layout**: ${p.layout}`);
7007
+ if (p.direction.length > 0) parts.push(`> **Direction**: ${p.direction}`);
7008
+ if (p.aesthetic.length > 0) {
7009
+ const [first, ...rest] = p.aesthetic.split("\n");
7010
+ parts.push([`> **Aesthetic** (\`${p.aestheticRef}\`): ${first ?? ""}`, ...rest.map((line) => `> ${line}`)].join("\n"));
7011
+ }
7012
+ return parts.join("\n>\n");
7013
+ }
7014
+ function buildStylingProfileJudgeBlock(profile) {
7015
+ const quote = renderProfileQuote(profile);
7016
+ if (quote.length === 0) return "";
7017
+ return ["## Declared styling profile", "", STYLING_PROFILE_JUDGE_FRAME, "", quote].join("\n");
7018
+ }
7019
+ function isDesignRenderingInput(relativePath) {
7020
+ const rel = relativePath.split("\\").join("/");
7021
+ if (rel.startsWith("__tests__/") || rel.includes("/__tests__/")) return false;
7022
+ if (/\.test\.[cm]?[jt]sx?$/.test(rel)) return false;
7023
+ if (/\.stories\.[cm]?[jt]sx?$/.test(rel)) return false;
7024
+ if (rel.endsWith(".md")) return false;
7025
+ return true;
7026
+ }
7027
+ function designTreeSha256(root) {
7028
+ const files = [];
7029
+ const walk = (dir) => {
7030
+ for (const name of readdirSync(dir).sort()) {
7031
+ const full = resolve(dir, name);
7032
+ if (statSync(full).isDirectory()) walk(full);
7033
+ else if (isDesignRenderingInput(full.slice(root.length + 1))) files.push(full);
7034
+ }
7035
+ };
7036
+ walk(root);
7037
+ const h = createHash("sha256");
7038
+ for (const f of files) {
7039
+ h.update(f.slice(root.length + 1) + "\n");
7040
+ h.update(readFileSync(f));
7041
+ h.update("\n");
7042
+ }
7043
+ return h.digest("hex");
7044
+ }
7045
+ var identityCache = /* @__PURE__ */ new Map();
7046
+ function judgeDesignIdentity(src) {
7047
+ const key = resolve(src);
7048
+ const cached = identityCache.get(key);
7049
+ if (cached !== void 0) return cached;
7050
+ let identity;
7051
+ try {
7052
+ identity = { src: key, srcSha256: designTreeSha256(key) };
7053
+ } catch (e) {
7054
+ const reason = e instanceof Error ? e.message : String(e);
7055
+ console.warn(`[visual-eval] design tree unreadable at ${key} \u2014 the judgement carries no design identity (${reason})`);
7056
+ identity = null;
7057
+ }
7058
+ identityCache.set(key, identity);
7059
+ return identity;
7060
+ }
7061
+ function resetJudgeDesignIdentityCache() {
7062
+ identityCache.clear();
6626
7063
  }
6627
7064
 
6628
7065
  // src/design-mode.ts
@@ -6640,6 +7077,9 @@ var CANVAS_VIEWPORTS = {
6640
7077
  lg: { width: 1024, height: 768 },
6641
7078
  xl: { width: 1440, height: 900 }
6642
7079
  };
7080
+ function displayModeForCanvas(canvas) {
7081
+ return canvas === "xs-chat-card" ? "inline" : "fullscreen";
7082
+ }
6643
7083
  function canvasDisplayModeForShell(shell) {
6644
7084
  return shell === "chat" || shell === "inline" ? "inline" : "fullscreen";
6645
7085
  }
@@ -6698,24 +7138,76 @@ var CANVAS_DESCRIPTORS = {
6698
7138
  };
6699
7139
  function describeCanvas(canvas) {
6700
7140
  const d = CANVAS_DESCRIPTORS[canvas];
6701
- return `This component renders on the \`${canvas}\` canvas (${d.label}): ${d.minWidthPx}\u2013${d.maxWidthPx}px wide, ${d.host}. Design for the whole range \u2014 the host may size the iframe anywhere inside it.`;
7141
+ return `This component renders on the \`${canvas}\` canvas (${d.label}): ${d.minWidthPx}\u2013${d.maxWidthPx}px wide, ${d.host}. Design for the whole range \u2014 the host may size the iframe anywhere inside it, and the host owns the height: size to your content, never to the viewport.`;
6702
7142
  }
6703
7143
 
6704
7144
  // src/evaluation/visual-evaluator.ts
7145
+ function canvasFitPolicy(canvas) {
7146
+ switch (canvas) {
7147
+ case "xs-chat-card":
7148
+ return { capture: "viewport", overflow: "fail" };
7149
+ case "mobile-fullscreen-small":
7150
+ return { capture: "full-page", overflow: "warn" };
7151
+ default:
7152
+ return { capture: "full-page", overflow: "none" };
7153
+ }
7154
+ }
7155
+ var JUDGE_SCOPE_CLASS = "ggui-judge-scope";
7156
+ function canvasFit(canvas) {
7157
+ return displayModeForCanvas(canvas) === "fullscreen" ? "fill" : void 0;
7158
+ }
7159
+ function canvasOverflowIssue(canvas, viewport, contentHeight, verdict) {
7160
+ const hidden = contentHeight - viewport.height;
7161
+ return {
7162
+ dimension: "canvas-overflow",
7163
+ severity: verdict === "fail" ? "critical" : "major",
7164
+ description: `Rendered content is ${contentHeight}px tall on the ${canvas} canvas (${viewport.width}\xD7${viewport.height}) \u2014 ${hidden}px ${verdict === "fail" ? "is cut off: the inline card does not scroll" : "sits below the first screen"}.`,
7165
+ fix: "Fit the composition to the canvas: fewer and shorter sections, one compact row of chips, no hero taller than a third of the box, no fixed min-heights or tall paddings \u2014 measure against the canvas, not the page."
7166
+ };
7167
+ }
6705
7168
  function resolveUiGenPackageDir() {
6706
7169
  const selfDir = dirname(fileURLToPath(import.meta.url));
6707
7170
  return resolve(selfDir, "..", "..");
6708
7171
  }
7172
+ function resolveWirePackageDir() {
7173
+ const uiGenDir = resolveUiGenPackageDir();
7174
+ const candidates = [
7175
+ resolve(uiGenDir, "node_modules", "@ggui-ai", "wire"),
7176
+ // workspace / nested install
7177
+ resolve(uiGenDir, "..", "wire"),
7178
+ // pnpm deploy: `.pnpm/<ui-gen>@<ver>/node_modules/@ggui-ai/{ui-gen,wire}`
7179
+ resolve(uiGenDir, "..", "..", "@ggui-ai", "wire")
7180
+ // hoisted flat install beside the scope dir
7181
+ ];
7182
+ for (const dir of candidates) {
7183
+ if (existsSync(resolve(dir, "package.json"))) return dir;
7184
+ }
7185
+ try {
7186
+ const entry = fileURLToPath(import.meta.resolve("@ggui-ai/wire"));
7187
+ let dir = dirname(entry);
7188
+ for (let i = 0; i < 6; i++) {
7189
+ if (existsSync(resolve(dir, "package.json"))) return dir;
7190
+ dir = dirname(dir);
7191
+ }
7192
+ } catch {
7193
+ }
7194
+ return null;
7195
+ }
6709
7196
  function resolveDesignPackageDir() {
6710
- const selfDir = dirname(fileURLToPath(import.meta.url));
6711
- return resolve(selfDir, "..", "..", "..", "design");
7197
+ try {
7198
+ return dirname(createRequire(import.meta.url).resolve("@ggui-ai/design/package.json"));
7199
+ } catch {
7200
+ const selfDir = dirname(fileURLToPath(import.meta.url));
7201
+ return resolve(selfDir, "..", "..", "..", "design");
7202
+ }
6712
7203
  }
6713
- async function bundleForRendering(compiledCode, sampleProps) {
7204
+ async function bundleForRendering(compiledCode, sampleProps, designSrc = resolve(resolveDesignPackageDir(), "src")) {
6714
7205
  const tmpDir = resolve(tmpdir(), "ggui-visual-eval-" + Date.now());
6715
7206
  mkdirSync(tmpDir, { recursive: true });
6716
7207
  const componentFile = resolve(tmpDir, "component.tsx");
6717
7208
  const entryFile = resolve(tmpDir, "entry.tsx");
6718
- const designPkgDir = resolveDesignPackageDir();
7209
+ const designPkgDir = resolve(designSrc, "..");
7210
+ const wirePkgDir = resolveWirePackageDir();
6719
7211
  writeFileSync(componentFile, compiledCode);
6720
7212
  const entryCode = `
6721
7213
  import React from 'react';
@@ -6763,6 +7255,10 @@ try {
6763
7255
  jsxImportSource: "react",
6764
7256
  external: ["react", "react-dom", "react-dom/client", "react/jsx-runtime"],
6765
7257
  alias: {
7258
+ // The wire hooks: resolved from ui-gen's own module graph so the
7259
+ // alias holds in a workspace checkout AND in a `pnpm deploy` image
7260
+ // (where deps are siblings, not children, of this package).
7261
+ ...wirePkgDir !== null ? { "@ggui-ai/wire": wirePkgDir } : {},
6766
7262
  // D1: generated code imports the single `@ggui-ai/design` barrel.
6767
7263
  // The subpath aliases stay for any first-party / legacy code.
6768
7264
  "@ggui-ai/design": resolve(designPkgDir, "src", "index.ts"),
@@ -6774,7 +7270,10 @@ try {
6774
7270
  // Bare imports in the generated component resolve against ui-gen's
6775
7271
  // own node_modules — the temp entry dir has none. Without this every
6776
7272
  // wire-bearing component (`@ggui-ai/wire`) failed to bundle.
6777
- nodePaths: [resolve(resolveUiGenPackageDir(), "node_modules")],
7273
+ // Workspace layout (deps under this package's node_modules) and the
7274
+ // `pnpm deploy` layout (deps as siblings of this package) — both
7275
+ // searched for any other allowlisted bare import.
7276
+ nodePaths: [resolve(resolveUiGenPackageDir(), "node_modules"), resolve(resolveUiGenPackageDir(), "..")],
6778
7277
  logLevel: "silent"
6779
7278
  });
6780
7279
  return result.outputFiles[0]?.text ?? "";
@@ -6789,7 +7288,7 @@ try {
6789
7288
  }
6790
7289
  }
6791
7290
  }
6792
- function buildRenderHTML(bundledCode, cssTokens) {
7291
+ function buildRenderHTML(bundledCode, cssTokens, fit) {
6793
7292
  const tokens = cssTokens ?? getCssTokens();
6794
7293
  return `<!DOCTYPE html>
6795
7294
  <html>
@@ -6805,10 +7304,11 @@ function buildRenderHTML(bundledCode, cssTokens) {
6805
7304
  color: var(--ggui-color-neutral-900, #111827);
6806
7305
  }
6807
7306
  .error { color: #dc2626; padding: 16px; font-family: monospace; white-space: pre-wrap; }
7307
+ ${fit === "fill" ? fillFitRule(JUDGE_SCOPE_CLASS) : ""}
6808
7308
  </style>
6809
7309
  </head>
6810
7310
  <body>
6811
- <div id="root"></div>
7311
+ <div id="root"${fit === "fill" ? ` class="${JUDGE_SCOPE_CLASS}"` : ""}></div>
6812
7312
  <script type="importmap">
6813
7313
  {
6814
7314
  "imports": {
@@ -6854,7 +7354,17 @@ async function launchWithPuppeteer(options) {
6854
7354
  const puppeteer = await import('puppeteer-core');
6855
7355
  return puppeteer.default.launch(options);
6856
7356
  }
6857
- async function captureScreenshot(html, viewport = DEFAULT_SCREENSHOT_VIEWPORT, deps = {}) {
7357
+ var CONTENT_HEIGHT_EXPRESSION = "Math.max(document.documentElement.scrollHeight, document.body ? document.body.scrollHeight : 0)";
7358
+ async function measureContentHeight(page) {
7359
+ try {
7360
+ const h = await page.evaluate(CONTENT_HEIGHT_EXPRESSION);
7361
+ return typeof h === "number" && Number.isFinite(h) ? Math.round(h) : null;
7362
+ } catch (e) {
7363
+ console.warn(`[visual-eval] content height unavailable: ${e instanceof Error ? e.message : String(e)}`);
7364
+ return null;
7365
+ }
7366
+ }
7367
+ async function captureScreenshotDetailed(html, viewport = DEFAULT_SCREENSHOT_VIEWPORT, deps = {}, capture = "full-page") {
6858
7368
  try {
6859
7369
  const launchOptions = await resolveLaunchOptions(viewport, deps);
6860
7370
  const browser = await (deps.launch ?? launchWithPuppeteer)(launchOptions);
@@ -6866,16 +7376,21 @@ async function captureScreenshot(html, viewport = DEFAULT_SCREENSHOT_VIEWPORT, d
6866
7376
  await page.waitForSelector("#root > *", { timeout: 1e4 }).catch(() => {
6867
7377
  });
6868
7378
  await new Promise((r) => setTimeout(r, deps.settleMs ?? 1e3));
6869
- const screenshot = await page.screenshot({ type: "png", fullPage: true });
6870
- return Buffer.from(screenshot);
7379
+ const contentHeight = await measureContentHeight(page);
7380
+ const screenshot = await page.screenshot({ type: "png", fullPage: capture === "full-page" });
7381
+ return { png: Buffer.from(screenshot), contentHeight };
6871
7382
  } finally {
6872
7383
  await browser.close();
6873
7384
  }
6874
7385
  } catch (e) {
6875
- console.warn(`[visual-eval] screenshot failed: ${e instanceof Error ? e.message : e}`);
6876
- return null;
7386
+ const reason = `screenshot failed: ${e instanceof Error ? e.message : String(e)}`;
7387
+ console.warn(`[visual-eval] ${reason}`);
7388
+ return { png: null, reason, contentHeight: null };
6877
7389
  }
6878
7390
  }
7391
+ async function captureScreenshot(html, viewport = DEFAULT_SCREENSHOT_VIEWPORT, deps = {}) {
7392
+ return (await captureScreenshotDetailed(html, viewport, deps)).png;
7393
+ }
6879
7394
  var VISUAL_EVAL_PROMPT = `You are a visual quality evaluator for ggui \u2014 a platform where AI agents generate React UI components on demand.
6880
7395
 
6881
7396
  ## Context
@@ -6903,6 +7418,8 @@ The screenshot shows a generated React component built from a design system with
6903
7418
  - Error messages like "Render error" or blank white space = rendering score 0
6904
7419
  - A component that renders but is just a wall of text with no structure should score low on layout and hierarchy
6905
7420
 
7421
+ List AT MOST 6 issues, most severe first, one sentence each for "description" and "fix" \u2014 a longer list is cut off and lost.
7422
+
6906
7423
  Respond with ONLY a JSON object (no markdown, no explanation):
6907
7424
  {
6908
7425
  "completeness": <0-100>,
@@ -6914,11 +7431,16 @@ Respond with ONLY a JSON object (no markdown, no explanation):
6914
7431
  ],
6915
7432
  "critique": "<2-3 sentences: what's good, what needs improvement, does it achieve the user's goal?>"
6916
7433
  }`;
6917
- async function callMultimodalLLM(config, model, prompt, screenshot, originalPrompt) {
7434
+ var VISUAL_JUDGE_MAX_OUTPUT_TOKENS = 4096;
7435
+ var VISUAL_JUDGE_PROMPT_VERSION = "v2-bounded-issues";
7436
+ var VISUAL_JUDGE_PROMPT_DIGEST = createHash("sha256").update(VISUAL_EVAL_PROMPT, "utf8").digest("hex");
7437
+ async function callMultimodalLLM(config, model, prompt, screenshot, originalPrompt, profileBlock = "") {
6918
7438
  const userPrompt = `## Original Request
6919
7439
  ${originalPrompt}
6920
7440
 
6921
- Evaluate the screenshot of the generated component.`;
7441
+ ` + (profileBlock.length > 0 ? `${profileBlock}
7442
+
7443
+ ` : "") + "Evaluate the screenshot of the generated component.";
6922
7444
  const agent = createVisionAgent({
6923
7445
  provider: config.provider === "claude" ? "anthropic" : config.provider,
6924
7446
  routeOverride: config.routeOverride,
@@ -6929,16 +7451,69 @@ Evaluate the screenshot of the generated component.`;
6929
7451
  prompt,
6930
7452
  userPrompt,
6931
7453
  { mediaType: "image/png", base64: screenshot.toString("base64") },
6932
- 1500
7454
+ VISUAL_JUDGE_MAX_OUTPUT_TOKENS
6933
7455
  );
6934
7456
  }
7457
+ function judgeCountFor(config, canvas) {
7458
+ const k = Math.max(1, Math.floor(config.judgeK ?? 1));
7459
+ if (k === 1) return 1;
7460
+ return config.judgeKCanvases === void 0 || config.judgeKCanvases.includes(canvas) ? k : 1;
7461
+ }
7462
+ function medianOf(values) {
7463
+ const sorted = [...values].sort((a, b) => a - b);
7464
+ return sorted[Math.floor((sorted.length - 1) / 2)];
7465
+ }
7466
+ function populationSigma(values) {
7467
+ if (values.length < 2) return 0;
7468
+ const mean = values.reduce((a, b) => a + b, 0) / values.length;
7469
+ const variance = values.reduce((a, b) => a + (b - mean) ** 2, 0) / values.length;
7470
+ return Math.round(Math.sqrt(variance) * 10) / 10;
7471
+ }
7472
+ async function judgeAndParse(judge, config, model, screenshot, originalPrompt, profileBlock, canvas) {
7473
+ let firstReason;
7474
+ for (let attempt = 1; attempt <= 2; attempt++) {
7475
+ const response = await judge(config, model, VISUAL_EVAL_PROMPT, screenshot, originalPrompt, profileBlock);
7476
+ try {
7477
+ const result = parseVisualResponse(response.text, config.passThreshold);
7478
+ return { kind: "ok", result, inputTokens: response.inputTokens, outputTokens: response.outputTokens };
7479
+ } catch (e) {
7480
+ const salvaged = salvageTruncatedVisualAnswer(response.text, config.passThreshold);
7481
+ if (salvaged !== null) {
7482
+ console.warn(`[visual-eval] visual_judge_truncated${canvas ? ` canvas=${canvas}` : ""}: answer cut after ${response.text.length} chars \u2014 score recovered from the closed prefix, issues list partial`);
7483
+ return { kind: "ok", result: salvaged, inputTokens: response.inputTokens, outputTokens: response.outputTokens };
7484
+ }
7485
+ const reason = `judge answer unparsable: ${e instanceof Error ? e.message : String(e)}`;
7486
+ if (attempt === 1) {
7487
+ firstReason = reason;
7488
+ console.warn(`[visual-eval] visual_judge_retry${canvas ? ` canvas=${canvas}` : ""}: ${reason}`);
7489
+ } else {
7490
+ console.warn(`[visual-eval] visual_judge_retry failed again${canvas ? ` canvas=${canvas}` : ""}: ${reason}`);
7491
+ }
7492
+ }
7493
+ }
7494
+ return { kind: "unparsable", reason: firstReason ?? "judge answer unparsable" };
7495
+ }
6935
7496
  async function runVisualEvaluation(context, config, deps = {}) {
7497
+ return (await runVisualEvaluationDetailed(context, config, deps)).result;
7498
+ }
7499
+ async function runVisualEvaluationDetailed(context, config, deps = {}) {
6936
7500
  const startTime = Date.now();
6937
7501
  const judge = deps.judge ?? callMultimodalLLM;
6938
- const bundledCode = await bundleForRendering(
6939
- context.compiledCode,
6940
- config.sampleProps ?? {}
6941
- );
7502
+ const designSrc = config.designSrcDir ?? resolve(resolveDesignPackageDir(), "src");
7503
+ const design = judgeDesignIdentity(designSrc);
7504
+ const stamp = (r) => ({
7505
+ ...r,
7506
+ ...design !== null ? { design } : {},
7507
+ ...config.themeMode !== void 0 ? { themeMode: config.themeMode } : {}
7508
+ });
7509
+ let bundledCode;
7510
+ try {
7511
+ bundledCode = await bundleForRendering(context.compiledCode, config.sampleProps ?? {}, designSrc);
7512
+ } catch (e) {
7513
+ const unavailableReason = `bundle failed: ${e instanceof Error ? e.message : String(e)}`;
7514
+ console.warn(`[visual-eval] ${unavailableReason}`);
7515
+ return { result: null, unavailableReason };
7516
+ }
6942
7517
  console.log(`[visual-eval] bundled: ${bundledCode.length}B (${Date.now() - startTime}ms)`);
6943
7518
  const html = buildRenderHTML(bundledCode, context.cssTokens);
6944
7519
  const model = config.model ?? getDefaultVisualModel(config.provider);
@@ -6947,25 +7522,62 @@ async function runVisualEvaluation(context, config, deps = {}) {
6947
7522
  const perCanvasResults = [];
6948
7523
  for (const canvas of config.canvases) {
6949
7524
  const viewport = CANVAS_VIEWPORTS[canvas];
6950
- const screenshot2 = await captureScreenshot(html, viewport, deps);
7525
+ const policy = canvasFitPolicy(canvas);
7526
+ const fit = canvasFit(canvas);
7527
+ const canvasHtml = fit !== void 0 ? buildRenderHTML(bundledCode, context.cssTokens, fit) : html;
7528
+ const attempt2 = await captureScreenshotDetailed(canvasHtml, viewport, deps, policy.capture);
7529
+ const screenshot2 = attempt2.png;
6951
7530
  if (!screenshot2) {
6952
- console.warn(`[visual-eval] no browser available at canvas ${canvas} \u2014 skipping visual evaluation`);
6953
- return null;
7531
+ const unavailableReason = attempt2.reason ?? "no browser available";
7532
+ console.warn(`[visual-eval] ${unavailableReason} at canvas ${canvas} \u2014 skipping visual evaluation`);
7533
+ return { result: null, unavailableReason, canvas };
7534
+ }
7535
+ const k = judgeCountFor(config, canvas);
7536
+ const profileBlock = buildStylingProfileJudgeBlock(context.profile);
7537
+ const answers = await Promise.all(
7538
+ Array.from({ length: k }, () => judgeAndParse(judge, config, model, screenshot2, context.originalPrompt, profileBlock, canvas))
7539
+ );
7540
+ const parsed = answers.filter((a) => a.kind === "ok");
7541
+ if (parsed.length === 0) {
7542
+ const first = answers.find((a) => a.kind === "unparsable");
7543
+ return { result: null, unavailableReason: first?.reason ?? "judge answer unparsable", canvas };
7544
+ }
7545
+ const samples = parsed.map((a) => a.result.finalScore);
7546
+ const median = medianOf(samples);
7547
+ const representative = parsed[samples.indexOf(median)];
7548
+ const result2 = representative.result;
7549
+ result2.finalScore = median;
7550
+ result2.passed = median >= config.passThreshold;
7551
+ result2.inputTokens = parsed.reduce((sum, a) => sum + a.inputTokens, 0);
7552
+ result2.outputTokens = parsed.reduce((sum, a) => sum + a.outputTokens, 0);
7553
+ const response2 = { inputTokens: result2.inputTokens, outputTokens: result2.outputTokens };
7554
+ const judgeRecord = {
7555
+ k,
7556
+ rule: "median",
7557
+ samples,
7558
+ sigma: populationSigma(samples),
7559
+ notes: parsed.map((a) => a.result.critique ?? "")
7560
+ };
7561
+ const contentHeight = attempt2.contentHeight;
7562
+ const overflow = contentHeight !== null && contentHeight > viewport.height;
7563
+ if (contentHeight !== null && contentHeight > viewport.height && policy.overflow !== "none") {
7564
+ result2.issues.push(canvasOverflowIssue(canvas, viewport, contentHeight, policy.overflow));
7565
+ if (policy.overflow === "fail") result2.passed = false;
6954
7566
  }
6955
- const response2 = await judge(config, model, VISUAL_EVAL_PROMPT, screenshot2, context.originalPrompt);
6956
- const result2 = parseVisualResponse(response2.text, config.passThreshold);
6957
- result2.inputTokens = response2.inputTokens;
6958
- result2.outputTokens = response2.outputTokens;
6959
7567
  perCanvasResults.push(result2);
6960
7568
  perCanvas.push({
6961
7569
  canvas,
6962
7570
  viewport,
6963
7571
  score: result2.finalScore,
6964
7572
  passed: result2.passed,
6965
- screenshotPng: screenshot2
7573
+ screenshotPng: screenshot2,
7574
+ contentHeight,
7575
+ overflow,
7576
+ judge: judgeRecord,
7577
+ ...fit !== void 0 ? { fit } : {}
6966
7578
  });
6967
7579
  console.log(
6968
- `[visual-eval] canvas=${canvas} ${viewport.width}\xD7${viewport.height} score=${result2.finalScore} ${result2.passed ? "pass" : "FAIL"} | in=${response2.inputTokens} out=${response2.outputTokens}`
7580
+ `[visual-eval] canvas=${canvas} ${viewport.width}\xD7${viewport.height} score=${result2.finalScore}${k > 1 ? ` (median of ${judgeRecord.samples.length}/${k}: ${judgeRecord.samples.join(",")} \u03C3=${judgeRecord.sigma})` : ""} ${result2.passed ? "pass" : "FAIL"} | content=${contentHeight ?? "?"}px${overflow ? ` OVERFLOW (${policy.overflow})` : ""} | in=${response2.inputTokens} out=${response2.outputTokens}`
6969
7581
  );
6970
7582
  }
6971
7583
  const aggregate = aggregateCanvasResults(perCanvas, perCanvasResults);
@@ -6973,27 +7585,34 @@ async function runVisualEvaluation(context, config, deps = {}) {
6973
7585
  console.log(
6974
7586
  `[visual-eval] score=${aggregate.finalScore} (mean of ${perCanvas.length} canvases; ${aggregate.passed ? "every canvas passed" : `${perCanvas.filter((c) => !c.passed).length} failed`}) (${elapsed2}ms)`
6975
7587
  );
6976
- return aggregate;
7588
+ return { result: stamp(aggregate) };
6977
7589
  }
6978
- const screenshot = await captureScreenshot(html, config.viewport, deps);
7590
+ const attempt = await captureScreenshotDetailed(html, config.viewport, deps);
7591
+ const screenshot = attempt.png;
6979
7592
  if (!screenshot) {
6980
- console.warn("[visual-eval] no browser available \u2014 skipping visual evaluation");
6981
- return null;
7593
+ const unavailableReason = attempt.reason ?? "no browser available";
7594
+ console.warn(`[visual-eval] ${unavailableReason} \u2014 skipping visual evaluation`);
7595
+ return { result: null, unavailableReason };
6982
7596
  }
6983
7597
  console.log(`[visual-eval] screenshot: ${screenshot.length}B (${Date.now() - startTime}ms)`);
6984
- const response = await judge(
7598
+ const answer = await judgeAndParse(
7599
+ judge,
6985
7600
  config,
6986
7601
  model,
6987
- VISUAL_EVAL_PROMPT,
6988
7602
  screenshot,
6989
- context.originalPrompt
7603
+ context.originalPrompt,
7604
+ buildStylingProfileJudgeBlock(context.profile)
6990
7605
  );
6991
- const result = parseVisualResponse(response.text, config.passThreshold);
6992
- result.inputTokens = response.inputTokens;
6993
- result.outputTokens = response.outputTokens;
7606
+ if (answer.kind === "unparsable") {
7607
+ return { result: null, unavailableReason: answer.reason };
7608
+ }
7609
+ const result = answer.result;
7610
+ result.inputTokens = answer.inputTokens;
7611
+ result.outputTokens = answer.outputTokens;
7612
+ const response = { inputTokens: answer.inputTokens, outputTokens: answer.outputTokens };
6994
7613
  const elapsed = Date.now() - startTime;
6995
7614
  console.log(`[visual-eval] score=${result.finalScore} (${elapsed}ms) | in=${response.inputTokens} out=${response.outputTokens}`);
6996
- return result;
7615
+ return { result: stamp(result) };
6997
7616
  }
6998
7617
  function aggregateCanvasResults(perCanvas, results) {
6999
7618
  const n = results.length;
@@ -7033,9 +7652,57 @@ function summarizeVisualResult(result) {
7033
7652
  canvas: c.canvas,
7034
7653
  viewport: c.viewport,
7035
7654
  score: c.score,
7036
- passed: c.passed
7655
+ passed: c.passed,
7656
+ contentHeight: c.contentHeight,
7657
+ overflow: c.overflow,
7658
+ judge: c.judge,
7659
+ ...c.fit !== void 0 ? { fit: c.fit } : {}
7037
7660
  }));
7038
- return { score: result.finalScore, passed: result.passed, canvases };
7661
+ return {
7662
+ score: result.finalScore,
7663
+ passed: result.passed,
7664
+ canvases,
7665
+ ...result.design !== void 0 ? { design: result.design } : {},
7666
+ ...result.themeMode !== void 0 ? { themeMode: result.themeMode } : {}
7667
+ };
7668
+ }
7669
+ function salvageTruncatedVisualAnswer(text, passThreshold) {
7670
+ const dim = (name) => {
7671
+ const m = text.match(new RegExp(`"${name}"\\s*:\\s*(\\d+(?:\\.\\d+)?)`));
7672
+ return m ? Number(m[1]) : null;
7673
+ };
7674
+ const completeness = dim("completeness");
7675
+ const layout = dim("layout");
7676
+ const hierarchy = dim("hierarchy");
7677
+ const aesthetics = dim("aesthetics");
7678
+ if (completeness === null || layout === null || hierarchy === null || aesthetics === null) return null;
7679
+ const issues = [];
7680
+ const issuesAt = text.indexOf('"issues"');
7681
+ if (issuesAt >= 0) {
7682
+ for (const m of text.slice(issuesAt).matchAll(/\{[^{}]*\}/g)) {
7683
+ try {
7684
+ const o = JSON.parse(m[0]);
7685
+ if (typeof o.description === "string") {
7686
+ issues.push({
7687
+ dimension: typeof o.dimension === "string" ? o.dimension : "layout",
7688
+ severity: typeof o.severity === "string" ? o.severity : "minor",
7689
+ description: o.description,
7690
+ fix: typeof o.fix === "string" ? o.fix : ""
7691
+ });
7692
+ }
7693
+ } catch {
7694
+ }
7695
+ }
7696
+ }
7697
+ const rebuilt = JSON.stringify({
7698
+ completeness,
7699
+ layout,
7700
+ hierarchy,
7701
+ aesthetics,
7702
+ issues,
7703
+ critique: `[truncated judge answer \u2014 score recovered from the closed prefix; ${issues.length} issue(s) kept, the rest and the critique were cut]`
7704
+ });
7705
+ return parseVisualResponse(rebuilt, passThreshold);
7039
7706
  }
7040
7707
  function parseVisualResponse(text, passThreshold) {
7041
7708
  const jsonMatch = text.match(/\{[\s\S]*\}/);
@@ -7097,7 +7764,11 @@ async function runVisualEval(context, config) {
7097
7764
  const summary = summarizeVisualResult(result);
7098
7765
  return summary === void 0 ? { issues } : { issues, summary };
7099
7766
  }
7767
+ function cssTokensForAppTheme(theme, mode = "light") {
7768
+ if (theme === void 0) return getCssTokens(mode);
7769
+ return composeThemeCss({ layer: "page", mode: theme.mode ?? mode, appTheme: theme });
7770
+ }
7100
7771
 
7101
- export { CANVAS_BREAKPOINTS, CANVAS_CLASSES, CANVAS_DESCRIPTORS, CANVAS_VIEWPORTS, CRITERIA, DEFAULT_EVAL_MAX_ROUNDS, DEFAULT_QUALITY_CONFIG, LLM_EVAL_STATIC_CRITERIA, MAX_EVAL_ROUNDS_HARD_LIMIT, buildCodingCriteriaSummary, buildFixPrompt, callMultimodalLLM, canvasDisplayModeForShell, canvasForRendering, canvasForViewportWidth, captureScreenshot, computeEvaluationScore, createEvaluationToolsServer, criteriaFor, describeCanvas, extractCompiledCode, extractCompiledCodeFromMessage, extractEvalResult, extractSourceCode, extractSourceCodeFromMessage, extractToolResultTexts, getActionableIssues, getCriteriaByPriority, getCriterionById, getEvaluatorSystemPrompt, getLLMCriteria, isBlocked, matches, notApplicableCoverage, priorityForIssue, resolveLaunchOptions, runAxisChecks, runEvaluation, runEvaluationLoop, runVisualEval, runVisualEvaluation, summarizeVisualResult };
7772
+ export { CANVAS_BREAKPOINTS, CANVAS_CLASSES, CANVAS_DESCRIPTORS, CANVAS_VIEWPORTS, CONTENT_HEIGHT_EXPRESSION, CRITERIA, DEFAULT_EVAL_MAX_ROUNDS, DEFAULT_QUALITY_CONFIG, LLM_EVAL_STATIC_CRITERIA, MAX_EVAL_ROUNDS_HARD_LIMIT, VISUAL_EVAL_PROMPT, VISUAL_JUDGE_MAX_OUTPUT_TOKENS, VISUAL_JUDGE_PROMPT_DIGEST, VISUAL_JUDGE_PROMPT_VERSION, buildCodingCriteriaSummary, buildFixPrompt, callMultimodalLLM, canvasDisplayModeForShell, canvasFitPolicy, canvasForRendering, canvasForViewportWidth, canvasOverflowIssue, captureScreenshot, captureScreenshotDetailed, computeEvaluationScore, createEvaluationToolsServer, criteriaFor, cssTokensForAppTheme, describeCanvas, designTreeSha256, extractCompiledCode, extractCompiledCodeFromMessage, extractEvalResult, extractSourceCode, extractSourceCodeFromMessage, extractToolResultTexts, getActionableIssues, getCriteriaByPriority, getCriterionById, getEvaluatorSystemPrompt, getLLMCriteria, isBlocked, judgeDesignIdentity, matches, notApplicableCoverage, priorityForIssue, resetJudgeDesignIdentityCache, resolveDesignPackageDir, resolveLaunchOptions, runAxisChecks, runEvaluation, runEvaluationLoop, runVisualEval, runVisualEvaluation, runVisualEvaluationDetailed, summarizeVisualResult };
7102
7773
  //# sourceMappingURL=index.js.map
7103
7774
  //# sourceMappingURL=index.js.map