@pixel-point/toolcraft 0.0.2

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 (257) hide show
  1. package/LICENSE.md +98 -0
  2. package/README.md +41 -0
  3. package/bin/create-toolcraft-app.mjs +8 -0
  4. package/bin/toolcraft.mjs +8 -0
  5. package/package.json +24 -0
  6. package/scripts/prepare-pack.mjs +29 -0
  7. package/src/cli.mjs +392 -0
  8. package/src/cli.test.mjs +284 -0
  9. package/src/copy-recursive.mjs +86 -0
  10. package/src/generate.mjs +212 -0
  11. package/src/generate.test.mjs +322 -0
  12. package/src/import-map.mjs +14 -0
  13. package/src/package-json.mjs +80 -0
  14. package/src/package-json.test.mjs +67 -0
  15. package/src/rewrite-imports.mjs +85 -0
  16. package/src/rewrite-imports.test.mjs +58 -0
  17. package/templates/runtime/contracts/component-contracts.test.ts +1165 -0
  18. package/templates/runtime/contracts/component-contracts.ts +1340 -0
  19. package/templates/runtime/contracts/decision-contracts.test.ts +206 -0
  20. package/templates/runtime/contracts/decision-contracts.ts +283 -0
  21. package/templates/runtime/contracts/index.test.ts +14 -0
  22. package/templates/runtime/contracts/index.ts +3 -0
  23. package/templates/runtime/contracts/types.ts +56 -0
  24. package/templates/runtime/export/export.test.ts +203 -0
  25. package/templates/runtime/export/export.ts +132 -0
  26. package/templates/runtime/export/index.ts +1 -0
  27. package/templates/runtime/index.ts +14 -0
  28. package/templates/runtime/react/canvas-shell.test.tsx +424 -0
  29. package/templates/runtime/react/canvas-shell.tsx +408 -0
  30. package/templates/runtime/react/control-renderers.ts +31 -0
  31. package/templates/runtime/react/controls-panel.test.tsx +3736 -0
  32. package/templates/runtime/react/controls-panel.tsx +2327 -0
  33. package/templates/runtime/react/curve-geometry.test.ts +70 -0
  34. package/templates/runtime/react/index.ts +15 -0
  35. package/templates/runtime/react/layer-tree.ts +96 -0
  36. package/templates/runtime/react/layers-panel.test.tsx +487 -0
  37. package/templates/runtime/react/layers-panel.tsx +1348 -0
  38. package/templates/runtime/react/media-file.ts +82 -0
  39. package/templates/runtime/react/panel-host-config.ts +80 -0
  40. package/templates/runtime/react/panel-host-geometry.test.ts +66 -0
  41. package/templates/runtime/react/panel-host-geometry.ts +109 -0
  42. package/templates/runtime/react/panel-host-types.ts +74 -0
  43. package/templates/runtime/react/panel-host.test.tsx +102 -0
  44. package/templates/runtime/react/panel-host.tsx +353 -0
  45. package/templates/runtime/react/runtime-public-api.test.tsx +132 -0
  46. package/templates/runtime/react/settings-transfer.test.ts +150 -0
  47. package/templates/runtime/react/settings-transfer.ts +279 -0
  48. package/templates/runtime/react/storage-key-migration.ts +48 -0
  49. package/templates/runtime/react/theme-runtime.tsx +177 -0
  50. package/templates/runtime/react/timeline-panel.test.tsx +668 -0
  51. package/templates/runtime/react/timeline-panel.tsx +2953 -0
  52. package/templates/runtime/react/toolbar-panel.test.tsx +212 -0
  53. package/templates/runtime/react/toolbar-panel.tsx +205 -0
  54. package/templates/runtime/react/toolcraft-app.integration.test.tsx +350 -0
  55. package/templates/runtime/react/toolcraft-app.test.tsx +339 -0
  56. package/templates/runtime/react/toolcraft-app.tsx +81 -0
  57. package/templates/runtime/react/toolcraft-root.test.tsx +347 -0
  58. package/templates/runtime/react/toolcraft-root.tsx +203 -0
  59. package/templates/runtime/react/use-toolcraft.ts +41 -0
  60. package/templates/runtime/schema/define-toolcraft.test.ts +1524 -0
  61. package/templates/runtime/schema/define-toolcraft.ts +1442 -0
  62. package/templates/runtime/schema/keyframe-capability.test.ts +90 -0
  63. package/templates/runtime/schema/keyframe-capability.ts +51 -0
  64. package/templates/runtime/schema/runtime-targets.ts +40 -0
  65. package/templates/runtime/schema/types.ts +370 -0
  66. package/templates/runtime/state/canvas-zoom.ts +8 -0
  67. package/templates/runtime/state/create-template-state.test.ts +242 -0
  68. package/templates/runtime/state/create-template-state.ts +95 -0
  69. package/templates/runtime/state/keyframe-evaluation.test.ts +141 -0
  70. package/templates/runtime/state/keyframe-evaluation.ts +203 -0
  71. package/templates/runtime/state/persistence.test.ts +217 -0
  72. package/templates/runtime/state/persistence.ts +511 -0
  73. package/templates/runtime/state/reducer.test.ts +937 -0
  74. package/templates/runtime/state/reducer.ts +1212 -0
  75. package/templates/runtime/state/timeline-readiness.ts +43 -0
  76. package/templates/runtime/state/types.ts +242 -0
  77. package/templates/runtime/styles.css +125 -0
  78. package/templates/runtime/testing/performance.test.ts +1058 -0
  79. package/templates/runtime/testing/performance.ts +1078 -0
  80. package/templates/starter/AGENTS.md +186 -0
  81. package/templates/starter/LICENSE.md +98 -0
  82. package/templates/starter/NOTICE.md +8 -0
  83. package/templates/starter/docs/toolcraft/README.md +41 -0
  84. package/templates/starter/docs/toolcraft/acceptance-testing.md +205 -0
  85. package/templates/starter/docs/toolcraft/agent-worklog.md +81 -0
  86. package/templates/starter/docs/toolcraft/assembly-workflow.md +206 -0
  87. package/templates/starter/docs/toolcraft/component-rules.md +299 -0
  88. package/templates/starter/docs/toolcraft/custom-controls.md +71 -0
  89. package/templates/starter/docs/toolcraft/decision-contract.md +71 -0
  90. package/templates/starter/docs/toolcraft/performance.md +112 -0
  91. package/templates/starter/docs/toolcraft/renderer-technique.md +48 -0
  92. package/templates/starter/docs/toolcraft/schema-reference.md +265 -0
  93. package/templates/starter/docs/toolcraft/workflow.md +87 -0
  94. package/templates/starter/e2e/app-browser-acceptance.spec.ts +785 -0
  95. package/templates/starter/e2e/app-controls.spec.ts +41 -0
  96. package/templates/starter/e2e/app-performance.spec.ts +326 -0
  97. package/templates/starter/e2e/canvas-handle-helpers.ts +244 -0
  98. package/templates/starter/e2e/performance-helpers.ts +612 -0
  99. package/templates/starter/e2e/product-observable-helpers.ts +170 -0
  100. package/templates/starter/index.html +12 -0
  101. package/templates/starter/package.json +52 -0
  102. package/templates/starter/playwright.config.ts +43 -0
  103. package/templates/starter/scripts/check-ai-skills.mjs +95 -0
  104. package/templates/starter/scripts/check-toolcraft-docs.mjs +159 -0
  105. package/templates/starter/scripts/check-toolcraft-integrity.mjs +232 -0
  106. package/templates/starter/scripts/run-vite-on-free-port.mjs +48 -0
  107. package/templates/starter/scripts/toolcraft-port.mjs +54 -0
  108. package/templates/starter/scripts/toolcraft-port.test.mjs +73 -0
  109. package/templates/starter/src/app/starter-acceptance.test.ts +5959 -0
  110. package/templates/starter/src/app/starter-acceptance.ts +2646 -0
  111. package/templates/starter/src/app/starter-performance.test.ts +1390 -0
  112. package/templates/starter/src/app/starter-performance.ts +12 -0
  113. package/templates/starter/src/app/starter-schema.test.ts +70 -0
  114. package/templates/starter/src/app/starter-schema.ts +15 -0
  115. package/templates/starter/src/main.tsx +18 -0
  116. package/templates/starter/src/router.tsx +16 -0
  117. package/templates/starter/src/routes/index.tsx +7 -0
  118. package/templates/starter/src/routes/root.tsx +19 -0
  119. package/templates/starter/src/styles.css +120 -0
  120. package/templates/starter/tsconfig.json +11 -0
  121. package/templates/starter/vite.config.ts +13 -0
  122. package/templates/ui/components/composites/accordion.tsx +73 -0
  123. package/templates/ui/components/composites/alert-dialog.tsx +190 -0
  124. package/templates/ui/components/composites/alert.tsx +74 -0
  125. package/templates/ui/components/composites/aspect-ratio.tsx +22 -0
  126. package/templates/ui/components/composites/avatar.tsx +98 -0
  127. package/templates/ui/components/composites/badge.tsx +69 -0
  128. package/templates/ui/components/composites/breadcrumb.tsx +106 -0
  129. package/templates/ui/components/composites/card.tsx +91 -0
  130. package/templates/ui/components/composites/combobox.tsx +486 -0
  131. package/templates/ui/components/composites/command.tsx +296 -0
  132. package/templates/ui/components/composites/context-menu.tsx +247 -0
  133. package/templates/ui/components/composites/dialog.tsx +282 -0
  134. package/templates/ui/components/composites/dropdown-menu.tsx +299 -0
  135. package/templates/ui/components/composites/empty.tsx +110 -0
  136. package/templates/ui/components/composites/hover-card.tsx +44 -0
  137. package/templates/ui/components/composites/index.ts +30 -0
  138. package/templates/ui/components/composites/menubar.tsx +214 -0
  139. package/templates/ui/components/composites/navigation-menu.tsx +167 -0
  140. package/templates/ui/components/composites/pagination.tsx +131 -0
  141. package/templates/ui/components/composites/progress.tsx +72 -0
  142. package/templates/ui/components/composites/radio-group.tsx +84 -0
  143. package/templates/ui/components/composites/resizable.tsx +42 -0
  144. package/templates/ui/components/composites/sheet.tsx +153 -0
  145. package/templates/ui/components/composites/sidebar-structural.tsx +310 -0
  146. package/templates/ui/components/composites/sidebar.tsx +431 -0
  147. package/templates/ui/components/composites/sonner.tsx +35 -0
  148. package/templates/ui/components/composites/spinner.tsx +43 -0
  149. package/templates/ui/components/composites/table.tsx +108 -0
  150. package/templates/ui/components/composites/tabs.tsx +83 -0
  151. package/templates/ui/components/control-layout/index.tsx +437 -0
  152. package/templates/ui/components/controls/actions/actions-control.tsx +139 -0
  153. package/templates/ui/components/controls/actions/index.ts +9 -0
  154. package/templates/ui/components/controls/anchor-grid/anchor-grid-control.tsx +107 -0
  155. package/templates/ui/components/controls/anchor-grid/index.ts +4 -0
  156. package/templates/ui/components/controls/boolean/boolean-controls.tsx +79 -0
  157. package/templates/ui/components/controls/boolean/index.ts +4 -0
  158. package/templates/ui/components/controls/channel-mixer/channel-mixer-control.tsx +95 -0
  159. package/templates/ui/components/controls/channel-mixer/index.ts +4 -0
  160. package/templates/ui/components/controls/channel-tabs/channel-tabs.tsx +42 -0
  161. package/templates/ui/components/controls/channel-tabs/index.ts +6 -0
  162. package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +90 -0
  163. package/templates/ui/components/controls/code-textarea/index.ts +4 -0
  164. package/templates/ui/components/controls/color/color-control.tsx +571 -0
  165. package/templates/ui/components/controls/color/color-picker-popover.tsx +104 -0
  166. package/templates/ui/components/controls/color/index.ts +41 -0
  167. package/templates/ui/components/controls/color/palette-control-data.ts +436 -0
  168. package/templates/ui/components/controls/color/palette-control.tsx +535 -0
  169. package/templates/ui/components/controls/color/style-guide-color-picker-channel-utils.ts +162 -0
  170. package/templates/ui/components/controls/color/style-guide-color-picker-interactions.ts +190 -0
  171. package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +485 -0
  172. package/templates/ui/components/controls/color/style-guide-color-picker-parts.tsx +710 -0
  173. package/templates/ui/components/controls/color/style-guide-color-picker.tsx +503 -0
  174. package/templates/ui/components/controls/control-types.ts +43 -0
  175. package/templates/ui/components/controls/curves/curve-geometry.ts +355 -0
  176. package/templates/ui/components/controls/curves/curve-graph.tsx +390 -0
  177. package/templates/ui/components/controls/curves/curves-control.tsx +445 -0
  178. package/templates/ui/components/controls/curves/index.ts +6 -0
  179. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +191 -0
  180. package/templates/ui/components/controls/file-drop/index.ts +5 -0
  181. package/templates/ui/components/controls/font-picker/font-catalog.json +15360 -0
  182. package/templates/ui/components/controls/font-picker/font-catalog.ts +116 -0
  183. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1202 -0
  184. package/templates/ui/components/controls/font-picker/font-preview-loader.ts +336 -0
  185. package/templates/ui/components/controls/font-picker/index.ts +24 -0
  186. package/templates/ui/components/controls/font-picker/use-hover-intent.ts +46 -0
  187. package/templates/ui/components/controls/gradient/gradient-control-utils.ts +190 -0
  188. package/templates/ui/components/controls/gradient/gradient-control.tsx +612 -0
  189. package/templates/ui/components/controls/gradient/gradient-stop-list.tsx +400 -0
  190. package/templates/ui/components/controls/gradient/gradient-toolbar.tsx +152 -0
  191. package/templates/ui/components/controls/gradient/index.ts +4 -0
  192. package/templates/ui/components/controls/image-picker/image-picker-control.tsx +139 -0
  193. package/templates/ui/components/controls/image-picker/index.ts +7 -0
  194. package/templates/ui/components/controls/index.ts +192 -0
  195. package/templates/ui/components/controls/range-input/index.ts +4 -0
  196. package/templates/ui/components/controls/range-input/range-input-control.tsx +173 -0
  197. package/templates/ui/components/controls/range-slider/index.ts +4 -0
  198. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +122 -0
  199. package/templates/ui/components/controls/range-slider/range-slider-value.ts +61 -0
  200. package/templates/ui/components/controls/segmented/index.ts +8 -0
  201. package/templates/ui/components/controls/segmented/segmented-control.tsx +94 -0
  202. package/templates/ui/components/controls/select/index.ts +4 -0
  203. package/templates/ui/components/controls/select/select-control.tsx +223 -0
  204. package/templates/ui/components/controls/slider/index.ts +4 -0
  205. package/templates/ui/components/controls/slider/slider-control.tsx +150 -0
  206. package/templates/ui/components/controls/slider/slider-value.ts +56 -0
  207. package/templates/ui/components/controls/text-input/index.ts +4 -0
  208. package/templates/ui/components/controls/text-input/text-input-control.tsx +158 -0
  209. package/templates/ui/components/controls/use-measured-element-width.ts +42 -0
  210. package/templates/ui/components/controls/vector/index.ts +8 -0
  211. package/templates/ui/components/controls/vector/vector-control.tsx +401 -0
  212. package/templates/ui/components/panel/index.ts +19 -0
  213. package/templates/ui/components/panel/panel-actions.tsx +165 -0
  214. package/templates/ui/components/panel/panel-header.tsx +61 -0
  215. package/templates/ui/components/panel/panel-icon-button.tsx +96 -0
  216. package/templates/ui/components/panel/panel-section.tsx +168 -0
  217. package/templates/ui/components/panel/panel-surface.tsx +206 -0
  218. package/templates/ui/components/panel/panel.tsx +210 -0
  219. package/templates/ui/components/primitives/animated-loader.tsx +61 -0
  220. package/templates/ui/components/primitives/button-group.tsx +134 -0
  221. package/templates/ui/components/primitives/button.tsx +429 -0
  222. package/templates/ui/components/primitives/checkbox.tsx +62 -0
  223. package/templates/ui/components/primitives/editable-slider-value-label.tsx +337 -0
  224. package/templates/ui/components/primitives/field.tsx +225 -0
  225. package/templates/ui/components/primitives/index.ts +82 -0
  226. package/templates/ui/components/primitives/input-group.tsx +298 -0
  227. package/templates/ui/components/primitives/input.tsx +61 -0
  228. package/templates/ui/components/primitives/internal/button-loading.tsx +178 -0
  229. package/templates/ui/components/primitives/label.tsx +16 -0
  230. package/templates/ui/components/primitives/popover.tsx +126 -0
  231. package/templates/ui/components/primitives/portal-layer-context.tsx +33 -0
  232. package/templates/ui/components/primitives/primitive-arrow-icon.tsx +38 -0
  233. package/templates/ui/components/primitives/scroll-fade-logic.ts +441 -0
  234. package/templates/ui/components/primitives/scroll-fade-render.tsx +75 -0
  235. package/templates/ui/components/primitives/scroll-fade-types.ts +41 -0
  236. package/templates/ui/components/primitives/scroll-fade.tsx +72 -0
  237. package/templates/ui/components/primitives/select.tsx +408 -0
  238. package/templates/ui/components/primitives/selection-state.ts +31 -0
  239. package/templates/ui/components/primitives/separator.tsx +21 -0
  240. package/templates/ui/components/primitives/slider/index.ts +4 -0
  241. package/templates/ui/components/primitives/slider/slider-interaction.tsx +96 -0
  242. package/templates/ui/components/primitives/slider/slider-parts.tsx +303 -0
  243. package/templates/ui/components/primitives/slider/slider-reset.ts +152 -0
  244. package/templates/ui/components/primitives/slider/slider-value.ts +114 -0
  245. package/templates/ui/components/primitives/slider/slider.tsx +511 -0
  246. package/templates/ui/components/primitives/switch.tsx +35 -0
  247. package/templates/ui/components/primitives/textarea.tsx +49 -0
  248. package/templates/ui/components/primitives/toggle-group.tsx +114 -0
  249. package/templates/ui/components/primitives/toggle.tsx +46 -0
  250. package/templates/ui/components/primitives/tooltip.tsx +100 -0
  251. package/templates/ui/hooks/use-mobile.ts +21 -0
  252. package/templates/ui/index.ts +31 -0
  253. package/templates/ui/lib/control-outline.ts +3 -0
  254. package/templates/ui/lib/input-control-style.ts +131 -0
  255. package/templates/ui/lib/style-guide-color-utils.ts +111 -0
  256. package/templates/ui/lib/utils.ts +6 -0
  257. package/templates/ui/styles.css +291 -0
@@ -0,0 +1,71 @@
1
+ # Decision Contract
2
+
3
+ Use this before writing a schema, spec, or implementation plan. It separates hard runtime rules from product decisions.
4
+
5
+ ## Rule Levels
6
+
7
+ | Level | Meaning | Required Handling |
8
+ | --- | --- | --- |
9
+ | Invariant | Cannot be violated | Follow it; tests should fail if broken |
10
+ | Default | Normal choice | Use it unless the spec proves a reason not to |
11
+ | Heuristic | Product-dependent decision | Decide from product behavior and test the choice |
12
+ | Escape hatch | Allowed exception | Explain why and add stronger coverage |
13
+ | Recommendation | Style guidance | Follow unless product behavior requires otherwise |
14
+
15
+ ## Area Guide
16
+
17
+ | Area | Decision Type | Summary |
18
+ | --- | --- | --- |
19
+ | Runtime shell | Invariant | Use `defineToolcraft` and `ToolcraftApp` |
20
+ | Canvas | Mixed | No app UI in `canvasContent`; handles are product-dependent |
21
+ | Panels | Mixed | Panel mechanics are hard; panel presence is product-dependent |
22
+ | Layers | Heuristic, then invariant | Enable only for real layer behavior; fully test when enabled |
23
+ | Timeline | Heuristic, then invariant | Choose from Animation Intent Inventory and transport behavior |
24
+ | Controls | Mixed | Bind every visible control and prove product output behavior |
25
+ | Renderer | Default with escape hatches | Choose technique from fidelity, reference behavior, and workload |
26
+ | Reference clone | Invariant | Preserve reference behavior unless redesign is explicit |
27
+ | Acceptance | Invariant | Prove product observables, not only runtime mutation |
28
+ | Performance | Mixed | Workload controls need workload budgets; ordinary controls need responsiveness coverage; animated previews yield to viewport interactions |
29
+ | Persistence | Default, then invariant | State persistence must be explicit; localStorage apps must prove reload restoration |
30
+ | Workflow | Invariant | Use required skills and browser verification |
31
+
32
+ The runtime shell invariant means product routes render `ToolcraftApp` directly. Do not hand-compose `ToolcraftRoot`, `CanvasShell`, `ControlsPanel`, `TimelinePanel`, `LayersPanel`, or `ToolbarPanel`; those surfaces are shared runtime design, not app-specific implementation details. App-specific source also must not render built-in control components directly; schema controls and `controlRenderers` are the allowed control extension points.
33
+
34
+ ## Rule Catalog
35
+
36
+ This catalog mirrors `TOOLCRAFT_DECISION_CONTRACT`. If runtime adds or renames a rule id, this page and `AGENTS.md` must list the same id.
37
+
38
+ | Rule ID | Level | Area |
39
+ | --- | --- | --- |
40
+ | `runtime-shell-required` | Invariant | Runtime shell |
41
+ | `canvas-no-app-ui` | Invariant | Canvas |
42
+ | `canvas-surface-preserved` | Invariant | Canvas |
43
+ | `canvas-handle-placement` | Heuristic | Canvas |
44
+ | `panel-host-behavior` | Invariant | Panels |
45
+ | `layers-enable-only-when-needed` | Heuristic | Layers |
46
+ | `layers-enabled-behavior` | Invariant | Layers |
47
+ | `timeline-mode-choice` | Heuristic | Timeline |
48
+ | `timeline-enabled-behavior` | Invariant | Timeline |
49
+ | `controls-product-coverage` | Invariant | Controls |
50
+ | `output-export-required` | Invariant | Controls |
51
+ | `controls-layout-heuristics` | Heuristic | Controls |
52
+ | `renderer-technique-inventory` | Default | Renderer |
53
+ | `reference-clone-source-of-truth` | Invariant | Reference clone |
54
+ | `acceptance-product-observable` | Invariant | Acceptance |
55
+ | `performance-coverage-levels` | Invariant | Performance |
56
+ | `persistence-policy-explicit` | Default | Persistence |
57
+ | `workflow-required` | Invariant | Workflow |
58
+
59
+ ## Enforcement
60
+
61
+ Hard rules belong in validators and browser gates, not only prose. Heuristics need decision criteria and tests for the chosen behavior.
62
+
63
+ | Enforcement | Use It For |
64
+ | --- | --- |
65
+ | Runtime behavior | Mechanics owned by the shared runtime |
66
+ | Schema normalization | Repeated layout and density decisions |
67
+ | Acceptance validator | Product behavior and control coverage |
68
+ | Performance validator | Workload and responsiveness budgets |
69
+ | Browser helper | Real UI interaction and visual breakage |
70
+ | CLI integrity check | Generated-app source boundaries |
71
+ | Local docs and `AGENTS.md` | Decision criteria and workflow instructions |
@@ -0,0 +1,112 @@
1
+ # Performance
2
+
3
+ Every visible non-action control needs a `performanceRole` and `performanceReason`.
4
+
5
+ Performance coverage has two levels:
6
+
7
+ - workload coverage for controls that change render cost;
8
+ - responsiveness coverage for ordinary controls that still must not freeze input or break the viewport.
9
+
10
+ ## Workload Coverage
11
+
12
+ Use workload coverage for controls that change rendering cost:
13
+
14
+ - output size;
15
+ - media resolution;
16
+ - sample count;
17
+ - character or grid density;
18
+ - particle count;
19
+ - blur radius;
20
+ - shader complexity;
21
+ - iterations;
22
+ - quality;
23
+ - timeline playback;
24
+ - keyframe scrubbing.
25
+
26
+ Sensitive controls need min/default/max scenarios, `stressFixture`, and real product-output checks.
27
+
28
+ `stressFixture` is the machine-checkable heavy case for a workload scenario. It must include:
29
+
30
+ - `kind`: `large-text`, `large-canvas`, `high-density`, `many-items`, `max-value`, `media`, or `custom`;
31
+ - `reason`: why this value is the heaviest useful product case;
32
+ - `value`: the actual value the browser performance test will apply.
33
+
34
+ For multiline text, prompt, code, JSON, CSS, shader, script, or template workload controls, use `kind: "large-text"`. The fixture must contain at least `50_000` characters and `1_000` lines unless the product has a stricter real-world maximum.
35
+
36
+ Choose heavy fixtures from this app's real controls, not from generic examples. Use the largest useful product canvas, longest useful text, highest density, largest media, highest item count, fastest animation, highest export quality, or strongest effect setting that the product exposes.
37
+
38
+ Browser performance tests for workload scenarios must read the heavy value through:
39
+
40
+ ```ts
41
+ getToolcraftPerformanceStressValue(appPerformance, "scenario-id")
42
+ ```
43
+
44
+ Do not type a separate short value in the Playwright test. If a test uses a toy value while `app-performance.ts` claims a heavy fixture, `pnpm verify:perf` must fail.
45
+
46
+ ## Responsiveness Coverage
47
+
48
+ Ordinary controls still need lightweight responsiveness checks. They should not cause:
49
+
50
+ - frozen pointer drag;
51
+ - delayed input;
52
+ - broken slider movement;
53
+ - stale async renders;
54
+ - canvas zoom or offset jumps;
55
+ - panel scroll affecting canvas zoom;
56
+ - timeline or layer interactions destabilizing the viewport.
57
+
58
+ ## Renderer Performance
59
+
60
+ Custom renderers should:
61
+
62
+ - initialize contexts, programs, shaders, pipelines, textures, and large buffers once;
63
+ - update uniforms or stable buffers when controls change;
64
+ - cache decoded media;
65
+ - debounce, coalesce, or defer heavy preview work;
66
+ - cancel stale async renders;
67
+ - avoid re-decoding media on every control change;
68
+ - cancel scheduled frames during cleanup.
69
+
70
+ Pixel-output renderers may use a capped preview pixel budget, but export/copy must render final product output at `state.canvas.size`.
71
+
72
+ Text-output and vector-output previews must preserve native output fidelity. Do not render low-resolution text/vector output into an offscreen canvas and upscale it.
73
+
74
+ Renderer strategy is not final until the heavy scenarios pass. When stress preview, animation, drag, zoom, or export tests exceed budget, first decide whether the chosen renderer is wrong for this workload. Move heavy work to WebGL/WebGPU, split semantic foreground from heavy backgrounds, cache atlases/buffers, or change the rendering layer model before reducing product quality or relaxing budgets.
75
+
76
+ ## Required Browser Checks
77
+
78
+ Use real interactions for:
79
+
80
+ - `preview-render`;
81
+ - `control-change`;
82
+ - `control-drag`;
83
+ - `media-import` when upload exists;
84
+ - `export-copy` for product export actions and clipboard actions; measure retina output dimensions, not CSS preview size;
85
+ - `timeline-playback` or `timeline-scrub` when timeline exists;
86
+ - `layers-interactions` when layers exist;
87
+ - `viewport-zoom-stress` for detail-heavy or animated custom renderers;
88
+ - `viewport-stability`.
89
+
90
+ Animated custom renderers also need `animation-viewport-drag`. Animation-only frame sampling and viewport-only stability are not enough: the browser test must sample frames while physically dragging or panning the canvas viewport. If SVG/DOM cannot pass that combined budget, choose a different renderer strategy from evidence instead of loosening the budget.
91
+
92
+ Detail-heavy or animated custom renderers also need `viewport-zoom-stress`. This test must use the real toolbar zoom controls while sampling frame gaps and long tasks. Do not satisfy it by calling `canvas.zoom`, mutating runtime state directly, or checking only the final zoom value.
93
+
94
+ Detail-heavy custom renderers also need a stress `preview-render` or `animation-frame` scenario with a `maxLongTaskMs` budget. A high-count Canvas 2D layer must carry that evidence before delivery. If it fails, revise renderer strategy from the measured failure instead of keeping Canvas 2D by default.
95
+
96
+ During canvas drag, pan, pinch, zoom, and radar/center interactions, animated preview renderers must suspend or coalesce non-essential animation work. This is an interaction-performance throttle, not a user-visible playback command: do not flip the user's Play/Pause state, do not reset timeline time, and do not change export behavior. After the interaction settles, resume from the correct timeline or autonomous time without canvas offset or zoom jumps.
97
+
98
+ Animation checks should sample enough frames to catch jank. Interaction budgets must match scenario type rather than using one universal number.
99
+
100
+ Use `app-performance.ts` as the single budget and fixture source. Browser performance tests must call `getToolcraftPerformanceStressValue(appPerformance, scenarioId)` for workload values and `expectToolcraftScenarioPerformanceBudget(..., appPerformance, scenarioId)` for budgets.
101
+
102
+ Run `pnpm verify:perf` for Tier 3 performance-sensitive edits and inside `pnpm verify:final` before delivery. It runs `e2e/app-performance.spec.ts` and every `browser perf:` scenario with one worker so budget failures are not hidden or created by unrelated parallel browser tests.
103
+
104
+ Run a full performance checkpoint with `pnpm verify:perf` when:
105
+
106
+ - the first working version of the app exists;
107
+ - renderer, canvas, animation, export, timeline, or layers change;
108
+ - a bug that previously broke functionality is fixed;
109
+ - a performance optimization lands;
110
+ - the user asks to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
111
+
112
+ Do not use the full performance suite as the default loop for Tier 0-2 edits. Those edits still need the targeted checks named by the verification tier, but they should not pay for renderer and viewport stress tests unless a checkpoint trigger applies. If a fast feature loop defers full performance, record the deferred check and reason in the worklog.
@@ -0,0 +1,48 @@
1
+ # Renderer Technique
2
+
3
+ Choose render technology per product layer. Do not choose a renderer because it is convenient; choose it from product output semantics, reference behavior, fidelity, and workload.
4
+
5
+ The initial renderer choice is provisional. It becomes accepted only after the app passes performance checks with the largest useful product canvas and the heaviest useful values for its own controls. If those checks show frame gaps, long tasks, viewport shaking, slow export, or interaction jank, revise the renderer strategy from that evidence before delivery.
6
+
7
+ ## Strategy Guide
8
+
9
+ - DOM: native product text, editable text, accessible product labels, low-count structured markup.
10
+ - SVG: crisp vector shapes, lines, icons, product foreground geometry, handles, and hit targets.
11
+ - Canvas 2D: medium raster compositing, text-to-canvas export, simple procedural previews, export passes.
12
+ - WebGL or WebGPU: dense pixels, shaders, image processing, high-resolution procedural output, large particle fields, heavy animated backgrounds.
13
+ - Mixed: use separate layers when background, product foreground, editing handles, and export composite have different semantics.
14
+
15
+ Dense backgrounds may use Canvas 2D, WebGL, or WebGPU when the spec names primitive count and performance reason. A dense raster background does not justify rasterizing low-count foreground geometry or text.
16
+
17
+ Do not force WebGL only because an app is visually rich, and do not keep Canvas 2D only because the primitive is text or vector. Use the stress results. High-count text, vectors, particles, grids, media, or procedural layers can stay on Canvas/SVG/DOM only when worst-case preview or animation tests prove they remain responsive. If they do not, split layers or move the heavy product renderer to WebGL/WebGPU.
18
+
19
+ ## Required Matrix
20
+
21
+ Custom renderer specs and `src/app/app-performance.ts` must mirror the decision:
22
+
23
+ - `sourceRepresentation`;
24
+ - `productRepresentation`;
25
+ - `previewRenderer`;
26
+ - `exportRenderer`;
27
+ - `rendererWorkload`;
28
+ - `rendererStrategy`;
29
+ - `whyNotAlternativeStrategies`;
30
+ - `fidelityRisks`;
31
+ - `performanceRisks`.
32
+
33
+ If text or vector output is intentionally rasterized, include `intentionalRasterizationReason`. If preview and export renderers differ, include `previewExportDifferenceReason`. If a reference runtime renderer changes, include `referenceRendererChangeReason`.
34
+
35
+ For heavy custom renderers, specs and `app-performance.ts` must also include stress preview or animation evidence using real maximum values from the app: max density, max text length, max item count, max canvas size, max animation speed, max export quality, max media size, or the nearest real heavy fixture for the product.
36
+
37
+ ## Layer Inventory
38
+
39
+ Custom renderer specs must classify product layers when they exist:
40
+
41
+ - `background`;
42
+ - `product-foreground`;
43
+ - `editing-handles`;
44
+ - `export-composite`.
45
+
46
+ Mirror these in `rendererTechnique.layers` with visible `uiSelector` values for browser verification.
47
+
48
+ Editing handles should be DOM/SVG overlays, must not appear in export/copy output, and must write through runtime state.
@@ -0,0 +1,265 @@
1
+ # Schema Reference
2
+
3
+ Edit `src/app/app-schema.ts` as the public product surface.
4
+
5
+ ## Runtime Shape
6
+
7
+ - Use `defineToolcraft`.
8
+ - Configure `canvas`, `panels`, `toolbar`, and `panelActions` through the schema instead of composing those surfaces by hand.
9
+ - Use `settingsTransfer: "auto"` for complex apps that should let users import/export control settings.
10
+ - Bind every control to a schema `target`.
11
+ - Use `defaultValue` for reset behavior.
12
+ - Use `description` for product-specific help beside a visible label. Keep `label` short. Omit `description` instead of writing label recaps like `Adjusts Opacity`; compound controls such as `fontPicker` must not use `description` to list their own fields.
13
+ - Use `disabled: true` only when the control is intentionally unavailable; the runtime renders the disabled visual and interaction state.
14
+ - Use `visibleWhen` when a control or section exists only for a specific template, type, mode, variant, or count. Hidden values are preserved. A section with no visible controls is hidden automatically.
15
+ - Use `disabledWhen` when a control belongs to the current entity but is temporarily unavailable in the selected state. The value is preserved while disabled.
16
+ - Conditions support `equals`, `notEquals`, `oneOf`, `notOneOf`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, and `lessThanOrEqual`.
17
+ - Use `orderRole` to make control order testable.
18
+ - Use `performanceRole` and `performanceReason` on every visible non-action control so performance coverage can be derived from the schema.
19
+ - Use built-in controls before custom renderers.
20
+ - Use `panelActions` only for sticky footer product actions.
21
+ - Do not use `panelActions` for settings import/export; `settingsTransfer` owns that body section.
22
+ - Do not use `panelActions` for reset. The controls panel header owns reset, and footer actions with `label`, `value`, or `command` containing reset fail acceptance.
23
+ - Still-output product apps expose `Export PNG`.
24
+ - Animated product apps expose `Export Video` and `Export PNG`.
25
+ - `Copy PNG` can be secondary, but it never replaces export.
26
+ - If an odd number of footer actions leaves one action alone in the final row, that final action spans the full row.
27
+ - Use `ToolcraftApp onPanelAction` for product-specific actions.
28
+ - Async export/download/copy/generate/apply handlers return the real Promise from `onPanelAction`; the runtime shows the sticky footer top accent indicator only while that Promise is pending and fills it from `reportProgress(0..1)` when determinate progress is available.
29
+ - Do not duplicate runtime-owned canvas, toolbar, panel, layer, or timeline internals.
30
+
31
+ ## Export
32
+
33
+ Use the standard export helpers from `@/toolcraft/runtime`.
34
+
35
+ ```ts
36
+ export: {
37
+ png: {
38
+ background: "include",
39
+ },
40
+ }
41
+ ```
42
+
43
+ `export.png.background` defaults to `"include"`. Product apps still expose runtime controls for the actual user choice:
44
+
45
+ - `appearance.background` or `scene.background` as a `color` control;
46
+ - `export.includeBackground` as a boolean/options control.
47
+
48
+ PNG exporters should call `createToolcraftPngExportCanvas({ background, includeBackground, state, render })`, where `background` and `includeBackground` come from runtime state. `includeBackground` controls only PNG alpha; live preview, workspace canvas backing, and video export keep the product background. Video export always includes the product background, uses `getToolcraftRetinaExportSize`, and must prove exported metadata duration matches the runtime timeline duration.
49
+
50
+ Animated apps with `Export Video` also expose a separate `Video Export` controls section. Do not mix video export settings into renderer/effect sections. Place this section as the final authored controls section directly above sticky footer export buttons. `Format` and `Resolution` are a compact semantic pair, so use an inline two-column layout by default; stack them only when labels or selected values would clip.
51
+
52
+ ```ts
53
+ {
54
+ title: "Video Export",
55
+ controls: {
56
+ videoFormat: {
57
+ defaultValue: "mp4",
58
+ label: "Format",
59
+ options: [
60
+ { label: "MP4", value: "mp4" },
61
+ { label: "WebM", value: "webm" },
62
+ ],
63
+ target: "export.video.format",
64
+ type: "select",
65
+ },
66
+ videoResolution: {
67
+ defaultValue: "current",
68
+ label: "Resolution",
69
+ options: [
70
+ { label: "Current", value: "current" },
71
+ { label: "4K", value: "4k" },
72
+ ],
73
+ target: "export.video.resolution",
74
+ type: "select",
75
+ },
76
+ },
77
+ layoutGroups: [
78
+ {
79
+ columns: 2,
80
+ controls: ["videoFormat", "videoResolution"],
81
+ layout: "inline",
82
+ },
83
+ ],
84
+ }
85
+ ```
86
+
87
+ Use `MediaRecorder.isTypeSupported(...)` or an explicit encoder/transcoder capability check before choosing the actual MIME/container. `MOV` and `ProRes` are not baseline browser outputs; use them only with a custom encoder/transcoder and dedicated acceptance plus performance coverage. `4K` is an export resolution target, not a reason to lock `canvas.size`. Offline rendered-frame video export must write timeline-based timestamps; `canvas.captureStream()` plus `MediaRecorder` records wall-clock time and cannot be the only duration mechanism for heavy renderers.
88
+
89
+ ## Canvas Sizing
90
+
91
+ Choose sizing from product context:
92
+
93
+ - `intrinsic-media`: a single uploaded or generated source defines `canvas.size`.
94
+ - `editable-output`: exportable output where users should edit width and height.
95
+ - `fixed-output`: product-defined output size that users must not edit.
96
+
97
+ For product output, export, copy, download, shader rendering, procedural rendering, or no single intrinsic source image, use `editable-output` unless the product explicitly needs `fixed-output`.
98
+
99
+ A prompt-provided base/default size is only the initial `canvas.size`. It must not remove the runtime Canvas width and Canvas height controls. Use `fixed-output` only when the reference or product explicitly locks dimensions, and add runtime acceptance with `canvasSizingCoverage: "fixed-output-size"`.
100
+
101
+ Resolved `canvas.size` exists for every canvas app, but visible `Canvas width` and `Canvas height` inputs are mandatory only for `editable-output` sizing. They do not depend on `settingsTransfer`: when settings transfer is off, the runtime prepends a technical `Setup` canvas size section without a visible heading; when settings transfer is on, the controls merge into the first technical `Setup` runtime settings section without a visible heading. Do not hand-build a duplicate size selector.
102
+
103
+ ## Panels
104
+
105
+ - Use `panels: {}` for the neutral starter or for products that have no user-facing panels yet.
106
+ - Controls panel is the primary editing panel once the product has schema controls.
107
+ - Layers are optional. Enable only for multiple editable objects, media objects, groups, visibility, selection, reorder, or selected-layer controls.
108
+ - Do not use `selectedLayer.*` targets when layers are disabled.
109
+ - Timeline is optional. Use no timeline, playback, keyframes, or custom reference timeline from product transport behavior.
110
+ - Do not add right-panel Play, Pause, Animate, or Restart controls for app-wide transport. Use the top timeline.
111
+
112
+ ## Built-In Control Types
113
+
114
+ Use built-ins before custom controls. Unknown `type` values render nothing unless the app passes a matching `controlRenderers` entry.
115
+
116
+ | `type` | Renders | Key fields |
117
+ | --- | --- | --- |
118
+ | `actions` | Inline local action buttons for the current section or nearby entity | `actions`, `target`, `label` |
119
+ | `anchorGrid` | Anchor picker | `defaultValue`, `target` |
120
+ | `channelMixer` | RGB-only channel matrix mixer with R/G/B tabs and Red/Green/Blue source sliders | `defaultValue`, `target`, `label` |
121
+ | `checkbox` | Checkbox field | `defaultValue`, `target`, `label` |
122
+ | `code` | Multiline textarea | `defaultValue`, `target`, `label` |
123
+ | `color` | Hex color picker | `defaultValue: { hex }`, `target`, `label` |
124
+ | `colorOpacity` | Hex color picker plus opacity percent input | `defaultValue: { hex, opacity }`, `target`, `label` |
125
+ | `curves` | RGB or single curve editor | `defaultValue`, `target`, `variant: "single"`, `interpolation: "smooth" \| "monotone"` |
126
+ | `fileDrop` | Upload/drop input | `accept`, `target` |
127
+ | `fontPicker` | Font preview select with popup, category search, weight, size, text case, text color/opacity, letter spacing, and line height; product text must consume `fontId`, `fontWeight`, `fontSize`, `letterSpacing`, `lineHeight`, `textCase`, `color`, and `opacity` | `defaultValue: { fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`, `target` |
128
+ | `gradient` | Gradient editor | `defaultValue: { angle, gradientType, stops }`, `target` |
129
+ | `imagePicker` | Image choice grid | `items`, `defaultValue`, `target` |
130
+ | `palette` | Constrained design-token palette picker for family + shade | `defaultValue: { family, shade }`, `target` |
131
+ | `panelActions` | Sticky footer product actions | `actions`, `target`, `variant` |
132
+ | `rangeInput` | Two compact text values | `defaultValue: { start, end }`, `target` |
133
+ | `rangeSlider` | Two-thumb slider | `defaultValue`, `min`, `max`, `step`, `variant` |
134
+ | `segmented` | Segmented control | `options`, `defaultValue`, `variant` |
135
+ | `select` | Select dropdown | `options`, `defaultValue` |
136
+ | `slider` | Single-value slider | `defaultValue`, `min`, `max`, `step`, `unit`, `variant` |
137
+ | `switch` | Binary switch | `defaultValue`, `target`, `label` |
138
+ | `text` | Single-line input | `defaultValue`, `target`, `label`, `commitMode` |
139
+ | `vector` | X/Y vector pad and fields | `defaultValue: { x, y }`, `xLabel`, `yLabel`, `variant` |
140
+
141
+ `text` defaults to `commitMode: "content"` and applies while typing for real content such as prompts, names, titles, tokens, and short text. 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. Canvas width and Canvas height always commit on blur or Enter. `code` / `CodeTextarea` is a content editor, applies while typing, and is capped at 12 visible lines. Long content scrolls inside the textarea instead of making the controls panel taller.
142
+
143
+ ## Control Selection Inventory
144
+
145
+ Before writing schema controls, map product needs to built-ins by value model, not visual similarity.
146
+
147
+ For every user-visible product setting or action, write:
148
+
149
+ ```txt
150
+ Product need:
151
+ Value model:
152
+ Candidate built-ins checked:
153
+ Best built-in:
154
+ Rejected alternatives:
155
+ Target:
156
+ Required acceptance:
157
+ ```
158
+
159
+ If a built-in exact owner matches the value model, use that built-in. If several controls fit, choose the best fit and record why. If no built-in fits, use the custom escape hatch from `component-rules.md`.
160
+
161
+ For `curves`, choose the variant explicitly. Use `variant: "single"` for acceleration, bend, easing, response, depth, mask, opacity, threshold, or remap curves. Omit it only for RGB/color-correction or channel-specific curves that intentionally need RGB/R/G/B tabs.
162
+
163
+ ## Animation Intent
164
+
165
+ Before adding animation controls, decide the animation owner:
166
+
167
+ - `timeline-playback`: product time controlled by the top timeline.
168
+ - `timeline-keyframes`: property animation controlled by keyframe diamonds and rows.
169
+ - `autonomous`: decorative/self-running output with no user-facing transport.
170
+
171
+ In keyframes mode, renderer code reads evaluated values from the runtime keyframe evaluator. Do not parse timeline labels and do not use raw `state.values` for targets with keyframes.
172
+
173
+ If the user asks for product animation and does not explicitly say it is decorative autoplay, use `panels.timeline: { mode: "playback" }`. If no timeline is used while animation controls remain visible, `starterTransferMode.animationIntent` must declare `mode: "autonomous"`, include a concrete reason, and include behavior coverage for no transport, no play/pause, no scrub, no duration control, no loop control, and no export-at-time.
174
+
175
+ ## Control Section Inventory
176
+
177
+ Before editing `panels.controls.sections`, write a short inventory in the spec or plan:
178
+
179
+ - section title;
180
+ - product entity or workflow stage;
181
+ - included schema targets;
182
+ - reason these controls belong together or reason for a real workflow split.
183
+
184
+ Group controls by product meaning, not by component type. Do not create sections named `Controls`, `Settings`, `Options`, `Sliders`, `Inputs`, `Buttons`, `Color`, or `Colors`.
185
+
186
+ 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.
187
+
188
+ Every visible section title renders through the standard 36px collapsible header row with vertically centered text and the runtime collapse icon. Do not hand-build section headers in generated apps.
189
+
190
+ Section expand/collapse uses the standard runtime height/opacity animation. Do not replace it with instant custom section visibility.
191
+
192
+ Ordinary section collapsed/expanded state persists as a per-app runtime UI preference. It is not undo/redo state, not settings import/export state, and `Reset controls` must not clear it. Runtime technical `Setup` / settings sections and sticky footer `Export` sections are not collapsible.
193
+
194
+ Ordinary controls-panel body sections use 8px top spacing and 24px bottom spacing for their control content. Runtime technical `Setup` / settings sections use 12px top and bottom spacing to match side padding. Sticky footer action sections keep their dedicated spacing.
195
+
196
+ Large built-in compound controls inside mixed sections render content-width internal dividers with 18px between each rendered divider and the control content. If a compound control is the first item in that section, render only its bottom internal divider and remove the top internal padding. If a section contains exactly one control, whether simple or compound, only the parent section dividers render. Single `curves` are not compound for dividers; RGB `curves` are compound.
197
+
198
+ Controls for the same product entity stay in the same section. For example, `squares.right.connections`, `squares.right.hoverRadius`, and `squares.right.color` belong in `Square 1 (Right)` with `Color` as the field label. A standalone color section is only valid when the color is the whole product entity, such as `Background`, `Accent`, `Connector`, or `Brand`.
199
+
200
+ Keep sections discrete. A section usually has two to seven product controls. If a section grows past seven controls, split it by the next product sub-entity or workflow stage instead of keeping a broad bucket. Broad titles such as `Flow`, `Icon`, `Shapes`, `Scene`, `Text`, `Typography`, or `Motion` are valid only for small cohesive groups; larger groups need specific titles such as `Flow Motion`, `Flow Geometry`, `Letter Burst`, `Shape Colors`, `Logo Glow`, `Logo Plate`, or `Text Block`. Section titles must be unique in the same panel.
201
+
202
+ Control labels are judged with their nearest visible context. Short property labels such as `Speed`, `Color`, `Size`, or `Opacity` are allowed when the section or group clearly names the edited product entity. If the section is generic, mixed, missing, or otherwise weak context, include the affected entity or role in the label, such as `Pattern color`, `Background opacity`, `Wave speed`, or `Stroke width`. Acceptance suggests a semantic replacement label; fix the schema label instead of relying on runtime fallback rewriting.
203
+
204
+ If a target prefix has to be split across sections, the spec must name the workflow reason. Otherwise the acceptance validator treats the split as a sectioning error.
205
+
206
+ Switch and checkbox labels name the setting context only. Do not prefix them with `Enable` or `Disable`; use `CRT`, `Glow`, `Loop`, or `Guides` instead. If the nearest section title already names the context, do not duplicate it as the visible toggle label. Use `label: false` for a visual-only toggle and keep the product meaning in `target` and `description`.
207
+
208
+ Inline two-column groups are preferred when controls tune one close product meaning and labels/values fit. Short numeric text pairs can be inline. Related short `select` pairs can be inline, especially workflow pairs such as `Format` + `Resolution`, `Codec` + `Profile`, or `Width unit` + `Height unit`. Use stacked one-control rows only as a fit fallback when a label, selected value, or option text would clip, truncate, or lose padding; record that fallback reason in the spec or worklog. A short numeric/text field may also pair with one related plain `color` field when both configure the same entity, such as `Mask size` and `Color` inside `Mask`. `colorOpacity` never renders in inline two-column groups; if either color control has opacity, keep the controls stacked. Color controls show visible field labels in mixed sections that contain any non-color control. Omit visible color labels only in color-only sections or in a section-title-owned toggle + parameter row, such as hidden `export.includeBackground` plus `appearance.background` inside `Background`. Mixed inline rows require visible labels on every field except this hidden-label toggle + parameter pattern. Color fields in other mixed rows must not be unlabeled. Two adjacent `switch` or `checkbox` controls for the same product entity must share one inline row when both visible labels fit without truncation; the runtime auto-pairs safe adjacent toggles by target entity, and schemas should stack them only when either label is too long. A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and both controls edit the same entity; hide the toggle label when the section title already supplies the context. Schema `slider` and `rangeSlider` controls always stay stacked at full width; the only built-in exception is the paired letter-spacing and line-height footer sliders inside `fontPicker`. Do not place sibling controls for case, color, opacity, size, weight, letter spacing, or line height when the same text entity already uses `fontPicker`.
209
+
210
+ `rangeSlider` is always a full-width two-thumb control. Do not include it in `layoutGroups`. Its `defaultValue` must start with different lower and upper values, such as `[20, 80]`, so the two handles do not collapse into one apparent slider. Manual range labels accept built-in separators such as slash, hyphen, spaces, and dashes.
211
+
212
+ ## Control Order
213
+
214
+ Order controls by decision flow inside each section:
215
+
216
+ - `input`: upload, source, and canvas-size controls;
217
+ - `mode`: mode, type, filter, blend, style, and preset selectors;
218
+ - `primary`, `spatial`, `color`: core product parameters;
219
+ - `strength`: intensity, opacity, scale, depth;
220
+ - `detail`: grain, noise, blur, density, radius, quality;
221
+ - `advanced`: secondary tuning;
222
+ - `action`: footer actions.
223
+
224
+ A selector that changes how later controls are interpreted must use `orderRole: "mode"` and sit above dependent parameters.
225
+
226
+ Use `visibleWhen` for mode-, type-, variant-, or count-exclusive controls and sections. Example: `Partner` is visible when `coBrand.identityMode` is `text`; `Partner logo` is visible when it is `logo`. Count-controlled banks use numeric conditions: `Shade 4` is visible when `shapes.shadeCount` is `greaterThanOrEqual: 4`. When every control in a section is hidden by `visibleWhen`, the whole section is hidden automatically. Use `disabledWhen` only when the control remains part of the current entity but temporarily has no effect.
227
+
228
+ If a selector says “use the first N”, “number of colors”, “number of stops”, “active slots”, or similar, dependent sibling controls must be hidden with `visibleWhen` when they are outside the current count. Do not leave all possible controls visible while making the renderer ignore the inactive ones.
229
+
230
+ App schema tests must assert visible control order with `getToolcraftControlOrderTargets(appSchema)` or an equivalent exact target-order check.
231
+
232
+ ## Persistence
233
+
234
+ State persistence is a product policy, not a hidden side effect.
235
+
236
+ Use `persistence: { storage: "localStorage", key, version, include }` when user-edited app settings should survive reload. Typical product editors persist `values`, `canvas`, and `panels`. If localStorage persistence is enabled and any runtime panel is visible, `include` must contain `"panels"` so dragged panel positions survive reload in that specific app. Add `timeline` when playback position, duration, loop, expansion, or keyframes should survive reload. Add `layers` only when the app has a real layer model.
237
+
238
+ Do not persist media blobs, files, generated images, or history stacks. Theme preference is runtime-owned separately. Do not write runtime state to `localStorage` directly from app code.
239
+
240
+ If `storage` is `"localStorage"`, add a runtime acceptance row with `persistenceCoverage: "reload"` and a browser test that changes a user-facing setting, reloads the page, and verifies the restored value or product output. Settings import/export is for preset transfer in complex apps; it is not proof that persistence works.
241
+
242
+ ## Settings Transfer
243
+
244
+ Use `settingsTransfer` when users should move a complex app setup between sessions or machines.
245
+
246
+ ```ts
247
+ settingsTransfer: "auto"
248
+ ```
249
+
250
+ Allowed values:
251
+
252
+ - `"auto"`: default. Runtime enables the first settings-transfer section when the app reaches the complexity threshold: 12 product controls, 5 product sections, or weighted score 18. Compound controls, layers, and timeline increase the weighted score.
253
+ - `true`: force the section on.
254
+ - `false`: force the section off.
255
+ - `{ enabled, appId, fileName }`: customize the exported JSON identity and file name.
256
+
257
+ When enabled, the runtime inserts a technical `Setup` settings-transfer section as the first controls-panel section. It renders without a visible section heading, exports and imports control values, `canvas.size`, and timeline state, ignores unknown targets on import, and pauses playback after importing.
258
+
259
+ After adding, removing, or reorganizing controls, sections, timeline, or layers, recalculate settings-transfer eligibility. If the threshold is reached, use `"auto"` / `true` or add an explicit `runtime.settingsTransfer` opt-out acceptance row with product evidence.
260
+
261
+ When settings transfer is enabled and the canvas uses `editable-output` sizing, the first technical `Setup` runtime section is mandatory and contains `Export Settings`, `Import Settings`, `Canvas width`, and `Canvas height` in that order. Do not split these into separate sections or recreate them manually.
262
+
263
+ A settings-transfer section with only `Export Settings` and `Import Settings` means the canvas is not `editable-output` or the app already declares its own `canvas.size.width` / `canvas.size.height` controls. For product-output apps, treat that as a schema decision to review.
264
+
265
+ Do not hand-write `settings-transfer.ts`, hidden file inputs, route handlers, or `panelActions` for settings import/export. Sticky footer `panelActions` remain product delivery only.
@@ -0,0 +1,87 @@
1
+ # Toolcraft Workflow
2
+
3
+ This file is the app-local routing layer for Toolcraft work. It does not replace the detailed contracts; it tells an agent which contract to read and which verification path to use before editing.
4
+
5
+ ## Required Preflight
6
+
7
+ Before planning or editing app code, runtime code, controls, canvas, panels, renderer, timeline, layers, export, or tests:
8
+
9
+ 1. Confirm the nearest `AGENTS.md` is the active project contract.
10
+ 2. Classify the project type:
11
+ - **Generated app**: use local `docs/toolcraft/*`.
12
+ - **Starter source**: use `starter/AGENTS.md`, local starter docs, and runtime contracts.
13
+ - **Runtime/template source**: use root `AGENTS.md` and runtime contracts.
14
+ 3. Classify the task type.
15
+ 4. Read the task-specific docs below.
16
+ 5. Choose and record a verification tier before implementation.
17
+
18
+ Do not edit implementation files until this preflight is complete.
19
+
20
+ ## Task Routing
21
+
22
+ Use the smallest reading set that covers the changed surface.
23
+
24
+ | Task type | Read before editing |
25
+ | --- | --- |
26
+ | App assembly, route structure, generated app porting | `assembly-workflow.md`, `decision-contract.md` |
27
+ | Schema, controls, defaults, persistence, actions | `schema-reference.md`, `component-rules.md`, `acceptance-testing.md` |
28
+ | Custom controls | `custom-controls.md`, `component-rules.md`, `acceptance-testing.md` |
29
+ | Renderer, canvas output, visual technique | `renderer-technique.md`, `performance.md`, `acceptance-testing.md` |
30
+ | Timeline, keyframes, animation transport | `decision-contract.md`, `component-rules.md`, `acceptance-testing.md`, `performance.md` |
31
+ | Layers | `decision-contract.md`, `component-rules.md`, `acceptance-testing.md` |
32
+ | Export, copy, media, background | `schema-reference.md`, `component-rules.md`, `acceptance-testing.md` |
33
+ | Broken control, visual mismatch, failed build, export bug, performance issue | `decision-contract.md`, the relevant component/runtime doc, and the failing test/log first |
34
+ | Figma implementation | Use Figma MCP/design context, then `assembly-workflow.md` and the relevant component docs |
35
+
36
+ ## Worklog Gate
37
+
38
+ For product app work, update `docs/toolcraft/agent-worklog.md` before reporting completion. Record:
39
+
40
+ - `Decision Trail` entries for each significant implementation pass, including:
41
+ - request;
42
+ - task type;
43
+ - user-visible result;
44
+ - source/reference checked;
45
+ - docs/contracts read;
46
+ - contract rules applied;
47
+ - decision;
48
+ - alternatives rejected;
49
+ - state/output mapping from controls, commands, timeline, layers, media, or renderer to the visible product;
50
+ - files changed;
51
+ - verification;
52
+ - skipped checks with reason;
53
+ - risks or follow-ups.
54
+ - updated high-level decisions for renderer, timeline, layers, controls, export, and performance when those choices change.
55
+
56
+ If the folder is still the neutral starter, do not invent product decisions. Once it becomes a product, switch the worklog to product mode and keep it concrete.
57
+
58
+ ## Runtime Boundary
59
+
60
+ Use the runtime extension points described in the current contracts:
61
+
62
+ - schema controls;
63
+ - `canvasContent` for product output only;
64
+ - `controlRenderers` only for true custom controls;
65
+ - `onPanelAction` for sticky product actions;
66
+ - runtime commands and hooks.
67
+
68
+ Do not recreate controls, panels, toolbar, timeline, layers, canvas shell, or runtime surfaces by hand. If a shared behavior is wrong, fix the shared runtime/template source and regenerate when needed.
69
+
70
+ ## Verification Gate
71
+
72
+ Choose the tier from `AGENTS.md` before editing. Use the tier to decide checks.
73
+
74
+ - Tier 0-1: targeted docs/typecheck/unit plus focused browser when visual.
75
+ - Tier 2: `pnpm verify:quick` plus relevant browser acceptance.
76
+ - Tier 3: `pnpm verify:quick`, targeted browser acceptance, and relevant `pnpm verify:perf` scenarios.
77
+ - Tier 4: `pnpm verify:final`, then start `pnpm dev` for the local URL.
78
+
79
+ Run a full performance checkpoint with `pnpm verify:perf` when:
80
+
81
+ - the first working version of the app exists;
82
+ - renderer, canvas, animation, export, timeline, or layers change;
83
+ - a bug that previously broke functionality is fixed;
84
+ - a performance optimization lands;
85
+ - the user asks to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
86
+
87
+ Fast feature loops may defer full performance only when none of these checkpoint triggers apply. Record the deferred check and reason in the worklog.