@pixel-point/toolcraft 0.0.7 → 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.
- package/package.json +1 -1
- package/templates/runtime/contracts/component-contracts.test.ts +84 -12
- package/templates/runtime/contracts/component-contracts.ts +40 -17
- package/templates/runtime/contracts/decision-contracts.ts +1 -1
- package/templates/runtime/export/export.test.ts +31 -0
- package/templates/runtime/export/export.ts +41 -0
- package/templates/runtime/react/canvas-shell.test.tsx +77 -1
- package/templates/runtime/react/canvas-shell.tsx +178 -23
- package/templates/runtime/react/control-conditions.ts +166 -0
- package/templates/runtime/react/controls-panel-filedrop-reorder.test.tsx +176 -0
- package/templates/runtime/react/controls-panel.test.tsx +404 -8
- package/templates/runtime/react/controls-panel.tsx +90 -4
- package/templates/runtime/react/media-file.ts +19 -0
- package/templates/runtime/schema/define-toolcraft.test.ts +1 -0
- package/templates/runtime/schema/define-toolcraft.ts +4 -2
- package/templates/runtime/schema/types.ts +4 -0
- package/templates/runtime/state/reducer.test.ts +304 -0
- package/templates/runtime/state/reducer.ts +148 -9
- package/templates/runtime/state/types.ts +10 -2
- package/templates/runtime/testing/performance.test.ts +1282 -48
- package/templates/runtime/testing/performance.ts +676 -39
- package/templates/starter/AGENTS.md +6 -5
- package/templates/starter/docs/toolcraft/acceptance-testing.md +11 -5
- package/templates/starter/docs/toolcraft/assembly-workflow.md +4 -2
- package/templates/starter/docs/toolcraft/component-rules.md +16 -7
- package/templates/starter/docs/toolcraft/custom-controls.md +8 -4
- package/templates/starter/docs/toolcraft/performance.md +47 -5
- package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
- package/templates/starter/docs/toolcraft/schema-reference.md +6 -6
- package/templates/starter/docs/toolcraft/workflow.md +2 -2
- package/templates/starter/e2e/app-performance.spec.ts +136 -3
- package/templates/starter/e2e/performance-helpers.ts +197 -0
- package/templates/starter/package.json +3 -0
- package/templates/starter/src/app/starter-acceptance.test.ts +529 -19
- package/templates/starter/src/app/starter-acceptance.ts +269 -12
- package/templates/starter/src/app/starter-performance.test.ts +66 -5
- package/templates/ui/components/controls/actions/actions-control.tsx +3 -5
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +340 -44
- package/templates/ui/components/controls/file-drop/index.ts +1 -1
- package/templates/ui/components/controls/index.ts +1 -0
- package/templates/ui/components/panel/panel-section.tsx +53 -1
|
@@ -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
|
|
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
|
|
31
|
-
17.
|
|
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
|
|
|
@@ -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,
|
|
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.
|
|
@@ -107,21 +107,27 @@ High-confidence wrong-substitution cases:
|
|
|
107
107
|
- segmented choices that clip instead of falling back to `select`;
|
|
108
108
|
- custom controls recreating built-ins.
|
|
109
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
|
+
|
|
110
112
|
Rows that use custom controls must include `customControlCoverage` and typed `builtInFitCheck`.
|
|
111
113
|
|
|
112
114
|
```ts
|
|
113
115
|
builtInFitCheck: {
|
|
114
|
-
checkedBuiltIns: ["fileDrop", "
|
|
116
|
+
checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
|
|
115
117
|
closestBuiltIn: "fileDrop",
|
|
116
118
|
whyInsufficient:
|
|
117
|
-
"FileDrop imports source files, but
|
|
119
|
+
"FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
|
|
118
120
|
productObservable:
|
|
119
|
-
"
|
|
121
|
+
"Changing a glyph density threshold changes which uploaded glyph renders for the same depth-map tone.",
|
|
120
122
|
}
|
|
121
123
|
```
|
|
122
124
|
|
|
123
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.
|
|
124
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
|
+
|
|
125
131
|
## Valid Evidence
|
|
126
132
|
|
|
127
133
|
Valid acceptance evidence includes:
|
|
@@ -150,7 +156,7 @@ Local `actions` acceptance must click every visible action and prove the nearby
|
|
|
150
156
|
|
|
151
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.
|
|
152
158
|
|
|
153
|
-
PNG export tests must prove runtime background behavior: changing the background color affects preview/export, turning `export.includeBackground` off
|
|
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`.
|
|
154
160
|
|
|
155
161
|
Invalid final acceptance evidence:
|
|
156
162
|
|
|
@@ -202,7 +208,7 @@ Component variants are acceptance requirements.
|
|
|
202
208
|
- Select, segmented, and image-picker controls should cover every visible option unless options come from separately tested runtime data.
|
|
203
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.
|
|
204
210
|
|
|
205
|
-
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
|
|
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.
|
|
206
212
|
|
|
207
213
|
## Fixtures
|
|
208
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.
|
|
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
|
|
|
@@ -22,7 +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, or
|
|
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.
|
|
26
27
|
- Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
|
|
27
28
|
|
|
28
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.
|
|
@@ -124,25 +125,31 @@ Keep color inside a section when it configures the same entity as nearby control
|
|
|
124
125
|
|
|
125
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.
|
|
126
127
|
|
|
127
|
-
|
|
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`.
|
|
128
129
|
|
|
129
|
-
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.
|
|
130
131
|
|
|
131
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.
|
|
132
133
|
|
|
133
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.
|
|
134
135
|
|
|
135
|
-
Mixed inline rows require label parity: every field in that row has a visible label. The required `Background` section row is the
|
|
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.
|
|
136
137
|
|
|
137
|
-
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
|
|
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.
|
|
138
139
|
|
|
139
140
|
## File Upload
|
|
140
141
|
|
|
141
142
|
Use `fileDrop` for source material uploads in the controls panel. Do not place upload UI on the canvas.
|
|
142
143
|
|
|
143
|
-
|
|
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.
|
|
144
145
|
|
|
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.
|
|
149
|
+
|
|
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.
|
|
146
153
|
|
|
147
154
|
In multi-layer apps, deletion and visibility belong to the Layers panel; `fileDrop` stays an upload target.
|
|
148
155
|
|
|
@@ -164,6 +171,8 @@ Use `fontPicker` for typography choices that need font preview plus weight, size
|
|
|
164
171
|
|
|
165
172
|
The value is one object: `{ fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`. Typography renderers and exports must consume all eight parts.
|
|
166
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
|
+
|
|
167
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.
|
|
168
177
|
|
|
169
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.
|
|
@@ -6,7 +6,7 @@ Built-ins come first: `slider`, `rangeSlider`, `select`, `segmented`, `switch`,
|
|
|
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, repeatable item add/remove, 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", "
|
|
34
|
+
checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
|
|
35
35
|
closestBuiltIn: "fileDrop",
|
|
36
36
|
whyInsufficient:
|
|
37
|
-
"FileDrop imports source files, but
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
|
@@ -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
|
|
@@ -45,7 +45,7 @@ export: {
|
|
|
45
45
|
- `appearance.background` or `scene.background` as a `color` control;
|
|
46
46
|
- `export.includeBackground` as a boolean/options control.
|
|
47
47
|
|
|
48
|
-
PNG exporters should call `createToolcraftPngExportCanvas({ background, includeBackground, resolution, state, render })`, where `background`, `includeBackground`, and `resolution` come from runtime state. `
|
|
48
|
+
PNG exporters should call `createToolcraftPngExportCanvas({ background, includeBackground, resolution, state, render })`, where `background`, `includeBackground`, and `resolution` come from runtime state. Live preview renderers should call `shouldIncludeToolcraftPreviewBackground(state)` and hide only the product-rendered background when it returns false; do not hide or replace the Toolcraft canvas shell/backing. For every app with `Export PNG`, `resolution` comes from `export.image.resolution`: `2k`, `4k`, and `8k` render actual 2048/4096/8192px long-edge PNGs. `current` or omitted resolution falls back to retina sizing. Video export always includes the product background, uses `getToolcraftRetinaExportSize`, and must prove exported metadata duration matches the runtime timeline duration.
|
|
49
49
|
|
|
50
50
|
Every app with `Export PNG` exposes a separate `Image Export` controls section. For still-output apps it sits directly above sticky footer actions. For animated apps with both `Export PNG` and `Export Video`, it sits immediately before `Video Export`:
|
|
51
51
|
|
|
@@ -166,8 +166,8 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
|
|
|
166
166
|
| `color` | Hex color picker | `defaultValue: { hex }`, `target`, `label` |
|
|
167
167
|
| `colorOpacity` | Hex color picker plus opacity percent input | `defaultValue: { hex, opacity }`, `target`, `label` |
|
|
168
168
|
| `curves` | RGB or single curve editor | `defaultValue`, `target`, `variant: "single"`, `interpolation: "smooth" \| "monotone"` |
|
|
169
|
-
| `fileDrop` | Upload/drop input | `accept`, `multiple`, `target` |
|
|
170
|
-
| `fontPicker` | Font preview select with popup, category search, weight, size, text case, text color/opacity, letter spacing, and line height; product text must consume `fontId`, `fontWeight`, `fontSize`, `letterSpacing`, `lineHeight`, `textCase`, `color`, and `opacity` | `defaultValue: { fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`, `target` |
|
|
169
|
+
| `fileDrop` | Upload/drop input; `assetKind: "image"` owns image previews and `assetKind: "file"` owns sortable arbitrary file lists | `assetKind`, `accept`, `multiple`, `defaultValue`, `target` |
|
|
170
|
+
| `fontPicker` | Font preview select with popup, category search, weight, size, text case, text color/opacity, letter spacing, and line height; product text must consume `fontId`, `fontWeight`, `fontSize`, `letterSpacing`, `lineHeight`, `textCase`, `color`, and `opacity`; default text color is `#FFFFFF` at `100` opacity | `defaultValue: { fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`, `target` |
|
|
171
171
|
| `gradient` | Gradient editor | `defaultValue: { angle, gradientType, stops }`, `target` |
|
|
172
172
|
| `imagePicker` | Image choice grid | `items`, `defaultValue`, `target` |
|
|
173
173
|
| `palette` | Constrained design-token palette picker for family + shade | `defaultValue: { family, shade }`, `target` |
|
|
@@ -185,7 +185,7 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
|
|
|
185
185
|
|
|
186
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`).
|
|
187
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 `
|
|
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`, `rangeInput`, and `fontPicker`; item controls still follow normal density rules, so plain colors use equal 50% columns when they fit. Use `fontPicker` as the item control when each repeated item is a text style or typography entity; do not split its font, weight, size, case, color/opacity, letter spacing, or line height into sibling collection fields.
|
|
189
189
|
|
|
190
190
|
## Control Selection Inventory
|
|
191
191
|
|
|
@@ -254,7 +254,7 @@ If a target prefix has to be split across sections, the spec must name the workf
|
|
|
254
254
|
|
|
255
255
|
Switch and checkbox labels name the setting context only. Do not prefix them with `Enable` or `Disable`; use `CRT`, `Glow`, `Loop`, or `Guides` instead. If the nearest section title already names the context, do not duplicate it as the visible toggle label. Use a short contextual label such as `Include` or, only for icon-only visual toggles, `label: false` with the product meaning in `target` and `description`.
|
|
256
256
|
|
|
257
|
-
Inline two-column groups are preferred when controls tune one close product meaning and labels/values fit. Short numeric text pairs can be inline. Related short `select` pairs can be inline, especially workflow pairs such as `Format` + `Resolution`, `Codec` + `Profile`, or `Width unit` + `Height unit`. Use stacked one-control rows only as a fit fallback when a label, selected value, or option text would clip, truncate, or lose padding; record that fallback reason in the spec or worklog. A short numeric/text field may also pair with one related plain `color` field when both configure the same entity, such as `Mask size` and `Color` inside `Mask`. `colorOpacity` never renders in inline two-column groups; if either color control has opacity, keep the controls stacked. Color
|
|
257
|
+
Inline two-column groups are preferred when controls tune one close product meaning and labels/values fit. Short numeric text pairs can be inline. Related short `select` pairs can be inline, especially workflow pairs such as `Format` + `Resolution`, `Codec` + `Profile`, or `Width unit` + `Height unit`. Use stacked one-control rows only as a fit fallback when a label, selected value, or option text would clip, truncate, or lose padding; record that fallback reason in the spec or worklog. A short numeric/text field may also pair with one related plain `color` field when both configure the same entity, such as `Mask size` and `Color` inside `Mask`. `colorOpacity` never renders in inline two-column groups; if either color control has opacity, keep the controls stacked. Color labels are semantic, not automatic: decide once for the whole color group, omit per-item labels such as `Color 1` for palette variation banks like `Accent Shades` or `Bead Colors`, and do not mix labeled and unlabeled items inside that bank. Sibling controls like `Spread` or `Randomness` do not force item labels; keep visible labels only when colors edit distinct roles such as `Fill`, `Stroke`, `Background`, `Connector`, or `Object`. Related plain color banks render two per row, and an odd trailing plain color remains half-width instead of stretching to a full row. Mixed inline rows require visible labels on every field except the required Background row and palette variation color banks whose group/section label already names the bank. Two adjacent `switch` or `checkbox` controls for the same product entity must share one inline row when both visible labels fit without truncation; the runtime auto-pairs safe adjacent toggles by target entity, and schemas should stack them only when either label is too long. A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and both controls edit the same entity; shorten the toggle label when the section title already supplies context. Toggle plus parameter rows are equal-width two-column rows: each control occupies one half, never intrinsic toggle width plus remaining space. The required Background row uses `Include` plus unlabeled background color. Schema `slider` and `rangeSlider` controls always stay stacked at full width; the only built-in exception is the paired letter-spacing and line-height footer sliders inside `fontPicker`. Do not place sibling controls for case, color, opacity, size, weight, letter spacing, or line height when the same text entity already uses `fontPicker`.
|
|
258
258
|
|
|
259
259
|
`rangeSlider` is always a full-width two-thumb control. Do not include it in `layoutGroups`. Its `defaultValue` must start with different lower and upper values, such as `[20, 80]`, so the two handles do not collapse into one apparent slider. Manual range labels accept built-in separators such as slash, hyphen, spaces, and dashes.
|
|
260
260
|
|
|
@@ -266,7 +266,7 @@ Order controls by decision flow inside each section:
|
|
|
266
266
|
- `mode`: mode, type, filter, blend, style, and preset selectors;
|
|
267
267
|
- `primary`, `spatial`, `color`: core product parameters;
|
|
268
268
|
- `strength`: intensity, opacity, scale, depth;
|
|
269
|
-
- `detail`:
|
|
269
|
+
- `detail`: noise, texture, blur, density, radius, quality;
|
|
270
270
|
- `advanced`: secondary tuning;
|
|
271
271
|
- `action`: footer actions.
|
|
272
272
|
|
|
@@ -19,7 +19,7 @@ Do not edit implementation files until this preflight is complete.
|
|
|
19
19
|
|
|
20
20
|
## Task Routing
|
|
21
21
|
|
|
22
|
-
Use the smallest reading set that covers the changed surface.
|
|
22
|
+
Use the smallest reading set that covers the changed surface. If a task touches multiple surfaces, combine every matching row instead of picking only the closest one.
|
|
23
23
|
|
|
24
24
|
| Task type | Read before editing |
|
|
25
25
|
| --- | --- |
|
|
@@ -29,7 +29,7 @@ Use the smallest reading set that covers the changed surface.
|
|
|
29
29
|
| Renderer, canvas output, visual technique | `renderer-technique.md`, `performance.md`, `acceptance-testing.md` |
|
|
30
30
|
| Timeline, keyframes, animation transport | `decision-contract.md`, `component-rules.md`, `acceptance-testing.md`, `performance.md` |
|
|
31
31
|
| Layers | `decision-contract.md`, `component-rules.md`, `acceptance-testing.md` |
|
|
32
|
-
| Export, copy, media, background | `schema-reference.md`, `component-rules.md`, `acceptance-testing.md` |
|
|
32
|
+
| Export, copy, media, background | `schema-reference.md`, `component-rules.md`, `acceptance-testing.md`, `performance.md` |
|
|
33
33
|
| Broken control, visual mismatch, failed build, export bug, performance issue | `decision-contract.md`, the relevant component/runtime doc, and the failing test/log first |
|
|
34
34
|
| Figma implementation | Use Figma MCP/design context, then `assembly-workflow.md` and the relevant component docs |
|
|
35
35
|
|