@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.
- package/package.json +1 -1
- package/src/generate.test.mjs +1 -1
- package/templates/runtime/contracts/component-contracts.test.ts +39 -3
- package/templates/runtime/contracts/component-contracts.ts +54 -3
- package/templates/runtime/contracts/decision-contracts.test.ts +3 -2
- package/templates/runtime/contracts/decision-contracts.ts +1 -1
- package/templates/runtime/react/controls-panel.test.tsx +186 -1
- package/templates/runtime/react/controls-panel.tsx +382 -6
- package/templates/runtime/schema/define-toolcraft.test.ts +1 -0
- package/templates/runtime/schema/define-toolcraft.ts +7 -1
- package/templates/runtime/schema/types.ts +23 -0
- package/templates/runtime/state/reducer.test.ts +53 -0
- package/templates/runtime/state/reducer.ts +47 -0
- package/templates/runtime/state/types.ts +2 -0
- package/templates/starter/AGENTS.md +7 -7
- package/templates/starter/docs/toolcraft/README.md +4 -3
- package/templates/starter/docs/toolcraft/acceptance-testing.md +6 -2
- package/templates/starter/docs/toolcraft/assembly-workflow.md +5 -5
- package/templates/starter/docs/toolcraft/component-rules.md +5 -1
- package/templates/starter/docs/toolcraft/custom-controls.md +2 -2
- package/templates/starter/docs/toolcraft/performance.md +4 -8
- package/templates/starter/docs/toolcraft/schema-reference.md +6 -1
- package/templates/starter/docs/toolcraft/workflow.md +5 -8
- package/templates/starter/package.json +1 -1
- package/templates/starter/src/app/starter-performance.test.ts +1 -1
- package/templates/ui/components/controls/collection-actions/collection-actions-control.tsx +60 -0
- package/templates/ui/components/controls/collection-actions/index.ts +4 -0
- package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -6
- package/templates/ui/components/controls/index.ts +8 -0
- 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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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`
|
|
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`
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
77
|
-
- Tier 4: `pnpm verify:final
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|
|
@@ -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.
|
|
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,
|