@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.
- package/dist/adapters/generation-dispatch.d.ts +6 -3
- package/dist/adapters/generation-dispatch.js +5161 -4312
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +8 -5
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +12 -8
- package/dist/advanced/index.js +4685 -3801
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +8 -12
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.d.ts +5 -2
- package/dist/boilerplate.js +120 -13
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +2 -2
- package/dist/check/index.js +72 -0
- package/dist/check/index.js.map +1 -1
- package/dist/compose.js +1 -1
- package/dist/compose.js.map +1 -1
- package/dist/{create-ui-generator-CCIeQaEe.d.ts → create-ui-generator-C8Ki9Wz1.d.ts} +18 -2
- package/dist/{design-mode-BHEE7flj.d.ts → design-mode-DLufhrNp.d.ts} +0 -6
- package/dist/{dispatch-BA5dJcg-.d.ts → dispatch-D2htzi9h.d.ts} +2 -2
- package/dist/evaluation/axis-checks/index.d.ts +4 -4
- package/dist/evaluation/axis-checks/index.js +347 -13
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -2
- package/dist/evaluation/axis-checks/registry.js +302 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +160 -8
- package/dist/evaluation/index.js +752 -81
- package/dist/evaluation/index.js.map +1 -1
- package/dist/fragments/index.js +1 -1
- package/dist/fragments/index.js.map +1 -1
- package/dist/{generate-8L_bc2iP.d.ts → generate-9IeNT4ZY.d.ts} +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +3 -2
- package/dist/harness/check/runtime-render/index.js +8 -12
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/index.d.ts +3 -2
- package/dist/harness/index.js +410 -255
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -2
- package/dist/harness/prompts.js +7 -3
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +3 -3
- package/dist/harness/runtime.d.ts +13 -4
- package/dist/harness/runtime.js +131 -20
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +13 -2
- package/dist/harness/types-public.js +410 -255
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -2
- package/dist/index.d.ts +29 -7
- package/dist/index.js +4691 -3805
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +3 -2
- package/dist/{prompts-k6m6reqe.d.ts → prompts-YU9piOgs.d.ts} +12 -46
- package/dist/styling-profile-CxfvV48g.d.ts +35 -0
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/{types-public-D24JLJgl.d.ts → types-public-DVSssdc-.d.ts} +39 -3
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +3 -2
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +6 -1
- package/src/boilerplate/free-design-prompt.ts +9 -0
- package/src/boilerplate/generate.ts +8 -1
- package/src/boilerplate/hard-sections.ts +45 -1
- package/src/boilerplate/styling-profile.ts +166 -0
- package/src/boilerplate/system-prompt.ts +22 -8
- package/src/boilerplate/templates/layouts/fullscreen-desktop.tsx.tmpl +5 -3
- package/src/boilerplate/templates/layouts/fullscreen-mobile.tsx.tmpl +3 -3
- package/src/boilerplate/templates/layouts/fullscreen-universal.tsx.tmpl +5 -3
- package/src/check/run-tier0.ts +154 -0
- package/src/contract-context.ts +28 -14
- package/src/create-harness.ts +4 -0
- package/src/create-ui-generator.ts +41 -2
- package/src/design-mode.ts +11 -1
- package/src/design-system-docs.ts +7 -4
- package/src/effort.ts +48 -0
- package/src/evaluation/axis-checks/checks/grid.ts +148 -0
- package/src/evaluation/axis-checks/checks/universal.ts +308 -0
- package/src/evaluation/axis-checks/dispatch.ts +78 -13
- package/src/evaluation/axis-checks/helpers.ts +12 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/design-identity.ts +96 -0
- package/src/evaluation/index.ts +17 -0
- package/src/evaluation/llm-evaluator.ts +13 -1
- package/src/evaluation/theme-css.ts +14 -0
- package/src/evaluation/types-public.ts +35 -1
- package/src/evaluation/visual-evaluator.ts +478 -45
- package/src/fragments/render.ts +1 -1
- package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
- package/src/harness/coding/init-session.ts +1 -0
- package/src/harness/coding/run-coding-turn.ts +6 -14
- package/src/harness/coding/run-eval-round.ts +31 -3
- package/src/harness/runtime.ts +10 -0
- package/src/harness/types-public.ts +11 -0
- package/src/index.ts +2 -0
- package/src/pin-fixtures.ts +98 -0
- package/src/run-check.ts +10 -10
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools.ts +2 -1
- package/src/validation/primitives.ts +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// The design tree a judge paints with is an INPUT to the judgement and a
|
|
2
|
+
// part of its identity (ggui#1042, D3): a judge-only re-judge renders every
|
|
3
|
+
// arm with the tree on disk, so the row must say WHICH tree — `srcSha256`
|
|
4
|
+
// over every RENDERING INPUT under the design package's `src`, sorted by
|
|
5
|
+
// relative path — and the local judge path must accept the tree explicitly.
|
|
6
|
+
// A served hello is compared to a judged one only when this hash and the
|
|
7
|
+
// runtime's design agree (`design@judge == design@runtime`, generation-triad.md).
|
|
8
|
+
//
|
|
9
|
+
// Rendering inputs only: tests (`*.test.*`, `__tests__/`), stories
|
|
10
|
+
// (`*.stories.*`) and docs (`*.md`) paint nothing, and a built image ships
|
|
11
|
+
// the design package without them — the first rows on candidate 34 stamped
|
|
12
|
+
// `e2dc89e4…` (123 files) while a checkout of the same git tree computed
|
|
13
|
+
// `2cd62cb7…` (199 files). One identity must read the same on every host
|
|
14
|
+
// that carries the same rendering inputs.
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
17
|
+
import { resolve } from 'node:path';
|
|
18
|
+
|
|
19
|
+
export interface JudgeDesignIdentity {
|
|
20
|
+
/** Absolute path of the design `src` tree the judge bundled against. */
|
|
21
|
+
readonly src: string;
|
|
22
|
+
/** sha256 over every rendering input under `src` (see {@link isDesignRenderingInput}) — sorted relative path, then contents. */
|
|
23
|
+
readonly srcSha256: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether a file under the design `src` tree is something the judge PAINTS
|
|
28
|
+
* with. Tests, stories and docs are not — and a built image carries the
|
|
29
|
+
* package without them, so they must not move the identity.
|
|
30
|
+
*/
|
|
31
|
+
export function isDesignRenderingInput(relativePath: string): boolean {
|
|
32
|
+
const rel = relativePath.split('\\').join('/');
|
|
33
|
+
if (rel.startsWith('__tests__/') || rel.includes('/__tests__/')) return false;
|
|
34
|
+
if (/\.test\.[cm]?[jt]sx?$/.test(rel)) return false;
|
|
35
|
+
if (/\.stories\.[cm]?[jt]sx?$/.test(rel)) return false;
|
|
36
|
+
if (rel.endsWith('.md')) return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** sha256 over every rendering input under `root` (sorted relative path + contents) — the identity of a design tree as it paints. */
|
|
41
|
+
export function designTreeSha256(root: string): string {
|
|
42
|
+
const files: string[] = [];
|
|
43
|
+
const walk = (dir: string): void => {
|
|
44
|
+
for (const name of readdirSync(dir).sort()) {
|
|
45
|
+
const full = resolve(dir, name);
|
|
46
|
+
if (statSync(full).isDirectory()) walk(full);
|
|
47
|
+
else if (isDesignRenderingInput(full.slice(root.length + 1))) files.push(full);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
walk(root);
|
|
51
|
+
const h = createHash('sha256');
|
|
52
|
+
for (const f of files) {
|
|
53
|
+
h.update(f.slice(root.length + 1) + '\n');
|
|
54
|
+
h.update(readFileSync(f));
|
|
55
|
+
h.update('\n');
|
|
56
|
+
}
|
|
57
|
+
return h.digest('hex');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const identityCache = new Map<string, JudgeDesignIdentity | null>();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The identity of the tree at `src`, computed once per process per path (a
|
|
64
|
+
* judge run paints hundreds of frames from one tree).
|
|
65
|
+
*
|
|
66
|
+
* `null` when the tree cannot be READ (ggui#1110): a deployed image ships the
|
|
67
|
+
* design package as built output with no `src/`, and an identity is a receipt
|
|
68
|
+
* about a judgement — never a precondition for making one. Before this,
|
|
69
|
+
* `scandir` threw ENOENT inside the evaluation round wherever the package ships
|
|
70
|
+
* built output only, the round returned no evaluation at all, and a bar refused
|
|
71
|
+
* a card that had cleared it. A row without the receipt says so; a round that cannot
|
|
72
|
+
* stamp one still runs.
|
|
73
|
+
*/
|
|
74
|
+
export function judgeDesignIdentity(src: string): JudgeDesignIdentity | null {
|
|
75
|
+
const key = resolve(src);
|
|
76
|
+
const cached = identityCache.get(key);
|
|
77
|
+
if (cached !== undefined) return cached;
|
|
78
|
+
let identity: JudgeDesignIdentity | null;
|
|
79
|
+
try {
|
|
80
|
+
identity = { src: key, srcSha256: designTreeSha256(key) };
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// Unreadable tree: no source directory (a built package), no permission,
|
|
83
|
+
// a path that is not a directory. The reason is logged ONCE per path —
|
|
84
|
+
// the judgement proceeds unstamped rather than not at all.
|
|
85
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
86
|
+
console.warn(`[visual-eval] design tree unreadable at ${key} — the judgement carries no design identity (${reason})`);
|
|
87
|
+
identity = null;
|
|
88
|
+
}
|
|
89
|
+
identityCache.set(key, identity);
|
|
90
|
+
return identity;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Test seam: forget cached identities (a test that rewrites a tree between runs). */
|
|
94
|
+
export function resetJudgeDesignIdentityCache(): void {
|
|
95
|
+
identityCache.clear();
|
|
96
|
+
}
|
package/src/evaluation/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type {
|
|
|
23
23
|
EvalCategory,
|
|
24
24
|
EvalResult,
|
|
25
25
|
VisualEvalSummary,
|
|
26
|
+
CanvasJudgeRecord,
|
|
26
27
|
CanvasVisualSummary,
|
|
27
28
|
EvalCriterion,
|
|
28
29
|
CriterionCoverage,
|
|
@@ -92,6 +93,16 @@ export {
|
|
|
92
93
|
captureScreenshot,
|
|
93
94
|
resolveLaunchOptions,
|
|
94
95
|
runVisualEvaluation,
|
|
96
|
+
resolveDesignPackageDir,
|
|
97
|
+
runVisualEvaluationDetailed,
|
|
98
|
+
VISUAL_JUDGE_MAX_OUTPUT_TOKENS,
|
|
99
|
+
VISUAL_JUDGE_PROMPT_VERSION,
|
|
100
|
+
VISUAL_JUDGE_PROMPT_DIGEST,
|
|
101
|
+
VISUAL_EVAL_PROMPT,
|
|
102
|
+
captureScreenshotDetailed,
|
|
103
|
+
canvasFitPolicy,
|
|
104
|
+
canvasOverflowIssue,
|
|
105
|
+
CONTENT_HEIGHT_EXPRESSION,
|
|
95
106
|
runVisualEval,
|
|
96
107
|
summarizeVisualResult,
|
|
97
108
|
} from './visual-evaluator.js';
|
|
@@ -101,7 +112,11 @@ export type {
|
|
|
101
112
|
VisualEvalDeps,
|
|
102
113
|
VisualEvalOutcome,
|
|
103
114
|
VisualEvaluationResult,
|
|
115
|
+
VisualEvalDetailed,
|
|
116
|
+
ScreenshotAttempt,
|
|
104
117
|
CanvasVisualResult,
|
|
118
|
+
CanvasFitPolicy,
|
|
119
|
+
CaptureMode,
|
|
105
120
|
ScreenshotDeps,
|
|
106
121
|
ScreenshotLauncher,
|
|
107
122
|
ScreenshotBrowser,
|
|
@@ -130,3 +145,5 @@ export type {
|
|
|
130
145
|
CanvasDisplayMode,
|
|
131
146
|
CanvasDescriptor,
|
|
132
147
|
} from '../design-mode.js';
|
|
148
|
+
export { cssTokensForAppTheme } from './theme-css.js';
|
|
149
|
+
export { designTreeSha256, judgeDesignIdentity, resetJudgeDesignIdentityCache, type JudgeDesignIdentity } from './design-identity.js';
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
// Caching: Anthropic ephemeral cache, OpenAI auto-cache, Google explicit cache.
|
|
15
15
|
// Same system prompt across all 7 calls → first call caches, 6 remaining hit cache.
|
|
16
16
|
|
|
17
|
+
import { buildStylingProfileJudgeBlock } from '../boilerplate/styling-profile.js';
|
|
18
|
+
import type { GenerationProfileInput } from '../boilerplate/styling-profile.js';
|
|
17
19
|
import type {
|
|
18
20
|
EvalIssue,
|
|
19
21
|
EvalResult,
|
|
@@ -75,6 +77,8 @@ export interface LLMEvalContext {
|
|
|
75
77
|
* means `constrained` (byte-identical to the pre-`designMode` prompt).
|
|
76
78
|
*/
|
|
77
79
|
designMode?: DesignMode;
|
|
80
|
+
/** The app's generation profile (#991) — the judge reads the same text the coder was given. */
|
|
81
|
+
profile?: GenerationProfileInput;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
export interface LLMEvalConfig {
|
|
@@ -119,6 +123,8 @@ export interface EvalContext {
|
|
|
119
123
|
designSystemSummary?: string;
|
|
120
124
|
/** See `LLMEvalContext.designMode`. */
|
|
121
125
|
designMode?: DesignMode;
|
|
126
|
+
/** See `LLMEvalContext.profile`. */
|
|
127
|
+
profile?: GenerationProfileInput;
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
/**
|
|
@@ -129,6 +135,7 @@ export interface EvalContext {
|
|
|
129
135
|
*/
|
|
130
136
|
const FREE_STYLING_RULES = `## Styling Rules (important for evaluation)
|
|
131
137
|
- Color: every color must be a bare \`var(--ggui-color-*)\` token — a hardcoded #hex, rgb()/hsl() or CSS named color is a fail, and so is a literal fallback inside \`var(--ggui-*, …)\`.
|
|
138
|
+
- Accent text (eyebrow, tagline, link, label) painted with a bare \`var(--ggui-color-primary-300…600)\` is a readability fail — the accent ink is \`var(--ggui-color-link)\`; a tint as text on a primary fill and a deep stop (700–900) on a light tint are fine
|
|
132
139
|
- Spacing, typography, radius, shadow geometry and layout values MAY be literals (\`padding: '20px'\`, \`fontSize: 18\`, \`borderRadius: 12\`) — NEVER flag them.
|
|
133
140
|
- Raw HTML elements with inline styles or \`<style>\` blocks are a first-class choice — never flag "not using the design system" or reward using it.
|
|
134
141
|
- When \`@ggui-ai/design\` primitives ARE used, their enum/scale props (\`variant="primary"\`, \`size="lg"\`, \`gap="md"\`, \`tone="muted"\`, \`surface="accent"\`) are valid.
|
|
@@ -136,7 +143,8 @@ const FREE_STYLING_RULES = `## Styling Rules (important for evaluation)
|
|
|
136
143
|
|
|
137
144
|
const CONSTRAINED_STYLING_RULES = `## Design System Rules (important for evaluation)
|
|
138
145
|
- Spacing props (\`gap\`, \`padding\`, \`margin\`) take a t-shirt-scale name: \`gap="md"\`, \`padding="lg"\` (\`none|xs|sm|md|lg|xl|2xl\`). **These ARE design tokens** — each resolves to a \`--ggui-spacing-*\` variable. A raw \`var(--ggui-spacing-*)\` string is an accepted escape hatch. NEVER flag a scale name as "hardcoded". Only a numeric prop (\`padding={24}\`) or a raw CSS length (\`gap="13px"\`) bypasses the scale and should be warned.
|
|
139
|
-
- All colors must use CSS variables: \`color="var(--ggui-color-
|
|
146
|
+
- All colors must use CSS variables: \`color="var(--ggui-color-onContainer)"\` — hardcoded #hex or rgb() is a fail
|
|
147
|
+
- Accent text (eyebrow, tagline, link, label) painted with a bare \`primary-300…600\` stop is a readability fail — the accent ink is \`var(--ggui-color-link)\` (or \`tone="emphasized"\`); tints as text on a primary fill and deep stops on a light tint are fine
|
|
140
148
|
- Component props that take enum/scale values — \`variant="primary"\`, \`size="lg"\`, \`gap="md"\`, \`padding="lg"\`, \`shadow="md"\`, \`radius="lg"\`, \`tone="muted"\`, \`surface="accent"\` — are all design system tokens and are always valid
|
|
141
149
|
- One \`export default function Component\` — helper components as named functions above it are fine`;
|
|
142
150
|
|
|
@@ -323,6 +331,8 @@ ${free ? FREE_RAW_CLICK_TARGET_GAP : CONSTRAINED_RAW_CLICK_TARGET_GAP}
|
|
|
323
331
|
- Inverted or skipped heading levels (an \`<Heading level={3}>\` rendered
|
|
324
332
|
before the page's \`<Heading level={1}>\`).`);
|
|
325
333
|
|
|
334
|
+
const profileBlock = buildStylingProfileJudgeBlock(ctx.profile);
|
|
335
|
+
if (profileBlock.length > 0) parts.push(profileBlock);
|
|
326
336
|
return parts.join('\n\n');
|
|
327
337
|
}
|
|
328
338
|
|
|
@@ -733,6 +743,7 @@ export async function preWarmEval(
|
|
|
733
743
|
contract: context.contract,
|
|
734
744
|
designSystemSummary: context.designContext,
|
|
735
745
|
designMode: context.designMode,
|
|
746
|
+
profile: context.profile,
|
|
736
747
|
});
|
|
737
748
|
|
|
738
749
|
return { motherPrompt, dynamicCriteria: [] };
|
|
@@ -837,6 +848,7 @@ export async function runLLMEvaluation(
|
|
|
837
848
|
contract: context.contract,
|
|
838
849
|
designSystemSummary: context.designContext,
|
|
839
850
|
designMode,
|
|
851
|
+
profile: context.profile,
|
|
840
852
|
});
|
|
841
853
|
|
|
842
854
|
// Add line numbers to source
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AppTheme } from '@ggui-ai/protocol';
|
|
2
|
+
import { composeThemeCss, getCssTokens } from '@ggui-ai/design/rendering';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The stylesheet a visual judge renders under for an app: the app's theme
|
|
6
|
+
* composed the way the runtime composes it (`composeThemeCss`, layer `page`
|
|
7
|
+
* — one composition, never a second), so a judged canvas paints the tokens a
|
|
8
|
+
* visitor's page paints. Absent theme ⇒ the design's default tokens,
|
|
9
|
+
* byte-for-byte what the judge used before.
|
|
10
|
+
*/
|
|
11
|
+
export function cssTokensForAppTheme(theme: AppTheme | undefined, mode: 'light' | 'dark' = 'light'): string {
|
|
12
|
+
if (theme === undefined) return getCssTokens(mode);
|
|
13
|
+
return composeThemeCss({ layer: 'page', mode: theme.mode ?? mode, appTheme: theme });
|
|
14
|
+
}
|
|
@@ -188,13 +188,43 @@ export interface EvalResult {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
/** One canvas's visual verdict, PNG-free — see `EvalResult.visual`. */
|
|
191
|
+
/**
|
|
192
|
+
* How one canvas's `score` was reached (ggui#1072): `k` vision calls on the
|
|
193
|
+
* SAME captured frame, aggregated by `rule`. Always present — `k: 1` ⇒
|
|
194
|
+
* `samples: [score]`, `sigma: 0`, `notes: [critique]` — so one shape serves
|
|
195
|
+
* a single judgement and a median of many. `samples`/`notes` carry only the
|
|
196
|
+
* calls the judge could parse (in call order); `k` stays the requested count,
|
|
197
|
+
* so a short `samples` is visible on the row rather than filled in.
|
|
198
|
+
*/
|
|
199
|
+
export interface CanvasJudgeRecord {
|
|
200
|
+
readonly k: number;
|
|
201
|
+
readonly rule: 'median';
|
|
202
|
+
readonly samples: number[];
|
|
203
|
+
/** Population σ of `samples` (0 when one sample). */
|
|
204
|
+
readonly sigma: number;
|
|
205
|
+
/** The judge's per-canvas critique per sample — the words beside a swing. */
|
|
206
|
+
readonly notes: string[];
|
|
207
|
+
}
|
|
208
|
+
|
|
191
209
|
export interface CanvasVisualSummary {
|
|
192
210
|
canvas: CanvasClass;
|
|
193
211
|
viewport: { width: number; height: number };
|
|
194
212
|
/** The judge's weighted score at this canvas (0-100). */
|
|
195
213
|
score: number;
|
|
196
|
-
/** `score >= passThreshold
|
|
214
|
+
/** `score >= passThreshold`, and on the inline card: no overflow (ggui#1027). */
|
|
197
215
|
passed: boolean;
|
|
216
|
+
/** The document's scroll height at this canvas, CSS px; `null` when unmeasurable (ggui#1027). */
|
|
217
|
+
contentHeight: number | null;
|
|
218
|
+
/** `contentHeight > viewport.height` — measured on every canvas, judged per `canvasFitPolicy`. */
|
|
219
|
+
overflow: boolean;
|
|
220
|
+
/** How `score` was reached — always present (ggui#1072). */
|
|
221
|
+
judge: CanvasJudgeRecord;
|
|
222
|
+
/**
|
|
223
|
+
* How the judge composed the mount (ggui#1100): `'fill'` on every
|
|
224
|
+
* fullscreen canvas — the served runtime's fit, the root stretched to
|
|
225
|
+
* the frame; absent on the inline card, which keeps its natural height.
|
|
226
|
+
*/
|
|
227
|
+
fit?: 'fill';
|
|
198
228
|
}
|
|
199
229
|
|
|
200
230
|
/** The visual leg's per-canvas summary — see `EvalResult.visual`. */
|
|
@@ -204,6 +234,10 @@ export interface VisualEvalSummary {
|
|
|
204
234
|
/** Every canvas passed. */
|
|
205
235
|
passed: boolean;
|
|
206
236
|
canvases: CanvasVisualSummary[];
|
|
237
|
+
/** The design tree the judge painted with (ggui#1042): `src` + `srcSha256` — `design@judge`. */
|
|
238
|
+
design?: { readonly src: string; readonly srcSha256: string };
|
|
239
|
+
/** The mode the judge's tokens were composed in, when the caller said (ggui#1076). */
|
|
240
|
+
themeMode?: 'light' | 'dark';
|
|
207
241
|
}
|
|
208
242
|
|
|
209
243
|
// ─── Quality mode ──────────────────────────────────────────────────────────
|