@pixel-point/toolcraft 0.0.7 → 0.0.9
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.mjs +34 -5
- package/src/generate.test.mjs +12 -0
- package/src/package-json.mjs +15 -0
- package/src/package-json.test.mjs +14 -1
- package/templates/runtime/contracts/component-contracts.test.ts +185 -23
- package/templates/runtime/contracts/component-contracts.ts +91 -36
- package/templates/runtime/contracts/decision-contracts.test.ts +5 -0
- package/templates/runtime/contracts/decision-contracts.ts +4 -4
- 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 +774 -17
- package/templates/runtime/react/controls-panel.tsx +155 -8
- package/templates/runtime/react/media-file.ts +19 -0
- package/templates/runtime/schema/define-toolcraft.test.ts +46 -1
- package/templates/runtime/schema/define-toolcraft.ts +29 -3
- package/templates/runtime/schema/types.ts +7 -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 +1424 -56
- package/templates/runtime/testing/performance.ts +710 -43
- package/templates/starter/AGENTS.md +10 -9
- package/templates/starter/docs/toolcraft/README.md +1 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +16 -8
- package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -7
- package/templates/starter/docs/toolcraft/component-rules.md +46 -16
- package/templates/starter/docs/toolcraft/custom-controls.md +8 -4
- package/templates/starter/docs/toolcraft/performance.md +54 -6
- package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
- package/templates/starter/docs/toolcraft/schema-reference.md +48 -19
- 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/gitignore +1 -0
- package/templates/starter/package.json +5 -0
- package/templates/starter/scripts/run-vite-on-free-port.mjs +39 -4
- package/templates/starter/scripts/toolcraft-port.mjs +102 -0
- package/templates/starter/scripts/toolcraft-port.test.mjs +60 -1
- package/templates/starter/src/app/starter-acceptance.test.ts +1288 -105
- package/templates/starter/src/app/starter-acceptance.ts +740 -24
- package/templates/starter/src/app/starter-performance.test.ts +66 -5
- package/templates/ui/components/control-layout/index.tsx +8 -3
- package/templates/ui/components/controls/actions/actions-control.tsx +10 -4
- package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +7 -3
- package/templates/ui/components/controls/color/index.ts +4 -1
- package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +7 -2
- package/templates/ui/components/controls/color/style-guide-color-picker.tsx +2 -2
- 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 +3 -0
- package/templates/ui/components/controls/range-input/range-input-control.tsx +12 -4
- package/templates/ui/components/controls/select/select-control.tsx +9 -4
- package/templates/ui/components/controls/slider/slider-value.ts +0 -1
- package/templates/ui/components/controls/text-input/text-input-control.tsx +4 -1
- package/templates/ui/components/controls/vector/index.ts +1 -0
- package/templates/ui/components/controls/vector/vector-control.tsx +84 -8
- package/templates/ui/components/panel/panel-section.tsx +82 -6
|
@@ -14,21 +14,22 @@ Then follow `workflow.md` to choose the required contract docs and verification
|
|
|
14
14
|
|
|
15
15
|
1. Build through `defineToolcraft` and `ToolcraftApp`.
|
|
16
16
|
2. Keep app state in Toolcraft runtime schema and commands.
|
|
17
|
-
3. Keep product output in `canvasContent`; never render app UI there.
|
|
17
|
+
3. Keep product output in `canvasContent`; never render app UI there. If upload/import is part of the source-material flow, do not invent canvas placeholder artwork, CTA copy, helper text, fake sample output, or preset source designs before real content exists.
|
|
18
18
|
4. Use built-in Toolcraft controls before custom controls.
|
|
19
19
|
5. Do not hand-compose runtime surfaces or render built-in control components directly in app code; use `ToolcraftApp`, schema controls, `canvasContent`, `controlRenderers`, `onPanelAction`, and runtime commands.
|
|
20
|
-
6. Before writing controls, make
|
|
20
|
+
6. Before writing controls, make and export `starterControlSectionInventory`: each product controls section declares its title, product entity or workflow stage, targets, and grouping reason. Group by product meaning, not UI component type.
|
|
21
21
|
7. Keep control `label` short but semantically sufficient with the nearest visible section/group context, and put product-specific behavior help in schema `description`; runtime renders the label help tooltip only when that description adds meaning beyond the label.
|
|
22
22
|
8. Enable layers and timeline only when product behavior requires them, then test the real UI.
|
|
23
23
|
9. Animated preview renderers suspend or coalesce non-essential animation work during canvas drag, pan, pinch, zoom, and radar/center interactions, then resume without changing user playback state.
|
|
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
|
-
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
|
|
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
|
|
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`. Product-output, exportable, shader, procedural, and reference-clone apps use `editable-output`; fixed/reference/base dimensions are initial `canvas.size` values, not reasons to hide `Aspect ratio`, `Canvas width`, or `Canvas height`. 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 scale 2 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, 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
|
|
|
@@ -163,7 +164,7 @@ Use `pnpm install` before this final gate when the folder is fresh or dependenci
|
|
|
163
164
|
|
|
164
165
|
`pnpm test` must include `node scripts/check-toolcraft-docs.mjs`, `node scripts/check-toolcraft-integrity.mjs`, and app tests. `pnpm verify:ui` / `pnpm test:browser` must run against the real app UI and product output. `pnpm verify:perf` / `pnpm test:browser:perf` remains available for the two full-performance triggers and must run the performance browser suite sequentially so budgets are measured without parallel e2e noise.
|
|
165
166
|
|
|
166
|
-
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
|
+
Do not stop or kill existing local servers to free a port during a first start. `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. When deliberately restarting this app server, use `pnpm dev:restart` or `pnpm preview:restart`; restart mode reuses the previously saved app port, stops the listener on that exact port if it is still running, force-stops it if it does not release the port, and starts on the same port again.
|
|
167
168
|
|
|
168
169
|
## App Completion Bar
|
|
169
170
|
|
|
@@ -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.
|
|
@@ -39,4 +39,4 @@ pnpm verify:perf # first working version or explicit performance complaint only
|
|
|
39
39
|
pnpm dev
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Do not kill existing local servers to free `3002
|
|
42
|
+
Do not kill existing local servers to free `3002` during a first start. Dev, preview, and browser verification prefer `3002`, then move to the next free port automatically. When restarting the same app server, use `pnpm dev:restart` or `pnpm preview:restart`; restart mode reuses the saved app port and stops only the listener on that exact port before starting again, forcing it only when the soft stop does not release the port.
|
|
@@ -53,12 +53,14 @@ Each row should name:
|
|
|
53
53
|
- exact `automatedTestName`;
|
|
54
54
|
- exact `browserTestName`.
|
|
55
55
|
- `controlPartCoverage` when the control is compound.
|
|
56
|
-
- `canvasSizingCoverage: "fixed-output-size"`
|
|
56
|
+
- `canvasSizingCoverage: "fixed-output-size"` only for non-product/internal `fixed-output` fixtures.
|
|
57
57
|
- `persistenceCoverage: "reload"` when schema `persistence.storage` is `"localStorage"`.
|
|
58
58
|
|
|
59
59
|
The test gate rejects rows without matching automated and browser test names.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Slider and range slider rows must prove live behavior. Browser tests should drag the real thumb and assert the runtime value and product-level canvas observable update during the drag, not only after pointer release, blur, an Apply action, or a final commit. Performance-sensitive sliders still need this live acceptance; jank is handled through renderer optimization and targeted performance coverage, not by making the slider deferred by default.
|
|
62
|
+
|
|
63
|
+
`fixed-output` canvas sizing must be deliberate and is not valid for generated product/output apps with export actions. A default, reference, or fixed-format size from the prompt should use `editable-output`, which keeps the runtime Aspect ratio, Canvas width, and Canvas height controls.
|
|
62
64
|
|
|
63
65
|
When localStorage persistence is enabled, add a runtime acceptance row that proves reload behavior. The browser test must change a real user-facing setting, wait for persistence to write, call a real page reload, and verify the restored control value or product output. Importing a settings JSON file is not persistence coverage.
|
|
64
66
|
|
|
@@ -107,21 +109,27 @@ High-confidence wrong-substitution cases:
|
|
|
107
109
|
- segmented choices that clip instead of falling back to `select`;
|
|
108
110
|
- custom controls recreating built-ins.
|
|
109
111
|
|
|
112
|
+
`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`.
|
|
113
|
+
|
|
110
114
|
Rows that use custom controls must include `customControlCoverage` and typed `builtInFitCheck`.
|
|
111
115
|
|
|
112
116
|
```ts
|
|
113
117
|
builtInFitCheck: {
|
|
114
|
-
checkedBuiltIns: ["fileDrop", "
|
|
118
|
+
checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
|
|
115
119
|
closestBuiltIn: "fileDrop",
|
|
116
120
|
whyInsufficient:
|
|
117
|
-
"FileDrop imports source files, but
|
|
121
|
+
"FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
|
|
118
122
|
productObservable:
|
|
119
|
-
"
|
|
123
|
+
"Changing a glyph density threshold changes which uploaded glyph renders for the same depth-map tone.",
|
|
120
124
|
}
|
|
121
125
|
```
|
|
122
126
|
|
|
123
127
|
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
128
|
|
|
129
|
+
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.
|
|
130
|
+
|
|
131
|
+
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.
|
|
132
|
+
|
|
125
133
|
## Valid Evidence
|
|
126
134
|
|
|
127
135
|
Valid acceptance evidence includes:
|
|
@@ -146,11 +154,11 @@ Animated app acceptance must also exercise the separate `Video Export` section:
|
|
|
146
154
|
|
|
147
155
|
Footer action acceptance must not include Reset. Reset is already available in the controls panel header and uses schema `defaultValue`; duplicating it in sticky `panelActions` fails acceptance.
|
|
148
156
|
|
|
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.
|
|
157
|
+
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. A single-button `actions` control fails validation when the control label duplicates the button label; the label must add concise context. Visual acceptance rejects side-label actions; labels sit above a two-column button grid where each button cell is 50% width.
|
|
150
158
|
|
|
151
159
|
`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
160
|
|
|
153
|
-
PNG export tests must prove runtime background behavior: changing the background color affects preview/export, turning `export.includeBackground` off
|
|
161
|
+
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
162
|
|
|
155
163
|
Invalid final acceptance evidence:
|
|
156
164
|
|
|
@@ -202,7 +210,7 @@ Component variants are acceptance requirements.
|
|
|
202
210
|
- Select, segmented, and image-picker controls should cover every visible option unless options come from separately tested runtime data.
|
|
203
211
|
- 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
212
|
|
|
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
|
|
213
|
+
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
214
|
|
|
207
215
|
## Fixtures
|
|
208
216
|
|
|
@@ -51,7 +51,7 @@ Do not leave `mode: "starter"` in a renamed product folder or after adding produ
|
|
|
51
51
|
|
|
52
52
|
## Control Sections
|
|
53
53
|
|
|
54
|
-
Before writing the schema, make
|
|
54
|
+
Before writing the schema, make and export `starterControlSectionInventory`. Each product controls section needs a product entity or workflow stage, included targets, and a reason for grouping. Do not group by control type. The exported inventory must match the schema targets exactly; if one target entity is intentionally split across sections, every split section needs `workflowStage` and `splitReason`.
|
|
55
55
|
|
|
56
56
|
Bad section titles: `Controls`, `Settings`, `Options`, `Sliders`, `Inputs`, `Buttons`, `Color`, `Colors`.
|
|
57
57
|
|
|
@@ -67,8 +67,12 @@ Ordinary controls-panel body sections use 8px top spacing and 24px bottom spacin
|
|
|
67
67
|
|
|
68
68
|
If a color, slider, input, or selector edits the same entity as nearby controls, keep it in that entity section. Split only when the product has a real workflow split and cover that decision in acceptance.
|
|
69
69
|
|
|
70
|
+
When a selector controls visibility or enabled state for another control through `visibleWhen` or `disabledWhen`, treat both as one dependency group if they share the same target entity or selected branch. Keep the selector and its gated branch controls in the same section; do not create a separate section that merely mirrors one selector option unless that branch is a genuinely separate product entity with its own workflow evidence.
|
|
71
|
+
|
|
70
72
|
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
73
|
|
|
74
|
+
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.
|
|
75
|
+
|
|
72
76
|
## Figma Source
|
|
73
77
|
|
|
74
78
|
When the prompt provides a Figma URL, treat the Figma file as the design source of truth.
|
|
@@ -86,6 +90,8 @@ Do not implement a Figma design by eye from an image, screenshot, exported PNG,
|
|
|
86
90
|
|
|
87
91
|
Use `canvasContent` only for product output: WebGL, Canvas 2D, SVG, DOM product text, shader previews, generated previews, export previews, or product editing handles.
|
|
88
92
|
|
|
93
|
+
If upload/import is part of the source-material flow, do not invent a design on the canvas before real content exists. The pre-content canvas stays neutral and runtime-backed; upload affordance belongs in `fileDrop`, not in canvas CTA text, helper copy, fake sample output, decorative placeholders, or agent-made source presets. A default procedural/reference source is allowed only when the prompt or reference explicitly defines it, and the worklog must record that evidence.
|
|
94
|
+
|
|
89
95
|
```tsx
|
|
90
96
|
<ToolcraftApp
|
|
91
97
|
canvasContent={<ProductRenderer />}
|
|
@@ -129,14 +135,14 @@ Animated preview renderers must prioritize viewport interactions. During canvas
|
|
|
129
135
|
|
|
130
136
|
## Canvas Sizing And Background
|
|
131
137
|
|
|
132
|
-
A base/default size in the prompt is the initial output size. It does not remove user-facing size controls.
|
|
138
|
+
A base/default, reference, or fixed-format size in the prompt is the initial output size. It does not remove user-facing size controls. Product-output, exportable, shader, procedural, and reference-clone apps use `editable-output`; keep fixed dimensions as editable `canvas.size` defaults instead of switching to `fixed-output`.
|
|
133
139
|
|
|
134
140
|
Every product app exposes output background controls:
|
|
135
141
|
|
|
136
142
|
- `appearance.background` or `scene.background` as a schema `color` control;
|
|
137
143
|
- `export.includeBackground` as a `switch`, `checkbox`, `select`, or `segmented` control.
|
|
138
144
|
|
|
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.
|
|
145
|
+
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
146
|
|
|
141
147
|
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
148
|
|
|
@@ -148,9 +154,9 @@ For complex apps, use schema `settingsTransfer: "auto"` or `true` for settings i
|
|
|
148
154
|
|
|
149
155
|
If the app also uses `editable-output` canvas sizing, that first technical `Setup` runtime section is mandatory and contains `Export Settings`, `Import Settings`, `Aspect ratio`, `Canvas width`, `Canvas height`, and optional `Resolution scale` in that order. Do not split the canvas size fields and settings-transfer actions into app-authored sections.
|
|
150
156
|
|
|
151
|
-
For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` to the same first technical section. The slider changes backing resolution from `
|
|
157
|
+
For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` to the same first technical section. The slider changes backing resolution from `1` to `2` without changing visible canvas size; DOM/SVG/vector-native previews should not use it.
|
|
152
158
|
|
|
153
|
-
If a controls panel shows only `Export Settings` and `Import Settings` in the first runtime section, check the canvas sizing decision. Product-output apps
|
|
159
|
+
If a controls panel shows only `Export Settings` and `Import Settings` in the first runtime section, check the canvas sizing decision. Product-output apps need `editable-output`; only intrinsic media and non-product/internal fixed fixtures should omit visible canvas size inputs.
|
|
154
160
|
|
|
155
161
|
For user-edited settings that should survive reload, use schema `persistence` with a stable app-specific key. When localStorage persistence is enabled, acceptance must prove a user setting restores after a real browser reload. Do not use settings import/export as a workaround for broken persistence.
|
|
156
162
|
|
|
@@ -168,7 +174,7 @@ Animated apps with `Export Video` must include a separate `Video Export` control
|
|
|
168
174
|
|
|
169
175
|
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
176
|
|
|
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`.
|
|
177
|
+
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
178
|
|
|
173
179
|
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
180
|
|
|
@@ -212,4 +218,4 @@ pnpm dev
|
|
|
212
218
|
|
|
213
219
|
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
220
|
|
|
215
|
-
Do not stop existing local servers to free `3002
|
|
221
|
+
Do not stop existing local servers to free `3002` during a first start. `pnpm dev`, `pnpm preview`, and browser verification prefer `3002`, then automatically use the next free port when it is occupied. When restarting an app server you already started, use `pnpm dev:restart` or `pnpm preview:restart`; restart mode reuses the previously saved app port, stops the listener on that exact port if needed, force-stops it if the port is still occupied, and starts on the same port again.
|
|
@@ -12,7 +12,7 @@ If a built-in owner is discovered after a custom workaround, replace the workaro
|
|
|
12
12
|
|
|
13
13
|
Common exact-owner choices:
|
|
14
14
|
|
|
15
|
-
- Use `gradient` for adjustable gradients, color transitions, gradient fills, stops, type, and angle. Do not replace it with two `color` controls. The built-in Gradient owns type/angle, the draggable stop track, and the Stops list; the full Gradient control uses content-width internal dividers only when it shares a section with sibling controls, with 18px between each divider and the control content. If Gradient is the first control in that section, only the bottom internal divider renders.
|
|
15
|
+
- Use `gradient` for adjustable gradients, color transitions, gradient fills, stops, type, and angle. Do not replace it with two `color` controls. The built-in Gradient owns type/angle, the draggable stop track, and the Stops list; the full Gradient control uses content-width internal dividers only when it shares a section with sibling controls, with 18px between each divider and the control content. If Gradient is the first control in that section, only the bottom internal divider renders; if it is last, only the top internal divider renders.
|
|
16
16
|
- Use `fontPicker` for typography that includes font family, weight, size, text case, text color/opacity, letter spacing, or line height.
|
|
17
17
|
- Use `colorOpacity` when one product entity owns both color and opacity.
|
|
18
18
|
- Use `rangeSlider` or `rangeInput` for lower/upper bounds or from/to ranges.
|
|
@@ -22,9 +22,15 @@ 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
|
+
- For a single `actions` button, the control label and the button label must not be identical. Keep the button as the command verb and make the control label a concise one- or two-word context such as `Ink wash`, `Palette action`, or `Current layer`.
|
|
27
|
+
- If an `actions` control has a visible label, the label is always above the buttons. Do not use a side-label layout with buttons on the right.
|
|
28
|
+
- Actions render in 50% cells: one button uses the left half, two buttons fill one row, and larger groups continue in two columns.
|
|
29
|
+
- Do not stretch an odd trailing action full-width or center it; keep it in the left 50% cell.
|
|
26
30
|
- Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
|
|
27
31
|
|
|
32
|
+
When upload/import is part of the source-material flow, `fileDrop` owns the empty/upload state. Do not put a custom pre-upload design, CTA, helper copy, fake sample output, decorative placeholder, or agent-made source preset on the canvas. A default procedural/reference source is allowed only when the prompt or reference explicitly defines it and the worklog records that evidence.
|
|
33
|
+
|
|
28
34
|
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.
|
|
29
35
|
|
|
30
36
|
For local reset-like `actions`, use product-specific values such as `reset-current-layer`, `reset-palette`, or `reset-current-stop` and handle them through `ToolcraftApp onPanelAction`. Do not use a bare `reset` value unless the action intentionally runs global `controls.reset`.
|
|
@@ -32,7 +38,7 @@ For local reset-like `actions`, use product-specific values such as `reset-curre
|
|
|
32
38
|
## Dividers
|
|
33
39
|
|
|
34
40
|
- Full-width dividers belong only to panel sections.
|
|
35
|
-
- Large built-in compound controls inside a section render content-width internal dividers only when their parent section contains more than one visible control item. Keep 18px between each rendered internal divider and the compound control content. If the compound control is the first item in that section, render only its bottom internal divider and remove the top internal padding. This applies to `gradient`, `fontPicker`, RGB `curves`, `channelMixer`, and `palette`. Single `curves` are one labeled control and do not render internal dividers.
|
|
41
|
+
- Large built-in compound controls inside a section render content-width internal dividers only when their parent section contains more than one visible control item. Keep 18px between each rendered internal divider and the compound control content. If the compound control is the first item in that section, render only its bottom internal divider and remove the top internal padding. If it is the last item, render only its top internal divider and remove the bottom internal padding. This applies to `gradient`, `fontPicker`, RGB `curves`, `channelMixer`, and `palette`. Single `curves` are one labeled control and do not render internal dividers.
|
|
36
42
|
- If a section contains exactly one control, whether simple or compound, render only the parent section dividers.
|
|
37
43
|
- Do not add full-width borders inside a compound control, and do not put dividers only around an internal subsection such as Gradient Stops.
|
|
38
44
|
- Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers.
|
|
@@ -52,7 +58,7 @@ Visual discrete sliders must declare `step`; the runtime derives one marker per
|
|
|
52
58
|
|
|
53
59
|
Schema sliders always render stacked at full width. Do not put `slider` or `rangeSlider` controls in two-column inline rows. The only built-in exception is `fontPicker`, whose letter-spacing and line-height footer sliders stay paired inside that component.
|
|
54
60
|
|
|
55
|
-
Use slider `unit` only for measurement
|
|
61
|
+
Use slider `unit` only for real measurement suffixes: `%`, `px`, `°`, `s`, `ms`, `fps`, `rows`, `cols`, or a similarly useful domain unit. Do not use `unit: "x"`; scale, multiplier, intensity, opacity, strength, depth, and shader amount sliders display plain numbers unless a real measurement unit applies. Do not use `unit` to repeat the entity already named by the section or label. Avoid `Letters` + `letters`, `Shape Density / Count` + `shapes`, `Words` + `words`, `Symbols` + `symbols`, `Items` + `items`, `Particles` + `particles`, and `Layers` + `layers`. If the numeric value needs an entity noun to make sense, rename the label or section instead of appending the noun to the value. Compact units render tight (`70%`, `24px`, `8s`); word or acronym units render with a space (`5 cols`, `17 fps`) only when they are truly needed.
|
|
56
62
|
|
|
57
63
|
Slider value labels are editable only when they contain a numeric value. Textual state labels such as `Normal` are display-only and must not expose hover or click editing affordances.
|
|
58
64
|
|
|
@@ -60,7 +66,7 @@ Range sliders are always full-width two-thumb controls. Do not put a `rangeSlide
|
|
|
60
66
|
|
|
61
67
|
Range slider value editing accepts common range separators such as `20/80`, `20-80`, `20 - 80`, `20 80`, and en-dash ranges. Use the built-in parser instead of adding custom label parsing.
|
|
62
68
|
|
|
63
|
-
Discrete sliders must still drag smoothly. Heavy preview work
|
|
69
|
+
Discrete sliders must still drag smoothly. Heavy preview work may be coalesced, cached, or split into lightweight live feedback plus heavier refinement, but the canvas/product output must not stay unchanged until pointer release.
|
|
64
70
|
|
|
65
71
|
When a slider or range slider is intentionally unavailable, use schema `disabled: true`. Do not draw custom disabled-looking slider rows or disable only the renderer response while leaving the control active.
|
|
66
72
|
|
|
@@ -82,6 +88,8 @@ The disabled value is preserved. When the user switches back to a mode where the
|
|
|
82
88
|
|
|
83
89
|
Use `visibleWhen` instead of `disabledWhen` when a control or section belongs only to another template, type, mode, variant, or count. Example: in a co-brand lockup, `Partner` belongs to text identity mode and `Partner logo` belongs to logo identity mode. For count-controlled banks, hide inactive siblings: if `Shades` is `2`, `Shade 3`, `Shade 4`, and `Shade 5` are not visible. Do not keep inactive controls visible and enabled while making the renderer ignore them.
|
|
84
90
|
|
|
91
|
+
If `visibleWhen` or `disabledWhen` points to a selector for the same target entity or selected branch, keep the selector and the dependent controls in the same semantic section. A section that exists only because one selector option is active is not a separate product section just because the branch uses a standalone control. Use one section with conditional controls; split only when the dependent branch is a separate product entity with its own workflow and acceptance evidence.
|
|
92
|
+
|
|
85
93
|
## Palette
|
|
86
94
|
|
|
87
95
|
Use `palette` only when the product needs a constrained token palette: family plus shade. It is for design-system color tokens, not for arbitrary color entry.
|
|
@@ -92,6 +100,8 @@ Use `color` for free hex colors, `colorOpacity` when opacity belongs to the same
|
|
|
92
100
|
|
|
93
101
|
Use segmented controls only for compact mode choices that preserve every cell's internal padding.
|
|
94
102
|
|
|
103
|
+
Segmented controls are full-width. Do not place `segmented` beside Switch, Color, Select, or another control in a two-column inline row; use `select` when a finite choice must occupy a half-width column.
|
|
104
|
+
|
|
95
105
|
Limits:
|
|
96
106
|
|
|
97
107
|
- at most four options;
|
|
@@ -100,10 +110,18 @@ Limits:
|
|
|
100
110
|
|
|
101
111
|
If cells clip, collide, lose padding, or force labels into adjacent cells, shorten labels first. If compact labels still fail, use `select`.
|
|
102
112
|
|
|
113
|
+
## Sliders
|
|
114
|
+
|
|
115
|
+
Slider and range slider controls are live canvas controls. Dragging a thumb must update runtime state and product output while the drag is in progress, not only on pointer release, blur, an Apply action, or a final commit. Browser acceptance should drag the real control and prove the canvas/product observable changes during the interaction.
|
|
116
|
+
|
|
117
|
+
If live slider updates are slow, fix the renderer path first: update uniforms or stable buffers, cache decoded media and expensive derived inputs, coalesce preview work to `requestAnimationFrame`, cancel stale async renders, move heavy work off React renders, or change renderer strategy. Only in an extreme measured performance ceiling may the app use a degraded live preview or delayed heavy refinement; even then, the user must see immediate canvas feedback during drag and the worklog must record the evidence.
|
|
118
|
+
|
|
103
119
|
## Sections
|
|
104
120
|
|
|
105
121
|
Build controls-panel sections from product entities and workflow stages, not component types. Keep sections discrete: two to seven product controls is the normal size. When a section grows past seven controls or mixes several meanings, split it into specific sections such as `Flow Motion`, `Flow Geometry`, `Letter Burst`, `Shape Colors`, `Logo Glow`, `Logo Plate`, or `Text Block`. Do not reuse the same section title for multiple sections.
|
|
106
122
|
|
|
123
|
+
Section splitting must preserve dependency cohesion. A selector that chooses a mode, type, source, variant, or include state stays with the controls it gates when they share the same product entity. Prefer internal compound-control dividers, tighter labels, or a more specific section title before moving a gated branch into a separate section.
|
|
124
|
+
|
|
107
125
|
Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created setup/settings sections use the technical title `Setup` but render without a visible heading; sticky footer action sections use the technical title `Export` but render without a visible heading. Do not omit a title on app-authored body sections to avoid naming decisions; choose the nearest honest product context instead.
|
|
108
126
|
|
|
109
127
|
Every visible section title renders through the standard 36px collapsible header row with vertically centered text and the runtime collapse icon. Do not hand-build section headers in generated apps.
|
|
@@ -124,25 +142,31 @@ Keep color inside a section when it configures the same entity as nearby control
|
|
|
124
142
|
|
|
125
143
|
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
144
|
|
|
127
|
-
|
|
145
|
+
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
146
|
|
|
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.
|
|
147
|
+
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
148
|
|
|
131
149
|
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
150
|
|
|
133
151
|
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
152
|
|
|
135
|
-
Mixed inline rows require label parity: every field in that row has a visible label.
|
|
153
|
+
Mixed inline rows usually require label parity: every field in that row has a visible label. Toggle-plus-parameter rows are the section-owned exception: keep the `switch`/`checkbox` label visible and set the non-toggle parameter to `label: false`; if the parameter label is needed, stack the controls instead. All 50/50 inline rows use the same horizontal column gap as paired `select` controls; do not give toggle-plus-parameter rows a separate wider or narrower gap. The required `Background` section row uses 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
154
|
|
|
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
|
|
155
|
+
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
156
|
|
|
139
157
|
## File Upload
|
|
140
158
|
|
|
141
159
|
Use `fileDrop` for source material uploads in the controls panel. Do not place upload UI on the canvas.
|
|
142
160
|
|
|
143
|
-
|
|
161
|
+
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.
|
|
162
|
+
|
|
163
|
+
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`.
|
|
164
|
+
|
|
165
|
+
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.
|
|
166
|
+
|
|
167
|
+
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.
|
|
144
168
|
|
|
145
|
-
|
|
169
|
+
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
170
|
|
|
147
171
|
In multi-layer apps, deletion and visibility belong to the Layers panel; `fileDrop` stays an upload target.
|
|
148
172
|
|
|
@@ -164,6 +188,8 @@ Use `fontPicker` for typography choices that need font preview plus weight, size
|
|
|
164
188
|
|
|
165
189
|
The value is one object: `{ fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`. Typography renderers and exports must consume all eight parts.
|
|
166
190
|
|
|
191
|
+
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.
|
|
192
|
+
|
|
167
193
|
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
194
|
|
|
169
195
|
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.
|
|
@@ -184,6 +210,10 @@ Use variants by product meaning:
|
|
|
184
210
|
- `chromaOffset`: RGB or chromatic offset;
|
|
185
211
|
- `toneBias`: split-tone, duotone, or color-grading bias.
|
|
186
212
|
|
|
213
|
+
Default/spatial vector pads use screen-coordinate movement. Dragging the pad left/up lowers `vector.x` and `vector.y`, so an object on the canvas moves left/up without renderer-side Y inversion. Use `coordinateMode: "cartesian"` only when the product intentionally exposes mathematical Y-up coordinates.
|
|
214
|
+
|
|
215
|
+
Holding Shift while dragging a vector pad locks movement to the dominant axis. Use the built-in `vector` control for constrained two-axis movement instead of creating a custom pad.
|
|
216
|
+
|
|
187
217
|
Do not add custom vector sizing props. Choose the right number, variant, and section grouping, then let runtime sizing handle the pad.
|
|
188
218
|
|
|
189
219
|
## Curves
|
|
@@ -207,11 +237,11 @@ Acceptance for curves should include an off-center control point near an edge so
|
|
|
207
237
|
|
|
208
238
|
## Text And Code
|
|
209
239
|
|
|
210
|
-
Use `text` for short single-line strings: names, small values, compact prompts, titles, and tokens.
|
|
240
|
+
Use `text` for short single-line strings: button labels, canvas labels, names, small values, compact prompts, titles, captions, badges, and tokens.
|
|
211
241
|
|
|
212
242
|
For `text`, separate content from settings. `commitMode` defaults to `"content"`: content strings such as prompts, names, titles, tokens, and short text update while the user types. Use `commitMode: "setting"` for text inputs that edit settings such as font size, numeric-like style values, dimensions, ids, or configuration fields; setting text commits on blur or Enter. Canvas width and Canvas height are runtime-owned editable-size fields and always commit on blur or Enter.
|
|
213
243
|
|
|
214
|
-
Use `code` / `CodeTextarea` as the base multiline content editor for any potentially long value: prompts, instructions, JSON, CSS, shader code, scripts, templates, or other structured text. It applies while typing, is capped at 12 visible lines, and long content scrolls inside the textarea instead of making the controls panel taller. Do not name a section `Code` unless the product value is actually code.
|
|
244
|
+
Use `code` / `CodeTextarea` as the base multiline content editor for any potentially long value: long prompts, multiline text, instructions, JSON, CSS, shader code, scripts, templates, or other structured text. It applies while typing, is capped at 12 visible lines, and long content scrolls inside the textarea instead of making the controls panel taller. Do not use it for short one-line button/canvas text such as `Glass`, `Submit`, `Title`, or `Badge`; use `text`. If the default value is short but the intended input is long or structured, make that reason explicit in `description`. Do not name a section `Code` unless the product value is actually code.
|
|
215
245
|
|
|
216
246
|
## Labels
|
|
217
247
|
|
|
@@ -231,7 +261,7 @@ Switch and checkbox labels name the setting context, not the action. Do not pref
|
|
|
231
261
|
|
|
232
262
|
Two adjacent `switch` or `checkbox` controls for the same product entity must share one inline row when every visible label fits without truncation. Use short one- or two-word labels such as `Snap X` and `Snap Y`, or `Glow` and `Loop`. The runtime auto-pairs safe adjacent toggles by target entity; use explicit layout groups only when pairing a toggle with a non-toggle parameter. If either label would truncate in half-width, remove the inline group and let the toggles stack.
|
|
233
263
|
|
|
234
|
-
A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and the controls edit the same entity. This row is always equal-width: each control occupies one half. Example: `Loop` plus
|
|
264
|
+
A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and the controls edit the same entity. This row is always equal-width: each control occupies one half, using the same horizontal column gap as a paired `select` row. The non-toggle parameter uses `label: false`; if that parameter label is needed for clarity, stack the controls instead. Example: `Loop` plus an unlabeled duration field, or `Include` plus unlabeled background color inside the required `Background` section. If the section title already names the toggle context, shorten the toggle label instead of repeating the title.
|
|
235
265
|
|
|
236
266
|
## Layers
|
|
237
267
|
|
|
@@ -281,11 +311,11 @@ Recalculate settings-transfer eligibility after adding, removing, or reorganizin
|
|
|
281
311
|
|
|
282
312
|
When settings transfer and editable-output canvas sizing are both enabled, the first technical `Setup` runtime section is mandatory and contains `Export Settings`, `Import Settings`, `Aspect ratio`, `Canvas width`, `Canvas height`, and optional `Resolution scale` in that order. Do not split these into separate app-authored sections, rename the controls, or rebuild the block by hand.
|
|
283
313
|
|
|
284
|
-
If only `Export Settings` and `Import Settings` appear in that section, the schema is not using `editable-output` canvas sizing or already owns `canvas.size.width` / `canvas.size.height` controls. For product-output apps,
|
|
314
|
+
If only `Export Settings` and `Import Settings` appear in that section, the schema is not using `editable-output` canvas sizing or already owns `canvas.size.width` / `canvas.size.height` controls. For product-output apps, fix the canvas sizing decision instead of adding hand-built size fields. A reference, previous app, fixed-format baseline, or user-provided default size does not justify hiding size controls; keep those dimensions as editable `canvas.size` defaults.
|
|
285
315
|
|
|
286
316
|
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.
|
|
287
317
|
|
|
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 `
|
|
318
|
+
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 `2` 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.
|
|
289
319
|
|
|
290
320
|
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.
|
|
291
321
|
|
|
@@ -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.
|