@pixel-point/toolcraft 0.0.6 → 0.0.7

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 (30) 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 +39 -3
  4. package/templates/runtime/contracts/component-contracts.ts +54 -3
  5. package/templates/runtime/contracts/decision-contracts.test.ts +3 -2
  6. package/templates/runtime/contracts/decision-contracts.ts +1 -1
  7. package/templates/runtime/react/controls-panel.test.tsx +186 -1
  8. package/templates/runtime/react/controls-panel.tsx +382 -6
  9. package/templates/runtime/schema/define-toolcraft.test.ts +1 -0
  10. package/templates/runtime/schema/define-toolcraft.ts +7 -1
  11. package/templates/runtime/schema/types.ts +23 -0
  12. package/templates/runtime/state/reducer.test.ts +53 -0
  13. package/templates/runtime/state/reducer.ts +47 -0
  14. package/templates/runtime/state/types.ts +2 -0
  15. package/templates/starter/AGENTS.md +7 -7
  16. package/templates/starter/docs/toolcraft/README.md +4 -3
  17. package/templates/starter/docs/toolcraft/acceptance-testing.md +6 -2
  18. package/templates/starter/docs/toolcraft/assembly-workflow.md +5 -5
  19. package/templates/starter/docs/toolcraft/component-rules.md +5 -1
  20. package/templates/starter/docs/toolcraft/custom-controls.md +2 -2
  21. package/templates/starter/docs/toolcraft/performance.md +4 -8
  22. package/templates/starter/docs/toolcraft/schema-reference.md +6 -1
  23. package/templates/starter/docs/toolcraft/workflow.md +5 -8
  24. package/templates/starter/package.json +1 -1
  25. package/templates/starter/src/app/starter-performance.test.ts +1 -1
  26. package/templates/ui/components/controls/collection-actions/collection-actions-control.tsx +60 -0
  27. package/templates/ui/components/controls/collection-actions/index.ts +4 -0
  28. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -6
  29. package/templates/ui/components/controls/index.ts +8 -0
  30. package/templates/ui/index.ts +1 -0
@@ -111,7 +111,7 @@ AI must work on this app through the required workflow skills when the environme
111
111
  - 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
112
  - 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
113
  - 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`.
114
+ - 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
115
  - Run `pnpm ai:check` before app generation or major changes.
116
116
  - 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
117
  - If skill installation is not available, stop before implementation and tell the user exactly which required skills are missing.
@@ -139,16 +139,16 @@ Choose the tier by blast radius, not by line count. If uncertain, move one tier
139
139
  | 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
140
  | 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
141
  | 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. |
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 targeted performance scenarios only 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`; 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
144
 
145
145
  Do not rerun `pnpm install` after every edit. Run it after fresh export, dependency changes, lockfile changes, or a missing package error.
146
146
 
147
- Do not run the full browser performance suite for Tier 0-2 edits unless a performance checkpoint trigger applies.
147
+ Do not run the full browser performance suite for Tier 0-2 edits.
148
148
 
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.
149
+ 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
150
 
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.
151
+ 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
152
 
153
153
  ## Required Checks
154
154
 
@@ -161,7 +161,7 @@ pnpm dev
161
161
 
162
162
  Use `pnpm install` before this final gate when the folder is fresh or dependencies changed.
163
163
 
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.
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` 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
165
 
166
166
  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
167
 
@@ -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`;
@@ -146,6 +148,8 @@ Footer action acceptance must not include Reset. Reset is already available in t
146
148
 
147
149
  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
150
 
151
+ `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.
152
+
149
153
  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`.
150
154
 
151
155
  Invalid final acceptance evidence:
@@ -190,8 +190,8 @@ Use these tiers:
190
190
  | 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
191
  | 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
192
  | 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. |
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 targeted performance scenarios only 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`; 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
195
 
196
196
  Choose the tier by blast radius, not by line count. If uncertain, move one tier higher, not automatically to Tier 4.
197
197
 
@@ -199,9 +199,9 @@ Do not rerun `pnpm install` after every edit. Run it after fresh export, depende
199
199
 
200
200
  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
201
 
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.
202
+ 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
203
 
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.
204
+ 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
205
 
206
206
  For final delivery, run:
207
207
 
@@ -210,6 +210,6 @@ pnpm verify:final
210
210
  pnpm dev
211
211
  ```
212
212
 
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.
213
+ 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
214
 
215
215
  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,7 @@ 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, or object 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`, or `rangeInput` before any custom renderer.
25
26
  - Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
26
27
 
27
28
  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 +36,7 @@ For local reset-like `actions`, use product-specific values such as `reset-curre
35
36
  - If a section contains exactly one control, whether simple or compound, render only the parent section dividers.
36
37
  - 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
38
  - Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers.
39
+ - `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
40
 
39
41
  ## Sliders
40
42
 
@@ -110,6 +112,8 @@ Section expand/collapse uses the standard runtime height/opacity animation. Do n
110
112
 
111
113
  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
114
 
115
+ 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`.
116
+
113
117
  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
118
 
115
119
  ## Colors
@@ -281,7 +285,7 @@ If only `Export Settings` and `Import Settings` appear in that section, the sche
281
285
 
282
286
  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
287
 
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.
288
+ 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
289
 
286
290
  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
291
 
@@ -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, 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
 
@@ -101,17 +101,13 @@ Animation checks should sample enough frames to catch jank. Interaction budgets
101
101
 
102
102
  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
103
 
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.
104
+ 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
105
 
106
- Run a full performance checkpoint with `pnpm verify:perf` when:
106
+ Run a full performance checkpoint with `pnpm verify:perf` only when:
107
107
 
108
108
  - 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.
109
+ - the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
114
110
 
115
111
  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
112
 
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.
113
+ 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.
@@ -140,7 +140,7 @@ Resolved `canvas.size` exists for every canvas app, but visible `Aspect ratio`,
140
140
 
141
141
  When the user manually edits `Canvas width` or `Canvas height`, the runtime treats that as an exact custom output size. It keeps the typed dimension, keeps the other dimension unchanged, switches `Aspect ratio` to `Custom`, and shows the reduced current ratio in the custom ratio inputs. Only selecting an aspect preset may resize both dimensions from a canonical preset.
142
142
 
143
- For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` after canvas sizing in the first technical `Setup` section. The slider ranges from `1x` to `2x`, defaults to `2x`, and changes the renderer backing pixels without changing the visible CSS size or product output dimensions. Adding or enabling this slider requires a full `pnpm verify:perf` checkpoint. Performance fixes must preserve the selected scale and keep the canvas responsive while dragging sliders or other high-frequency controls at that scale. Diagnose whether lag comes from renderer technique, React update frequency, decoded media, shader/program setup, buffer uploads, layout work, stale async renders, or animation scheduling before reducing 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; use native vector rendering for those.
143
+ For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` after canvas sizing in the first technical `Setup` section. The slider ranges from `1x` to `2x`, defaults to `2x`, and changes the renderer backing pixels without changing the visible CSS size or product output dimensions. 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. Diagnose whether lag comes from renderer technique, React update frequency, decoded media, shader/program setup, buffer uploads, layout work, stale async renders, or animation scheduling before reducing 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; use native vector rendering for those.
144
144
 
145
145
  ## Panels
146
146
 
@@ -161,6 +161,7 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
161
161
  | `anchorGrid` | Anchor picker | `defaultValue`, `target` |
162
162
  | `channelMixer` | RGB-only channel matrix mixer with R/G/B tabs and Red/Green/Blue source sliders | `defaultValue`, `target`, `label` |
163
163
  | `checkbox` | Checkbox field | `defaultValue`, `target`, `label` |
164
+ | `collectionActions` | Add/remove buttons for repeatable product entities and their runtime-backed item controls | `defaultValue: []`, `itemControl`, `itemDefaultValue`, `itemLabel`, `minItems`, `recommendedMaxItems`, `hardMaxItems`, `target` |
164
165
  | `code` | Multiline textarea | `defaultValue`, `target`, `label` |
165
166
  | `color` | Hex color picker | `defaultValue: { hex }`, `target`, `label` |
166
167
  | `colorOpacity` | Hex color picker plus opacity percent input | `defaultValue: { hex, opacity }`, `target`, `label` |
@@ -184,6 +185,8 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
184
185
 
185
186
  For `slider` and `rangeSlider`, `unit` is a measurement or scale suffix, not the entity being counted. Use units such as `%`, `px`, `°`, `x`, `s`, `ms`, `fps`, `rows`, or `cols` only when they clarify the number. Do not add repeated nouns such as `letters`, `shapes`, `words`, `symbols`, `items`, `particles`, or `layers` when the label or section already names that entity. If the value needs a noun, rename the label or section. Word or acronym units, when truly needed, render with a space (`5 cols`, `17 fps`); compact symbol/CSS units stay tight (`70%`, `24px`).
186
187
 
188
+ Use `collectionActions` when the product owns a growable/shrinkable item list. `minItems` protects the smallest valid output, `recommendedMaxItems` is only a design recommendation, and `hardMaxItems` is valid only for a real product or technical limit. Adding/removing items must update the runtime array and the renderer/export must consume that same array. Do not pair a count slider with hidden fixed item controls when the user needs to add or remove actual entities. The collection label is on the left and remove/add buttons stay on the right. Homogeneous repeated items do not show visible per-item labels when the collection label already names the group. `itemControl.type` supports normal item built-ins such as `color`, `colorOpacity`, `text`, `select`, `segmented`, `slider`, `switch`, `checkbox`, and `rangeInput`; item controls still follow normal density rules, so plain colors use equal 50% columns when they fit.
189
+
187
190
  ## Control Selection Inventory
188
191
 
189
192
  Before writing schema controls, map product needs to built-ins by value model, not visual similarity.
@@ -235,6 +238,8 @@ Section expand/collapse uses the standard runtime height/opacity animation. Do n
235
238
 
236
239
  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.
237
240
 
241
+ 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`.
242
+
238
243
  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.
239
244
 
240
245
  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.
@@ -73,15 +73,12 @@ Choose the tier from `AGENTS.md` before editing. Use the tier to decide checks.
73
73
 
74
74
  - Tier 0-1: targeted docs/typecheck/unit plus focused browser when visual.
75
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.
76
+ - Tier 3: `pnpm verify:quick`, targeted browser acceptance, and targeted performance scenarios only for touched workload/viewport/export paths.
77
+ - Tier 4: `pnpm verify:final`; add `pnpm verify:perf` only for the first working app version or explicit performance complaints, then start `pnpm dev` for the local URL.
78
78
 
79
- Run a full performance checkpoint with `pnpm verify:perf` when:
79
+ Run a full performance checkpoint with `pnpm verify:perf` only when:
80
80
 
81
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.
82
+ - the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance.
86
83
 
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.
84
+ Fast 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.
@@ -16,7 +16,7 @@
16
16
  "verify:quick": "pnpm ai:check && pnpm test",
17
17
  "verify:ui": "pnpm test:browser",
18
18
  "verify:perf": "pnpm test:browser:perf",
19
- "verify:final": "pnpm ai:check && pnpm test && pnpm build && pnpm test:browser && pnpm test:browser:perf"
19
+ "verify:final": "pnpm ai:check && pnpm test && pnpm build && pnpm test:browser"
20
20
  },
21
21
  "dependencies": {
22
22
  "@base-ui/react": "^1.4.1",
@@ -299,7 +299,7 @@ describe("Toolcraft template app performance coverage", () => {
299
299
  expect(packageJson.scripts?.["verify:ui"]).toBe("pnpm test:browser");
300
300
  expect(packageJson.scripts?.["verify:perf"]).toBe("pnpm test:browser:perf");
301
301
  expect(packageJson.scripts?.["verify:final"]).toBe(
302
- "pnpm ai:check && pnpm test && pnpm build && pnpm test:browser && pnpm test:browser:perf",
302
+ "pnpm ai:check && pnpm test && pnpm build && pnpm test:browser",
303
303
  );
304
304
  });
305
305
 
@@ -0,0 +1,60 @@
1
+ "use client";
2
+
3
+ import { MinusIcon, PlusIcon } from "@phosphor-icons/react";
4
+ import type * as React from "react";
5
+
6
+ import { ControlFieldLabel } from "../../control-layout";
7
+ import { Button, Field } from "../../primitives";
8
+
9
+ export type CollectionActionsControlProps = {
10
+ addLabel?: string;
11
+ canAdd?: boolean;
12
+ canRemove?: boolean;
13
+ name: string;
14
+ onAdd?: () => void;
15
+ onRemove?: () => void;
16
+ removeLabel?: string;
17
+ };
18
+
19
+ export function CollectionActionsControl({
20
+ addLabel = "Add item",
21
+ canAdd = true,
22
+ canRemove = true,
23
+ name,
24
+ onAdd,
25
+ onRemove,
26
+ removeLabel = "Remove item",
27
+ }: CollectionActionsControlProps): React.JSX.Element {
28
+ return (
29
+ <Field
30
+ aria-label={name}
31
+ className="min-w-0 items-center justify-between gap-2"
32
+ data-slot="collection-actions-control-header"
33
+ orientation="horizontal"
34
+ >
35
+ <ControlFieldLabel className="flex-1">{name}</ControlFieldLabel>
36
+ <div className="inline-flex shrink-0 items-center gap-1">
37
+ <Button
38
+ aria-label={removeLabel}
39
+ disabled={!canRemove}
40
+ onClick={onRemove}
41
+ size="icon-sm"
42
+ type="button"
43
+ variant="outline"
44
+ >
45
+ <MinusIcon />
46
+ </Button>
47
+ <Button
48
+ aria-label={addLabel}
49
+ disabled={!canAdd}
50
+ onClick={onAdd}
51
+ size="icon-sm"
52
+ type="button"
53
+ variant="outline"
54
+ >
55
+ <PlusIcon />
56
+ </Button>
57
+ </div>
58
+ </Field>
59
+ );
60
+ }
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ export { CollectionActionsControl } from "./collection-actions-control";
4
+ export type { CollectionActionsControlProps } from "./collection-actions-control";
@@ -92,7 +92,6 @@ const defaultFontPickerFontSizePx = 16;
92
92
  const defaultFontPickerColor = "#FFFFFF";
93
93
  const defaultFontPickerOpacity = 100;
94
94
  const minFontPickerFontSizePx = 1;
95
- const maxFontPickerFontSizePx = 512;
96
95
 
97
96
  const menuItemInteractionClassName =
98
97
  "hover:bg-[color:color-mix(in_oklab,var(--muted-foreground)_10%,transparent)] hover:text-[color:var(--foreground)] focus:bg-[color:color-mix(in_oklab,var(--muted-foreground)_10%,transparent)] focus:text-[color:var(--foreground)]";
@@ -254,10 +253,7 @@ function normalizeFontPickerFontSize(value: unknown): number {
254
253
  return defaultFontPickerFontSizePx;
255
254
  }
256
255
 
257
- return Math.min(
258
- maxFontPickerFontSizePx,
259
- Math.max(minFontPickerFontSizePx, Math.round(value)),
260
- );
256
+ return Math.max(minFontPickerFontSizePx, Math.round(value));
261
257
  }
262
258
 
263
259
  function getFontFamilyStyle(font: FontPickerFontCatalogEntry | null): React.CSSProperties | undefined {
@@ -1131,7 +1127,6 @@ export function FontPickerControl({
1131
1127
  aria-label="Font size"
1132
1128
  className="[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
1133
1129
  disabled={disabled}
1134
- max={maxFontPickerFontSizePx}
1135
1130
  min={minFontPickerFontSizePx}
1136
1131
  onBlur={() => commitFontSizeDraft()}
1137
1132
  onChange={(event) => setFontSizeDraft(event.target.value)}
@@ -84,6 +84,14 @@ export type {
84
84
  ChannelMixerControlProps as ChannelMixerProps,
85
85
  ChannelMixerValues,
86
86
  } from "./channel-mixer";
87
+ export {
88
+ CollectionActionsControl,
89
+ CollectionActionsControl as CollectionActions,
90
+ } from "./collection-actions";
91
+ export type {
92
+ CollectionActionsControlProps,
93
+ CollectionActionsControlProps as CollectionActionsProps,
94
+ } from "./collection-actions";
87
95
  export { createControlHistoryGroupId } from "./control-types";
88
96
  export type {
89
97
  ControlChangeHistoryMode,
@@ -12,6 +12,7 @@ export {
12
12
  ChannelMixer,
13
13
  Checkbox,
14
14
  CodeTextarea,
15
+ CollectionActions,
15
16
  Color,
16
17
  ColorOpacity,
17
18
  ColorValue,