@ggui-ai/ui-gen 0.27.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 -19095
- 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 -21789
- 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 -1818
- 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 -1748
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +49 -132
- package/dist/evaluation/index.js +302 -6368
- 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 -5124
- 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 +18 -6
- 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 +51 -12
- package/dist/index.js +57 -21850
- 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 +21 -17
- 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/provider-router.ts +1 -0
- 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/create-ui-generator.ts +4 -2
- package/src/design-mode.ts +4 -9
- package/src/evaluation/axis-checks/checks/state-merge.ts +3 -5
- 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 +246 -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/evaluator.ts +2 -2
- package/src/evaluation/index.ts +3 -5
- package/src/evaluation/llm-evaluator.ts +2 -2
- package/src/evaluation/mcp-server.ts +0 -50
- package/src/evaluation/types-public.ts +6 -1
- package/src/evaluation/visual-evaluator.ts +162 -31
- 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/assemble-result.ts +1 -0
- package/src/harness/coding/dupe-break.ts +70 -0
- package/src/harness/coding/generate-task-runner.ts +6 -1
- package/src/harness/coding/run-coding-turn.ts +59 -1
- package/src/harness/coding/run-eval-round.ts +9 -0
- package/src/harness/llm-router.ts +50 -2
- package/src/harness/result-types.ts +12 -0
- package/src/harness/runtime.ts +17 -0
- package/src/index.ts +10 -1
- 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
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// src/fragments/render.ts
|
|
2
|
+
var renderFragments = {
|
|
3
|
+
static: {
|
|
4
|
+
axis: "render",
|
|
5
|
+
value: "static",
|
|
6
|
+
cacheTier: "axisDelta"
|
|
7
|
+
// Static = single entity detail. Stable prefix already covers the
|
|
8
|
+
// obvious pattern (Card + Stack + Heading + fields). No prompt text
|
|
9
|
+
// here keeps low-risk fixtures fast.
|
|
10
|
+
},
|
|
11
|
+
list: {
|
|
12
|
+
axis: "render",
|
|
13
|
+
value: "list",
|
|
14
|
+
cacheTier: "axisDelta",
|
|
15
|
+
promptText: "## Render: list\nVertical column of items. Each item is a <Card> with <Row> + <Text>. Use key={item.id} on each item. Do not paginate unless the contract has a fetch tool with cursor/offset."
|
|
16
|
+
},
|
|
17
|
+
grid: {
|
|
18
|
+
axis: "render",
|
|
19
|
+
value: "grid",
|
|
20
|
+
cacheTier: "axisDelta",
|
|
21
|
+
promptText: '## Render: grid\nTwo shapes share this axis \u2014 read the contract:\n- A TILE grid (a gallery, a stat grid, items that may carry row/col fields): `<Grid columns={N} gap="md">` (or `<CardGrid>`); if items carry row/col fields, position each tile at (row, col).\n- A BOARD (a columns / lanes / stages array whose items are cards \u2014 a kanban): the columns sit SIDE BY SIDE at md and wider \u2014 `<Grid columns={{ base: 1, md: columns.length }} gap="md">` wrapping the columns map, each column its own `<Stack>` of `<Card>`s with its header and count; NEVER a `<Stack>` of column sections (that is a list \u2014 tier-0 fails it).\nNever hand-roll `display: grid`. Do not scroll horizontally.'
|
|
22
|
+
},
|
|
23
|
+
spatial: {
|
|
24
|
+
axis: "render",
|
|
25
|
+
value: "spatial",
|
|
26
|
+
cacheTier: "axisDelta",
|
|
27
|
+
promptText: "## Render: spatial\nGeo/coord-positioned items. Use absolute positioning inside a relative container, or a map primitive if available. Normalize coords to the container's bounds. Treat lat/lng as y/x, not strings."
|
|
28
|
+
},
|
|
29
|
+
timeline: {
|
|
30
|
+
axis: "render",
|
|
31
|
+
value: "timeline",
|
|
32
|
+
cacheTier: "axisDelta",
|
|
33
|
+
promptText: "## Render: timeline\nGroup items by day (or other temporal bucket). Render a sticky date header per group, then the grouped items. Sort newest-first unless the contract says otherwise. Memoize the grouping with useMemo."
|
|
34
|
+
},
|
|
35
|
+
chart: {
|
|
36
|
+
axis: "render",
|
|
37
|
+
value: "chart",
|
|
38
|
+
cacheTier: "axisDelta",
|
|
39
|
+
promptText: "## Render: chart\nNumeric \u2192 visual. Render with inline SVG (no chart library). Compute the viewBox from data min/max. Add axis labels and one value label on the latest/peak point. Keep it readable on a 400px container."
|
|
40
|
+
},
|
|
41
|
+
"master-detail": {
|
|
42
|
+
axis: "render",
|
|
43
|
+
value: "master-detail",
|
|
44
|
+
cacheTier: "axisDelta",
|
|
45
|
+
promptText: "## Render: master-detail\nSplit view: list on one side, detail panel on the other. Track `selectedId` in useState, default to the first item's id. Desktop = side-by-side; mobile/chat shell = stacked with back button."
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// src/fragments/state.ts
|
|
50
|
+
var stateFragments = {
|
|
51
|
+
none: {
|
|
52
|
+
axis: "state",
|
|
53
|
+
value: "none",
|
|
54
|
+
cacheTier: "axisDelta"
|
|
55
|
+
// Pure props → JSX. Stable prefix is enough — no useState call at all.
|
|
56
|
+
},
|
|
57
|
+
"ui-affordance": {
|
|
58
|
+
axis: "state",
|
|
59
|
+
value: "ui-affordance",
|
|
60
|
+
cacheTier: "axisDelta",
|
|
61
|
+
promptText: "## State: ui-affordance\nLocal UI state for filter text / selected id / active tab / quantity. Use useState with a sensible default. Do NOT mirror props into state \u2014 read props directly and track only the affordance value."
|
|
62
|
+
},
|
|
63
|
+
merge: {
|
|
64
|
+
axis: "state",
|
|
65
|
+
value: "merge",
|
|
66
|
+
cacheTier: "axisDelta",
|
|
67
|
+
promptText: "## State: merge (live entity reconciliation)\n1. Seed `const [items, setItems] = useState(props.items ?? [])` AND re-seed on prop change: `useEffect(() => setItems(props.items ?? []), [props.items])` \u2014 a `ggui_amend` repaint must move the list.\n2. On stream updates: `setItems(prev => prev.map(it => it.id === event.id ? { ...it, ...event } : it))` \u2014 merge by id, do NOT append.\n3. Memoize derived views (grouping/sorting/filtering) with useMemo.\n4. Per-item actions pass `{ id, ... }` in the payload.\n5. Never push the stream payload into an append-only list unless realtime=append.",
|
|
68
|
+
boilerplateMarker: [
|
|
69
|
+
"",
|
|
70
|
+
" // \u2500\u2500 Live entity state (merge-by-id) \u2500\u2500",
|
|
71
|
+
" // useState(props.items ?? []); re-seed via useEffect on [props.items]; merge stream events by item.id; never append.",
|
|
72
|
+
""
|
|
73
|
+
].join("\n")
|
|
74
|
+
},
|
|
75
|
+
payload: {
|
|
76
|
+
axis: "state",
|
|
77
|
+
value: "payload",
|
|
78
|
+
cacheTier: "axisDelta",
|
|
79
|
+
promptText: "## State: payload (form assembly)\nAccumulate form fields in a single state object. Validate on blur or on submit \u2014 do NOT block keystrokes. The submit action fires once, with the assembled payload.",
|
|
80
|
+
boilerplateMarker: [
|
|
81
|
+
"",
|
|
82
|
+
" // \u2500\u2500 Form payload \u2500\u2500",
|
|
83
|
+
" // useState<FormData> seeded with defaults from props or empty; validate on submit.",
|
|
84
|
+
""
|
|
85
|
+
].join("\n")
|
|
86
|
+
},
|
|
87
|
+
draft: {
|
|
88
|
+
axis: "state",
|
|
89
|
+
value: "draft",
|
|
90
|
+
cacheTier: "axisDelta",
|
|
91
|
+
promptText: "## State: draft (in-place editor)\nEdit one item at a time. Track `draft` as a separate useState (not mutating the source). Support cancel (discard draft) and save (commit draft \u2192 fire action)."
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/fragments/writes.ts
|
|
96
|
+
var writeFragments = {
|
|
97
|
+
none: {
|
|
98
|
+
axis: "writes",
|
|
99
|
+
value: "none",
|
|
100
|
+
cacheTier: "axisDelta"
|
|
101
|
+
},
|
|
102
|
+
commit: {
|
|
103
|
+
axis: "writes",
|
|
104
|
+
value: "commit",
|
|
105
|
+
cacheTier: "axisDelta",
|
|
106
|
+
promptText: "## Writes: commit\nOne action with a small payload (e.g., addToCart). Wire useAction on a single Button. Do not block the UI on completion \u2014 this is fire-and-forget."
|
|
107
|
+
},
|
|
108
|
+
"multi-commit": {
|
|
109
|
+
axis: "writes",
|
|
110
|
+
value: "multi-commit",
|
|
111
|
+
cacheTier: "axisDelta",
|
|
112
|
+
promptText: "## Writes: multi-commit\nMultiple unrelated single-commit actions (e.g., cancel / change destination / contact driver). Each action has its own Button. No shared payload assembly."
|
|
113
|
+
},
|
|
114
|
+
"per-item": {
|
|
115
|
+
axis: "writes",
|
|
116
|
+
value: "per-item",
|
|
117
|
+
cacheTier: "axisDelta",
|
|
118
|
+
promptText: "## Writes: per-item\nEach item in the entity list has its own action button(s). The payload must include the item's id. Example: `onClick={() => toggle({ id: item.id, completed: !item.completed })}`.",
|
|
119
|
+
boilerplateMarker: [
|
|
120
|
+
"",
|
|
121
|
+
" // \u2500\u2500 Per-item actions \u2500\u2500",
|
|
122
|
+
" // Pass {id, ...} in the payload; wire to a Button inside each item's card.",
|
|
123
|
+
""
|
|
124
|
+
].join("\n")
|
|
125
|
+
},
|
|
126
|
+
submit: {
|
|
127
|
+
axis: "writes",
|
|
128
|
+
value: "submit",
|
|
129
|
+
cacheTier: "axisDelta",
|
|
130
|
+
promptText: "## Writes: submit\nTerminal form submit. One action fires with the full assembled payload at the end. Disable the submit button while pending and after success."
|
|
131
|
+
},
|
|
132
|
+
compose: {
|
|
133
|
+
axis: "writes",
|
|
134
|
+
value: "compose",
|
|
135
|
+
cacheTier: "axisDelta",
|
|
136
|
+
promptText: "## Writes: compose (cross-entity action)\nOne trigger references ids from two or more entity lists (e.g., {eventId, calendarId}). Track both selections in local state. Only enable the trigger once both are chosen."
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var writeTriggerFragments = {
|
|
140
|
+
click: {
|
|
141
|
+
axis: "writeTrigger",
|
|
142
|
+
value: "click",
|
|
143
|
+
cacheTier: "axisDelta"
|
|
144
|
+
// Default case — covered by stable prefix.
|
|
145
|
+
},
|
|
146
|
+
drag: {
|
|
147
|
+
axis: "writeTrigger",
|
|
148
|
+
value: "drag",
|
|
149
|
+
cacheTier: "axisDelta",
|
|
150
|
+
promptText: "## Trigger: drag\nDrag-drop interaction. Use HTML5 drag events (onDragStart, onDragOver, onDrop) on the item and drop zones. Track the dragged item id in useState. Set data-ggui-draggable on draggable elements. Do NOT pull in an external dnd library.",
|
|
151
|
+
boilerplateMarker: [
|
|
152
|
+
"",
|
|
153
|
+
" // \u2500\u2500 Drag state \u2500\u2500",
|
|
154
|
+
" // Track the dragged item id; fire the action on drop with {id, destination}.",
|
|
155
|
+
""
|
|
156
|
+
].join("\n")
|
|
157
|
+
},
|
|
158
|
+
swipe: {
|
|
159
|
+
axis: "writeTrigger",
|
|
160
|
+
value: "swipe",
|
|
161
|
+
cacheTier: "axisDelta",
|
|
162
|
+
promptText: "## Trigger: swipe\nTouch gesture \u2192 one of N actions. Use onTouchStart/onTouchMove/onTouchEnd. Also expose fallback Buttons so desktop users can trigger the same actions by click."
|
|
163
|
+
},
|
|
164
|
+
keystroke: {
|
|
165
|
+
axis: "writeTrigger",
|
|
166
|
+
value: "keystroke",
|
|
167
|
+
cacheTier: "axisDelta",
|
|
168
|
+
promptText: "## Trigger: keystroke\nKeyboard shortcut. Attach onKeyDown to the container (with tabIndex={0}) or use a window listener inside useEffect with cleanup. Document visible shortcuts in the UI."
|
|
169
|
+
},
|
|
170
|
+
auto: {
|
|
171
|
+
axis: "writeTrigger",
|
|
172
|
+
value: "auto",
|
|
173
|
+
cacheTier: "axisDelta",
|
|
174
|
+
promptText: "## Trigger: auto\nEffect-driven. Use useEffect with a debounce (setTimeout + clearTimeout). Do not fire the action on every keystroke."
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/fragments/realtime.ts
|
|
179
|
+
var realtimeFragments = {
|
|
180
|
+
none: {
|
|
181
|
+
axis: "realtime",
|
|
182
|
+
value: "none",
|
|
183
|
+
cacheTier: "axisDelta"
|
|
184
|
+
},
|
|
185
|
+
merge: {
|
|
186
|
+
axis: "realtime",
|
|
187
|
+
value: "merge",
|
|
188
|
+
cacheTier: "axisDelta",
|
|
189
|
+
promptText: "## Realtime: merge\nStream events carry an id; merge into LOCAL STATE. Subscribing alone is not enough \u2014 the local list must update on each event for the DOM to re-render.\n\n```tsx\nconst stream = useStream<UpdateT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint \u2014 props stay the source of truth\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => prev.map((it) => it.id === stream.latest!.id ? { ...it, ...stream.latest } : it));\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all.\n```\n\nNever append \u2014 stream=merge means the entity already exists locally."
|
|
190
|
+
},
|
|
191
|
+
append: {
|
|
192
|
+
axis: "realtime",
|
|
193
|
+
value: "append",
|
|
194
|
+
cacheTier: "axisDelta",
|
|
195
|
+
promptText: "## Realtime: append\nStream events are new entities; APPEND to local state. Subscribing alone is not enough \u2014 the local list must update for the DOM to re-render.\n\n```tsx\nconst stream = useStream<EventT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint \u2014 props stay the source of truth\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => [...prev, stream.latest!]);\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all (which accumulates only post-mount).\n```\n\nUse head for newest-first, tail for chat-style. Cap list length if needed. Do not dedupe unless the contract guarantees at-most-once."
|
|
196
|
+
},
|
|
197
|
+
status: {
|
|
198
|
+
axis: "realtime",
|
|
199
|
+
value: "status",
|
|
200
|
+
cacheTier: "axisDelta",
|
|
201
|
+
promptText: "## Realtime: status\nStream replaces a singleton (e.g., marketStatus, rideStatus). `stream.latest` is the current value \u2014 bind directly into JSX, no local state needed for the singleton itself:\n\n```tsx\nconst statusStream = useStream<StatusT>('streamName');\nconst status = statusStream.latest;\n// Render `<Badge>{status?.state ?? 'loading'}</Badge>`.\n```\n\nIf you also need the timestamp of the last update, derive it from `useEffect` on `statusStream.latest`."
|
|
202
|
+
},
|
|
203
|
+
presence: {
|
|
204
|
+
axis: "realtime",
|
|
205
|
+
value: "presence",
|
|
206
|
+
cacheTier: "axisDelta",
|
|
207
|
+
promptText: "## Realtime: presence\nEphemeral per-user state (typing, cursors, online). Do NOT persist to a list. Sync into a `Set` and clear entries based on the active flag or a timeout:\n\n```tsx\nconst stream = useStream<{ sender: string; active: boolean }>('streamName');\nconst [active, setActive] = useState<Set<string>>(new Set());\nuseEffect(() => {\n if (!stream.latest) return;\n setActive((prev) => {\n const next = new Set(prev);\n if (stream.latest!.active) next.add(stream.latest!.sender);\n else next.delete(stream.latest!.sender);\n return next;\n });\n}, [stream.latest]);\n```"
|
|
208
|
+
},
|
|
209
|
+
mixed: {
|
|
210
|
+
axis: "realtime",
|
|
211
|
+
value: "mixed",
|
|
212
|
+
cacheTier: "axisDelta",
|
|
213
|
+
promptText: "## Realtime: mixed\nMultiple stream channels with DIFFERENT semantics. Each `streamKinds` entry in the contract maps to one of: merge, append, status, presence. Subscribe + sync to local state PER CHANNEL \u2014 subscribing alone never re-renders the DOM.\n\nExample (chat-interface \u2014 message=append, typing=presence):\n\n```tsx\nconst messages = useStream<MessageT>('message');\nconst typing = useStream<TypingT>('typing');\nconst [messageList, setMessageList] = useState(props.messages);\nconst [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());\n\nuseEffect(() => setMessageList(props.messages), [props.messages]); // re-seed on ggui_amend repaint\n\nuseEffect(() => {\n if (!messages.latest) return;\n setMessageList((prev) => [...prev, messages.latest!]);\n}, [messages.latest]);\n\nuseEffect(() => {\n if (!typing.latest) return;\n setTypingUsers((prev) => {\n const next = new Set(prev);\n if (typing.latest!.active) next.add(typing.latest!.sender);\n else next.delete(typing.latest!.sender);\n return next;\n });\n}, [typing.latest]);\n```\n\nDo NOT treat all events the same. Do NOT skip the local-state sync \u2014 `useStream` alone won't trigger DOM updates of your derived view.",
|
|
214
|
+
boilerplateMarker: [
|
|
215
|
+
"",
|
|
216
|
+
" // \u2500\u2500 Mixed stream handlers \u2500\u2500",
|
|
217
|
+
" // One useStream + useEffect+setState per channel \u2014 see realtime fragment.",
|
|
218
|
+
""
|
|
219
|
+
].join("\n")
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/fragments/fetch.ts
|
|
224
|
+
var fetchFragments = {
|
|
225
|
+
none: {
|
|
226
|
+
axis: "fetch",
|
|
227
|
+
value: "none",
|
|
228
|
+
cacheTier: "axisDelta"
|
|
229
|
+
},
|
|
230
|
+
pagination: {
|
|
231
|
+
axis: "fetch",
|
|
232
|
+
value: "pagination",
|
|
233
|
+
cacheTier: "axisDelta",
|
|
234
|
+
promptText: "## Fetch: pagination\nTrack `cursor` (or offset/page) in useState. Expose a 'Load more' Button that calls the tool with the next cursor and appends results. Show a spinner while tool.isPending."
|
|
235
|
+
},
|
|
236
|
+
search: {
|
|
237
|
+
axis: "fetch",
|
|
238
|
+
value: "search",
|
|
239
|
+
cacheTier: "axisDelta",
|
|
240
|
+
promptText: "## Fetch: search\nDebounced query \u2192 tool call. Track `query` in useState; fire the tool inside useEffect after a 300ms debounce. Replace (don't append) results on each call. Guard against stale responses."
|
|
241
|
+
},
|
|
242
|
+
"drill-down": {
|
|
243
|
+
axis: "fetch",
|
|
244
|
+
value: "drill-down",
|
|
245
|
+
cacheTier: "axisDelta",
|
|
246
|
+
promptText: "## Fetch: drill-down\nOn item click, call the tool with the item's id and show the detail. Track `selectedId` and `detail` separately; render a loading placeholder while tool.isPending."
|
|
247
|
+
},
|
|
248
|
+
refresh: {
|
|
249
|
+
axis: "fetch",
|
|
250
|
+
value: "refresh",
|
|
251
|
+
cacheTier: "axisDelta",
|
|
252
|
+
promptText: "## Fetch: refresh\nUser-triggered re-fetch (e.g., pull-to-refresh or a Refresh button). Re-call the tool with the same args; show a small spinner while pending."
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/fragments/layout.ts
|
|
257
|
+
var layoutFragments = {
|
|
258
|
+
single: {
|
|
259
|
+
axis: "layout",
|
|
260
|
+
value: "single",
|
|
261
|
+
cacheTier: "axisDelta"
|
|
262
|
+
},
|
|
263
|
+
"multi-step": {
|
|
264
|
+
axis: "layout",
|
|
265
|
+
value: "multi-step",
|
|
266
|
+
cacheTier: "axisDelta",
|
|
267
|
+
// 2026-04-27: anti-pattern warnings added after 6× n=3 benches showed
|
|
268
|
+
// survey-form + onboarding-wizard accounted for 9 of 11 probe FAILs
|
|
269
|
+
// ("Too many re-renders" 6×, "function is not iterable" 2×, TDZ 1×).
|
|
270
|
+
// All 3 classes trace to the same multi-step-specific anti-patterns
|
|
271
|
+
// below; non-multi-step fixtures had 0 fails across the same benches.
|
|
272
|
+
promptText: "## Layout: multi-step\nWizard. Track `step` in useState (0-indexed). Render `<Stepper steps={STEPS} current={step} />` as the progress indicator \u2014 do NOT hand-roll a '2 of 4' counter, and do NOT reach for Tabs/Progress/Breadcrumb. Stepper is display-only: your Next/Back buttons move `step`. Keep all step state in a single payload object; do NOT reset previous-step data on navigation.\n\n### Anti-patterns that crash at runtime \u2014 DO NOT do these:\n\n1. **No setState in render body** \u2014 causes 'Too many re-renders'.\n```tsx\n// \u274C WRONG \u2014 fires every render, infinite loop\nconst Form = () => {\n const [errors, setErrors] = useState({});\n setErrors(validate(values)); // setState in render \u2192 loop\n};\n// \u2705 RIGHT \u2014 derive with useMemo (no state)\nconst Form = () => {\n const errors = useMemo(() => validate(values), [values]);\n};\n```\n\n2. **No setState in useEffect with state-derived deps** \u2014 also causes 'Too many re-renders'.\n```tsx\n// \u274C WRONG \u2014 payload identity changes \u2192 setIsValid \u2192 re-render \u2192 ...\nuseEffect(() => { setIsValid(check(payload)); }, [payload]);\n// \u2705 RIGHT \u2014 useMemo, no setState\nconst isValid = useMemo(() => check(payload), [payload]);\n```\n\n3. **Declare `steps` array as a top-level `const`, NEVER state.** Default arrays (`fields`, `options`) the same way. Iterating over an undefined or unstable array throws 'function is not iterable'.\n```tsx\n// \u274C WRONG \u2014 useState('steps array...') is a string, .map crashes\nconst [steps] = useState('Welcome,Profile,Done');\n// \u2705 RIGHT \u2014 top-level const\nconst STEPS = ['Welcome', 'Profile', 'Done'] as const;\n```\n\n4. **Hooks/`const` declarations come BEFORE any useEffect/useMemo/useCallback that reads them.** Otherwise: TDZ 'Cannot access X before initialization'.\n```tsx\n// \u274C WRONG \u2014 useEffect reads `total` before its `const` line\nuseEffect(() => log(total), [total]);\nconst total = items.reduce(sum, 0);\n// \u2705 RIGHT \u2014 declare first, read after\nconst total = items.reduce(sum, 0);\nuseEffect(() => log(total), [total]);\n```"
|
|
273
|
+
},
|
|
274
|
+
"master-detail": {
|
|
275
|
+
axis: "layout",
|
|
276
|
+
value: "master-detail",
|
|
277
|
+
cacheTier: "axisDelta",
|
|
278
|
+
promptText: "## Layout: master-detail\nSplit container: master list on the left/top, detail panel on the right/bottom. Track `selectedId` and show a 'select an item' placeholder in the detail panel when null."
|
|
279
|
+
},
|
|
280
|
+
overlay: {
|
|
281
|
+
axis: "layout",
|
|
282
|
+
value: "overlay",
|
|
283
|
+
cacheTier: "axisDelta",
|
|
284
|
+
promptText: "## Layout: overlay\nFloating controls layered on top of content. Use position: absolute with explicit insets. Ensure the overlay does not block critical content (leave a safe area)."
|
|
285
|
+
},
|
|
286
|
+
modal: {
|
|
287
|
+
axis: "layout",
|
|
288
|
+
value: "modal",
|
|
289
|
+
cacheTier: "axisDelta",
|
|
290
|
+
promptText: "## Layout: modal\nUse the <Modal> primitive. Track `isOpen` in useState. Provide a clear close affordance (X button + clicking backdrop). Trap focus inside the modal."
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// src/fragments/tooling.ts
|
|
295
|
+
var toolingFragments = {
|
|
296
|
+
none: {
|
|
297
|
+
axis: "tooling",
|
|
298
|
+
value: "none",
|
|
299
|
+
cacheTier: "axisDelta"
|
|
300
|
+
// Nothing to say — contract carries no agentCapabilities.tools /
|
|
301
|
+
// clientCapabilities.gadgets.
|
|
302
|
+
},
|
|
303
|
+
wired: {
|
|
304
|
+
axis: "tooling",
|
|
305
|
+
value: "wired",
|
|
306
|
+
cacheTier: "axisDelta",
|
|
307
|
+
promptText: "## Tooling: agent-side tools (catalog only)\nThe contract declares `agentCapabilities.tools[X]` for tools the AGENT invokes \u2014 the component never calls these directly. References surface via `actionSpec[Y].nextStep = 'X'` (the agent's next-turn hint forwarded on action events) and `streamSpec[Z].source.tool = 'X'` (the runtime polls / subscribes the tool, deliveries land on the stream channel). Author UI controls fire actions via `useAction`; data feeds appear via `useStream`. The `useWiredTool` hook from before 2026-05-11 is RETIRED."
|
|
308
|
+
},
|
|
309
|
+
client: {
|
|
310
|
+
axis: "tooling",
|
|
311
|
+
value: "client",
|
|
312
|
+
cacheTier: "axisDelta",
|
|
313
|
+
promptText: "## Tooling: gadgets (browser-capability hooks)\nThe contract declares `clientCapabilities.gadgets[X]` for browser-capability gadget hooks the UI mounts. The boilerplate has pre-emitted a direct import per gadget package \u2014 `import { useFoo, useBar } from '<package>';` \u2014 above a `// DO NOT EDIT` banner (STDLIB hooks come from `@ggui-ai/gadgets`, third-party hooks from the package on `clientCapabilities.gadgets[*].package`). Call the imported hook inside the component (`const loc = useGeolocation();`) and trigger via `.start()` from a UI control. Read `.value` / `.status` to render. If the agent needs to observe the result, thread `.value` into a `contextSpec` slot or an `actionSpec` payload. Library hooks are UI-owned lifecycle; the agent never invokes them. **KEEP every pre-emitted gadget import \u2014 do NOT delete it and do NOT change its package**; self_check rejects the code with `gadget_preservation:<hook>` if a gadget import disappears. The pre-2026-05-11 `useClientTool(name, handler)` shape is RETIRED, as is the `@ggui-ai/client-tools` package name (renamed to `@ggui-ai/gadgets`)."
|
|
314
|
+
},
|
|
315
|
+
both: {
|
|
316
|
+
axis: "tooling",
|
|
317
|
+
value: "both",
|
|
318
|
+
cacheTier: "axisDelta",
|
|
319
|
+
promptText: "## Tooling: both surfaces present\nThe contract declares BOTH `agentCapabilities.tools` (agent-invoked catalog; referenced via `actionSpec.nextStep` / `streamSpec.source.tool` \u2014 NO component hook) AND `clientCapabilities.gadgets` (browser-capability gadget hooks the component direct-imports \u2014 the boilerplate pre-emits `import { useCamera } from '@ggui-ai/gadgets';` (STDLIB) or `import { useFoo } from '<package>';` (third-party) above a `// DO NOT EDIT` banner). Don't conflate: agentCapabilities entries are catalog declarations the agent uses, NOT component hooks. clientCapabilities entries DO emit component-side hook calls (e.g., `const cam = useCamera();`)."
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// src/fragments/index.ts
|
|
324
|
+
var FRAGMENT_REGISTRY = {
|
|
325
|
+
render: renderFragments,
|
|
326
|
+
state: stateFragments,
|
|
327
|
+
writes: writeFragments,
|
|
328
|
+
writeTrigger: writeTriggerFragments,
|
|
329
|
+
realtime: realtimeFragments,
|
|
330
|
+
fetch: fetchFragments,
|
|
331
|
+
layout: layoutFragments,
|
|
332
|
+
tooling: toolingFragments
|
|
333
|
+
};
|
|
334
|
+
function lookupFragment(axis, value) {
|
|
335
|
+
return FRAGMENT_REGISTRY[axis]?.[value];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export { FRAGMENT_REGISTRY, lookupFragment };
|
|
339
|
+
//# sourceMappingURL=chunk-XW7ADFFV.js.map
|
|
340
|
+
//# sourceMappingURL=chunk-XW7ADFFV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fragments/render.ts","../src/fragments/state.ts","../src/fragments/writes.ts","../src/fragments/realtime.ts","../src/fragments/fetch.ts","../src/fragments/layout.ts","../src/fragments/tooling.ts","../src/fragments/index.ts"],"names":[],"mappings":"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","file":"chunk-XW7ADFFV.js","sourcesContent":["// packages/ui-gen/src/fragments/render.ts\n//\n// Render-axis fragments. One per RenderShape. Kept minimal — the stable\n// prefix already covers primitives + design tokens. These only add the\n// per-shape layout pattern the LLM needs to commit to up front.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const renderFragments: Record<string, HarnessFragment> = {\n static: {\n axis: \"render\",\n value: \"static\",\n cacheTier: \"axisDelta\",\n // Static = single entity detail. Stable prefix already covers the\n // obvious pattern (Card + Stack + Heading + fields). No prompt text\n // here keeps low-risk fixtures fast.\n },\n list: {\n axis: \"render\",\n value: \"list\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: list\\nVertical column of items. Each item is a <Card> with <Row> + <Text>. Use key={item.id} on each item. Do not paginate unless the contract has a fetch tool with cursor/offset.\",\n },\n grid: {\n axis: \"render\",\n value: \"grid\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: grid\\nTwo shapes share this axis — read the contract:\\n- A TILE grid (a gallery, a stat grid, items that may carry row/col fields): `<Grid columns={N} gap=\\\"md\\\">` (or `<CardGrid>`); if items carry row/col fields, position each tile at (row, col).\\n- A BOARD (a columns / lanes / stages array whose items are cards — a kanban): the columns sit SIDE BY SIDE at md and wider — `<Grid columns={{ base: 1, md: columns.length }} gap=\\\"md\\\">` wrapping the columns map, each column its own `<Stack>` of `<Card>`s with its header and count; NEVER a `<Stack>` of column sections (that is a list — tier-0 fails it).\\nNever hand-roll `display: grid`. Do not scroll horizontally.\",\n },\n spatial: {\n axis: \"render\",\n value: \"spatial\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: spatial\\nGeo/coord-positioned items. Use absolute positioning inside a relative container, or a map primitive if available. Normalize coords to the container's bounds. Treat lat/lng as y/x, not strings.\",\n },\n timeline: {\n axis: \"render\",\n value: \"timeline\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: timeline\\nGroup items by day (or other temporal bucket). Render a sticky date header per group, then the grouped items. Sort newest-first unless the contract says otherwise. Memoize the grouping with useMemo.\",\n },\n chart: {\n axis: \"render\",\n value: \"chart\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: chart\\nNumeric → visual. Render with inline SVG (no chart library). Compute the viewBox from data min/max. Add axis labels and one value label on the latest/peak point. Keep it readable on a 400px container.\",\n },\n \"master-detail\": {\n axis: \"render\",\n value: \"master-detail\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Render: master-detail\\nSplit view: list on one side, detail panel on the other. Track `selectedId` in useState, default to the first item's id. Desktop = side-by-side; mobile/chat shell = stacked with back button.\",\n },\n};\n","// packages/ui-gen/src/fragments/state.ts\n//\n// State-axis fragments. This is the most error-prone axis — \"merge\" in\n// particular collapses into append-only bugs without explicit guidance.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const stateFragments: Record<string, HarnessFragment> = {\n none: {\n axis: \"state\",\n value: \"none\",\n cacheTier: \"axisDelta\",\n // Pure props → JSX. Stable prefix is enough — no useState call at all.\n },\n \"ui-affordance\": {\n axis: \"state\",\n value: \"ui-affordance\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## State: ui-affordance\\nLocal UI state for filter text / selected id / active tab / quantity. Use useState with a sensible default. Do NOT mirror props into state — read props directly and track only the affordance value.\",\n },\n merge: {\n axis: \"state\",\n value: \"merge\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## State: merge (live entity reconciliation)\\n1. Seed `const [items, setItems] = useState(props.items ?? [])` AND re-seed on prop change: `useEffect(() => setItems(props.items ?? []), [props.items])` — a `ggui_amend` repaint must move the list.\\n2. On stream updates: `setItems(prev => prev.map(it => it.id === event.id ? { ...it, ...event } : it))` — merge by id, do NOT append.\\n3. Memoize derived views (grouping/sorting/filtering) with useMemo.\\n4. Per-item actions pass `{ id, ... }` in the payload.\\n5. Never push the stream payload into an append-only list unless realtime=append.\",\n boilerplateMarker: [\n \"\",\n \" // ── Live entity state (merge-by-id) ──\",\n \" // useState(props.items ?? []); re-seed via useEffect on [props.items]; merge stream events by item.id; never append.\",\n \"\",\n ].join(\"\\n\"),\n },\n payload: {\n axis: \"state\",\n value: \"payload\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## State: payload (form assembly)\\nAccumulate form fields in a single state object. Validate on blur or on submit — do NOT block keystrokes. The submit action fires once, with the assembled payload.\",\n boilerplateMarker: [\n \"\",\n \" // ── Form payload ──\",\n \" // useState<FormData> seeded with defaults from props or empty; validate on submit.\",\n \"\",\n ].join(\"\\n\"),\n },\n draft: {\n axis: \"state\",\n value: \"draft\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## State: draft (in-place editor)\\nEdit one item at a time. Track `draft` as a separate useState (not mutating the source). Support cancel (discard draft) and save (commit draft → fire action).\",\n },\n};\n","// packages/ui-gen/src/fragments/writes.ts\n//\n// Writes-axis fragments. Covers both WriteShape (what) and WriteTrigger (how).\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const writeFragments: Record<string, HarnessFragment> = {\n none: {\n axis: \"writes\",\n value: \"none\",\n cacheTier: \"axisDelta\",\n },\n commit: {\n axis: \"writes\",\n value: \"commit\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Writes: commit\\nOne action with a small payload (e.g., addToCart). Wire useAction on a single Button. Do not block the UI on completion — this is fire-and-forget.\",\n },\n \"multi-commit\": {\n axis: \"writes\",\n value: \"multi-commit\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Writes: multi-commit\\nMultiple unrelated single-commit actions (e.g., cancel / change destination / contact driver). Each action has its own Button. No shared payload assembly.\",\n },\n \"per-item\": {\n axis: \"writes\",\n value: \"per-item\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Writes: per-item\\nEach item in the entity list has its own action button(s). The payload must include the item's id. Example: `onClick={() => toggle({ id: item.id, completed: !item.completed })}`.\",\n boilerplateMarker: [\n \"\",\n \" // ── Per-item actions ──\",\n \" // Pass {id, ...} in the payload; wire to a Button inside each item's card.\",\n \"\",\n ].join(\"\\n\"),\n },\n submit: {\n axis: \"writes\",\n value: \"submit\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Writes: submit\\nTerminal form submit. One action fires with the full assembled payload at the end. Disable the submit button while pending and after success.\",\n },\n compose: {\n axis: \"writes\",\n value: \"compose\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Writes: compose (cross-entity action)\\nOne trigger references ids from two or more entity lists (e.g., {eventId, calendarId}). Track both selections in local state. Only enable the trigger once both are chosen.\",\n },\n};\n\n// WriteTrigger is a separate axis but lives in the same file for locality.\nexport const writeTriggerFragments: Record<string, HarnessFragment> = {\n click: {\n axis: \"writeTrigger\",\n value: \"click\",\n cacheTier: \"axisDelta\",\n // Default case — covered by stable prefix.\n },\n drag: {\n axis: \"writeTrigger\",\n value: \"drag\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Trigger: drag\\nDrag-drop interaction. Use HTML5 drag events (onDragStart, onDragOver, onDrop) on the item and drop zones. Track the dragged item id in useState. Set data-ggui-draggable on draggable elements. Do NOT pull in an external dnd library.\",\n boilerplateMarker: [\n \"\",\n \" // ── Drag state ──\",\n \" // Track the dragged item id; fire the action on drop with {id, destination}.\",\n \"\",\n ].join(\"\\n\"),\n },\n swipe: {\n axis: \"writeTrigger\",\n value: \"swipe\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Trigger: swipe\\nTouch gesture → one of N actions. Use onTouchStart/onTouchMove/onTouchEnd. Also expose fallback Buttons so desktop users can trigger the same actions by click.\",\n },\n keystroke: {\n axis: \"writeTrigger\",\n value: \"keystroke\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Trigger: keystroke\\nKeyboard shortcut. Attach onKeyDown to the container (with tabIndex={0}) or use a window listener inside useEffect with cleanup. Document visible shortcuts in the UI.\",\n },\n auto: {\n axis: \"writeTrigger\",\n value: \"auto\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Trigger: auto\\nEffect-driven. Use useEffect with a debounce (setTimeout + clearTimeout). Do not fire the action on every keystroke.\",\n },\n};\n","// packages/ui-gen/src/fragments/realtime.ts\n//\n// Realtime-axis fragments. Stream semantics drive whether you merge,\n// append, replace, or do nothing. \"mixed\" needs per-event kind dispatch.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const realtimeFragments: Record<string, HarnessFragment> = {\n none: {\n axis: \"realtime\",\n value: \"none\",\n cacheTier: \"axisDelta\",\n },\n merge: {\n axis: \"realtime\",\n value: \"merge\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Realtime: merge\\nStream events carry an id; merge into LOCAL STATE. Subscribing alone is not enough — the local list must update on each event for the DOM to re-render.\\n\\n```tsx\\nconst stream = useStream<UpdateT>('streamName');\\nconst [items, setItems] = useState(props.items);\\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint — props stay the source of truth\\nuseEffect(() => {\\n if (!stream.latest) return;\\n setItems((prev) => prev.map((it) => it.id === stream.latest!.id ? { ...it, ...stream.latest } : it));\\n}, [stream.latest]);\\n// Render `items.map(...)` — NOT props.items, NOT stream.all.\\n```\\n\\nNever append — stream=merge means the entity already exists locally.\",\n },\n append: {\n axis: \"realtime\",\n value: \"append\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Realtime: append\\nStream events are new entities; APPEND to local state. Subscribing alone is not enough — the local list must update for the DOM to re-render.\\n\\n```tsx\\nconst stream = useStream<EventT>('streamName');\\nconst [items, setItems] = useState(props.items);\\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint — props stay the source of truth\\nuseEffect(() => {\\n if (!stream.latest) return;\\n setItems((prev) => [...prev, stream.latest!]);\\n}, [stream.latest]);\\n// Render `items.map(...)` — NOT props.items, NOT stream.all (which accumulates only post-mount).\\n```\\n\\nUse head for newest-first, tail for chat-style. Cap list length if needed. Do not dedupe unless the contract guarantees at-most-once.\",\n },\n status: {\n axis: \"realtime\",\n value: \"status\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Realtime: status\\nStream replaces a singleton (e.g., marketStatus, rideStatus). `stream.latest` is the current value — bind directly into JSX, no local state needed for the singleton itself:\\n\\n```tsx\\nconst statusStream = useStream<StatusT>('streamName');\\nconst status = statusStream.latest;\\n// Render `<Badge>{status?.state ?? 'loading'}</Badge>`.\\n```\\n\\nIf you also need the timestamp of the last update, derive it from `useEffect` on `statusStream.latest`.\",\n },\n presence: {\n axis: \"realtime\",\n value: \"presence\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Realtime: presence\\nEphemeral per-user state (typing, cursors, online). Do NOT persist to a list. Sync into a `Set` and clear entries based on the active flag or a timeout:\\n\\n```tsx\\nconst stream = useStream<{ sender: string; active: boolean }>('streamName');\\nconst [active, setActive] = useState<Set<string>>(new Set());\\nuseEffect(() => {\\n if (!stream.latest) return;\\n setActive((prev) => {\\n const next = new Set(prev);\\n if (stream.latest!.active) next.add(stream.latest!.sender);\\n else next.delete(stream.latest!.sender);\\n return next;\\n });\\n}, [stream.latest]);\\n```\",\n },\n mixed: {\n axis: \"realtime\",\n value: \"mixed\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Realtime: mixed\\nMultiple stream channels with DIFFERENT semantics. Each `streamKinds` entry in the contract maps to one of: merge, append, status, presence. Subscribe + sync to local state PER CHANNEL — subscribing alone never re-renders the DOM.\\n\\nExample (chat-interface — message=append, typing=presence):\\n\\n```tsx\\nconst messages = useStream<MessageT>('message');\\nconst typing = useStream<TypingT>('typing');\\nconst [messageList, setMessageList] = useState(props.messages);\\nconst [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());\\n\\nuseEffect(() => setMessageList(props.messages), [props.messages]); // re-seed on ggui_amend repaint\\n\\nuseEffect(() => {\\n if (!messages.latest) return;\\n setMessageList((prev) => [...prev, messages.latest!]);\\n}, [messages.latest]);\\n\\nuseEffect(() => {\\n if (!typing.latest) return;\\n setTypingUsers((prev) => {\\n const next = new Set(prev);\\n if (typing.latest!.active) next.add(typing.latest!.sender);\\n else next.delete(typing.latest!.sender);\\n return next;\\n });\\n}, [typing.latest]);\\n```\\n\\nDo NOT treat all events the same. Do NOT skip the local-state sync — `useStream` alone won't trigger DOM updates of your derived view.\",\n boilerplateMarker: [\n \"\",\n \" // ── Mixed stream handlers ──\",\n \" // One useStream + useEffect+setState per channel — see realtime fragment.\",\n \"\",\n ].join(\"\\n\"),\n },\n};\n","// packages/ui-gen/src/fragments/fetch.ts\n//\n// Fetch-axis fragments. Covers how the component reacts to agent-fetched\n// data — agent-side tools live in `agentCapabilities.tools` (catalog\n// only); the component itself never invokes them directly.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const fetchFragments: Record<string, HarnessFragment> = {\n none: {\n axis: \"fetch\",\n value: \"none\",\n cacheTier: \"axisDelta\",\n },\n pagination: {\n axis: \"fetch\",\n value: \"pagination\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Fetch: pagination\\nTrack `cursor` (or offset/page) in useState. Expose a 'Load more' Button that calls the tool with the next cursor and appends results. Show a spinner while tool.isPending.\",\n },\n search: {\n axis: \"fetch\",\n value: \"search\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Fetch: search\\nDebounced query → tool call. Track `query` in useState; fire the tool inside useEffect after a 300ms debounce. Replace (don't append) results on each call. Guard against stale responses.\",\n },\n \"drill-down\": {\n axis: \"fetch\",\n value: \"drill-down\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Fetch: drill-down\\nOn item click, call the tool with the item's id and show the detail. Track `selectedId` and `detail` separately; render a loading placeholder while tool.isPending.\",\n },\n refresh: {\n axis: \"fetch\",\n value: \"refresh\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Fetch: refresh\\nUser-triggered re-fetch (e.g., pull-to-refresh or a Refresh button). Re-call the tool with the same args; show a small spinner while pending.\",\n },\n};\n","// packages/ui-gen/src/fragments/layout.ts\n//\n// Layout-axis fragments. Structural composition — how the screen is split.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const layoutFragments: Record<string, HarnessFragment> = {\n single: {\n axis: \"layout\",\n value: \"single\",\n cacheTier: \"axisDelta\",\n },\n \"multi-step\": {\n axis: \"layout\",\n value: \"multi-step\",\n cacheTier: \"axisDelta\",\n // 2026-04-27: anti-pattern warnings added after 6× n=3 benches showed\n // survey-form + onboarding-wizard accounted for 9 of 11 probe FAILs\n // (\"Too many re-renders\" 6×, \"function is not iterable\" 2×, TDZ 1×).\n // All 3 classes trace to the same multi-step-specific anti-patterns\n // below; non-multi-step fixtures had 0 fails across the same benches.\n promptText:\n \"## Layout: multi-step\\n\" +\n \"Wizard. Track `step` in useState (0-indexed). Render \" +\n \"`<Stepper steps={STEPS} current={step} />` as the progress \" +\n \"indicator — do NOT hand-roll a '2 of 4' counter, and do NOT \" +\n \"reach for Tabs/Progress/Breadcrumb. Stepper is display-only: \" +\n \"your Next/Back buttons move `step`. Keep all step state in a \" +\n \"single payload object; do NOT reset previous-step data on \" +\n \"navigation.\\n\\n\" +\n \"### Anti-patterns that crash at runtime — DO NOT do these:\\n\\n\" +\n \"1. **No setState in render body** — causes 'Too many re-renders'.\\n\" +\n \"```tsx\\n\" +\n \"// ❌ WRONG — fires every render, infinite loop\\n\" +\n \"const Form = () => {\\n\" +\n \" const [errors, setErrors] = useState({});\\n\" +\n \" setErrors(validate(values)); // setState in render → loop\\n\" +\n \"};\\n\" +\n \"// ✅ RIGHT — derive with useMemo (no state)\\n\" +\n \"const Form = () => {\\n\" +\n \" const errors = useMemo(() => validate(values), [values]);\\n\" +\n \"};\\n\" +\n \"```\\n\\n\" +\n \"2. **No setState in useEffect with state-derived deps** — also \" +\n \"causes 'Too many re-renders'.\\n\" +\n \"```tsx\\n\" +\n \"// ❌ WRONG — payload identity changes → setIsValid → re-render → ...\\n\" +\n \"useEffect(() => { setIsValid(check(payload)); }, [payload]);\\n\" +\n \"// ✅ RIGHT — useMemo, no setState\\n\" +\n \"const isValid = useMemo(() => check(payload), [payload]);\\n\" +\n \"```\\n\\n\" +\n \"3. **Declare `steps` array as a top-level `const`, NEVER state.** \" +\n \"Default arrays (`fields`, `options`) the same way. Iterating over \" +\n \"an undefined or unstable array throws 'function is not iterable'.\\n\" +\n \"```tsx\\n\" +\n \"// ❌ WRONG — useState('steps array...') is a string, .map crashes\\n\" +\n \"const [steps] = useState('Welcome,Profile,Done');\\n\" +\n \"// ✅ RIGHT — top-level const\\n\" +\n \"const STEPS = ['Welcome', 'Profile', 'Done'] as const;\\n\" +\n \"```\\n\\n\" +\n \"4. **Hooks/`const` declarations come BEFORE any useEffect/\" +\n \"useMemo/useCallback that reads them.** Otherwise: TDZ 'Cannot \" +\n \"access X before initialization'.\\n\" +\n \"```tsx\\n\" +\n \"// ❌ WRONG — useEffect reads `total` before its `const` line\\n\" +\n \"useEffect(() => log(total), [total]);\\n\" +\n \"const total = items.reduce(sum, 0);\\n\" +\n \"// ✅ RIGHT — declare first, read after\\n\" +\n \"const total = items.reduce(sum, 0);\\n\" +\n \"useEffect(() => log(total), [total]);\\n\" +\n \"```\",\n },\n \"master-detail\": {\n axis: \"layout\",\n value: \"master-detail\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Layout: master-detail\\nSplit container: master list on the left/top, detail panel on the right/bottom. Track `selectedId` and show a 'select an item' placeholder in the detail panel when null.\",\n },\n overlay: {\n axis: \"layout\",\n value: \"overlay\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Layout: overlay\\nFloating controls layered on top of content. Use position: absolute with explicit insets. Ensure the overlay does not block critical content (leave a safe area).\",\n },\n modal: {\n axis: \"layout\",\n value: \"modal\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Layout: modal\\nUse the <Modal> primitive. Track `isOpen` in useState. Provide a clear close affordance (X button + clicking backdrop). Trap focus inside the modal.\",\n },\n};\n","// packages/ui-gen/src/fragments/tooling.ts\n//\n// tooling-axis fragments. The contract carries two tooling surfaces:\n//\n// - `agentCapabilities.tools` — catalog of agent-invoked tools the\n// contract references via `actionSpec[*].nextStep` and\n// `streamSpec[*].source.tool`. NO component-side hook surface.\n// - `clientCapabilities.gadgets` — catalog of browser-capability\n// gadget hooks (geolocation, mic, camera, clipboard, file picker,\n// notifications). The component imports + calls these hooks\n// from `@ggui-ai/gadgets` (or the third-party package named in\n// `entry.package`).\n//\n// The `wired` / `client` / `both` axis values map onto these surfaces\n// by what the contract carries; the prompt text steers the LLM toward\n// the correct patterns.\n\nimport type { HarnessFragment } from \"./types.js\";\n\nexport const toolingFragments: Record<string, HarnessFragment> = {\n none: {\n axis: \"tooling\",\n value: \"none\",\n cacheTier: \"axisDelta\",\n // Nothing to say — contract carries no agentCapabilities.tools /\n // clientCapabilities.gadgets.\n },\n wired: {\n axis: \"tooling\",\n value: \"wired\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Tooling: agent-side tools (catalog only)\\nThe contract declares `agentCapabilities.tools[X]` for tools the AGENT invokes — the component never calls these directly. References surface via `actionSpec[Y].nextStep = 'X'` (the agent's next-turn hint forwarded on action events) and `streamSpec[Z].source.tool = 'X'` (the runtime polls / subscribes the tool, deliveries land on the stream channel). Author UI controls fire actions via `useAction`; data feeds appear via `useStream`. The `useWiredTool` hook from before 2026-05-11 is RETIRED.\",\n },\n client: {\n axis: \"tooling\",\n value: \"client\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Tooling: gadgets (browser-capability hooks)\\nThe contract declares `clientCapabilities.gadgets[X]` for browser-capability gadget hooks the UI mounts. The boilerplate has pre-emitted a direct import per gadget package — `import { useFoo, useBar } from '<package>';` — above a `// DO NOT EDIT` banner (STDLIB hooks come from `@ggui-ai/gadgets`, third-party hooks from the package on `clientCapabilities.gadgets[*].package`). Call the imported hook inside the component (`const loc = useGeolocation();`) and trigger via `.start()` from a UI control. Read `.value` / `.status` to render. If the agent needs to observe the result, thread `.value` into a `contextSpec` slot or an `actionSpec` payload. Library hooks are UI-owned lifecycle; the agent never invokes them. **KEEP every pre-emitted gadget import — do NOT delete it and do NOT change its package**; self_check rejects the code with `gadget_preservation:<hook>` if a gadget import disappears. The pre-2026-05-11 `useClientTool(name, handler)` shape is RETIRED, as is the `@ggui-ai/client-tools` package name (renamed to `@ggui-ai/gadgets`).\",\n },\n both: {\n axis: \"tooling\",\n value: \"both\",\n cacheTier: \"axisDelta\",\n promptText:\n \"## Tooling: both surfaces present\\nThe contract declares BOTH `agentCapabilities.tools` (agent-invoked catalog; referenced via `actionSpec.nextStep` / `streamSpec.source.tool` — NO component hook) AND `clientCapabilities.gadgets` (browser-capability gadget hooks the component direct-imports — the boilerplate pre-emits `import { useCamera } from '@ggui-ai/gadgets';` (STDLIB) or `import { useFoo } from '<package>';` (third-party) above a `// DO NOT EDIT` banner). Don't conflate: agentCapabilities entries are catalog declarations the agent uses, NOT component hooks. clientCapabilities entries DO emit component-side hook calls (e.g., `const cam = useCamera();`).\",\n },\n};\n","// packages/ui-gen/src/fragments/index.ts\n//\n// Public barrel for the `@ggui-ai/ui-gen/fragments` subpath.\n//\n// Stable public surface (what external consumers should build against):\n// - `FRAGMENT_REGISTRY` — the axis-keyed registry\n// - `lookupFragment` — the one-call lookup helper\n// - fragment types — HarnessFragment, ComposedHarness, CacheTier, AxisKey\n//\n// Per-axis fragment records (renderFragments, stateFragments, etc.) are\n// intentionally NOT re-exported here — consumers who genuinely need one\n// can read `FRAGMENT_REGISTRY.render` / `.state` / etc. Keeping the public\n// surface to a single entry point preserves our freedom to reshape the\n// per-axis files without breaking semver.\n\nimport type { AxisKey, HarnessFragment } from \"./types.js\";\nimport { renderFragments } from \"./render.js\";\nimport { stateFragments } from \"./state.js\";\nimport { writeFragments, writeTriggerFragments } from \"./writes.js\";\nimport { realtimeFragments } from \"./realtime.js\";\nimport { fetchFragments } from \"./fetch.js\";\nimport { layoutFragments } from \"./layout.js\";\nimport { toolingFragments } from \"./tooling.js\";\n\nexport type { HarnessFragment, ComposedHarness, CacheTier, AxisKey } from \"./types.js\";\n\nexport const FRAGMENT_REGISTRY: Record<AxisKey, Record<string, HarnessFragment>> = {\n render: renderFragments,\n state: stateFragments,\n writes: writeFragments,\n writeTrigger: writeTriggerFragments,\n realtime: realtimeFragments,\n fetch: fetchFragments,\n layout: layoutFragments,\n tooling: toolingFragments,\n};\n\nexport function lookupFragment(axis: AxisKey, value: string): HarnessFragment | undefined {\n return FRAGMENT_REGISTRY[axis]?.[value];\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { lookupFragment } from './chunk-XW7ADFFV.js';
|
|
2
|
+
|
|
3
|
+
// src/compose.ts
|
|
4
|
+
var AXIS_ORDER = [
|
|
5
|
+
"render",
|
|
6
|
+
"layout",
|
|
7
|
+
"state",
|
|
8
|
+
"writes",
|
|
9
|
+
"writeTrigger",
|
|
10
|
+
"realtime",
|
|
11
|
+
"fetch",
|
|
12
|
+
"tooling"
|
|
13
|
+
];
|
|
14
|
+
function compose(classification) {
|
|
15
|
+
const v = classification.vector;
|
|
16
|
+
const matched = [];
|
|
17
|
+
for (const axis of AXIS_ORDER) {
|
|
18
|
+
const value = v[axis];
|
|
19
|
+
if (!value) continue;
|
|
20
|
+
const frag = lookupFragment(axis, value);
|
|
21
|
+
if (frag) matched.push(frag);
|
|
22
|
+
}
|
|
23
|
+
const promptParts = matched.filter((f) => f.promptText && f.promptText.trim().length > 0).map((f) => f.promptText.trim());
|
|
24
|
+
const boilerplateParts = matched.filter((f) => f.boilerplateMarker && f.boilerplateMarker.trim().length > 0).map((f) => f.boilerplateMarker);
|
|
25
|
+
return {
|
|
26
|
+
promptText: promptParts.join("\n\n"),
|
|
27
|
+
boilerplateSections: boilerplateParts.join(""),
|
|
28
|
+
fragments: matched
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { compose };
|
|
33
|
+
//# sourceMappingURL=chunk-YIQ42IUX.js.map
|
|
34
|
+
//# sourceMappingURL=chunk-YIQ42IUX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compose.ts"],"names":[],"mappings":";;;AAoBA,IAAM,UAAA,GAAwB;AAAA,EAC5B,QAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA;AAEO,SAAS,QAAQ,cAAA,EAAiD;AACvE,EAAA,MAAM,IAAI,cAAA,CAAe,MAAA;AACzB,EAAA,MAAM,UAA6B,EAAC;AAEpC,EAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,IAAA,MAAM,KAAA,GAAQ,EAAE,IAAI,CAAA;AACpB,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,MAAM,IAAA,GAAO,cAAA,CAAe,IAAA,EAAM,KAAK,CAAA;AACvC,IAAA,IAAI,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EAC7B;AAEA,EAAA,MAAM,WAAA,GAAc,QACjB,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,UAAA,IAAc,EAAE,UAAA,CAAW,IAAA,GAAO,MAAA,GAAS,CAAC,EAC5D,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,UAAA,CAAY,MAAM,CAAA;AAElC,EAAA,MAAM,mBAAmB,OAAA,CACtB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,qBAAqB,CAAA,CAAE,iBAAA,CAAkB,IAAA,EAAK,CAAE,SAAS,CAAC,CAAA,CAC1E,IAAI,CAAC,CAAA,KAAM,EAAE,iBAAkB,CAAA;AAElC,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAAA,IACnC,mBAAA,EAAqB,gBAAA,CAAiB,IAAA,CAAK,EAAE,CAAA;AAAA,IAC7C,SAAA,EAAW;AAAA,GACb;AACF","file":"chunk-YIQ42IUX.js","sourcesContent":["// packages/ui-gen/src/compose.ts\n//\n// Materializes a Classification into prompt sections + boilerplate markers.\n// Sits between the stable prefix (design system docs, pitfalls) and the\n// volatile feedback (eval issues) in the system prompt's cache tiering.\n//\n// Consumes the `./classifier` and `./fragments` subpaths — it's the\n// bridge between an axis vector and the prompt/boilerplate the LLM\n// actually sees.\n\nimport type { Classification } from \"./classifier/axes.js\";\nimport {\n lookupFragment,\n type AxisKey,\n type ComposedHarness,\n type HarnessFragment,\n} from \"./fragments/index.js\";\n\n// Axis processing order inside axisDelta. Puts the most behaviourally\n// consequential axes first so the LLM reads them before scanning details.\nconst AXIS_ORDER: AxisKey[] = [\n \"render\",\n \"layout\",\n \"state\",\n \"writes\",\n \"writeTrigger\",\n \"realtime\",\n \"fetch\",\n \"tooling\",\n];\n\nexport function compose(classification: Classification): ComposedHarness {\n const v = classification.vector;\n const matched: HarnessFragment[] = [];\n\n for (const axis of AXIS_ORDER) {\n const value = v[axis] as string | undefined;\n if (!value) continue;\n const frag = lookupFragment(axis, value);\n if (frag) matched.push(frag);\n }\n\n const promptParts = matched\n .filter((f) => f.promptText && f.promptText.trim().length > 0)\n .map((f) => f.promptText!.trim());\n\n const boilerplateParts = matched\n .filter((f) => f.boilerplateMarker && f.boilerplateMarker.trim().length > 0)\n .map((f) => f.boilerplateMarker!);\n\n return {\n promptText: promptParts.join(\"\\n\\n\"),\n boilerplateSections: boilerplateParts.join(\"\"),\n fragments: matched,\n };\n}\n\nexport type { ComposedHarness };\n"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
|
|
3
|
+
// src/adapters/extract-code.ts
|
|
4
|
+
function createCapture() {
|
|
5
|
+
return { compiledCode: "", sourceCode: void 0, stream: void 0, generatorMeta: void 0 };
|
|
6
|
+
}
|
|
7
|
+
function captureSourceCode(capture, toolName, args) {
|
|
8
|
+
if (toolName === "compile_component" && typeof args.code === "string") {
|
|
9
|
+
capture.sourceCode = args.code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function captureCompiledCode(capture, toolName, result) {
|
|
13
|
+
if (toolName !== "compile_component" || result.isError) return;
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
16
|
+
if (parsed.success && parsed.compiledCode) {
|
|
17
|
+
capture.compiledCode = parsed.compiledCode;
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function captureMarkers(capture, text) {
|
|
23
|
+
if (!capture.stream && text.includes("__GGUI_STREAM_SPEC__")) {
|
|
24
|
+
const match = text.match(/__GGUI_STREAM_SPEC__\s*([\s\S]*?)\s*__GGUI_STREAM_SPEC_END__/);
|
|
25
|
+
if (match) {
|
|
26
|
+
try {
|
|
27
|
+
capture.stream = JSON.parse(
|
|
28
|
+
match[1].replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\")
|
|
29
|
+
);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (!capture.generatorMeta && text.includes("__GGUI_META__")) {
|
|
35
|
+
const match = text.match(/__GGUI_META__\s*([\s\S]*?)\s*__GGUI_META_END__/);
|
|
36
|
+
if (match) {
|
|
37
|
+
try {
|
|
38
|
+
const stripped = match[1].replace(/```(?:json)?\s*/g, "").trim();
|
|
39
|
+
const raw = stripped.replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
if (parsed.category) {
|
|
42
|
+
capture.generatorMeta = { category: parsed.category, description: parsed.description ?? "" };
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function extractCodeFromText(finalOutput, tools, capture) {
|
|
50
|
+
if (finalOutput.length <= 100) return;
|
|
51
|
+
const codeMatch = finalOutput.match(/```(?:tsx?|jsx?|typescript|javascript)?\s*\n([\s\S]*?)```/) ?? finalOutput.match(/```\s*\n([\s\S]*?)```/);
|
|
52
|
+
let code = codeMatch ? codeMatch[1].trim() : "";
|
|
53
|
+
if (!code && (finalOutput.includes("export default") || finalOutput.includes("export function"))) {
|
|
54
|
+
code = finalOutput;
|
|
55
|
+
}
|
|
56
|
+
if (!code || !(code.includes("export default") || code.includes("export function"))) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
capture.sourceCode = code;
|
|
60
|
+
const compileTool = tools.find((t) => t.name === "compile_component");
|
|
61
|
+
if (!compileTool) return;
|
|
62
|
+
const compileResult = await compileTool.handler({ code, filename: "Component.tsx" });
|
|
63
|
+
if (compileResult.isError) {
|
|
64
|
+
console.warn("[extractCodeFromText] compile_component failed:", compileResult.content[0]?.text?.slice(0, 300));
|
|
65
|
+
}
|
|
66
|
+
captureCompiledCode(capture, "compile_component", compileResult);
|
|
67
|
+
}
|
|
68
|
+
async function compileLastResort(capture, allTextOutput) {
|
|
69
|
+
if (capture.compiledCode) return;
|
|
70
|
+
let code = capture.sourceCode;
|
|
71
|
+
if (!code && allTextOutput && allTextOutput.length > 100) {
|
|
72
|
+
const codeMatch = allTextOutput.match(/```(?:tsx?|jsx?|typescript|javascript)?\s*\n([\s\S]*?)```/) ?? allTextOutput.match(/```\s*\n([\s\S]*?)```/);
|
|
73
|
+
code = codeMatch ? codeMatch[1].trim() : void 0;
|
|
74
|
+
if (!code && (allTextOutput.includes("export default") || allTextOutput.includes("export function"))) {
|
|
75
|
+
code = allTextOutput;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!code || !(code.includes("export default") || code.includes("export function"))) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = await esbuild.transform(code, {
|
|
83
|
+
loader: "tsx",
|
|
84
|
+
target: "es2020",
|
|
85
|
+
format: "esm",
|
|
86
|
+
jsx: "automatic",
|
|
87
|
+
jsxImportSource: "react",
|
|
88
|
+
minify: true,
|
|
89
|
+
sourcefile: "Component.tsx"
|
|
90
|
+
});
|
|
91
|
+
capture.compiledCode = result.code;
|
|
92
|
+
capture.sourceCode = code;
|
|
93
|
+
console.warn("[compileLastResort] Compiled code bypassing self-checks \u2014 may have quality issues");
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.warn("[compileLastResort] esbuild failed:", err instanceof Error ? err.message : String(err));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { captureCompiledCode, captureMarkers, captureSourceCode, compileLastResort, createCapture, extractCodeFromText };
|
|
100
|
+
//# sourceMappingURL=chunk-YJZTUY7B.js.map
|
|
101
|
+
//# sourceMappingURL=chunk-YJZTUY7B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/extract-code.ts"],"names":[],"mappings":"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","file":"chunk-YJZTUY7B.js","sourcesContent":["// packages/ui-gen/src/adapters/extract-code.ts\n//\n// Shared helpers for extracting artifacts from tool call results\n// and LLM text output. Used by all adapters to avoid duplication.\n\nimport * as esbuild from 'esbuild';\nimport type { ToolDefinition, ToolResult } from './types';\nimport type { JsonObject } from '@ggui-ai/protocol';\n\n/**\n * Mutable state bag passed through the agentic loop.\n * Each adapter creates one of these and passes it to the\n * extraction helpers on every tool call and text output.\n */\nexport interface CodeCapture {\n compiledCode: string;\n sourceCode: string | undefined;\n stream: JsonObject | undefined;\n generatorMeta: { category: string; description: string } | undefined;\n}\n\nexport function createCapture(): CodeCapture {\n return { compiledCode: '', sourceCode: undefined, stream: undefined, generatorMeta: undefined };\n}\n\n/**\n * Before calling a tool handler: capture source code if this is\n * a compile_component call.\n */\nexport function captureSourceCode(\n capture: CodeCapture,\n toolName: string,\n args: JsonObject,\n): void {\n if (toolName === 'compile_component' && typeof args.code === 'string') {\n capture.sourceCode = args.code;\n }\n}\n\n/**\n * After a tool handler returns: extract compiled code if this was\n * a successful compile_component call.\n */\nexport function captureCompiledCode(\n capture: CodeCapture,\n toolName: string,\n result: ToolResult,\n): void {\n if (toolName !== 'compile_component' || result.isError) return;\n\n try {\n const parsed = JSON.parse(result.content[0].text);\n if (parsed.success && parsed.compiledCode) {\n capture.compiledCode = parsed.compiledCode;\n }\n } catch {\n // Not parseable — ignore\n }\n}\n\n/**\n * Scan text output for __GGUI_STREAM_SPEC__ and __GGUI_META__ markers.\n * Called on each text block in assistant messages (raw adapters)\n * or on the full message stream (SDK adapters via message-parser).\n */\nexport function captureMarkers(capture: CodeCapture, text: string): void {\n if (!capture.stream && text.includes('__GGUI_STREAM_SPEC__')) {\n const match = text.match(/__GGUI_STREAM_SPEC__\\s*([\\s\\S]*?)\\s*__GGUI_STREAM_SPEC_END__/);\n if (match) {\n try {\n capture.stream = JSON.parse(\n match[1].replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\'),\n );\n } catch { /* ignore */ }\n }\n }\n\n if (!capture.generatorMeta && text.includes('__GGUI_META__')) {\n const match = text.match(/__GGUI_META__\\s*([\\s\\S]*?)\\s*__GGUI_META_END__/);\n if (match) {\n try {\n const stripped = match[1].replace(/```(?:json)?\\s*/g, '').trim();\n const raw = stripped.replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n const parsed = JSON.parse(raw) as { category?: string; description?: string };\n if (parsed.category) {\n capture.generatorMeta = { category: parsed.category, description: parsed.description ?? '' };\n }\n } catch { /* ignore */ }\n }\n }\n}\n\n/**\n * Fallback: try to extract code from a text output (used by OpenAI SDK\n * adapter when the model writes code as text instead of calling tools).\n */\nexport async function extractCodeFromText(\n finalOutput: string,\n tools: ToolDefinition[],\n capture: CodeCapture,\n): Promise<void> {\n if (finalOutput.length <= 100) return;\n\n const codeMatch =\n finalOutput.match(/```(?:tsx?|jsx?|typescript|javascript)?\\s*\\n([\\s\\S]*?)```/) ??\n finalOutput.match(/```\\s*\\n([\\s\\S]*?)```/);\n let code = codeMatch ? codeMatch[1].trim() : '';\n\n if (!code && (finalOutput.includes('export default') || finalOutput.includes('export function'))) {\n code = finalOutput;\n }\n\n if (!code || !(code.includes('export default') || code.includes('export function'))) {\n return;\n }\n\n capture.sourceCode = code;\n const compileTool = tools.find((t) => t.name === 'compile_component');\n if (!compileTool) return;\n\n const compileResult = await compileTool.handler({ code, filename: 'Component.tsx' });\n if (compileResult.isError) {\n console.warn('[extractCodeFromText] compile_component failed:', compileResult.content[0]?.text?.slice(0, 300));\n }\n captureCompiledCode(capture, 'compile_component', compileResult);\n}\n\n/**\n * Last-resort compilation: bypass self-checks and compile directly with esbuild.\n *\n * Used when compile_component kept rejecting code due to self-check violations\n * (hex colors, raw pixels, forbidden imports, etc.) but the model DID produce\n * source code. Getting imperfect compiled code is better than getting nothing.\n *\n * Also tries text output as a source if sourceCode is empty.\n */\nexport async function compileLastResort(\n capture: CodeCapture,\n allTextOutput?: string,\n): Promise<void> {\n if (capture.compiledCode) return; // Already have compiled code\n\n // Try capture.sourceCode first (from compile_component calls that failed self-checks)\n let code = capture.sourceCode;\n\n // Fallback: extract from text output\n if (!code && allTextOutput && allTextOutput.length > 100) {\n const codeMatch =\n allTextOutput.match(/```(?:tsx?|jsx?|typescript|javascript)?\\s*\\n([\\s\\S]*?)```/) ??\n allTextOutput.match(/```\\s*\\n([\\s\\S]*?)```/);\n code = codeMatch ? codeMatch[1].trim() : undefined;\n\n if (!code && (allTextOutput.includes('export default') || allTextOutput.includes('export function'))) {\n code = allTextOutput;\n }\n }\n\n if (!code || !(code.includes('export default') || code.includes('export function'))) {\n return;\n }\n\n try {\n const result = await esbuild.transform(code, {\n loader: 'tsx',\n target: 'es2020',\n format: 'esm',\n jsx: 'automatic',\n jsxImportSource: 'react',\n minify: true,\n sourcefile: 'Component.tsx',\n });\n\n capture.compiledCode = result.code;\n capture.sourceCode = code;\n console.warn('[compileLastResort] Compiled code bypassing self-checks — may have quality issues');\n } catch (err) {\n console.warn('[compileLastResort] esbuild failed:', err instanceof Error ? err.message : String(err));\n }\n}\n"]}
|