@ggui-ai/ui-gen 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapter-h4_V69LI.d.ts → adapter-DLQ-5COm.d.ts} +1 -1
- package/dist/adapters/base.js +2 -27
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/generation-dispatch.d.ts +14 -6
- package/dist/adapters/generation-dispatch.js +40 -19260
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +25 -3561
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/types.js +2 -9
- package/dist/adapters/types.js.map +1 -1
- package/dist/advanced/index.d.ts +13 -12
- package/dist/advanced/index.js +63 -21954
- package/dist/advanced/index.js.map +1 -1
- package/dist/{bank-Ctdp3FUA.d.ts → bank-LWaiAtaA.d.ts} +1 -1
- package/dist/blueprint-validator.js +6 -2301
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.d.ts +3 -2
- package/dist/boilerplate.js +6 -1867
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +3 -2
- package/dist/check/index.js +6 -1923
- package/dist/check/index.js.map +1 -1
- package/dist/chunk-33B3BAWS.js +24 -0
- package/dist/chunk-33B3BAWS.js.map +1 -0
- package/dist/chunk-3HSGA6NI.js +591 -0
- package/dist/chunk-3HSGA6NI.js.map +1 -0
- package/dist/chunk-3NNNGNTO.js +1707 -0
- package/dist/chunk-3NNNGNTO.js.map +1 -0
- package/dist/chunk-3T2HMTON.js +79 -0
- package/dist/chunk-3T2HMTON.js.map +1 -0
- package/dist/chunk-3ZSSDV3L.js +123 -0
- package/dist/chunk-3ZSSDV3L.js.map +1 -0
- package/dist/chunk-4MBR5GA3.js +99 -0
- package/dist/chunk-4MBR5GA3.js.map +1 -0
- package/dist/chunk-56NJFD55.js +337 -0
- package/dist/chunk-56NJFD55.js.map +1 -0
- package/dist/chunk-5AGTY555.js +26 -0
- package/dist/chunk-5AGTY555.js.map +1 -0
- package/dist/chunk-5EJM7EGY.js +115 -0
- package/dist/chunk-5EJM7EGY.js.map +1 -0
- package/dist/chunk-5KXCQLLR.js +529 -0
- package/dist/chunk-5KXCQLLR.js.map +1 -0
- package/dist/chunk-5VBXHVEH.js +171 -0
- package/dist/chunk-5VBXHVEH.js.map +1 -0
- package/dist/chunk-6KDDEING.js +515 -0
- package/dist/chunk-6KDDEING.js.map +1 -0
- package/dist/chunk-6VDRWAP6.js +123 -0
- package/dist/chunk-6VDRWAP6.js.map +1 -0
- package/dist/chunk-732IBFEJ.js +2400 -0
- package/dist/chunk-732IBFEJ.js.map +1 -0
- package/dist/chunk-73AURS3I.js +46 -0
- package/dist/chunk-73AURS3I.js.map +1 -0
- package/dist/chunk-77H5SKLX.js +8 -0
- package/dist/chunk-77H5SKLX.js.map +1 -0
- package/dist/chunk-7QE3AHLE.js +35 -0
- package/dist/chunk-7QE3AHLE.js.map +1 -0
- package/dist/chunk-A375NNWN.js +11 -0
- package/dist/chunk-A375NNWN.js.map +1 -0
- package/dist/chunk-AOU33EFS.js +140 -0
- package/dist/chunk-AOU33EFS.js.map +1 -0
- package/dist/chunk-BA4QYA6E.js +461 -0
- package/dist/chunk-BA4QYA6E.js.map +1 -0
- package/dist/chunk-DBP764GL.js +3 -0
- package/dist/chunk-DBP764GL.js.map +1 -0
- package/dist/chunk-DUU3ZJDQ.js +41 -0
- package/dist/chunk-DUU3ZJDQ.js.map +1 -0
- package/dist/chunk-DXKRBXNU.js +30 -0
- package/dist/chunk-DXKRBXNU.js.map +1 -0
- package/dist/chunk-FB7OEEAH.js +1467 -0
- package/dist/chunk-FB7OEEAH.js.map +1 -0
- package/dist/chunk-FLRNMZV2.js +18 -0
- package/dist/chunk-FLRNMZV2.js.map +1 -0
- package/dist/chunk-FWJIMKFZ.js +2032 -0
- package/dist/chunk-FWJIMKFZ.js.map +1 -0
- package/dist/chunk-FXRHWYIQ.js +163 -0
- package/dist/chunk-FXRHWYIQ.js.map +1 -0
- package/dist/chunk-G3PMV62Z.js +33 -0
- package/dist/chunk-G3PMV62Z.js.map +1 -0
- package/dist/chunk-GAJI7Y3P.js +13 -0
- package/dist/chunk-GAJI7Y3P.js.map +1 -0
- package/dist/chunk-GYINWMPR.js +2052 -0
- package/dist/chunk-GYINWMPR.js.map +1 -0
- package/dist/chunk-HD6ABIK7.js +484 -0
- package/dist/chunk-HD6ABIK7.js.map +1 -0
- package/dist/chunk-HDQVHYO6.js +100 -0
- package/dist/chunk-HDQVHYO6.js.map +1 -0
- package/dist/chunk-IHUT3SIW.js +7 -0
- package/dist/chunk-IHUT3SIW.js.map +1 -0
- package/dist/chunk-JDH5EGNZ.js +126 -0
- package/dist/chunk-JDH5EGNZ.js.map +1 -0
- package/dist/chunk-JMD4RGSV.js +37 -0
- package/dist/chunk-JMD4RGSV.js.map +1 -0
- package/dist/chunk-KJWGZL5B.js +33 -0
- package/dist/chunk-KJWGZL5B.js.map +1 -0
- package/dist/chunk-MKMDQFZ7.js +86 -0
- package/dist/chunk-MKMDQFZ7.js.map +1 -0
- package/dist/chunk-NF6JUZWF.js +85 -0
- package/dist/chunk-NF6JUZWF.js.map +1 -0
- package/dist/chunk-O2YC6XDC.js +1933 -0
- package/dist/chunk-O2YC6XDC.js.map +1 -0
- package/dist/chunk-OVLH5ICV.js +29 -0
- package/dist/chunk-OVLH5ICV.js.map +1 -0
- package/dist/chunk-PBOYHGSD.js +106 -0
- package/dist/chunk-PBOYHGSD.js.map +1 -0
- package/dist/chunk-PJFH7QWA.js +81 -0
- package/dist/chunk-PJFH7QWA.js.map +1 -0
- package/dist/chunk-PUH4CXGZ.js +284 -0
- package/dist/chunk-PUH4CXGZ.js.map +1 -0
- package/dist/chunk-QLXXPGTV.js +18 -0
- package/dist/chunk-QLXXPGTV.js.map +1 -0
- package/dist/chunk-R5GIGPW2.js +2845 -0
- package/dist/chunk-R5GIGPW2.js.map +1 -0
- package/dist/chunk-RH4IUSTO.js +83 -0
- package/dist/chunk-RH4IUSTO.js.map +1 -0
- package/dist/chunk-S2GVXKKY.js +153 -0
- package/dist/chunk-S2GVXKKY.js.map +1 -0
- package/dist/chunk-TVHGKFJS.js +58 -0
- package/dist/chunk-TVHGKFJS.js.map +1 -0
- package/dist/chunk-U7GALWLG.js +66 -0
- package/dist/chunk-U7GALWLG.js.map +1 -0
- package/dist/chunk-VTC263LT.js +256 -0
- package/dist/chunk-VTC263LT.js.map +1 -0
- package/dist/chunk-WEOXFEVE.js +739 -0
- package/dist/chunk-WEOXFEVE.js.map +1 -0
- package/dist/chunk-WYHP46LT.js +28 -0
- package/dist/chunk-WYHP46LT.js.map +1 -0
- package/dist/chunk-XGLO5LGG.js +274 -0
- package/dist/chunk-XGLO5LGG.js.map +1 -0
- package/dist/chunk-XRBCRKA2.js +29 -0
- package/dist/chunk-XRBCRKA2.js.map +1 -0
- package/dist/chunk-XW7ADFFV.js +340 -0
- package/dist/chunk-XW7ADFFV.js.map +1 -0
- package/dist/chunk-YIQ42IUX.js +34 -0
- package/dist/chunk-YIQ42IUX.js.map +1 -0
- package/dist/chunk-YJZTUY7B.js +101 -0
- package/dist/chunk-YJZTUY7B.js.map +1 -0
- package/dist/chunk-Z3CXME3K.js +107 -0
- package/dist/chunk-Z3CXME3K.js.map +1 -0
- package/dist/classifier/index.js +2 -527
- package/dist/classifier/index.js.map +1 -1
- package/dist/client-JBPCXU7W.js +4 -0
- package/dist/client-JBPCXU7W.js.map +1 -0
- package/dist/compose.js +3 -367
- package/dist/compose.js.map +1 -1
- package/dist/cost-tracker-HHGQ4FTW.js +67 -0
- package/dist/cost-tracker-HHGQ4FTW.js.map +1 -0
- package/dist/{create-ui-generator-BrZHF0cf.d.ts → create-ui-generator-BY-WeaPi.d.ts} +3 -3
- package/dist/{design-mode-DGWN_Gkm.d.ts → design-mode-XyOQmzE0.d.ts} +4 -3
- package/dist/{dispatch-B07TI30d.d.ts → dispatch-8LzTddKw.d.ts} +3 -2
- package/dist/dist-UALN6ZVB.js +543 -0
- package/dist/dist-UALN6ZVB.js.map +1 -0
- package/dist/dupe-break-X6K7QFIS.js +4 -0
- package/dist/dupe-break-X6K7QFIS.js.map +1 -0
- package/dist/evaluation/axis-checks/index.d.ts +5 -4
- package/dist/evaluation/axis-checks/index.js +6 -1914
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +3 -2
- package/dist/evaluation/axis-checks/registry.js +3 -1844
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +49 -132
- package/dist/evaluation/index.js +302 -6466
- package/dist/evaluation/index.js.map +1 -1
- package/dist/evaluation/types.d.ts +3 -2
- package/dist/evaluation/types.js +2 -5
- package/dist/evaluation/types.js.map +1 -1
- package/dist/fragments/index.js +2 -338
- package/dist/fragments/index.js.map +1 -1
- package/dist/{generate-tSO_shQ0.d.ts → generate-DQ4ci90s.d.ts} +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +35 -15
- package/dist/harness/check/runtime-render/index.js +6 -2390
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +4 -1739
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +3 -2
- package/dist/harness/index.js +23 -5126
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.js +2 -24
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -2
- package/dist/harness/prompts.js +8 -324
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +6 -5
- package/dist/harness/result-types.js +2 -0
- package/dist/harness/result-types.js.map +1 -1
- package/dist/harness/runtime.d.ts +22 -5
- package/dist/harness/runtime.js +10 -2313
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +3 -2
- package/dist/harness/types-public.js +16 -2954
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +3 -2
- package/dist/harness/validator-trace-sink.js +2 -26
- package/dist/harness/validator-trace-sink.js.map +1 -1
- package/dist/hash.js +2 -39
- package/dist/hash.js.map +1 -1
- package/dist/hashline-5OKREIRM.js +4 -0
- package/dist/hashline-5OKREIRM.js.map +1 -0
- package/dist/index.d.ts +50 -11
- package/dist/index.js +57 -22015
- package/dist/index.js.map +1 -1
- package/dist/llm-evaluator-PN3WFCS4.js +653 -0
- package/dist/llm-evaluator-PN3WFCS4.js.map +1 -0
- package/dist/magic-string.es-2NZTC4GZ.js +1307 -0
- package/dist/magic-string.es-2NZTC4GZ.js.map +1 -0
- package/dist/patch.d.ts +3 -2
- package/dist/patch.js +2 -56
- package/dist/patch.js.map +1 -1
- package/dist/policy-VCYKGSAC.js +5 -0
- package/dist/policy-VCYKGSAC.js.map +1 -0
- package/dist/policy.js +2 -35
- package/dist/policy.js.map +1 -1
- package/dist/primitive-index-2OF6F4TZ.js +131 -0
- package/dist/primitive-index-2OF6F4TZ.js.map +1 -0
- package/dist/primitive-slice-WTMBLYFJ.js +235 -0
- package/dist/primitive-slice-WTMBLYFJ.js.map +1 -0
- package/dist/{prompts-BhNUuRoZ.d.ts → prompts-CLZGywMP.d.ts} +1 -1
- package/dist/provider-adapter-contract.js +10407 -14204
- package/dist/provider-adapter-contract.js.map +1 -1
- package/dist/provider-adapter.js +2 -44
- package/dist/provider-adapter.js.map +1 -1
- package/dist/providers/index.d.ts +89 -1
- package/dist/providers/index.js +115 -44
- package/dist/providers/index.js.map +1 -1
- package/dist/raw-C6KBH5I3.js +141 -0
- package/dist/raw-C6KBH5I3.js.map +1 -0
- package/dist/raw-FXSBXQTZ.js +7 -0
- package/dist/raw-FXSBXQTZ.js.map +1 -0
- package/dist/raw-MURDUPGK.js +8 -0
- package/dist/raw-MURDUPGK.js.map +1 -0
- package/dist/raw-T3GW3UVX.js +8 -0
- package/dist/raw-T3GW3UVX.js.map +1 -0
- package/dist/render-check-host-ZNIS6BJ5.js +5 -0
- package/dist/render-check-host-ZNIS6BJ5.js.map +1 -0
- package/dist/sdk-4JNSDNSR.js +6 -0
- package/dist/sdk-4JNSDNSR.js.map +1 -0
- package/dist/sdk-DIVJMHCX.js +7 -0
- package/dist/sdk-DIVJMHCX.js.map +1 -0
- package/dist/sdk-QRJOCRTS.js +7 -0
- package/dist/sdk-QRJOCRTS.js.map +1 -0
- package/dist/tag-balance-CTHCTRJ7.js +123 -0
- package/dist/tag-balance-CTHCTRJ7.js.map +1 -0
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +3 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/tools/render-check-worker.js +3 -40
- package/dist/tools/render-check-worker.js.map +1 -1
- package/dist/{types-public-h8IMnYuh.d.ts → types-public-hYlrerZh.d.ts} +35 -2
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +3 -498
- package/dist/validation/index.js.map +1 -1
- package/dist/{visual-evaluator-C2strxqD.d.ts → visual-evaluator-KskajxD5.d.ts} +14 -4
- package/dist/visual-evaluator-TGEXYQTP.js +12 -0
- package/dist/visual-evaluator-TGEXYQTP.js.map +1 -0
- package/dist/workflows.d.ts +3 -2
- package/dist/workflows.js +2 -151
- package/dist/workflows.js.map +1 -1
- package/package.json +20 -16
- package/src/adapters/claude/sdk.ts +4 -2
- package/src/adapters/claude/tool-bridge.ts +2 -1
- package/src/adapters/generation-dispatch.ts +9 -0
- package/src/adapters/openai/raw.ts +4 -0
- package/src/adapters/openai/sdk.ts +6 -1
- package/src/adapters/openai/tokens.ts +21 -7
- package/src/adapters/tool-bridge.ts +1 -1
- package/src/adapters/tools.ts +1 -1
- package/src/advanced/feedback.ts +1 -2
- package/src/advanced/generator.ts +4 -3
- package/src/boilerplate/free-design-prompt.ts +7 -0
- package/src/boilerplate/generate.ts +18 -5
- package/src/boilerplate/hard-sections.ts +8 -3
- package/src/boilerplate/react-hooks.ts +56 -0
- package/src/boilerplate/system-prompt.ts +12 -3
- package/src/check/run-tier0.ts +233 -8
- package/src/check/type-checker.ts +128 -16
- package/src/coding-agent/tools.ts +26 -3
- package/src/design-mode.ts +4 -9
- package/src/evaluation/axis-checks/checks/state-merge.ts +2 -4
- package/src/evaluation/axis-checks/checks/state-payload.ts +1 -28
- package/src/evaluation/axis-checks/checks/tint.ts +197 -0
- package/src/evaluation/axis-checks/helpers.ts +121 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/criteria/__fixtures__/edge-v2-draw2.json +4629 -0
- package/src/evaluation/criteria/bank.ts +1 -1
- package/src/evaluation/criteria/edge-instruments.ts +14 -0
- package/src/evaluation/criteria/edge-probe-v2.ts +352 -0
- package/src/evaluation/criteria/edge-v2.ts +242 -0
- package/src/evaluation/criteria/resolve.ts +24 -5
- package/src/evaluation/index.ts +3 -5
- package/src/evaluation/mcp-server.ts +0 -50
- package/src/evaluation/types-public.ts +6 -1
- package/src/evaluation/visual-evaluator.ts +161 -30
- package/src/harness/check/runtime-render/index.ts +28 -4
- package/src/harness/check/runtime-render/render-check.ts +20 -21
- package/src/harness/claude-agent-sdk-loader.ts +93 -0
- package/src/harness/claude-code-login-query.ts +1 -1
- package/src/harness/coding/generate-task-runner.ts +1 -0
- package/src/harness/coding/run-coding-turn.ts +3 -0
- package/src/harness/coding/run-eval-round.ts +9 -0
- package/src/harness/llm-router.ts +50 -2
- package/src/harness/runtime.ts +17 -0
- package/src/index.ts +9 -0
- package/src/providers/anthropic-tool-call.ts +263 -0
- package/src/providers/index.ts +13 -0
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools/render-check.ts +4 -4
- package/src/validation/primitives.ts +2 -1
- package/src/evaluation/loop.ts +0 -218
package/src/design-mode.ts
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
// blueprint-reuse shape, and brand-bearing color via the closed
|
|
19
19
|
// `--ggui-color-*` token manifest (no literals, no fallbacks).
|
|
20
20
|
|
|
21
|
+
import type { CanvasClass } from "@ggui-ai/design/rendering";
|
|
22
|
+
|
|
21
23
|
export const DESIGN_MODES = ["constrained", "free"] as const;
|
|
22
24
|
|
|
23
25
|
export type DesignMode = (typeof DESIGN_MODES)[number];
|
|
@@ -38,15 +40,8 @@ export const DEFAULT_DESIGN_MODE: DesignMode = "constrained";
|
|
|
38
40
|
// breakpoints. `canvasForViewportWidth` is monotonic in width per
|
|
39
41
|
// display mode (pinned by `design-mode.test.ts`).
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"mobile-fullscreen-small",
|
|
44
|
-
"md",
|
|
45
|
-
"lg",
|
|
46
|
-
"xl",
|
|
47
|
-
] as const;
|
|
48
|
-
|
|
49
|
-
export type CanvasClass = (typeof CANVAS_CLASSES)[number];
|
|
43
|
+
// The vocabulary itself lives in `@ggui-ai/design/rendering`, where the runtime reads it too.
|
|
44
|
+
export { CANVAS_CLASSES, type CanvasClass } from "@ggui-ai/design/rendering";
|
|
50
45
|
|
|
51
46
|
export interface CanvasViewport {
|
|
52
47
|
readonly width: number;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getEntityCollections,
|
|
13
13
|
getMutatedEntityCollections,
|
|
14
14
|
mkIssue,
|
|
15
|
+
seedsFromProp,
|
|
15
16
|
} from "../helpers.js";
|
|
16
17
|
|
|
17
18
|
function runStateSeededFromProps(input: AxisCheckInput): EvalIssue[] {
|
|
@@ -21,10 +22,7 @@ function runStateSeededFromProps(input: AxisCheckInput): EvalIssue[] {
|
|
|
21
22
|
const entities = getMutatedEntityCollections(input.contract, all);
|
|
22
23
|
const issues: EvalIssue[] = [];
|
|
23
24
|
for (const e of entities) {
|
|
24
|
-
|
|
25
|
-
`useState(?:<[^>]*>)?\\s*\\([\\s\\S]{0,400}?props\\.${e.name}\\b`,
|
|
26
|
-
);
|
|
27
|
-
if (re.test(src)) continue;
|
|
25
|
+
if (seedsFromProp(src, e.name)) continue;
|
|
28
26
|
issues.push(
|
|
29
27
|
mkIssue(
|
|
30
28
|
"state.merge.seeded_from_props",
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getInitialValuePropNames,
|
|
13
13
|
getSubmitActions,
|
|
14
14
|
mkIssue,
|
|
15
|
+
seedsFromProp,
|
|
15
16
|
} from "../helpers.js";
|
|
16
17
|
|
|
17
18
|
function runSubmitHookPresent(input: AxisCheckInput): EvalIssue[] {
|
|
@@ -79,34 +80,6 @@ function runStateCoversPayload(input: AxisCheckInput): EvalIssue[] {
|
|
|
79
80
|
return issues;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
/**
|
|
83
|
-
* Whether a `useState` initializer reads `props.<name>`: directly, or through a local bound from
|
|
84
|
-
* it (`const initial = normalize(props.initialProfile)` then `useState(initial.name)`, or a
|
|
85
|
-
* destructure `const { name } = props.initialProfile ?? {}` then `useState(name)`). The property
|
|
86
|
-
* this check guards is "form state is seeded from the prop", not a spelling: ggui#1261's Exp 011
|
|
87
|
-
* cell re-seeded correctly through a normalized local, was refused, and spent a turn restoring
|
|
88
|
-
* the literal form.
|
|
89
|
-
*/
|
|
90
|
-
function seedsFromProp(src: string, name: string): boolean {
|
|
91
|
-
const propRef = `props\\??\\.${name}\\b`;
|
|
92
|
-
const initializerReads = (ref: string): boolean =>
|
|
93
|
-
new RegExp(`useState(?:<[^>]*>)?\\s*\\([\\s\\S]{0,400}?${ref}`).test(src);
|
|
94
|
-
if (initializerReads(propRef)) return true;
|
|
95
|
-
const locals = new Set<string>();
|
|
96
|
-
for (const m of src.matchAll(new RegExp(`(?:const|let|var)\\s+(\\w+)\\s*(?::[^=]{1,80})?=\\s*[^;]{0,300}?${propRef}`, "g"))) {
|
|
97
|
-
const id = m[1];
|
|
98
|
-
if (id !== undefined) locals.add(id);
|
|
99
|
-
}
|
|
100
|
-
for (const m of src.matchAll(new RegExp(`(?:const|let|var)\\s*\\{([^}]{1,400})\\}\\s*=\\s*[^;]{0,300}?${propRef}`, "g"))) {
|
|
101
|
-
for (const part of (m[1] ?? "").split(",")) {
|
|
102
|
-
const id = part.split(":").pop()?.split("=")[0]?.trim();
|
|
103
|
-
if (id !== undefined && /^\w+$/.test(id)) locals.add(id);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
for (const id of locals) if (initializerReads(`\\b${id}\\b`)) return true;
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
83
|
function runInitialValuesSeeded(input: AxisCheckInput): EvalIssue[] {
|
|
111
84
|
if (input.compiledCode === null) return [];
|
|
112
85
|
const src = input.sourceCode;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// packages/ui-gen/src/evaluation/axis-checks/checks/tint.ts
|
|
2
|
+
//
|
|
3
|
+
// ggui#1846 — the categorical `tint` fill's usage rules, as WARNINGS. The kit
|
|
4
|
+
// gives `Box` / `Card` eight theme-tracked steps (`tint-1` … `tint-8`) for DATA
|
|
5
|
+
// categories. The rules (ggui-team-cdo's, taught in the system prompt) are a
|
|
6
|
+
// property of the rendered card, and from source each can only be read by a
|
|
7
|
+
// heuristic over names or JSX structure. A heuristic that FAILS loops a
|
|
8
|
+
// generation that is correct but trips it, so these warn: the model sees the
|
|
9
|
+
// repair line as feedback, nothing blocks, and the follow and false-fire rates
|
|
10
|
+
// are measured. An out-of-range step fails through the prop's own type instead.
|
|
11
|
+
//
|
|
12
|
+
// Not checked here: "the same category bound to two steps in one card". Reading
|
|
13
|
+
// that needs data flow, not the tag's text.
|
|
14
|
+
|
|
15
|
+
import type { EvalIssue } from "../../types-public.js";
|
|
16
|
+
import type { AxisCheck, AxisCheckInput } from "../types.js";
|
|
17
|
+
import { mkIssue } from "../helpers.js";
|
|
18
|
+
|
|
19
|
+
const ALL_RENDER_VALUES = [
|
|
20
|
+
"static", "list", "grid", "spatial", "timeline", "chart", "master-detail",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
/** One tinted `Box` / `Card`: the tag, the `tint` value's source text, and the JSX between its open and close tags. */
|
|
24
|
+
export interface TintedElement {
|
|
25
|
+
readonly tag: "Box" | "Card";
|
|
26
|
+
readonly tint: string;
|
|
27
|
+
/** The JSX between the open and close tag; empty when self-closing. */
|
|
28
|
+
readonly inner: string;
|
|
29
|
+
/** For a self-closing element, its following siblings up to the next close tag (a swatch's label); else empty. */
|
|
30
|
+
readonly next: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface OpenTag {
|
|
34
|
+
readonly attrs: string;
|
|
35
|
+
readonly end: number;
|
|
36
|
+
readonly selfClosing: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read a JSX open tag whose `<Name` starts at `from`. Braces and string
|
|
41
|
+
* literals are skipped as units, so an `onClick={() => …}` arrow does not end
|
|
42
|
+
* the tag the way a `[^<>]` pattern would.
|
|
43
|
+
*/
|
|
44
|
+
function scanOpenTag(src: string, from: number, nameLength: number): OpenTag | null {
|
|
45
|
+
let depth = 0;
|
|
46
|
+
let quote = "";
|
|
47
|
+
for (let i = from + 1 + nameLength; i < src.length; i++) {
|
|
48
|
+
const ch = src[i];
|
|
49
|
+
if (quote !== "") {
|
|
50
|
+
if (ch === "\\") i++;
|
|
51
|
+
else if (ch === quote) quote = "";
|
|
52
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
53
|
+
quote = ch;
|
|
54
|
+
} else if (ch === "{") {
|
|
55
|
+
depth++;
|
|
56
|
+
} else if (ch === "}") {
|
|
57
|
+
depth--;
|
|
58
|
+
} else if (ch === ">" && depth === 0) {
|
|
59
|
+
const attrs = src.slice(from + 1 + nameLength, i);
|
|
60
|
+
return { attrs, end: i + 1, selfClosing: attrs.trimEnd().endsWith("/") };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The value of a top-level `tint=` attribute, braces or quotes included; null when absent. */
|
|
67
|
+
function readTintAttr(attrs: string): string | null {
|
|
68
|
+
let depth = 0;
|
|
69
|
+
let quote = "";
|
|
70
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
71
|
+
const ch = attrs[i];
|
|
72
|
+
if (quote !== "") {
|
|
73
|
+
if (ch === "\\") i++;
|
|
74
|
+
else if (ch === quote) quote = "";
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (ch === '"' || ch === "'" || ch === "`") quote = ch;
|
|
78
|
+
else if (ch === "{") depth++;
|
|
79
|
+
else if (ch === "}") depth--;
|
|
80
|
+
else if (depth === 0 && /\s/.test(attrs[i - 1] ?? " ")) {
|
|
81
|
+
const m = /^tint\s*=\s*/.exec(attrs.slice(i));
|
|
82
|
+
if (m === null) continue;
|
|
83
|
+
const v = i + m[0].length;
|
|
84
|
+
const open = attrs[v];
|
|
85
|
+
if (open === '"' || open === "'") {
|
|
86
|
+
const close = attrs.indexOf(open, v + 1);
|
|
87
|
+
return close < 0 ? null : attrs.slice(v, close + 1);
|
|
88
|
+
}
|
|
89
|
+
if (open !== "{") return null;
|
|
90
|
+
let d = 0;
|
|
91
|
+
for (let j = v; j < attrs.length; j++) {
|
|
92
|
+
if (attrs[j] === "{") d++;
|
|
93
|
+
else if (attrs[j] === "}" && --d === 0) return attrs.slice(v, j + 1);
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Every tinted `Box` / `Card` in the source, with its inner JSX (empty when self-closing). */
|
|
102
|
+
export function findTintedElements(sourceCode: string): TintedElement[] {
|
|
103
|
+
const found: TintedElement[] = [];
|
|
104
|
+
for (const m of sourceCode.matchAll(/<(Box|Card)\b/g)) {
|
|
105
|
+
const tag = m[1] === "Card" ? "Card" : "Box";
|
|
106
|
+
const from = m.index ?? 0;
|
|
107
|
+
const open = scanOpenTag(sourceCode, from, tag.length);
|
|
108
|
+
if (open === null) continue;
|
|
109
|
+
const tint = readTintAttr(open.attrs);
|
|
110
|
+
if (tint === null) continue;
|
|
111
|
+
if (open.selfClosing) {
|
|
112
|
+
const close = sourceCode.indexOf("</", open.end);
|
|
113
|
+
found.push({ tag, tint, inner: "", next: sourceCode.slice(open.end, close < 0 ? sourceCode.length : close) });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Walk to the matching close tag, counting nested opens of the same name.
|
|
117
|
+
const tagRx = new RegExp(`<(/?)${tag}\\b`, "g");
|
|
118
|
+
tagRx.lastIndex = open.end;
|
|
119
|
+
let depth = 1;
|
|
120
|
+
let end = sourceCode.length;
|
|
121
|
+
for (let t = tagRx.exec(sourceCode); t !== null; t = tagRx.exec(sourceCode)) {
|
|
122
|
+
if (t[1] === "/") {
|
|
123
|
+
if (--depth === 0) {
|
|
124
|
+
end = t.index;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const nested = scanOpenTag(sourceCode, t.index, tag.length);
|
|
130
|
+
if (nested === null) break;
|
|
131
|
+
tagRx.lastIndex = nested.end;
|
|
132
|
+
if (!nested.selfClosing) depth++;
|
|
133
|
+
}
|
|
134
|
+
found.push({ tag, tint, inner: sourceCode.slice(open.end, end), next: "" });
|
|
135
|
+
}
|
|
136
|
+
return found;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const STATE_WORD_RX = /\b(status|state|isLive|live|draft|signal|error|success|warning|danger|isActive|active|selected|isSelected|online|offline|pending|loading|disabled)\b/i;
|
|
140
|
+
|
|
141
|
+
/** Text a reader sees inside the cell: a text primitive, a JSX expression child, or literal JSX text. */
|
|
142
|
+
function hasWords(inner: string): boolean {
|
|
143
|
+
if (/<(Text|Heading|Label|Badge|Caption|Link)\b/.test(inner)) return true;
|
|
144
|
+
if (/\{[^{}]+\}/.test(inner.replace(/\b[a-zA-Z]+=\{[^{}]*\}/g, ""))) return true;
|
|
145
|
+
return />\s*[^<>{}\s][^<>{}]*</.test(`>${inner}<`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function runTintStateBound(input: AxisCheckInput): EvalIssue[] {
|
|
149
|
+
if (input.compiledCode === null) return [];
|
|
150
|
+
const bound = findTintedElements(input.sourceCode).filter((e) => e.tint.startsWith("{") && STATE_WORD_RX.test(e.tint));
|
|
151
|
+
if (bound.length === 0) return [];
|
|
152
|
+
return [
|
|
153
|
+
mkIssue(
|
|
154
|
+
"universal.tint_state_bound",
|
|
155
|
+
`\`tint\` reads as bound to a state (${bound.map((e) => `\`${e.tint}\``).join(", ")}) — tints are for data categories, never for state.`,
|
|
156
|
+
"A status takes live, draft or signal, not a tint. Keep `tint` for a data category (series, lane, kind, group), and show state with its own props.",
|
|
157
|
+
"warn",
|
|
158
|
+
),
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function runTintCategoryInWords(input: AxisCheckInput): EvalIssue[] {
|
|
163
|
+
if (input.compiledCode === null) return [];
|
|
164
|
+
const tinted = findTintedElements(input.sourceCode);
|
|
165
|
+
if (tinted.length === 0) return [];
|
|
166
|
+
if (/\blegend\b/i.test(input.sourceCode)) return [];
|
|
167
|
+
if (tinted.every((e) => hasWords(e.inner) || hasWords(e.next))) return [];
|
|
168
|
+
return [
|
|
169
|
+
mkIssue(
|
|
170
|
+
"universal.tint_category_in_words",
|
|
171
|
+
"A tinted cell carries no words, and the card has no legend — colour is the only way to tell its category.",
|
|
172
|
+
"Give this cell a label, or add a legend to the card. The category is always also in words.",
|
|
173
|
+
"warn",
|
|
174
|
+
),
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const MUTED_TONE_RX = /\btone\s*=\s*["'](subtle|muted)["']/;
|
|
179
|
+
|
|
180
|
+
function runTintMutedText(input: AxisCheckInput): EvalIssue[] {
|
|
181
|
+
if (input.compiledCode === null) return [];
|
|
182
|
+
if (!findTintedElements(input.sourceCode).some((e) => MUTED_TONE_RX.test(e.inner))) return [];
|
|
183
|
+
return [
|
|
184
|
+
mkIssue(
|
|
185
|
+
"universal.tint_muted_text",
|
|
186
|
+
'Muted text (`tone="subtle"` / `tone="muted"`) sits on a tint — it can read under 4.5:1 there.',
|
|
187
|
+
"Text on a tint is the theme's ink only, one short line (a label, a symbol, a value). Put secondary or muted text beside the cell or in the legend.",
|
|
188
|
+
"warn",
|
|
189
|
+
),
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const TINT_CHECKS: readonly AxisCheck[] = [
|
|
194
|
+
{ id: "universal.tint_state_bound", axis: "render", values: ALL_RENDER_VALUES, run: runTintStateBound },
|
|
195
|
+
{ id: "universal.tint_category_in_words", axis: "render", values: ALL_RENDER_VALUES, run: runTintCategoryInWords },
|
|
196
|
+
{ id: "universal.tint_muted_text", axis: "render", values: ALL_RENDER_VALUES, run: runTintMutedText },
|
|
197
|
+
];
|
|
@@ -411,6 +411,127 @@ export function isMultiStepPrompt(prompt: string): boolean {
|
|
|
411
411
|
// Issue factory
|
|
412
412
|
// =============================================================================
|
|
413
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Whether a `useState` initializer reads `props.<name>`: directly, or through a local bound from
|
|
416
|
+
* it (`const initial = normalize(props.initialProfile)` then `useState(initial.name)`, or a
|
|
417
|
+
* destructure `const { name } = props.initialProfile ?? {}` then `useState(name)`). The property
|
|
418
|
+
* this check guards is "form state is seeded from the prop", not a spelling: ggui#1261's Exp 011
|
|
419
|
+
* cell re-seeded correctly through a normalized local, was refused, and spent a turn restoring
|
|
420
|
+
* the literal form. ggui#1869: a nested generic (`useState<Array<Task>>(…)`), `props?.`,
|
|
421
|
+
* destructured props and destructured component parameters are all correct seeding too, and
|
|
422
|
+
* `state.merge.seeded_from_props` — a FAIL — refused them.
|
|
423
|
+
*/
|
|
424
|
+
export function seedsFromProp(src: string, name: string): boolean {
|
|
425
|
+
const propRef = `props\\??\\.${name}\\b`;
|
|
426
|
+
const inits = useStateInitializers(src);
|
|
427
|
+
const anyInitReads = (rx: RegExp): boolean => inits.some((t) => rx.test(t));
|
|
428
|
+
if (anyInitReads(new RegExp(`\\b${propRef}`))) return true;
|
|
429
|
+
const locals = new Set<string>();
|
|
430
|
+
for (const m of src.matchAll(new RegExp(`(?:const|let|var)\\s+(\\w+)\\s*(?::[^=]{1,80})?=\\s*[^;]{0,300}?\\b${propRef}`, "g"))) {
|
|
431
|
+
const id = m[1];
|
|
432
|
+
if (id !== undefined) locals.add(id);
|
|
433
|
+
}
|
|
434
|
+
// `const { a, b } = props.<name> ?? {}` — every name bound reads the prop.
|
|
435
|
+
for (const m of src.matchAll(new RegExp(`(?:const|let|var)\\s*\\{([^}]{1,400})\\}\\s*=\\s*[^;]{0,300}?\\b${propRef}`, "g"))) {
|
|
436
|
+
for (const part of (m[1] ?? "").split(",")) {
|
|
437
|
+
const id = part.split(":").pop()?.split("=")[0]?.trim();
|
|
438
|
+
if (id !== undefined && /^\w+$/.test(id)) locals.add(id);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// `const { <name>: alias } = props` and `export default function Component({ <name>: alias }: Props)` — only the
|
|
442
|
+
// binding of <name>, and only the component's own parameter (a helper's destructured parameter is not its props).
|
|
443
|
+
const fromProps = /(?:const|let|var)\s*\{([^}]{1,400})\}\s*(?::[^=]{1,200})?=\s*props\b(?!\s*\??\.)|export\s+default\s+function\s*\w*\s*\(\s*\{([^}]{1,400})\}/g;
|
|
444
|
+
for (const m of src.matchAll(fromProps)) {
|
|
445
|
+
for (const part of (m[1] ?? m[2] ?? "").split(",")) {
|
|
446
|
+
const [key, alias] = part.split(":");
|
|
447
|
+
if (key?.split("=")[0]?.trim() !== name) continue;
|
|
448
|
+
const id = (alias ?? key).split("=")[0]?.trim();
|
|
449
|
+
if (id !== undefined && /^\w+$/.test(id)) locals.add(id);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
for (const id of locals) if (anyInitReads(new RegExp(`\\b${id}\\b`))) return true;
|
|
453
|
+
return seedsContextSlotFromProp(src, [new RegExp(`\\b${propRef}`), ...[...locals].map((id) => new RegExp(`\\b${id}\\b`))]);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* ggui#1869 — a generated context slot is seeded without any `useState`. The boilerplate
|
|
458
|
+
* declares each slot as `const [value, setValue] = useGguiContext<T>('key')`, the runtime owns
|
|
459
|
+
* its state, and the hook takes no initial value, so a form built on slots can only pre-fill in
|
|
460
|
+
* two ways, and both count:
|
|
461
|
+
* - an effect calls a slot setter with an argument that reads the prop
|
|
462
|
+
* (`useEffect(() => { if (p.name && !name) setName(p.name); }, [props.initialProfile])`);
|
|
463
|
+
* - a slot's value falls back to the prop (`const nameValue = name ?? props.initialProfile?.name`).
|
|
464
|
+
* A setter called from an event handler is not seeding (it runs on a click, not on mount), and a
|
|
465
|
+
* prop read that is merely near a slot is not one either.
|
|
466
|
+
*/
|
|
467
|
+
function seedsContextSlotFromProp(src: string, readsProp: readonly RegExp[]): boolean {
|
|
468
|
+
const reads = (text: string): boolean => readsProp.some((rx) => rx.test(text));
|
|
469
|
+
const values: string[] = [];
|
|
470
|
+
const setters: string[] = [];
|
|
471
|
+
for (const m of src.matchAll(/(?:const|let|var)\s*\[\s*(\w+)\s*,\s*(\w+)\s*\]\s*=\s*useGguiContext\b/g)) {
|
|
472
|
+
if (m[1] !== undefined) values.push(m[1]);
|
|
473
|
+
if (m[2] !== undefined) setters.push(m[2]);
|
|
474
|
+
}
|
|
475
|
+
if (values.length === 0) return false;
|
|
476
|
+
for (const v of values) {
|
|
477
|
+
for (const m of src.matchAll(new RegExp(`(?<![.\\w$])${v}\\s*\\?\\?\\s*([^;\\n]{1,200})`, "g"))) {
|
|
478
|
+
if (reads(m[1] ?? "")) return true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
for (const body of callArguments(src, /\buse(?:Layout)?Effect\b/g)) {
|
|
482
|
+
for (const setter of setters) {
|
|
483
|
+
for (const arg of callArguments(body, new RegExp(`\\b${setter}\\b`, "g"))) if (reads(arg)) return true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* The argument text of every `useState(...)` call. A type argument is skipped with its angle
|
|
491
|
+
* brackets balanced (`useState<Record<string, Task[]>>(…)`; an arrow's `=>` inside it is not a
|
|
492
|
+
* close), and the argument runs to its matching paren, string literals skipped as units.
|
|
493
|
+
*/
|
|
494
|
+
function useStateInitializers(src: string): string[] {
|
|
495
|
+
return callArguments(src, /\buseState\b/g);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* The argument text of every call to a name `callee` matches (a global regex). A type argument is
|
|
500
|
+
* skipped with its angle brackets balanced, and the argument runs to its matching paren, string
|
|
501
|
+
* literals skipped as units. A match not followed by `(` is not a call and is skipped.
|
|
502
|
+
*/
|
|
503
|
+
function callArguments(src: string, callee: RegExp): string[] {
|
|
504
|
+
const out: string[] = [];
|
|
505
|
+
for (const m of src.matchAll(callee)) {
|
|
506
|
+
let i = (m.index ?? 0) + m[0].length;
|
|
507
|
+
while (/\s/.test(src[i] ?? "")) i++;
|
|
508
|
+
if (src[i] === "<") {
|
|
509
|
+
let depth = 0;
|
|
510
|
+
for (; i < src.length; i++) {
|
|
511
|
+
if (src[i] === "<") depth++;
|
|
512
|
+
else if (src[i] === ">" && src[i - 1] !== "=" && --depth === 0) break;
|
|
513
|
+
}
|
|
514
|
+
i++;
|
|
515
|
+
while (/\s/.test(src[i] ?? "")) i++;
|
|
516
|
+
}
|
|
517
|
+
if (src[i] !== "(") continue;
|
|
518
|
+
const start = i + 1;
|
|
519
|
+
let depth = 0;
|
|
520
|
+
let quote = "";
|
|
521
|
+
for (; i < src.length; i++) {
|
|
522
|
+
const ch = src[i];
|
|
523
|
+
if (quote !== "") {
|
|
524
|
+
if (ch === "\\") i++;
|
|
525
|
+
else if (ch === quote) quote = "";
|
|
526
|
+
} else if (ch === '"' || ch === "'" || ch === "`") quote = ch;
|
|
527
|
+
else if (ch === "(") depth++;
|
|
528
|
+
else if (ch === ")" && --depth === 0) break;
|
|
529
|
+
}
|
|
530
|
+
out.push(src.slice(start, i));
|
|
531
|
+
}
|
|
532
|
+
return out;
|
|
533
|
+
}
|
|
534
|
+
|
|
414
535
|
export function mkIssue(
|
|
415
536
|
subcategory: string,
|
|
416
537
|
description: string,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { AxisCheck } from "./types.js";
|
|
7
7
|
import { UNIVERSAL_CHECKS } from "./checks/universal.js";
|
|
8
|
+
import { TINT_CHECKS } from "./checks/tint.js";
|
|
8
9
|
import { STATE_MERGE_CHECKS } from "./checks/state-merge.js";
|
|
9
10
|
import { STATE_UI_AFFORDANCE_CHECKS } from "./checks/state-ui-affordance.js";
|
|
10
11
|
import { REALTIME_CHECKS } from "./checks/realtime.js";
|
|
@@ -16,6 +17,7 @@ import { EXTRA_CHECKS } from "./extras.js";
|
|
|
16
17
|
|
|
17
18
|
export const REGISTRY: readonly AxisCheck[] = [
|
|
18
19
|
...UNIVERSAL_CHECKS,
|
|
20
|
+
...TINT_CHECKS,
|
|
19
21
|
...STATE_MERGE_CHECKS,
|
|
20
22
|
...STATE_UI_AFFORDANCE_CHECKS,
|
|
21
23
|
...REALTIME_CHECKS,
|