@pixel-point/toolcraft 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/src/generate.mjs +34 -5
  3. package/src/generate.test.mjs +12 -0
  4. package/src/package-json.mjs +15 -0
  5. package/src/package-json.test.mjs +14 -1
  6. package/templates/runtime/contracts/component-contracts.test.ts +104 -14
  7. package/templates/runtime/contracts/component-contracts.ts +54 -22
  8. package/templates/runtime/contracts/decision-contracts.test.ts +5 -0
  9. package/templates/runtime/contracts/decision-contracts.ts +3 -3
  10. package/templates/runtime/react/controls-panel.test.tsx +374 -13
  11. package/templates/runtime/react/controls-panel.tsx +65 -4
  12. package/templates/runtime/schema/define-toolcraft.test.ts +45 -1
  13. package/templates/runtime/schema/define-toolcraft.ts +25 -1
  14. package/templates/runtime/schema/types.ts +3 -0
  15. package/templates/runtime/testing/performance.test.ts +134 -0
  16. package/templates/runtime/testing/performance.ts +34 -4
  17. package/templates/starter/AGENTS.md +4 -4
  18. package/templates/starter/docs/toolcraft/README.md +1 -1
  19. package/templates/starter/docs/toolcraft/acceptance-testing.md +5 -3
  20. package/templates/starter/docs/toolcraft/assembly-workflow.md +9 -5
  21. package/templates/starter/docs/toolcraft/component-rules.md +32 -11
  22. package/templates/starter/docs/toolcraft/performance.md +7 -1
  23. package/templates/starter/docs/toolcraft/schema-reference.md +43 -14
  24. package/templates/starter/gitignore +1 -0
  25. package/templates/starter/package.json +2 -0
  26. package/templates/starter/scripts/run-vite-on-free-port.mjs +39 -4
  27. package/templates/starter/scripts/toolcraft-port.mjs +102 -0
  28. package/templates/starter/scripts/toolcraft-port.test.mjs +60 -1
  29. package/templates/starter/src/app/starter-acceptance.test.ts +739 -66
  30. package/templates/starter/src/app/starter-acceptance.ts +471 -12
  31. package/templates/ui/components/control-layout/index.tsx +8 -3
  32. package/templates/ui/components/controls/actions/actions-control.tsx +11 -3
  33. package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +7 -3
  34. package/templates/ui/components/controls/color/index.ts +4 -1
  35. package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +7 -2
  36. package/templates/ui/components/controls/color/style-guide-color-picker.tsx +2 -2
  37. package/templates/ui/components/controls/index.ts +2 -0
  38. package/templates/ui/components/controls/range-input/range-input-control.tsx +12 -4
  39. package/templates/ui/components/controls/select/select-control.tsx +9 -4
  40. package/templates/ui/components/controls/slider/slider-value.ts +0 -1
  41. package/templates/ui/components/controls/text-input/text-input-control.tsx +4 -1
  42. package/templates/ui/components/controls/vector/index.ts +1 -0
  43. package/templates/ui/components/controls/vector/vector-control.tsx +84 -8
  44. package/templates/ui/components/panel/panel-section.tsx +29 -5
@@ -39,20 +39,26 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
39
39
  "Only FontPicker may pair its internal letter-spacing and line-height sliders.",
40
40
  ],
41
41
  requiredAcceptance: [
42
- "Prove changing the slider changes product output or the intended runtime side effect.",
42
+ "Prove dragging the slider changes product output or the intended runtime side effect while the drag is in progress, not only after pointer release, blur, Apply, or a final commit.",
43
43
  "For discrete sliders, prove the discrete variant renders markers and dragging remains smooth.",
44
44
  ],
45
45
  }),
46
46
  aiUsageRules: [
47
+ "Sliders are live canvas controls: dragging must update runtime state and product output in real time by default.",
48
+ "Do not implement slider values as deferred local drafts, Apply-only updates, pointer-up-only commits, or renderer changes that appear only after the user asks again.",
49
+ "Slider performance coverage must use a real control-drag scenario; control-change coverage is not enough to prove live canvas feedback or drag smoothness.",
50
+ "If a live slider causes jank, optimize the renderer path first: update uniforms or stable buffers, cache expensive inputs, coalesce preview work to requestAnimationFrame, cancel stale async renders, move heavy work off React, or switch renderer strategy.",
51
+ "Only in an extreme documented performance ceiling may a slider use a degraded live preview or delayed heavy refinement; the user must still see immediate canvas feedback while dragging and the worklog must record the measured reason.",
47
52
  "Slider step means numeric snapping only; it does not make the slider visually discrete by itself.",
48
53
  "Classify every stepped slider as stepped continuous or visual discrete before writing the schema.",
49
54
  'Small semantic integer domains such as rows, cols, gaps, jitter, counts, levels, bands, passes, points, tiles, and segments must use variant: "discrete".',
50
55
  'Finite animation step domains such as flip depth, character count, glyph steps, and frame steps must use variant: "discrete" when the marker count stays within the Toolcraft visual budget.',
51
56
  "Large or precision stepped ranges such as speed, FPS, rate, duration, density, size, and intensity stay visually continuous even when they declare step.",
52
- "Use slider unit only for measurement or scale suffixes such as %, px, °, x, s, ms, fps, rows/cols, or similar domain units.",
57
+ "Use slider unit only for real measurement suffixes such as %, px, °, s, ms, fps, rows/cols, or similar domain units.",
53
58
  "Do not use unit for repeated entity nouns already named by the section or label, such as Letters + letters, Shape Density / Count + shapes, Words + words, Symbols + symbols, Items + items, Particles + particles, or Layers + layers.",
59
+ 'Do not use unit: "x"; scale, multiplier, intensity, opacity, strength, depth, and shader amount sliders display plain numbers unless a real measurement unit applies.',
54
60
  "When the value needs an entity noun to make sense, improve the label or section title instead of appending that noun as the value unit.",
55
- "Compact symbol/CSS units render tight, such as 70%, 24px, 1.2x, and 8s; word units render with a space, such as 5 cols, when they are truly needed.",
61
+ "Compact symbol/CSS units render tight, such as 70%, 24px, and 8s; word units render with a space, such as 5 cols, when they are truly needed.",
56
62
  "Slider valueLabel is editable only when it contains a numeric value; textual state labels such as Normal are display-only and must not expose hover or click editing affordances.",
57
63
  "Schema sliders render stacked at full width; do not put sliders in two-column inline layout groups.",
58
64
  "The fontPicker component is the only built-in exception with two internal footer sliders for letter spacing and line height.",
@@ -90,16 +96,21 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
90
96
  "Default lower and upper values must be different.",
91
97
  ],
92
98
  requiredAcceptance: [
93
- "Prove rangeSlider.lower and rangeSlider.upper both affect product output.",
99
+ "Prove dragging rangeSlider.lower and rangeSlider.upper both affect product output while the drag is in progress, not only after pointer release, blur, Apply, or a final commit.",
94
100
  ],
95
101
  }),
96
102
  aiUsageRules: [
103
+ "Range sliders are live canvas controls: dragging either thumb must update runtime state and product output in real time by default.",
104
+ "Do not implement range slider values as deferred local drafts, Apply-only updates, pointer-up-only commits, or renderer changes that appear only after the user asks again.",
105
+ "Range slider performance coverage must use a real control-drag scenario; control-change coverage is not enough to prove live canvas feedback or drag smoothness.",
106
+ "If a live range slider causes jank, optimize the renderer path first: update uniforms or stable buffers, cache expensive inputs, coalesce preview work to requestAnimationFrame, cancel stale async renders, move heavy work off React, or switch renderer strategy.",
107
+ "Only in an extreme documented performance ceiling may a range slider use a degraded live preview or delayed heavy refinement; the user must still see immediate canvas feedback while dragging and the worklog must record the measured reason.",
97
108
  "Range slider step means numeric snapping only; it does not make the range slider visually discrete by itself.",
98
109
  "Classify every stepped range slider as stepped continuous or visual discrete before writing the schema.",
99
110
  'Small semantic integer domains such as rows, cols, gaps, jitter, counts, levels, bands, passes, points, tiles, and segments must use variant: "discrete".',
100
111
  'Finite animation step domains such as flip depth, character count, glyph steps, and frame steps must use variant: "discrete" when the marker count stays within the Toolcraft visual budget.',
101
112
  "Large or precision stepped ranges such as speed, FPS, rate, duration, density, size, and intensity stay visually continuous even when they declare step.",
102
- "Use rangeSlider unit only for measurement or scale suffixes; do not use it for repeated entity nouns already named by the section or label.",
113
+ "Use rangeSlider unit only for real measurement suffixes; do not use it for repeated entity nouns already named by the section or label, and do not use x as a unit.",
103
114
  "When a range label needs an entity noun to make sense, improve the label or section title instead of appending that noun as the value unit.",
104
115
  "Compact symbol/CSS units render tight, such as 20% – 80% or 12px – 48px; word units render with a space when truly needed.",
105
116
  "RangeSlider is always a full-width two-thumb control; never place it in an inline two-column layout group with another slider or range slider.",
@@ -165,6 +176,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
165
176
  ],
166
177
  layoutConstraints: [
167
178
  "Keep text segmented controls to at most four options and compact labels.",
179
+ "Segmented controls are full-width controls and must not be placed in two-column inline or half-width layout groups.",
168
180
  "Fallback to Select when cells collide, clip, or lose padding.",
169
181
  ],
170
182
  requiredAcceptance: [
@@ -174,6 +186,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
174
186
  }),
175
187
  aiUsageRules: [
176
188
  "Use Segmented only for compact mode choices where every cell keeps its internal padding.",
189
+ "Do not place Segmented beside Switch, Color, Select, or another control in an inline row; use Select when a finite choice must occupy a half-width column.",
177
190
  "If a segmented control is too wide, first shorten option labels; if the compact labels still exceed the width budget, use Select because it has the same selection mechanics without broken cells.",
178
191
  "Generated schemas should keep text segmented controls to at most four options, no option label longer than nine characters, and no more than twenty-four total option-label characters.",
179
192
  "Browser verification must treat collided, clipped, or paddingless segmented cells as a broken component and switch to shorter labels or Select.",
@@ -212,7 +225,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
212
225
  'Use labels such as "CRT", "Background", "Glow", or "Loop" instead of "Enable CRT" or "Disable background".',
213
226
  "Two adjacent Switch controls for the same product entity must share one inline row when every visible label fits without truncation. Keep paired labels to short one- or two-word names; the runtime auto-pairs safe adjacent switches by target entity, and generated schemas should stack switches only when any label would truncate.",
214
227
  "When the nearest section title already names the switch context, do not duplicate that title as the visible switch label. Use label false for a visual-only toggle and keep the meaning in target/description.",
215
- 'A Switch may share an inline row with one related parameter control when the visible switch label is short enough to fit. That row uses equal-width columns; never shrink the switch column to intrinsic width. In section-owned rows, use a short visible label such as "Include" instead of repeating the section title, such as "Include background" inside Background.',
228
+ 'A Switch may share an inline row with one related parameter control when the visible switch label is short enough to fit. That row uses equal-width columns and the same horizontal column gap as paired Select controls; never shrink the switch column to intrinsic width. The non-switch parameter uses label false in that row; if its label is needed, stack the controls instead. In section-owned rows, use a short visible switch label such as "Include" instead of repeating the section title, such as "Include background" inside Background.',
216
229
  ],
217
230
  },
218
231
  checkbox: {
@@ -247,7 +260,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
247
260
  'Use labels such as "Transparent background", "Guides", or "Loop" instead of "Enable transparent background".',
248
261
  "When the nearest section title already names the checkbox context, do not duplicate that title as the visible checkbox label. Use label false for a visual-only checkbox and keep the meaning in target/description.",
249
262
  "Two adjacent Checkbox controls for the same product entity must share one inline row when every visible label fits without truncation. Keep paired labels to short one- or two-word names; the runtime auto-pairs safe adjacent checkboxes by target entity, and generated schemas should stack checkboxes only when any label would truncate.",
250
- "A Checkbox may share an inline row with one related parameter control when the visible checkbox label is short enough to fit. That row uses equal-width columns; never shrink the checkbox column to intrinsic width. Hide the checkbox label when the section title provides the visible context.",
263
+ "A Checkbox may share an inline row with one related parameter control when the visible checkbox label is short enough to fit. That row uses equal-width columns and the same horizontal column gap as paired Select controls; never shrink the checkbox column to intrinsic width. The non-checkbox parameter uses label false in that row; if its label is needed, stack the controls instead. Hide the checkbox label when the section title provides the visible context.",
251
264
  ],
252
265
  },
253
266
  colorOpacity: {
@@ -295,7 +308,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
295
308
  "single-line setting value",
296
309
  ],
297
310
  useWhen: [
298
- "Use TextInput for short names, titles, tokens, compact prompts, and small setting strings.",
311
+ "Use TextInput for short names, button labels, titles, captions, tokens, compact prompts, and small setting strings.",
299
312
  "Use commitMode content for real product content and commitMode setting for configuration-like values.",
300
313
  ],
301
314
  doNotReplaceWith: [
@@ -313,6 +326,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
313
326
  ],
314
327
  }),
315
328
  aiUsageRules: [
329
+ "TextInput owns short single-line product text: button labels, labels on the canvas, names, titles, captions, badges, short tokens, and compact prompts.",
316
330
  'TextInput commitMode defaults to "content": text content, prompts, names, tokens, titles, and instructions apply while typing.',
317
331
  'Use commitMode: "setting" for text inputs that edit settings such as font size, numeric-like style values, dimensions, ids, or configuration fields; setting text commits on blur or Enter.',
318
332
  "Canvas width and Canvas height are runtime editable-size fields and always commit on blur or Enter like editor size fields.",
@@ -375,7 +389,10 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
375
389
  layoutConstraints: [
376
390
  "Keep local actions close to the entity they affect.",
377
391
  "Keep action labels short and scoped by the section title; prefer Randomize, Clear, Sort, Normalize, Duplicate, or Reset when the section already names the target.",
378
- "When an Actions button group wraps below its label, the wrapped button row starts at the left edge of the control content instead of centering or staying right-aligned.",
392
+ "Do not set an Actions control label to the exact same visible text as its only button; use a short one- or two-word context label such as Ink wash, Palette action, or Current layer while the button keeps the command verb.",
393
+ "Actions never use a side-label layout. If a visible label exists, it sits above the buttons.",
394
+ "Actions buttons render as a two-column grid. One visible button occupies the left half of the section; two buttons occupy one half each; more than two buttons wrap into additional 50% cells.",
395
+ "Do not center or right-align a partial final Actions row; an odd trailing button stays in the left 50% cell.",
379
396
  ],
380
397
  requiredAcceptance: [
381
398
  "Prove each action dispatches the intended command or product side effect for the nearby entity only.",
@@ -389,7 +406,10 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
389
406
  "Do not use Actions for final product delivery actions; use sticky panelActions for Export, Copy, Download, Generate, or Apply.",
390
407
  "Do not use Actions for global reset; the controls panel header owns global reset.",
391
408
  "Do not use Actions for animation transport; Play, Pause, Resume, Restart, and Scrub belong to the top timeline when timeline behavior exists.",
392
- "When an Actions button group no longer fits beside its label and wraps to a second row, align the wrapped button row to the left.",
409
+ "For a single visible Actions button, the control label and button label must not be identical; make the control label a concise context and the button label the command.",
410
+ "Render the Actions label above the buttons; do not put the label on the left with buttons on the right.",
411
+ "Render Actions buttons in 50% cells: one button uses the left half, two buttons fill one row, and larger groups continue in two columns.",
412
+ "Do not stretch an odd trailing Actions button full-width.",
393
413
  'For local reset-like actions, use product-specific values such as "reset-current-layer" or "reset-palette" and handle them through ToolcraftApp onPanelAction; do not use a bare "reset" value unless the action intentionally runs controls.reset.',
394
414
  "Acceptance and browser tests must click each Actions button and prove the product output or runtime state for the nearby entity changed.",
395
415
  ],
@@ -542,7 +562,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
542
562
  ],
543
563
  layoutConstraints: [
544
564
  "Palette is a standalone compound control.",
545
- "Palette renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders, and if it is the only control in the section, only the parent section dividers render.",
565
+ "Palette renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders; if it is the last control in that section, only the top internal divider renders; and if it is the only control in the section, only the parent section dividers render.",
546
566
  ],
547
567
  requiredAcceptance: [
548
568
  "Prove palette.family and palette.shade both affect product output.",
@@ -597,6 +617,9 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
597
617
  'Use variant: "chromaOffset" for RGB/chromatic offset vectors where the X/Y movement controls channel separation.',
598
618
  'Use variant: "toneBias" for split-tone, duotone, or color-grading vectors where both axes describe tone or hue bias.',
599
619
  'Use the default vector variant for spatial values such as position, offset, direction, focus, anchor, and light direction.',
620
+ 'Default spatial vector pads use coordinateMode: "screen": dragging left/up makes vector.x and vector.y smaller so canvas objects move left/up without renderer-side Y inversion.',
621
+ "Holding Shift while dragging a vector pad locks movement to the dominant axis; do not build a custom pad just to support axis-constrained movement.",
622
+ 'Use coordinateMode: "cartesian" only when the product intentionally exposes mathematical Y-up coordinates instead of canvas/screen movement.',
600
623
  "Do not add custom vector sizing props in generated schemas; choose the number, variant, and section grouping from product need and let the runtime size the pads.",
601
624
  "Vector is a compound control; acceptance must prove vector.x and vector.y both affect the product output.",
602
625
  ],
@@ -645,7 +668,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
645
668
  "Never use generic Color or Colors as a generated section title. If no meaningful color role exists and the colors are just basic colors, use a neutral section title such as Appearance instead of omitting the title.",
646
669
  "Do not split a grouped object section into a separate generated Color section; if the color role is unclear, ask the user before implementation.",
647
670
  "When one short numeric/text field and one Color field configure the same entity, keep them in one two-column inline layout group.",
648
- 'Mixed inline rows require visible labels on both controls. The required Background row is the section-title-owned exception: use the Switch label "Include" and set the background Color control label to false. Palette variation color banks are the other exception when the group/section label already names the color bank.',
671
+ 'Mixed inline rows usually require visible labels on both controls. Toggle-plus-parameter rows are the section-owned exception: keep the Switch/Checkbox label visible and set the non-toggle parameter label to false; if the parameter label is needed, stack the controls instead. All 50/50 inline rows use the same horizontal column gap as paired Select controls. The required Background row uses the Switch label "Include" and sets the background Color control label to false. Palette variation color banks are the other exception when the group/section label already names the color bank.',
649
672
  "Plain Color popovers must not show opacity controls. If opacity is editable, use ColorOpacity instead.",
650
673
  "Product-output apps always expose renderer-owned output background color as a schema color target such as appearance.background or scene.background.",
651
674
  'Pair renderer-owned output background color with export.includeBackground in one Background section directly before export settings. Use an equal-width inline row with the export.includeBackground Switch labeled "Include" on the left and the background Color parameter with label false on the right; each control occupies one half of the row.',
@@ -681,7 +704,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
681
704
  ],
682
705
  layoutConstraints: [
683
706
  "Gradient is a full standalone compound control.",
684
- "Gradient renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders, and if it is the only control in the section, only the parent section dividers render.",
707
+ "Gradient renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders; if it is the last control in that section, only the top internal divider renders; and if it is the only control in the section, only the parent section dividers render.",
685
708
  ],
686
709
  requiredAcceptance: [
687
710
  "Prove gradientType, angle, stop position, stop color, and stop opacity affect product output or export output.",
@@ -722,7 +745,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
722
745
  ],
723
746
  layoutConstraints: [
724
747
  "FontPicker owns its popup and internal footer controls.",
725
- "FontPicker renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders, and if it is the only control in the section, only the parent section dividers render.",
748
+ "FontPicker renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders; if it is the last control in that section, only the top internal divider renders; and if it is the only control in the section, only the parent section dividers render.",
726
749
  ],
727
750
  requiredAcceptance: [
728
751
  "Prove fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, and opacity affect actual product text output.",
@@ -766,7 +789,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
766
789
  layoutConstraints: [
767
790
  "Use variant single for one standalone curve and RGB variant only for color-correction or channel-specific curves.",
768
791
  "Single Curves is one labeled control and does not render internal dividers, even inside mixed sections.",
769
- "RGB Curves is a compound channel control and renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each rendered divider and the control content; if it is the first control in that section, only the bottom internal divider renders and the top internal padding is removed, and if it is the only control in the section, only the parent section dividers render.",
792
+ "RGB Curves is a compound channel control and renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each rendered divider and the control content; if it is the first control in that section, only the bottom internal divider renders and the top internal padding is removed; if it is the last control in that section, only the top internal divider renders and the bottom internal padding is removed; and if it is the only control in the section, only the parent section dividers render.",
770
793
  ],
771
794
  requiredAcceptance: [
772
795
  "Prove curves.points affect product output; RGB curves also prove activeChannel affects output.",
@@ -836,7 +859,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
836
859
  ],
837
860
  layoutConstraints: [
838
861
  "ChannelMixer is a standalone compound control.",
839
- "ChannelMixer renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders, and if it is the only control in the section, only the parent section dividers render.",
862
+ "ChannelMixer renders content-width internal dividers only when it shares a panel section with sibling controls, with 18px vertical spacing between each divider and the control content; if it is the first control in that section, only the bottom internal divider renders; if it is the last control in that section, only the top internal divider renders; and if it is the only control in the section, only the parent section dividers render.",
840
863
  ],
841
864
  requiredAcceptance: [
842
865
  "Prove channelMixer.activeChannel and channelMixer.values both affect product output.",
@@ -864,6 +887,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
864
887
  ],
865
888
  doNotReplaceWith: [
866
889
  "Do not place upload UI on the canvas.",
890
+ "Do not draw a custom canvas empty-state design, CTA, fake sample output, decorative placeholder, or agent-invented preset source while waiting for uploaded content.",
867
891
  "Do not build custom file buttons for source media import.",
868
892
  ],
869
893
  acceptableAlternatives: [
@@ -881,6 +905,8 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
881
905
  aiUsageRules: [
882
906
  "Use fileDrop for source material uploads in the controls panel, not on the canvas.",
883
907
  'Use fileDrop with assetKind: "image" for image-only source media and assetKind: "file" for arbitrary uploaded files.',
908
+ "When uploaded/imported content is part of the source-material flow, the canvas must not show agent-invented artwork, CTA text, fake sample output, decorative placeholders, or preset source designs before real content exists; keep the canvas neutral/runtime-backed and put upload affordance in fileDrop.",
909
+ "Do not add procedural Source Preset modes only to avoid an empty canvas. A default procedural or reference source is allowed only when the prompt/reference explicitly defines it and the worklog records that evidence.",
884
910
  "In single-layer apps, the runtime shows the uploaded image as the fileDrop preview and provides the clear action.",
885
911
  "In file mode, the runtime shows uploaded files as a sortable list with paperclip icons, file names, remove buttons, and --border/5 separators.",
886
912
  "In single-layer apps, global Reset controls and section reset must remove uploaded fileDrop source media and return the fileDrop target to defaultValue.",
@@ -961,8 +987,10 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
961
987
  }),
962
988
  aiUsageRules: [
963
989
  "CodeTextarea is the multiline text input for any potentially long value, not only source code.",
964
- "Use text for short single-line strings such as names, small numeric values, compact prompts, titles, and short tokens.",
965
- "Use code when the user may enter long prompts, multiline text, JSON, CSS, shader code, scripts, templates, or other long structured data.",
990
+ "Do not use CodeTextarea for short single-line canvas text, button labels, names, titles, captions, badges, or short tokens; use TextInput.",
991
+ "Use text for short single-line strings such as names, button labels, small numeric values, compact prompts, titles, captions, and short tokens.",
992
+ "Use code only when the user may enter long prompts, multiline text, instructions, JSON, CSS, shader code, scripts, templates, or other long structured data.",
993
+ "If CodeTextarea has a short single-line default value, the schema description must make the long, multiline, or structured-content reason explicit.",
966
994
  "CodeTextarea is a content editor and applies values while typing; do not wait for blur, Enter, or Cmd/Ctrl+Enter to update runtime state.",
967
995
  "CodeTextarea height is capped at 12 visible text lines; long content scrolls inside the textarea instead of making the controls panel taller.",
968
996
  "Do not name a section Code unless the product value is actually code; use the product role such as Prompt, Instructions, Template, JSON, Shader, or CSS.",
@@ -1018,16 +1046,17 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
1018
1046
  aiUsageRules: [
1019
1047
  "Choose canvas.sizing.mode from product context instead of copying a universal 1024px artboard.",
1020
1048
  "Use intrinsic-media for single-layer upload/generation apps so imported media natural size becomes canvas.size.",
1021
- "Use editable-output by default for generated, exportable, shader, poster, badge, wall, banner, thumbnail, and product-output apps where users should see or edit width and height.",
1049
+ "Use editable-output for generated, exportable, shader, poster, badge, wall, banner, thumbnail, procedural, reference-clone, and product-output apps so users always see Aspect ratio, Canvas width, and Canvas height.",
1022
1050
  "When no explicit product size is provided, the runtime default canvas is 16:9 at 1920x1080; do not reset a new product-output app to 1:1 unless the product meaning requires it.",
1023
- "A user-provided base/default size is not a reason to remove size controls; model it as canvas.size plus editable-output unless the prompt or reference explicitly locks output dimensions.",
1024
- "Use fixed-output only when the product output size must not be user-editable, and prove that lock with canvasSizingCoverage fixed-output-size acceptance.",
1051
+ "A user-provided, reference, fixed-format, or base/default size is not a reason to remove size controls; model it as canvas.size plus editable-output so the size is an initial value, not a hidden lock.",
1052
+ "Do not use fixed-output for generated product/output apps with export actions. Reserve fixed-output for non-product internal fixtures where width and height truly must never be user-editable, and prove that lock with canvasSizingCoverage fixed-output-size acceptance.",
1053
+ "A reference or previous app lacking a size editor, or defining a fixed-size baseline, is not a fixed-output reason for a generated product app; product-output clones still use editable-output.",
1025
1054
  "Resolved canvas.size exists for every canvas app, but visible Canvas width and Canvas height controls are mandatory only for editable-output sizing and do not depend on settingsTransfer.",
1026
1055
  "If canvas.size is provided without an explicit sizing mode, defineToolcraft treats it as editable-output and adds Canvas width and Canvas height controls.",
1027
1056
  "The runtime Canvas width and Canvas height block uses the technical Setup section and renders without a visible section heading; do not add a separate Canvas section label above these fields.",
1028
1057
  "When the user manually edits Canvas width or Canvas height, the runtime keeps the typed dimension, keeps the other dimension unchanged, switches Aspect ratio to Custom, and shows the reduced current ratio in the custom ratio inputs.",
1029
1058
  "Aspect ratio presets are the only interaction that may resize both canvas dimensions from a preset; manual size inputs are exact output dimensions.",
1030
- "For non-vector raster, Canvas 2D, WebGL, and WebGPU previews, set canvas.renderScale: true so the runtime adds Resolution scale after canvas sizing. The scale changes backing pixels from 1x to 2x without changing visible canvas size, and adding/enabling it requires targeted browser evidence that the canvas stays responsive at the selected scale.",
1059
+ "For non-vector raster, Canvas 2D, WebGL, and WebGPU previews, set canvas.renderScale: true so the runtime adds Resolution scale after canvas sizing. The scale changes backing pixels from 1 to 2 without changing visible canvas size, and adding/enabling it requires targeted browser evidence that the canvas stays responsive at the selected scale.",
1031
1060
  "After enabling canvas.renderScale, verify that canvas preview stays responsive while dragging sliders and other high-frequency controls at the selected scale.",
1032
1061
  "Performance fixes for canvas.renderScale must preserve the selected visual quality; do not silently downsample, stretch a lower-resolution backing canvas, blur output, or clamp canvas.renderScale below the user's chosen value to pass budgets.",
1033
1062
  "Do not enable canvas.renderScale for DOM/SVG/vector-native previews; preserve vector fidelity through native vector rendering instead of raster supersampling.",
@@ -1101,6 +1130,7 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
1101
1130
  "Component variants are accepted entities too; tests should fail if a non-default Toolcraft control variant falls back to the default variant or custom markup.",
1102
1131
  "Conditional entities require fixtures that make the condition observable.",
1103
1132
  "Use visibleWhen for mode-, type-, variant-, or count-exclusive sections or controls that do not belong to the current selected state.",
1133
+ "When visibleWhen or disabledWhen references a selector for the same product entity or selected branch, keep the selector and its dependent controls in one semantic section. A section that exists only because one selector option is active is not a separate product section just because the branch uses a standalone control.",
1104
1134
  "When a count/quantity control determines how many sibling controls are available, hide unavailable siblings with visibleWhen; do not render all possible controls while the renderer reads only the first N.",
1105
1135
  "Use disabledWhen for controls that belong to the current entity but are temporarily unavailable for the selected mode; the disabled value must be preserved.",
1106
1136
  "Do not leave inactive conditional controls visible and enabled while making the renderer ignore them.",
@@ -1207,6 +1237,8 @@ export const TOOLCRAFT_COMPONENT_CONTRACTS = {
1207
1237
  "When the section is generic, mixed, missing, or otherwise weak context, include the affected entity or role in the label: Pattern color, Background opacity, Wave speed, Stroke width.",
1208
1238
  "Acceptance validators suggest semantic replacement labels for weak generic labels; fix the schema label instead of relying on runtime fallback rewriting.",
1209
1239
  "Controls-panel sections should stay discrete: two to seven product controls is the normal size, and larger sections must split by product sub-entity or workflow stage.",
1240
+ "Generated product apps must export starterControlSectionInventory beside acceptance. Every product controls section declares title, exact targets, groupingReason, and entity or workflowStage; intentional splits of one target entity require workflowStage and splitReason on every split section.",
1241
+ "Section splitting must preserve dependency cohesion: a selector stays with the visibleWhen/disabledWhen controls it gates when they share the same target entity or selected branch. Use internal spacing/dividers or a more specific section title before splitting dependent branch controls away.",
1210
1242
  "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created setup/settings sections use the technical title Setup but render without a visible heading; sticky footer action sections use the technical title Export but render without a visible heading.",
1211
1243
  "Every visible controls-panel section title renders through the standard 36px collapsible header row with vertically centered text and the runtime collapse icon; generated apps must not hand-build section headers.",
1212
1244
  "Controls-panel section expand and collapse uses the standard runtime height/opacity animation; generated apps must not replace it with instant custom section visibility.",
@@ -40,6 +40,11 @@ describe("Toolcraft template decision contract", () => {
40
40
  expect(rule?.verdict).toBe("move-to-validator");
41
41
  expect(rule?.enforcement).toContain("browser-helper");
42
42
  expect(rule?.desiredBehavior).toMatch(/product result/i);
43
+ expect(rule?.desiredBehavior).toMatch(/uploaded or imported content is part/i);
44
+ expect(rule?.desiredBehavior).toMatch(/fake sample output/i);
45
+ expect(rule?.desiredBehavior).toMatch(/agent-invented preset modes/i);
46
+ expect(rule?.desiredBehavior).toMatch(/worklog records that evidence/i);
47
+ expect(rule?.currentConstraint).toMatch(/uploaded\/imported source-material flows/i);
43
48
  });
44
49
 
45
50
  it("keeps runtime shell bypass prevention in validators, not only docs", () => {
@@ -68,9 +68,9 @@ export const TOOLCRAFT_DECISION_CONTRACT = [
68
68
  {
69
69
  area: "canvas",
70
70
  currentConstraint:
71
- "canvasContent must not contain buttons, forms, CTAs, helper text, upload prompts, menus, or settings UI.",
71
+ "canvasContent must not contain buttons, forms, CTAs, helper text, upload prompts, menus, settings UI, or invented placeholder/demo artwork for apps with uploaded/imported source-material flows.",
72
72
  desiredBehavior:
73
- "Canvas renders only product result, source material, renderer output, and valid product editing handles.",
73
+ "Canvas renders only real product result, source material, renderer output derived from current state, and valid product editing handles. If uploaded or imported content is part of the product source flow, the pre-content canvas stays neutral and runtime-backed until real content exists. Do not add agent-invented preset modes, demo backgrounds, CTA copy, fake sample output, or decorative placeholders to fill the canvas; a default procedural/reference source is allowed only when the prompt or reference explicitly defines it and the worklog records that evidence.",
74
74
  enforcement: ["browser-helper", "starter-agents", "spec-checklist"],
75
75
  id: "canvas-no-app-ui",
76
76
  level: "invariant",
@@ -189,7 +189,7 @@ export const TOOLCRAFT_DECISION_CONTRACT = [
189
189
  currentConstraint:
190
190
  "Labels, color placement, section grouping, selector order, and inline density need product-aware decisions.",
191
191
  desiredBehavior:
192
- "Apps define a Control Section Inventory before schema authoring: every controls section has a product entity or workflow stage, related targets stay together, and sections are not named after UI component types.",
192
+ "Apps export a Control Section Inventory before schema authoring: every product controls section has a product entity or workflow stage, exact targets, a grouping reason, and split evidence when one target entity is intentionally divided.",
193
193
  enforcement: ["acceptance-validator", "schema-normalization", "docs", "starter-agents"],
194
194
  id: "controls-layout-heuristics",
195
195
  level: "heuristic",