@pixel-point/toolcraft 0.0.6 → 0.0.8

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/generate.test.mjs +1 -1
  3. package/templates/runtime/contracts/component-contracts.test.ts +123 -15
  4. package/templates/runtime/contracts/component-contracts.ts +93 -19
  5. package/templates/runtime/contracts/decision-contracts.test.ts +3 -2
  6. package/templates/runtime/contracts/decision-contracts.ts +2 -2
  7. package/templates/runtime/export/export.test.ts +31 -0
  8. package/templates/runtime/export/export.ts +41 -0
  9. package/templates/runtime/react/canvas-shell.test.tsx +77 -1
  10. package/templates/runtime/react/canvas-shell.tsx +178 -23
  11. package/templates/runtime/react/control-conditions.ts +166 -0
  12. package/templates/runtime/react/controls-panel-filedrop-reorder.test.tsx +176 -0
  13. package/templates/runtime/react/controls-panel.test.tsx +590 -9
  14. package/templates/runtime/react/controls-panel.tsx +472 -10
  15. package/templates/runtime/react/media-file.ts +19 -0
  16. package/templates/runtime/schema/define-toolcraft.test.ts +2 -0
  17. package/templates/runtime/schema/define-toolcraft.ts +11 -3
  18. package/templates/runtime/schema/types.ts +27 -0
  19. package/templates/runtime/state/reducer.test.ts +357 -0
  20. package/templates/runtime/state/reducer.ts +195 -9
  21. package/templates/runtime/state/types.ts +12 -2
  22. package/templates/runtime/testing/performance.test.ts +1282 -48
  23. package/templates/runtime/testing/performance.ts +676 -39
  24. package/templates/starter/AGENTS.md +13 -12
  25. package/templates/starter/docs/toolcraft/README.md +4 -3
  26. package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -7
  27. package/templates/starter/docs/toolcraft/assembly-workflow.md +9 -7
  28. package/templates/starter/docs/toolcraft/component-rules.md +20 -7
  29. package/templates/starter/docs/toolcraft/custom-controls.md +9 -5
  30. package/templates/starter/docs/toolcraft/performance.md +51 -13
  31. package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
  32. package/templates/starter/docs/toolcraft/schema-reference.md +11 -6
  33. package/templates/starter/docs/toolcraft/workflow.md +7 -10
  34. package/templates/starter/e2e/app-performance.spec.ts +136 -3
  35. package/templates/starter/e2e/performance-helpers.ts +197 -0
  36. package/templates/starter/package.json +4 -1
  37. package/templates/starter/src/app/starter-acceptance.test.ts +529 -19
  38. package/templates/starter/src/app/starter-acceptance.ts +269 -12
  39. package/templates/starter/src/app/starter-performance.test.ts +67 -6
  40. package/templates/ui/components/controls/actions/actions-control.tsx +3 -5
  41. package/templates/ui/components/controls/collection-actions/collection-actions-control.tsx +60 -0
  42. package/templates/ui/components/controls/collection-actions/index.ts +4 -0
  43. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +340 -44
  44. package/templates/ui/components/controls/file-drop/index.ts +1 -1
  45. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -6
  46. package/templates/ui/components/controls/index.ts +9 -0
  47. package/templates/ui/components/panel/panel-section.tsx +53 -1
  48. package/templates/ui/index.ts +1 -0
@@ -24,11 +24,12 @@ Then follow `workflow.md` to choose the required contract docs and verification
24
24
  10. If a Figma URL is provided, inspect the Figma file through MCP and rebuild from its structure; never implement from a screenshot or by eye.
25
25
  11. Choose an explicit persistence policy; use schema `persistence` for user-edited app settings that should survive reload, and test real reload restoration when localStorage is enabled.
26
26
  12. Use schema `settingsTransfer: "auto"` for complex apps that need import/export of control settings; never implement settings import/export through `panelActions` or route-local file inputs. After adding, removing, or reorganizing controls, sections, timeline, or layers, recalculate settings-transfer eligibility. The runtime threshold is 12 product controls, 5 product sections, or weighted score 18. Visible `Aspect ratio`, `Canvas width`, and `Canvas height` controls are owned by `editable-output` canvas sizing, not by settings transfer. Runtime aspect presets apply canonical canvas sizes, with `16:9` equal to `1920x1080`; manual Canvas width/height edits keep the typed dimension, keep the other dimension unchanged, switch Aspect ratio to Custom, and show the reduced current ratio in custom ratio inputs; when no explicit product size is provided, the runtime default canvas size is also `1920x1080`. Non-vector raster, Canvas 2D, WebGL, and WebGPU previews set `canvas.renderScale: true`; the first technical runtime section then appends `Resolution scale` after canvas sizing so backing pixels can increase up to 2x without changing CSS/output size. Performance fixes must preserve the selected render scale and keep canvas preview responsive to sliders/high-frequency controls at that scale; diagnose the bottleneck before reducing quality. Do not pass budgets by silently downsampling, stretching a lower-resolution backing canvas, blurring output, or clamping `canvas.renderScale` below the user's chosen value. When settings transfer and editable-output canvas sizing are both enabled, the first technical `Setup` runtime section contains `Export Settings`, `Import Settings`, `Aspect ratio`, `Canvas width`, `Canvas height`, and, for raster outputs, `Resolution scale` in that order and renders without a visible section heading.
27
- 13. Product apps expose a required `Background` section directly before export settings. It contains a Switch labeled `Include` and a background color control with `label: false` in one equal-width inline row; PNG export wires those runtime values into the standard export helper while live preview, workspace canvas backing, and video export keep the background. Every app with `Export PNG` exposes `Image Export` with `export.image.format` and `export.image.resolution` as two `select` controls in one compact two-column inline row, and passes the selected resolution to `createToolcraftPngExportCanvas({ resolution })` so 2K/4K/8K change actual PNG dimensions. Animated apps with both PNG and video export place `Image Export` immediately before `Video Export`.
27
+ 13. Product apps expose a required `Background` section directly before export settings. It contains a Switch labeled `Include` and a background color control with `label: false` in one equal-width inline row; PNG export wires those runtime values into the standard export helper, live preview uses `shouldIncludeToolcraftPreviewBackground(state)` so Include can hide the product background, and video export keeps the background. Every app with `Export PNG` exposes `Image Export` with `export.image.format` and `export.image.resolution` as two `select` controls in one compact two-column inline row, and passes the selected resolution to `createToolcraftPngExportCanvas({ resolution })` so 2K/4K/8K change actual PNG dimensions. Animated apps with both PNG and video export place `Image Export` immediately before `Video Export`.
28
28
  14. Keep `docs/toolcraft/agent-worklog.md` current with a decision trail, product decisions, evidence, verification, and risks.
29
29
  15. Prove every visible entity through acceptance, browser, and performance coverage.
30
- 16. Workload performance scenarios must declare `stressFixture`; browser perf tests must use `getToolcraftPerformanceStressValue(appPerformance, scenarioId)` so heavy-case tests cannot use toy values.
31
- 17. Classify every implementation pass with a verification tier before editing. Use targeted checks for incremental edits and the full final gate only for final delivery, exports, or architecture/runtime/template changes.
30
+ 16. Workload performance scenarios must declare `stressFixture` for the tested control value; browser perf tests must use `getToolcraftPerformanceStressValue(appPerformance, scenarioId)` so heavy-case tests cannot use toy values. When the tested control is not itself the whole heavy source, declare `workloadFixture` and apply it first with `getToolcraftPerformanceWorkloadValue` or `applyToolcraftPerformanceWorkloadFixture`; this is the app baseline such as large media, long text, many items, or high render scale, and it must be paired with the measured `stressFixture`. Media import and image-processing workloads use `kind: "media"` fixtures at least `1920x1080`-equivalent, and heavy pixel/media Canvas 2D must evaluate WebGL/WebGPU with measured evidence before staying on CPU.
31
+ 17. Custom renderer apps declare a Render Pipeline Inventory in typed `rendererPipeline`: render passes, cache keys, execution location, preview/export quality, and interaction invalidation.
32
+ 18. Classify every implementation pass with a verification tier before editing. Use targeted checks for incremental edits and the full final gate only for final delivery, exports, or architecture/runtime/template changes.
32
33
 
33
34
  ## Starter Baseline
34
35
 
@@ -111,7 +112,7 @@ AI must work on this app through the required workflow skills when the environme
111
112
  - Before editing code from an approved spec, use `writing-plans` to produce a deterministic implementation plan focused on app files, tests, build, and browser verification.
112
113
  - Before fixing any broken control, failed test, build failure, visual mismatch, export issue, or runtime regression, use `systematic-debugging` to find the root cause first.
113
114
  - When the prompt includes a Figma URL, use Figma MCP/design context before implementation. Read the actual node, layer, component, variable, and asset structure; screenshots are only for final visual QA, not the source of truth.
114
- - After implementation, use the `browser` workflow or equivalent local browser verification to test the running app, not only typecheck/build output. The automated browser gates are `pnpm test:browser` and `pnpm test:browser:perf`.
115
+ - After implementation, use the `browser` workflow or equivalent local browser verification to test the running app, not only typecheck/build output. The default browser gate is `pnpm test:browser`; `pnpm test:browser:perf` is reserved for full performance checkpoints.
115
116
  - Run `pnpm ai:check` before app generation or major changes.
116
117
  - If a required skill is missing and the environment supports skill installation, install it before implementation and restart or refresh the session if the skill list does not update.
117
118
  - If skill installation is not available, stop before implementation and tell the user exactly which required skills are missing.
@@ -139,16 +140,16 @@ Choose the tier by blast radius, not by line count. If uncertain, move one tier
139
140
  | Tier 0 — docs/copy | Documentation, comments, copy, labels, or titles change without schema targets, values, runtime behavior, renderer output, or layout mechanics. | Targeted docs/typecheck or targeted app test. Browser is not required unless visual text fitting is the risk. |
140
141
  | Tier 1 — local control presentation | One control or panel visual state changes: spacing, hover, focus, disabled, marker visibility, label fit, or component variant display. Runtime state shape and product renderer are unchanged. | Targeted unit/component test plus one focused browser check for the affected control or panel. |
141
142
  | Tier 2 — schema/product behavior | Controls, sections, defaults, persistence, panel actions, export actions, acceptance rows, or product behavior mapping changes. | `pnpm verify:quick` plus relevant browser acceptance. Run perf only when the changed control affects renderer workload or responsiveness. |
142
- | Tier 3 — renderer/canvas/runtime feature | Custom renderer, animation loop, canvas sizing, upload/media, timeline, layers, toolbar, export bytes, WebGL/Canvas/SVG output, zoom, radar, history, heavy control behavior changes, or a post-generation iteration that touches renderer workload or viewport stability. | `pnpm verify:quick`, targeted browser acceptance, and relevant `pnpm verify:perf` scenarios for touched workload/viewport/export paths. |
143
- | Tier 4 — final delivery/template architecture | Fresh generated app completion, folder export, commit-ready delivery, dependency changes, runtime/template/contract/CLI changes, broad refactors, or major post-generation iterations that rewrite renderer, canvas, animation, timeline/keyframes, layers, media, export, or control mapping. | Fresh folders run `pnpm install` once, then `pnpm verify:final`, then start `pnpm dev` to provide the local URL. |
143
+ | Tier 3 — renderer/canvas/runtime feature | Custom renderer, animation loop, canvas sizing, upload/media, timeline, layers, toolbar, export bytes, WebGL/Canvas/SVG output, zoom, radar, history, heavy control behavior changes, or a post-generation iteration that touches renderer workload or viewport stability. | `pnpm verify:quick`, targeted browser acceptance, and targeted performance scenarios only for touched workload/viewport/export paths. |
144
+ | Tier 4 — final delivery/template architecture | Fresh generated app completion, folder export, commit-ready delivery, dependency changes, runtime/template/contract/CLI changes, broad refactors, or major post-generation iterations that rewrite renderer, canvas, animation, timeline/keyframes, layers, media, export, or control mapping. | Fresh folders run `pnpm install` once, then `pnpm verify:final`; add `pnpm verify:perf` only for the first working app version or explicit performance complaints, then start `pnpm dev` to provide the local URL. |
144
145
 
145
146
  Do not rerun `pnpm install` after every edit. Run it after fresh export, dependency changes, lockfile changes, or a missing package error.
146
147
 
147
- Do not run the full browser performance suite for Tier 0-2 edits unless a performance checkpoint trigger applies.
148
+ Do not run the full browser performance suite for Tier 0-2 edits.
148
149
 
149
- Run a full performance checkpoint with `pnpm verify:perf` when the first working version of an app exists, when renderer/canvas/animation/export/timeline/layers change, when `canvas.renderScale` or the `Resolution scale` retina slider is added/enabled, after fixing a bug that previously broke functionality, after any performance optimization, or when the user asks to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
150
+ Run a full performance checkpoint with `pnpm verify:perf` only when the first working version of an app exists, or when the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
150
151
 
151
- Fast feature loops may defer full performance only when none of the checkpoint triggers above apply. Record the deferred check and reason in the verification note or worklog.
152
+ Feature loops after the first working version do not run the full performance suite by default. Renderer, canvas, animation, export, timeline, layers, `canvas.renderScale`, bug fixes, and performance-sensitive controls still need targeted functional/browser checks first, plus targeted performance scenarios only when they directly exercise the touched workload/viewport/export path. Record any skipped full performance run and reason in the verification note or worklog.
152
153
 
153
154
  ## Required Checks
154
155
 
@@ -161,7 +162,7 @@ pnpm dev
161
162
 
162
163
  Use `pnpm install` before this final gate when the folder is fresh or dependencies changed.
163
164
 
164
- `pnpm test` must include `node scripts/check-toolcraft-docs.mjs`, `node scripts/check-toolcraft-integrity.mjs`, and app tests. `pnpm verify:ui` / `pnpm test:browser` must run against the real app UI and product output. `pnpm verify:perf` / `pnpm test:browser:perf` must run the performance browser suite sequentially so budgets are measured without parallel e2e noise.
165
+ `pnpm test` must include `node scripts/check-toolcraft-docs.mjs`, `node scripts/check-toolcraft-integrity.mjs`, and app tests. `pnpm verify:ui` / `pnpm test:browser` must run against the real app UI and product output. `pnpm verify:perf` / `pnpm test:browser:perf` remains available for the two full-performance triggers and must run the performance browser suite sequentially so budgets are measured without parallel e2e noise.
165
166
 
166
167
  Do not stop or kill existing local servers to free a port. `pnpm dev`, `pnpm preview`, and browser verification prefer port `3002`, but automatically move to the next free port when it is busy. Use `TOOLCRAFT_PORT`, `TOOLCRAFT_DEV_PORT`, or `TOOLCRAFT_TEST_PORT` only to change the preferred starting port.
167
168
 
@@ -176,7 +177,7 @@ The app is complete only when:
176
177
  - reset returns schema controls to `defaultValue`;
177
178
  - sticky footer export actions operate on final product output at `state.canvas.size`;
178
179
  - still products expose Export PNG; animated products expose Export Video plus Export PNG;
179
- - PNG export uses the required `Background` section with `Include` plus unlabeled background color runtime controls, while live preview, workspace canvas backing, and video keep background;
180
+ - PNG export uses the required `Background` section with `Include` plus unlabeled background color runtime controls, live preview hides product background when Include is off, and video keeps background;
180
181
  - every PNG export includes `Image Export` format/resolution `select` controls, and passes `export.image.resolution` into `createToolcraftPngExportCanvas`;
181
182
  - animated products with both PNG and video export place `Image Export` immediately before `Video Export`;
182
183
  - all export paths use retina output dimensions from the standard export helper;
@@ -184,5 +185,5 @@ The app is complete only when:
184
185
  - timeline is absent, playback, keyframes, or custom reference timeline according to product behavior;
185
186
  - performance checks cover workload and responsiveness for all relevant controls;
186
187
  - detail-heavy or animated custom renderers pass real viewport drag and zoom stress checks;
187
- - workload browser perf tests use the declared `stressFixture` value from `app-performance.ts`;
188
+ - workload browser perf tests use the declared `stressFixture` value from `app-performance.ts`, and apply `workloadFixture` first whenever the scenario declares an independent heavy app baseline;
188
189
  - browser tests verify upload/clear, controls, canvas sizing, toolbar, timeline/layers when enabled, sticky actions, output dimensions, and viewport stability.
@@ -26,15 +26,16 @@ Every implementation pass must choose a verification tier before editing. Use th
26
26
  | Tier 0 | Docs/copy only | targeted docs/typecheck |
27
27
  | Tier 1 | One control or panel visual state | targeted test + focused browser check |
28
28
  | Tier 2 | Schema, defaults, persistence, actions, product mapping | `pnpm verify:quick` + relevant browser acceptance |
29
- | Tier 3 | Renderer, canvas, timeline, layers, upload, export, zoom, heavy controls, or a performance checkpoint trigger | `pnpm verify:quick` + targeted browser/perf scenarios |
30
- | Tier 4 | Final delivery, fresh export, runtime/template/contract changes, broad renderer/product rewrites | `pnpm verify:final` |
29
+ | Tier 3 | Renderer, canvas, timeline, layers, upload, export, zoom, heavy controls, or a touched performance-sensitive path | `pnpm verify:quick` + targeted browser checks, plus targeted perf scenarios only for the touched path |
30
+ | Tier 4 | Final delivery, fresh export, runtime/template/contract changes, broad renderer/product rewrites | `pnpm verify:final`; add `pnpm verify:perf` only for the first working app version or explicit performance complaints |
31
31
 
32
- Run `pnpm verify:perf` when a performance checkpoint is triggered: first working app version, renderer/canvas/animation/export/timeline/layers changes, adding/enabling `canvas.renderScale` or the `Resolution scale` retina slider, a fix for previously broken functionality, any performance optimization, or a user request to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
32
+ Run the full `pnpm verify:perf` suite only when the first working app version exists, or when the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
33
33
 
34
34
  Fresh folders or dependency changes need `pnpm install` before verification. Final delivery still starts the local app after the gate:
35
35
 
36
36
  ```bash
37
37
  pnpm verify:final
38
+ pnpm verify:perf # first working version or explicit performance complaint only
38
39
  pnpm dev
39
40
  ```
40
41
 
@@ -14,9 +14,9 @@ Every visible product entity must prove it works. A control is not accepted beca
14
14
  - `e2e/app-performance.spec.ts`
15
15
  - `e2e/product-observable-helpers.ts`
16
16
 
17
- `pnpm verify:final` must pass before final delivery. Incremental edits use the verification tier classifier from `assembly-workflow.md`: run targeted browser acceptance for the changed entity, and add `pnpm verify:perf` whenever a performance checkpoint is triggered.
17
+ `pnpm verify:final` must pass before final delivery. Incremental edits use the verification tier classifier from `assembly-workflow.md`: run targeted browser acceptance for the changed entity, and add full `pnpm verify:perf` only for the first working app version or an explicit performance complaint.
18
18
 
19
- A performance checkpoint is triggered by the first working app version, renderer/canvas/animation/export/timeline/layers changes, adding/enabling `canvas.renderScale` or the `Resolution scale` retina slider, a fix for previously broken functionality, any performance optimization, or a user request to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
19
+ A full performance checkpoint is triggered only by the first working app version, or by a user request to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise investigate poor performance.
20
20
 
21
21
  ## Product Readiness
22
22
 
@@ -72,6 +72,7 @@ Required parts:
72
72
  | --- | --- |
73
73
  | `anchorGrid` | `anchorGrid.position` |
74
74
  | `channelMixer` | `channelMixer.activeChannel`, `channelMixer.values`; only for RGB channel matrix behavior |
75
+ | `collectionActions` | `collectionActions.add`, `collectionActions.remove`, `collectionActions.items` |
75
76
  | `colorOpacity` | `colorOpacity.hex`, `colorOpacity.opacity` |
76
77
  | `curves` | RGB variant: `curves.activeChannel`, `curves.points`; `variant: "single"`: `curves.points` |
77
78
  | `fontPicker` | `fontPicker.fontId`, `fontPicker.fontWeight`, `fontPicker.fontSize`, `fontPicker.letterSpacing`, `fontPicker.lineHeight`, `fontPicker.textCase`, `fontPicker.color`, `fontPicker.opacity` |
@@ -97,6 +98,7 @@ High-confidence wrong-substitution cases:
97
98
  - typography without `fontPicker`;
98
99
  - sibling typography controls that split case, color, opacity, size, weight, letter spacing, or line height away from `fontPicker`;
99
100
  - color plus opacity without `colorOpacity`;
101
+ - repeatable user-editable item sets without `collectionActions` or another justified collection owner;
100
102
  - from/to range without `rangeSlider` or `rangeInput`;
101
103
  - curve, remap, easing, or response without `curves`;
102
104
  - position, direction, focus, or vector without `vector`;
@@ -105,21 +107,27 @@ High-confidence wrong-substitution cases:
105
107
  - segmented choices that clip instead of falling back to `select`;
106
108
  - custom controls recreating built-ins.
107
109
 
110
+ `fileDrop` media-lifecycle rows must prove upload/import, clear/remove, thumbnail reorder for `multiple: true`, and global or section reset. A test that only clicks the clear button is not enough because Reset controls must also return uploaded source material to `defaultValue`.
111
+
108
112
  Rows that use custom controls must include `customControlCoverage` and typed `builtInFitCheck`.
109
113
 
110
114
  ```ts
111
115
  builtInFitCheck: {
112
- checkedBuiltIns: ["fileDrop", "imagePicker", "select"],
116
+ checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
113
117
  closestBuiltIn: "fileDrop",
114
118
  whyInsufficient:
115
- "FileDrop imports source files, but it does not provide ordering, preview, remove, and density mapping in one runtime value.",
119
+ "FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
116
120
  productObservable:
117
- "Ordering uploaded glyphs changes the rendered glyph ramp output.",
121
+ "Changing a glyph density threshold changes which uploaded glyph renders for the same depth-map tone.",
118
122
  }
119
123
  ```
120
124
 
121
125
  The fit check names real checked built-ins, the closest built-in or `"none"`, why it is insufficient, and the product-observable evidence that proves the custom control works.
122
126
 
127
+ For collection-like custom controls, the fit check must include `collectionActions` and `actions`. Collection-like is decided from the runtime value model and workflow: arrays, `{ items: [...] }` objects, selected-item state, grow/shrink item sets, ordering, add, remove, delete, or reorder behavior. Acceptance should fail if the row compares only unrelated built-ins such as `vector` or `select` while the actual value model is a collection.
128
+
129
+ Custom controls cannot be justified by icons, layout, styling, compactness, or custom buttons alone. `whyInsufficient` must name the product interaction or value model that built-ins cannot express.
130
+
123
131
  ## Valid Evidence
124
132
 
125
133
  Valid acceptance evidence includes:
@@ -146,7 +154,9 @@ Footer action acceptance must not include Reset. Reset is already available in t
146
154
 
147
155
  Local `actions` acceptance must click every visible action and prove the nearby entity changed through runtime state or product output. A section-level `Randomize palette` must change palette output, `Normalize weights` must change weights/output, and `Clear selection` must clear only the scoped selection. Do not accept a test that only proves the button rendered.
148
156
 
149
- PNG export tests must prove runtime background behavior: changing the background color affects preview/export, turning `export.includeBackground` off creates transparent PNG output while live preview, workspace canvas backing, and video keep the background, turning it on includes the current background color in PNG, and exported pixel dimensions are retina size, at least `state.canvas.size * 2`.
157
+ `collectionActions` acceptance must click plus and minus in the real panel, prove the runtime target array length changes, prove `minItems` prevents invalid removal, prove `recommendedMaxItems` is not a hidden hard limit, and prove preview/export consumes the changed item list.
158
+
159
+ PNG export tests must prove runtime background behavior: changing the background color affects preview/export, turning `export.includeBackground` off hides the live preview product background and creates transparent PNG output, video export still keeps the background, turning Include on includes the current background color in PNG, and exported pixel dimensions are retina size, at least `state.canvas.size * 2`.
150
160
 
151
161
  Invalid final acceptance evidence:
152
162
 
@@ -198,7 +208,7 @@ Component variants are acceptance requirements.
198
208
  - Select, segmented, and image-picker controls should cover every visible option unless options come from separately tested runtime data.
199
209
  - Custom controls must declare `customControlCoverage` and `builtInFitCheck`. Coverage proves the custom control is not a built-in replacement, uses kit chrome, keeps only necessary UI, writes through runtime state, and changes product output; the fit check proves which built-ins were considered and why the custom interaction is necessary.
200
210
 
201
- Performance browser tests must assert budgets through `expectToolcraftScenarioPerformanceBudget(..., appPerformance, scenarioId)`. Workload browser tests must apply values from `getToolcraftPerformanceStressValue(appPerformance, scenarioId)`. Do not hardcode budget numbers or toy workload values in e2e tests; `app-performance.ts` is the single source of truth.
211
+ Performance browser tests must assert budgets through `expectToolcraftScenarioPerformanceBudget(..., appPerformance, scenarioId)`. Workload browser tests must apply values from `getToolcraftPerformanceStressValue(appPerformance, scenarioId)`. If the scenario declares `workloadFixture`, apply it first with `getToolcraftPerformanceWorkloadValue` or `applyToolcraftPerformanceWorkloadFixture`. Do not hardcode budget numbers, toy control values, or toy baseline app states in e2e tests; `app-performance.ts` is the single source of truth.
202
212
 
203
213
  ## Fixtures
204
214
 
@@ -69,6 +69,8 @@ If a color, slider, input, or selector edits the same entity as nearby controls,
69
69
 
70
70
  Before choosing the concrete control type for each target, check `component-rules.md` and `schema-reference.md`. Built-in compound controls must stay compound: for example, typography with font choice, weight, size, color/opacity, and text rhythm uses `fontPicker`, not a plain `select` plus separate inputs/sliders. The product renderer and acceptance rows must cover every semantic value part of the chosen component.
71
71
 
72
+ For custom renderers, write the Renderer Technique Decision Matrix and Render Pipeline Inventory before code. The implementation plan must map every performance-sensitive control to the pass it invalidates.
73
+
72
74
  ## Figma Source
73
75
 
74
76
  When the prompt provides a Figma URL, treat the Figma file as the design source of truth.
@@ -136,7 +138,7 @@ Every product app exposes output background controls:
136
138
  - `appearance.background` or `scene.background` as a schema `color` control;
137
139
  - `export.includeBackground` as a `switch`, `checkbox`, `select`, or `segmented` control.
138
140
 
139
- Preview, PNG export, and video export read the background color runtime value. PNG export passes the include-background runtime value to the export helper. Turning `export.includeBackground` off makes only PNG output transparent; live preview, workspace canvas backing, and video output keep the background.
141
+ Preview, PNG export, and video export read the background color runtime value. PNG export passes the include-background runtime value to the export helper. Live preview calls `shouldIncludeToolcraftPreviewBackground(state)` and hides only the product-rendered background when Include is off; the Toolcraft canvas backing stays visible. Video output keeps the background.
140
142
 
141
143
  Keep those controls together in one required `Background` section directly before the first export settings section. With PNG export that first settings section is `Image Export`; with video-only export it is `Video Export`. Use an equal-width inline row with `export.includeBackground` on the left and the background color parameter on the right; each control occupies half the row. The switch label is `Include`; the color control uses `label: false` because the section title already supplies the background context.
142
144
 
@@ -168,7 +170,7 @@ Animated apps with `Export Video` must include a separate `Video Export` control
168
170
 
169
171
  Place `Video Export` as the final authored controls section directly above sticky footer export buttons. Treat `Format` and `Resolution` as a compact semantic pair and put them in one two-column inline row by default. Use vertical rows only when the compact row would clip labels or selected values, and record that fallback reason in the worklog.
170
172
 
171
- Use standard export helpers. `createToolcraftPngExportCanvas` accepts `includeBackground` for runtime PNG transparency and `resolution` for image-export output size. Pass the selected `export.image.resolution` into the PNG helper so 2K/4K/8K produce actual 2048/4096/8192px long-edge PNGs. Do not rely on static `export.png.background` alone when the UI exposes background controls. Video export keeps background and still uses `getToolcraftRetinaExportSize`.
173
+ Use standard export helpers. `createToolcraftPngExportCanvas` accepts `includeBackground` for runtime PNG transparency and `resolution` for image-export output size. `shouldIncludeToolcraftPreviewBackground(state)` controls live preview product-background visibility. Pass the selected `export.image.resolution` into the PNG helper so 2K/4K/8K produce actual 2048/4096/8192px long-edge PNGs. Do not rely on static `export.png.background` alone when the UI exposes background controls. Video export keeps background and still uses `getToolcraftRetinaExportSize`.
172
174
 
173
175
  Video export must choose the actual MIME/container with `MediaRecorder.isTypeSupported(...)` or an explicit encoder/transcoder capability check. `MOV` and `ProRes` are allowed only when the app provides a custom encoder/transcoder and proves it with acceptance plus performance coverage. Treat `4K` as an export resolution target, not a hardcoded canvas lock. Offline rendered-frame export must encode or mux frame timestamps from runtime timeline time; real-time `canvas.captureStream()` plus `MediaRecorder` records wall-clock export time and is not enough when renderer work can be slower than playback. Browser acceptance must load the exported blob as a video, wait for metadata, and compare `video.duration` with the edited timeline duration; `blobSize > 0`, `blobType`, parser fallback, or assigning the expected duration in `catch` is not enough.
174
176
 
@@ -190,8 +192,8 @@ Use these tiers:
190
192
  | Tier 0 — docs/copy | Documentation, comments, copy, labels, or titles change without schema targets, values, runtime behavior, renderer output, or layout mechanics. | Targeted docs/typecheck or targeted app test. Browser is not required unless visual text fitting is the risk. |
191
193
  | Tier 1 — local control presentation | One control or panel visual state changes: spacing, hover, focus, disabled, marker visibility, label fit, or component variant display. Runtime state shape and product renderer are unchanged. | Targeted unit/component test plus one focused browser check for the affected control or panel. |
192
194
  | Tier 2 — schema/product behavior | Controls, sections, defaults, persistence, panel actions, export actions, acceptance rows, or product behavior mapping changes. | `pnpm verify:quick` plus relevant browser acceptance. Run perf only when the changed control affects renderer workload or responsiveness. |
193
- | Tier 3 — renderer/canvas/runtime feature | Custom renderer, animation loop, canvas sizing, upload/media, timeline, layers, toolbar, export bytes, WebGL/Canvas/SVG output, zoom, radar, history, heavy control behavior changes, or a post-generation iteration that touches renderer workload or viewport stability. | `pnpm verify:quick`, targeted browser acceptance, and relevant `pnpm verify:perf` scenarios for touched workload/viewport/export paths. |
194
- | Tier 4 — final delivery/template architecture | Fresh generated app completion, folder export, commit-ready delivery, dependency changes, runtime/template/contract/CLI changes, broad refactors, or major post-generation iterations that rewrite renderer, canvas, animation, timeline/keyframes, layers, media, export, or control mapping. | Fresh folders run `pnpm install` once, then `pnpm verify:final`, then start `pnpm dev` to provide the local URL. |
195
+ | Tier 3 — renderer/canvas/runtime feature | Custom renderer, animation loop, canvas sizing, upload/media, timeline, layers, toolbar, export bytes, WebGL/Canvas/SVG output, zoom, radar, history, heavy control behavior changes, or a post-generation iteration that touches renderer workload or viewport stability. | `pnpm verify:quick`, targeted browser acceptance, and targeted performance scenarios only for touched workload/viewport/export paths. |
196
+ | Tier 4 — final delivery/template architecture | Fresh generated app completion, folder export, commit-ready delivery, dependency changes, runtime/template/contract/CLI changes, broad refactors, or major post-generation iterations that rewrite renderer, canvas, animation, timeline/keyframes, layers, media, export, or control mapping. | Fresh folders run `pnpm install` once, then `pnpm verify:final`; add `pnpm verify:perf` only for the first working app version or explicit performance complaints, then start `pnpm dev` to provide the local URL. |
195
197
 
196
198
  Choose the tier by blast radius, not by line count. If uncertain, move one tier higher, not automatically to Tier 4.
197
199
 
@@ -199,9 +201,9 @@ Do not rerun `pnpm install` after every edit. Run it after fresh export, depende
199
201
 
200
202
  Use `pnpm verify:ui` when a tier calls for the browser acceptance suite without the performance suite. Use a focused named Playwright test instead when only one entity changed and the relevant test is already known.
201
203
 
202
- Run a full performance checkpoint with `pnpm verify:perf` when the first working version of the app exists, when renderer/canvas/animation/export/timeline/layers change, when `canvas.renderScale` or the `Resolution scale` retina slider is added/enabled, after fixing a bug that previously broke functionality, after any performance optimization, or when the user asks to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
204
+ Run a full performance checkpoint with `pnpm verify:perf` only when the first working version of the app exists, or when the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
203
205
 
204
- Fast feature loops may defer full performance only when none of the checkpoint triggers apply. Record the deferred check and reason in the worklog.
206
+ Feature loops after the first working version do not run the full performance suite by default. Renderer, canvas, animation, export, timeline, layers, `canvas.renderScale`, bug fixes, and performance-sensitive controls still need targeted functional/browser checks first, plus targeted performance scenarios only when they directly exercise the touched path. Record any skipped full performance run and reason in the worklog.
205
207
 
206
208
  For final delivery, run:
207
209
 
@@ -210,6 +212,6 @@ pnpm verify:final
210
212
  pnpm dev
211
213
  ```
212
214
 
213
- Browser verification must use the real Toolcraft shell plus renderer output. `pnpm verify:final` runs the full static, build, browser, and browser performance gate. `pnpm dev` is intentionally separate because it keeps the local server running.
215
+ Browser verification must use the real Toolcraft shell plus renderer output. `pnpm verify:final` runs the full static, build, and browser functional gate. `pnpm verify:perf` is intentionally separate and only runs for the two full-performance triggers. `pnpm dev` is intentionally separate because it keeps the local server running.
214
216
 
215
217
  Do not stop existing local servers to free `3002`. `pnpm dev`, `pnpm preview`, and browser verification prefer `3002`, then automatically use the next free port when it is occupied.
@@ -22,6 +22,8 @@ Common exact-owner choices:
22
22
  - Use `imagePicker` for choosing one visual option from a set.
23
23
  - Use `palette` only for constrained design-token color choices with both family and shade: brand palette, Tailwind-like token color, style-guide color scale, semantic palette family, or theme accent token.
24
24
  - Use `actions` for local section commands that affect only the nearby entity, such as randomize palette, normalize weights, sort glyphs, clear selection, duplicate item, or reset current stop.
25
+ - Use `collectionActions` for repeatable product entities whose actual item list can grow or shrink, such as colors, glyphs, symbols, points, rules, variants, object entries, or typography style entries. Use it instead of a count slider when the user edits the actual set. The item list must be runtime state that changes preview/export, not panel-only row chrome. The collection control shows the collection `label` on the left and remove/add icon buttons on the right. Homogeneous repeated items do not show visible per-item labels like `Color 1`, `Color 2`, `Item 1`, or `Item 2` when the collection label already names the group. Item controls should use built-ins such as `color`, `colorOpacity`, `text`, `select`, `segmented`, `slider`, `switch`, `checkbox`, `rangeInput`, or `fontPicker` before any custom renderer. Use `fontPicker` as the item control when each item is a text style or typography entity; do not split its owned fields into neighboring collection controls.
26
+ - `actions` buttons stay beside the label when they fit. If the button group wraps to a second row, the wrapped row starts from the left edge of the control content; do not center it or keep it right-aligned.
25
27
  - Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
26
28
 
27
29
  Small action buttons inside custom controls are for item-level actions such as remove, reorder, add stop, or delete stop. Use schema `actions` for section-level local commands. Keep final product actions in `panelActions`, keep timeline transport in the top timeline, and keep global reset in the controls panel header.
@@ -35,6 +37,7 @@ For local reset-like `actions`, use product-specific values such as `reset-curre
35
37
  - If a section contains exactly one control, whether simple or compound, render only the parent section dividers.
36
38
  - Do not add full-width borders inside a compound control, and do not put dividers only around an internal subsection such as Gradient Stops.
37
39
  - Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers.
40
+ - `collectionActions` is a compound control when it shares a section with generated item controls, so it follows the same content-width divider rules. Place it at the start of the controlled section. Generated item controls still follow normal density rules: plain colors use equal 50% columns when they fit, while color+opacity items stay stacked.
38
41
 
39
42
  ## Sliders
40
43
 
@@ -110,6 +113,8 @@ Section expand/collapse uses the standard runtime height/opacity animation. Do n
110
113
 
111
114
  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.
112
115
 
116
+ Ordinary section headers expose the runtime section reset action before the collapse button. It dispatches `controls.resetTargets` and restores only that section's control targets to their schema `defaultValue`.
117
+
113
118
  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.
114
119
 
115
120
  ## Colors
@@ -120,25 +125,31 @@ Keep color inside a section when it configures the same entity as nearby control
120
125
 
121
126
  Standalone color section titles must describe product role. Never generate a section titled `Color` or `Colors`. If no meaningful role exists, use a neutral title such as `Appearance` instead of omitting the title.
122
127
 
123
- Show visible field labels for `color` and `colorOpacity` controls when the section also contains any non-color controls. Omit visible color labels only when the whole section is made of color controls.
128
+ Decide color label visibility from the user's point of view and apply that decision to the whole semantic group. Omit per-item labels such as `Color 1`, `Color 2`, or `Color 3` when the colors only add variety to one shared palette/color bank such as `Accent Shades`, `Bead Colors`, or `palette.accent1..5`, even if sibling controls like `Spread`, `Mix`, or `Randomness` tune distribution. Do not mix labeled and unlabeled items inside one semantic color bank. Keep visible labels when each color edits a distinct user-facing entity or role, such as `Fill`, `Stroke`, `Background`, `Connector`, `Object`, or `Highlight`.
124
129
 
125
- Multiple related plain colors stay in the same section and render at most two per row. If any color control has opacity, keep it stacked instead of placing it in a two-column row.
130
+ Multiple related plain colors stay in the same section and render at most two per row. If the bank has an odd trailing plain `color`, the last color still keeps the same half-width footprint instead of stretching to a full row. If any color control has opacity, keep it stacked instead of placing it in a two-column row.
126
131
 
127
132
  Use `colorOpacity` when one product entity owns both color and opacity, such as text color, shadow color, glow color, overlay color, or stroke color. Do not split that into a separate `color` plus opacity slider/input.
128
133
 
129
134
  When one short numeric/text field and one plain `color` field configure the same entity, they can share a two-column inline row. Example: `Mask size` and `Color` belong in the same `Mask` row instead of two stacked rows. Do not put `colorOpacity` in inline rows.
130
135
 
131
- Mixed inline rows require label parity: every field in that row has a visible label. The required `Background` section row is the only section-title-owned exception: use the switch label `Include` beside the background color parameter with `label: false`. Color fields in other mixed rows must not be unlabeled.
136
+ Mixed inline rows require label parity: every field in that row has a visible label. The required `Background` section row is the section-title-owned exception: use the switch label `Include` beside the background color parameter with `label: false`. Palette variation color banks are the other exception when the group or section label already names the color bank.
132
137
 
133
- Renderer-owned output background is a base product control. Use a schema `color` target such as `appearance.background` or `scene.background`, add an `export.includeBackground` control for PNG transparency, and make preview/export read those runtime values. Keep them in one required `Background` section directly before the first export settings section. With PNG export, that first section is `Image Export`; with video-only export, it is `Video Export`. Use one equal-width inline row with `export.includeBackground` on the left and `appearance.background` on the right when no other fit rule is violated. The switch label is `Include`, not `Include background`; the background color control uses `label: false`. Each control occupies one half of the row; do not shrink the toggle column to intrinsic width. `export.includeBackground` controls only PNG alpha; it must not make live preview, workspace canvas backing, or video output transparent. Do not hardcode a configurable background in CSS, Canvas `fillStyle`, or WebGL clear color.
138
+ Renderer-owned output background is a base product control. Use a schema `color` target such as `appearance.background` or `scene.background`, add an `export.includeBackground` control for PNG transparency, and make preview/export read those runtime values. Keep them in one required `Background` section directly before the first export settings section. With PNG export, that first section is `Image Export`; with video-only export, it is `Video Export`. Use one equal-width inline row with `export.includeBackground` on the left and `appearance.background` on the right when no other fit rule is violated. The switch label is `Include`, not `Include background`; the background color control uses `label: false`. Each control occupies one half of the row; do not shrink the toggle column to intrinsic width. `export.includeBackground` controls PNG alpha and live preview product-background visibility through `shouldIncludeToolcraftPreviewBackground(state)`; it must not make the Toolcraft canvas shell/backing or video output transparent. Do not hardcode a configurable background in CSS, Canvas `fillStyle`, or WebGL clear color.
134
139
 
135
140
  ## File Upload
136
141
 
137
142
  Use `fileDrop` for source material uploads in the controls panel. Do not place upload UI on the canvas.
138
143
 
139
- In single-layer apps, the runtime shows uploaded image preview and clear button in the file control. Clearing removes source material from the renderer and canvas.
144
+ Use `assetKind: "image"` for image-only source uploads and `assetKind: "file"` for arbitrary uploaded files. Image mode accepts images only by default. File mode accepts any file by default unless `accept` narrows the allowed extensions or MIME types.
145
+
146
+ In single-layer apps, the runtime shows uploaded image preview and clear button in the file control. Clearing, global Reset controls, and section reset remove source material from the renderer and canvas and return the fileDrop target to `defaultValue`.
147
+
148
+ Use `multiple: true` when the app needs several uploaded images as one source set. The runtime appends media, switches to a sortable four-column thumbnail grid when more than one image is present, puts the add-more tile last, and keeps per-image removal inside the file control. Dragging thumbnails updates runtime media order; preview, export, and renderer mapping must consume that order instead of keeping a separate product-only order.
140
149
 
141
- Use `multiple: true` when the app needs several uploaded images as one source set. The runtime appends media, switches to a four-column thumbnail grid when more than one image is present, puts the add-more tile last, and keeps per-image removal inside the file control.
150
+ In file mode, uploaded files render as a sortable list with a paperclip icon, filename, remove button, and `--border/5` separators. Do not build custom file lists, custom upload buttons, or custom sorting for generic source files when `fileDrop` can represent the source set.
151
+
152
+ When an app contains both image and file uploaders, canvas drops route by asset kind. Image files prefer visible image uploaders; non-image files prefer visible file uploaders; file uploaders may accept images only when no image uploader matches. Product renderers must consume `state.mediaAssets` filtered by `sourceTarget` and runtime media order.
142
153
 
143
154
  In multi-layer apps, deletion and visibility belong to the Layers panel; `fileDrop` stays an upload target.
144
155
 
@@ -160,6 +171,8 @@ Use `fontPicker` for typography choices that need font preview plus weight, size
160
171
 
161
172
  The value is one object: `{ fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`. Typography renderers and exports must consume all eight parts.
162
173
 
174
+ The standard/default text color is `#FFFFFF` with opacity `100`. Omit `color`/`opacity` or use those values unless the prompt or reference explicitly requires a different initial text color.
175
+
163
176
  If `fontPicker` controls product text, the preview renderer and export renderer must apply the selected `fontId`, `fontWeight`, `fontSize`, `letterSpacing`, `lineHeight`, `textCase`, `color`, and `opacity` to that actual text. Do not stop at updating runtime state, the select label, or the popup preview.
164
177
 
165
178
  The component owns search, category filters, virtualized scrolling, font preview loading, selected-row behavior, the font-weight select, the font-size input, the text-case select, the color/opacity control, and the two footer sliders. Browser acceptance must choose a different font, change weight, change size, change text case, change color/opacity, move Letter spacing, and move Line height.
@@ -281,7 +294,7 @@ If only `Export Settings` and `Import Settings` appear in that section, the sche
281
294
 
282
295
  Manual `Canvas width` or `Canvas height` edits are exact output-size edits. They keep the other dimension unchanged, switch `Aspect ratio` to `Custom`, and update the custom ratio inputs to the reduced current ratio. Do not recreate the old behavior where typing one size field stays locked to the previous aspect preset.
283
296
 
284
- Enable `canvas.renderScale: true` for non-vector raster previews such as Canvas 2D, WebGL, or WebGPU output. Runtime adds a `Resolution scale` slider after canvas sizing; it defaults to `2x` and lets users trade preview quality/performance without changing output size. Adding or enabling this slider requires a full `pnpm verify:perf` checkpoint. Performance fixes must preserve the selected scale and keep canvas preview responsive while dragging sliders or other high-frequency controls. Diagnose the actual bottleneck before lowering quality; do not silently downsample, stretch a lower-resolution backing canvas, blur output, or clamp `canvas.renderScale` below the user's chosen value. Do not enable it for DOM/SVG/vector-native previews.
297
+ Enable `canvas.renderScale: true` for non-vector raster previews such as Canvas 2D, WebGL, or WebGPU output. Runtime adds a `Resolution scale` slider after canvas sizing; it defaults to `2x` and lets users trade preview quality/performance without changing output size. Adding or enabling this slider requires targeted browser evidence that the canvas stays responsive while dragging sliders or other high-frequency controls at the selected scale. Full `pnpm verify:perf` is required only for the first working app version or explicit performance complaints. Performance fixes must preserve the selected scale and keep canvas preview responsive. Diagnose the actual bottleneck before lowering quality; do not silently downsample, stretch a lower-resolution backing canvas, blur output, or clamp `canvas.renderScale` below the user's chosen value. Do not enable it for DOM/SVG/vector-native previews.
285
298
 
286
299
  Reset belongs to the controls panel header reset button. Do not add a footer action with `label`, `value`, or `command` containing reset; acceptance treats that as a duplicate Reset.
287
300
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  Use a custom control only when no built-in Toolcraft control represents the product interaction.
4
4
 
5
- Built-ins come first: `slider`, `rangeSlider`, `select`, `segmented`, `switch`, `checkbox`, `color`, `colorOpacity`, `vector`, `gradient`, `curves`, `fontPicker`, `imagePicker`, `fileDrop`, `text`, `code`, `rangeInput`, `palette`, `actions`, and `panelActions`.
5
+ Built-ins come first: `slider`, `rangeSlider`, `select`, `segmented`, `switch`, `checkbox`, `color`, `colorOpacity`, `vector`, `gradient`, `curves`, `fontPicker`, `imagePicker`, `fileDrop`, `text`, `code`, `rangeInput`, `palette`, `actions`, `collectionActions`, and `panelActions`.
6
6
 
7
7
  Register custom renderers through `ToolcraftApp controlRenderers`.
8
8
 
9
- Do not use `controlRenderers` to recreate a built-in control. If the product needs a slider, select, segmented mode picker, color input, gradient editor, font picker, upload, textarea, local action group, or footer action, declare the matching schema control instead of rendering the component manually.
9
+ Do not use `controlRenderers` to recreate a built-in control. If the product needs a slider, select, segmented mode picker, color input, gradient editor, font picker, image upload, arbitrary file upload, textarea, local action group, repeatable item add/remove, or footer action, declare the matching schema control instead of rendering the component manually.
10
10
 
11
11
  Do not edit `ControlsPanel`, copied `src/toolcraft`, or Toolcraft internals inside a generated app.
12
12
 
@@ -31,17 +31,21 @@ Custom control schemas still need:
31
31
 
32
32
  ```ts
33
33
  builtInFitCheck: {
34
- checkedBuiltIns: ["fileDrop", "imagePicker", "select"],
34
+ checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
35
35
  closestBuiltIn: "fileDrop",
36
36
  whyInsufficient:
37
- "FileDrop imports source files, but it does not provide ordering, preview, remove, and density mapping in one runtime value.",
37
+ "FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
38
38
  productObservable:
39
- "Ordering uploaded glyphs changes the rendered glyph ramp output.",
39
+ "Changing a glyph density threshold changes which uploaded glyph renders for the same depth-map tone.",
40
40
  }
41
41
  ```
42
42
 
43
43
  `checkedBuiltIns` must name real Toolcraft built-in controls. `closestBuiltIn` must be one of those checked controls or `"none"` when no built-in is meaningfully close. `whyInsufficient` explains the missing interaction. `productObservable` names the output or side effect that proves the custom control is necessary.
44
44
 
45
+ If the custom control owns a growable, removable, selectable, or reorderable runtime item set, `checkedBuiltIns` must include both `collectionActions` and `actions`. Decide this from the value model and workflow, such as arrays, `{ items: [...] }` objects, selected-item state, or add/remove/reorder behavior, not from entity names like masks or glyphs. This applies even when the empty state visually looks like a few icon buttons: the fit check must prove why `collectionActions` cannot own the runtime list and why `actions` alone cannot represent the collection state.
46
+
47
+ Do not justify a custom control with icons, layout, styling, compactness, or custom buttons alone. If the built-in control has the right value model and mechanics, use it or improve that built-in instead.
48
+
45
49
  ## State Rules
46
50
 
47
51
  Custom renderers must write through the provided `setValue(nextValue, meta)` callback or existing runtime commands.
@@ -25,7 +25,11 @@ Use workload coverage for controls that change rendering cost:
25
25
 
26
26
  Sensitive controls need min/default/max scenarios, `stressFixture`, and real product-output checks.
27
27
 
28
- `stressFixture` is the machine-checkable heavy case for a workload scenario. It must include:
28
+ `stressFixture` is the machine-checkable value for the scenario itself. For a slider drag, it is the exact slider value to apply. For a text input, it can be the long text value. For preview, zoom, animation, or export stress scenarios, it can be a combined object state.
29
+
30
+ When the scenario control is not itself the whole heavy source, add `workloadFixture` as the independent heavy baseline that must be applied before measuring. Examples: a large uploaded image before dragging an effect slider, long text before dragging a density slider, many items before dragging spacing, or `renderScale: 2` before checking high-frequency controls. `workloadFixture` must be paired with `stressFixture`: first apply the app baseline, then apply the measured scenario value. Do not encode this as a named-control rule; decide from the renderer pipeline and product workload.
31
+
32
+ Both fixture types must include:
29
33
 
30
34
  - `kind`: `large-text`, `large-canvas`, `high-density`, `many-items`, `max-value`, `media`, or `custom`;
31
35
  - `reason`: why this value is the heaviest useful product case;
@@ -33,15 +37,38 @@ Sensitive controls need min/default/max scenarios, `stressFixture`, and real pro
33
37
 
34
38
  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
39
 
40
+ For uploaded images, videos, source media, and image-processing workloads, use `kind: "media"` with a `value` object containing numeric `width` and `height`. The fixture must be at least `1920x1080`-equivalent, and products that claim high-resolution media or export quality should test a 4K-class source when that is the realistic heavy case. Do not satisfy media import, preview, effect-slider, or image-processing performance with `640x480`, thumbnails, or toy fixtures.
41
+
36
42
  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
43
 
38
- Browser performance tests for workload scenarios must read the heavy value through:
44
+ Browser performance tests for workload and stress scenarios must read the scenario value through:
39
45
 
40
46
  ```ts
41
47
  getToolcraftPerformanceStressValue(appPerformance, "scenario-id")
42
48
  ```
43
49
 
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.
50
+ For workload sliders, use `dragToolcraftSliderToPerformanceStressValue(page, label, appPerformance, "scenario-id")` so the test applies the exact numeric value through the real slider min/max range. Do not divide a stress value by the slider max, type a separate short value, or hardcode a ratio in the Playwright test. If a test uses a toy value while `app-performance.ts` claims a heavy fixture, `pnpm verify:perf` must fail.
51
+
52
+ When a scenario declares `workloadFixture`, apply it first with `getToolcraftPerformanceWorkloadValue` or `applyToolcraftPerformanceWorkloadFixture`, then apply `stressFixture`, then measure. A control-drag scenario that only sets its own slider value while leaving the source media, text, item count, render scale, or dense scene at defaults is invalid.
53
+
54
+ For combined worst cases, put every relevant independent baseline value in `workloadFixture.value` for control scenarios, such as `{ sourceMedia: { width: 3840, height: 2160 }, renderScale: 2 }`, and put the tested control value in `stressFixture.value`. For preview, zoom, drag, animation, or export scenarios that stress the entire state instead of one control, use `stressFixture.value`, such as `{ detail: 96, scale: 0.6, renderScale: 2 }`. Testing workload controls one-by-one is not enough when the product exposes combinations that multiply render cost.
55
+
56
+ Use `kind: "custom"` only for combined object fixtures. Single numeric/string heavy values should use a semantic kind such as `max-value`, `high-density`, `large-canvas`, `many-items`, or `large-text`. Browser tests for custom object fixtures must call:
57
+
58
+ ```ts
59
+ await applyToolcraftPerformanceStressFixture(page, appPerformance, "scenario-id", {
60
+ detail: async (value) => {
61
+ await dragToolcraftSliderToValue(page, "Detail", Number(value));
62
+ },
63
+ scale: async (value) => {
64
+ await dragToolcraftSliderToValue(page, "Scale", Number(value));
65
+ },
66
+ });
67
+ ```
68
+
69
+ The applier object must contain exactly one entry for each key in `stressFixture.value`. Missing keys and stale extra keys are test failures, so every heavy-state part is intentionally mapped through the real UI before measurement.
70
+
71
+ For `workloadFixture.value` object fixtures, use `applyToolcraftPerformanceWorkloadFixture` with the same exact-key rule. `workloadFixture` is the app baseline; `stressFixture` is the action under test.
45
72
 
46
73
  ## Responsiveness Coverage
47
74
 
@@ -69,11 +96,25 @@ Custom renderers should:
69
96
  - avoid re-decoding media on every control change;
70
97
  - cancel scheduled frames during cleanup.
71
98
 
99
+ Custom renderers must declare `rendererPipeline` in `src/app/app-performance.ts`. This is the machine-checkable Render Pipeline Inventory:
100
+
101
+ - every render pass has an `id`, `kind`, `runsOn`, `output`, `quality`, `inputs`, and `invalidatedBy`;
102
+ - cache-sensitive passes such as `decode`, `preprocess`, `pixel-transform`, `text-layout`, `rasterize`, and `composite` include `cacheKey`;
103
+ - `interactionInvalidation` maps controls and high-frequency interactions to the passes they invalidate;
104
+ - animation frames, drag, pan, zoom, timeline playback, timeline scrub, and mask movement must not invalidate upstream decode/preprocess/pixel-transform work unless that runtime target truly changes the upstream result;
105
+ - each `workloadTargets` entry appears in `interactionInvalidation.targets` so tests can prove which control changes renderer cost.
106
+
107
+ Write this inventory before custom renderer code. If the inventory says a slider only updates a shader uniform, the implementation should update a uniform or stable buffer, not rebuild media decode, glyph layout, or raster caches. If the inventory says media import invalidates source decode, browser performance must include a realistic `media-import` scenario.
108
+
72
109
  Pixel-output renderers may use a capped preview pixel budget, but export/copy must render final product output at `state.canvas.size`.
73
110
 
74
111
  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.
75
112
 
76
- 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.
113
+ For heavy bitmap-media, shader-like, noise/texture, filters, halftone, mesh, and per-pixel image-processing paths, WebGL/WebGPU is a required candidate before choosing CPU Canvas 2D. Canvas 2D is allowed only when `rendererTechnique.whyNotAlternativeStrategies` or `performanceRisks` records measured stress evidence that the CPU path passes the real media fixture and GPU would not improve the product. A worker can protect the UI thread, but it is not a substitute for GPU acceleration when the workload is fundamentally per-pixel.
114
+
115
+ 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, coalesce high-frequency preview updates, or change the rendering layer model before reducing product quality or relaxing budgets.
116
+
117
+ Do not pass performance by lowering output quality, render scale, canvas backing pixels, export resolution, media fidelity, maximum detail, item count, or animation fidelity. If all reasonable optimizations are exhausted and the app still cannot meet the target budget, document the measured ceiling in the worklog with: the exact stress fixture, attempted optimizations, before/after measurements, why further changes would harm product quality or require a different product scope, and the remaining user-visible risk.
77
118
 
78
119
  ## Required Browser Checks
79
120
 
@@ -82,6 +123,7 @@ Use real interactions for:
82
123
  - `preview-render`;
83
124
  - `control-change`;
84
125
  - `control-drag`;
126
+ - `mask-drag` when canvas handles, masks, pins, or on-canvas anchors affect output;
85
127
  - `media-import` when upload exists;
86
128
  - `export-copy` for product export actions and clipboard actions; measure retina output dimensions, not CSS preview size;
87
129
  - `timeline-playback` or `timeline-scrub` when timeline exists;
@@ -91,7 +133,7 @@ Use real interactions for:
91
133
 
92
134
  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.
93
135
 
94
- 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.
136
+ Detail-heavy or animated custom renderers also need `viewport-zoom-stress`. This test must apply the combined worst-case stress fixture first, then 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, checking only the final zoom value, or zooming a default/lightweight output.
95
137
 
96
138
  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.
97
139
 
@@ -101,17 +143,13 @@ Animation checks should sample enough frames to catch jank. Interaction budgets
101
143
 
102
144
  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.
103
145
 
104
- 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.
146
+ Run targeted performance scenarios for Tier 3 performance-sensitive edits when they directly exercise the touched workload, viewport, or export path. `pnpm verify:perf` is the full performance suite; 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.
105
147
 
106
- Run a full performance checkpoint with `pnpm verify:perf` when:
148
+ Run a full performance checkpoint with `pnpm verify:perf` only when:
107
149
 
108
150
  - the first working version of the app exists;
109
- - renderer, canvas, animation, export, timeline, or layers change;
110
- - `canvas.renderScale` or the `Resolution scale` retina slider is added/enabled;
111
- - a bug that previously broke functionality is fixed;
112
- - a performance optimization lands;
113
- - the user asks to optimize performance, fix lag, remove jank, speed up animation, or stabilize drag/zoom.
151
+ - the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
114
152
 
115
153
  Performance fixes must preserve selected output and preview quality. Do not pass budgets by lowering image quality, selected `canvas.renderScale`, export resolution, source media fidelity, or canvas backing pixels unless the user explicitly chooses that lower-quality value through a visible control. Prefer coalescing slider updates, caching expensive inputs, moving work off the React render path, reusing GPU resources, or changing renderer strategy over reducing visual fidelity.
116
154
 
117
- 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.
155
+ Do not use the full performance suite as the default loop for feature work after the first working version. Those edits still need the targeted checks named by the verification tier, but they should not pay for every renderer and viewport stress test unless one of the two full-performance triggers applies. If a feature loop skips full performance, record the reason in the worklog.
@@ -14,6 +14,8 @@ The initial renderer choice is provisional. It becomes accepted only after the a
14
14
 
15
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
16
 
17
+ Heavy bitmap-media, shader-like, noise/texture, filter, halftone, mesh, and per-pixel image-processing layers must evaluate WebGL/WebGPU before delivery. Keeping the pixel work on Canvas 2D requires measured worst-case evidence in `whyNotAlternativeStrategies` or `performanceRisks`, using the real media/canvas stress fixture. Do not keep CPU Canvas 2D by default and then pass budgets by downsampling, lowering render scale, or testing a small upload.
18
+
17
19
  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
20
 
19
21
  ## Required Matrix
@@ -32,6 +34,8 @@ Custom renderer specs and `src/app/app-performance.ts` must mirror the decision:
32
34
 
33
35
  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
36
 
37
+ `rendererTechnique` chooses the technology. `rendererPipeline` proves the architecture. Do not start a custom renderer until both are written: technology without pass invalidation still lets an app recompute too much work.
38
+
35
39
  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
40
 
37
41
  ## Layer Inventory