@pixel-point/toolcraft 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +4 -2
  2. package/package.json +1 -1
  3. package/src/cli.mjs +18 -45
  4. package/src/cli.test.mjs +141 -6
  5. package/src/command-runner.mjs +71 -0
  6. package/src/dependency-install.mjs +87 -0
  7. package/src/generate.mjs +32 -4
  8. package/src/generate.test.mjs +116 -15
  9. package/src/package-json.mjs +11 -2
  10. package/src/package-json.test.mjs +27 -0
  11. package/src/package-manager.mjs +123 -0
  12. package/src/package-manager.test.mjs +80 -0
  13. package/templates/runtime/contracts/component-contracts.test.ts +1 -1
  14. package/templates/runtime/contracts/component-contracts.ts +1 -1
  15. package/templates/starter/AGENTS.md +4 -3
  16. package/templates/starter/docs/toolcraft/README.md +15 -0
  17. package/templates/starter/docs/toolcraft/acceptance-testing.md +2 -0
  18. package/templates/starter/docs/toolcraft/assembly-workflow.md +35 -171
  19. package/templates/starter/docs/toolcraft/component-rules.md +12 -188
  20. package/templates/starter/docs/toolcraft/core/control-selection.md +93 -0
  21. package/templates/starter/docs/toolcraft/core/layout.md +104 -0
  22. package/templates/starter/docs/toolcraft/core/media-upload.md +85 -0
  23. package/templates/starter/docs/toolcraft/core/performance.md +83 -0
  24. package/templates/starter/docs/toolcraft/core/reference-study.md +115 -0
  25. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +53 -0
  26. package/templates/starter/docs/toolcraft/core/setup-export.md +86 -0
  27. package/templates/starter/docs/toolcraft/core/timeline-animation.md +67 -0
  28. package/templates/starter/docs/toolcraft/custom-controls.md +2 -0
  29. package/templates/starter/docs/toolcraft/performance.md +2 -0
  30. package/templates/starter/docs/toolcraft/renderer-technique.md +2 -0
  31. package/templates/starter/docs/toolcraft/schema-reference.md +117 -367
  32. package/templates/starter/docs/toolcraft/workflow.md +12 -10
  33. package/templates/starter/package.json +1 -0
  34. package/templates/starter/scripts/check-toolcraft-docs.mjs +28 -6
  35. package/templates/starter/scripts/run-vite-on-free-port.mjs +25 -6
  36. package/templates/starter/src/app/starter-acceptance.test.ts +24 -15
  37. package/templates/starter/src/app/starter-performance.test.ts +17 -7
@@ -1,5 +1,7 @@
1
1
  # Assembly Workflow
2
2
 
3
+ > Reading route: start with `workflow.md`. Core generated-app rules live in `core/*`; this file is the focused runtime assembly path.
4
+
3
5
  Build the app from the local Toolcraft runtime copy. Do not recreate controls, panels, toolbar, canvas behavior, timeline, layers, or app chrome by hand.
4
6
 
5
7
  Use:
@@ -9,6 +11,8 @@ Use:
9
11
  - `@/toolcraft/runtime/styles.css` for runtime styles.
10
12
  - `@/toolcraft/ui` for visual components.
11
13
 
14
+ ## Runtime Path
15
+
12
16
  Declare the product with `defineToolcraft`. Render through `ToolcraftApp`.
13
17
 
14
18
  ```tsx
@@ -26,15 +30,23 @@ export function AppHome() {
26
30
  }
27
31
  ```
28
32
 
29
- Routes must render `ToolcraftApp` directly. Do not compose `ToolcraftRoot`, `CanvasShell`, `ControlsPanel`, `LayersPanel`, `TimelinePanel`, or `ToolbarPanel` by hand in product routes. If a runtime surface has a performance or behavior issue, fix the shared runtime contract instead of replacing the surface with app-level UI.
33
+ Routes must render `ToolcraftApp` directly. Do not compose `ToolcraftRoot`, `CanvasShell`, `ControlsPanel`, `LayersPanel`, `TimelinePanel`, or `ToolbarPanel` by hand. If a runtime surface has a performance or behavior issue, fix the shared runtime instead of replacing the surface locally.
34
+
35
+ Allowed app extension points:
30
36
 
31
- App-specific source may use only runtime extension points: schema controls, `canvasContent` for product output, `controlRenderers` for true custom controls, `onPanelAction` for sticky footer actions, and runtime commands/hooks. Do not render built-in control components such as `SliderControl`, `SelectControl`, `ColorControl`, `GradientControl`, `FontPickerControl`, `FileDropControl`, or `PanelActionsControl` directly in app code. Declare them in schema so layout, reset, history, visibility, keyframes, labels, and tests stay runtime-owned.
37
+ | Extension point | Use for |
38
+ | --- | --- |
39
+ | Schema controls | Built-in controls, targets, defaults, visibility, panel actions. |
40
+ | `canvasContent` | Product output only. |
41
+ | `controlRenderers` | True custom controls only after the built-in fit check. |
42
+ | `onPanelAction` | Sticky footer product actions. |
43
+ | Runtime commands/hooks | History, media, canvas, timeline, layers, and controlled app behavior. |
32
44
 
33
- Read `appSchema.assembly` before adding custom JSX. It lists enabled surfaces, capabilities, commands, and runtime assumptions.
45
+ Do not render built-in controls such as `SliderControl`, `SelectControl`, `ColorControl`, `GradientControl`, `FontPickerControl`, `FileDropControl`, or `PanelActionsControl` directly in app code. Declare them in schema so layout, reset, history, visibility, keyframes, labels, and tests stay runtime-owned.
34
46
 
35
- Toolbar history owns undo/redo buttons and keyboard shortcuts. When `toolbar.history` is enabled, the runtime handles `Cmd/Ctrl+Z`, `Cmd/Ctrl+Shift+Z`, and `Ctrl+Y`, while ignoring shortcuts inside inputs, textareas, selects, and editable value labels. Do not add route-local undo/redo keyboard listeners.
47
+ ## Product Readiness
36
48
 
37
- The starter baseline is deliberately neutral. It must not include demo controls, prompt fields, timeline, or layers until the product behavior requires them. Use tests and docs fixtures to exercise component coverage; do not expose those fixtures in the starting product schema.
49
+ The starter baseline is neutral: canvas/upload/toolbar shell only. Do not include demo controls, prompt fields, timeline, or layers until product behavior requires them.
38
50
 
39
51
  Once the folder is a real product, switch `src/app/app-acceptance.ts` from neutral readiness to:
40
52
 
@@ -47,169 +59,45 @@ export const appProductReadiness = {
47
59
  } as const;
48
60
  ```
49
61
 
50
- Do not leave `mode: "starter"` in a renamed product folder or after adding product controls, `canvasContent`, timeline, layers, or acceptance rows.
51
-
52
- ## Control Sections
53
-
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
-
56
- Bad section titles: `Controls`, `Settings`, `Options`, `Sliders`, `Inputs`, `Buttons`, `Color`, `Colors`.
57
-
58
- Good section titles name the thing being edited: `Background`, `Object`, `Square 1 (Right)`, `Token Pattern`, `Motion`, `Tone Mapping`, `Export`.
59
-
60
- Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created `Setup` renders as the first visible headerless controls block with no title, reset action, collapse button, or collapsed state; sticky footer action sections use the technical title `Export` but render without a visible heading.
61
-
62
- 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.
63
-
64
- Section expand/collapse uses the standard runtime height/opacity animation. Do not replace it with instant custom section visibility.
65
-
66
- Runtime `Setup` and ordinary controls-panel body sections use 8px top spacing and 24px bottom spacing for their control content. Sticky footer action sections keep their dedicated spacing.
67
-
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
-
70
- When a selector controls branch visibility through `visibleWhen`, treat both the selector and its branch controls 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. Do not use `disabledWhen` for product branches.
71
-
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.
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
-
76
- ## Figma Source
77
-
78
- When the prompt provides a Figma URL, treat the Figma file as the design source of truth.
62
+ ## Controls
79
63
 
80
- Required flow:
64
+ Before writing product sections, export `starterControlSectionInventory`. Each product section declares its title, targets, product entity or workflow stage, and grouping reason.
81
65
 
82
- - Use Figma MCP/design context before implementation.
83
- - Inspect the target node, layer tree, component instances, variants, text nodes, variables, styles, and assets.
84
- - Recreate the design from the Figma structure and Toolcraft runtime/component contracts.
85
- - Use screenshots only for final visual QA after reading the file structure.
66
+ Use `core/layout.md` for section grouping, dependency cohesion, headers, reset, collapse, spacing, dividers, labels, and inline rows. Use `core/control-selection.md` and `component-rules.md` before choosing concrete controls. Built-in compound controls stay compound; extend the kit instead of splitting owned fields into neighboring controls.
86
67
 
87
- Do not implement a Figma design by eye from an image, screenshot, exported PNG, or rough visual memory. If the Figma URL is not node-specific, inspect the file/page metadata and choose the relevant node only when it is unambiguous; otherwise ask for a node-specific link.
88
-
89
- ## Video References
90
-
91
- When the prompt provides a video, GIF, screen recording, contact sheet, or extracted-frame sequence as a reference, study it as behavior before implementation. This applies to new Toolcraft apps and reference-runtime-clone work.
92
-
93
- Write a Video Reference Study before coding. Record the inspected source, extraction method, timecoded storyboard frames, visible state in each frame, behavior inferred from each frame, frame-to-frame transition analysis, behavior decomposition, and acceptance mapping. The transition analysis must explain what changes between frames: which entities move, which anchors or state persist, what releases or retargets, what input or timeline state drives the change, and which behavior should be copied.
94
-
95
- Do not implement a video reference from a single screenshot, a generic visual summary, or a few static style observations. If the video cannot be opened or frames cannot be extracted, stop and record the blocker instead of guessing the temporal behavior.
96
-
97
- Declare `starterTransferMode.videoReferenceStudy` when the app uses a video reference. Each `acceptanceMapping` item must point to a real acceptance row that proves the copied behavior with automated and browser coverage. `docs/toolcraft/agent-worklog.md` must include the Video Reference Study evidence when `Reference inputs`, `Source/reference checked`, or `Source reviewed` cites a video, GIF, screen recording, contact sheet, or extracted frames.
98
-
99
- ## Product Output
68
+ ## Canvas And Product Output
100
69
 
101
70
  Use `canvasContent` only for product output: WebGL, Canvas 2D, SVG, DOM product text, shader previews, generated previews, export previews, or product editing handles.
102
71
 
103
- 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. If the default source is a file, image, or background image, declare it in `media.defaultAssets` with the matching `fileDrop` `sourceTarget`; it must render as an attached file that users can remove and Reset can restore.
104
-
105
- If the uploaded image is background/source material inside the product canvas, keep the current `canvas.size` and render the image as cover/crop inside those bounds. Do not switch to `intrinsic-media`, do not resize the canvas to the image, and do not hide `Setup` canvas controls. Reserve `intrinsic-media` for explicitly justified media-viewer/source-native products with acceptance coverage.
106
-
107
- ```tsx
108
- <ToolcraftApp
109
- canvasContent={<ProductRenderer />}
110
- renderDefaultCanvasMedia={false}
111
- schema={appSchema}
112
- />
113
- ```
114
-
115
72
  `canvasContent` must not contain app UI: buttons, forms, CTAs, upload prompts, helper text, settings, menus, labels, placeholder copy, or empty-state instructions.
116
73
 
117
- Product text rendered as DOM must be marked with `data-toolcraft-product-output` or `data-toolcraft-product-text`. Product editing handles must be textless overlays, write to runtime state, and stay out of export/copy output.
118
-
119
- Preserve the runtime canvas surface. Product renderers may draw their own output background, but must not hide, replace, or make the Toolcraft canvas backing transparent.
120
-
121
- ## Reference Runtime Clone
122
-
123
- When porting an existing app, use `transferMode: "reference-runtime-clone"` unless the user explicitly asks for redesign.
124
-
125
- Preserve the reference runtime as source of truth:
126
-
127
- - animation loop and time ownership;
128
- - refs and mutable renderer state;
129
- - particles, objects, connections, spawn cadence, and lifetime rules;
130
- - pause/resume, restart, progress, export, and copy semantics;
131
- - canvas sizing and media lifecycle;
132
- - control-to-renderer mapping.
74
+ If upload/import is part of the source-material flow, use `fileDrop` and keep the pre-content canvas neutral. Do not invent canvas placeholder artwork, source CTAs, fake sample output, or hidden preset files. Use `media.defaultAssets` when the prompt or reference actually provides default files.
133
75
 
134
- Before implementation, create `starterTransferMode.referenceFeatureInventory` from the inspected reference source/runtime/UI. Include every user-visible and output-affecting behavior: controls, modes, generated objects, renderer state, media import lifecycle, canvas sizing, layers/selection, timeline/transport, export/copy, persistence, randomization, and reset behavior when present.
76
+ Use `core/runtime-boundary.md` for shell boundaries, `core/media-upload.md` for upload behavior, and `core/setup-export.md` for editable output size, background, and export sections.
135
77
 
136
- Each inventory item must name the reference feature, cite source evidence, cite feature-level behavior evidence from the original/restored/source-only reference study, describe the original behavior, describe the Toolcraft mapping, and point to an `acceptanceId` that proves the behavior. If behavior is intentionally changed or omitted, mark it `status: "intentionally-changed"` and cite explicit user approval or redesign/change-request evidence.
78
+ ## Reference And Design Sources
137
79
 
138
- Do not rely on the user to find missing reference functionality after delivery. The port is incomplete until the inventory and acceptance coverage prove the reference functionality was reviewed and transferred.
80
+ If a Figma URL is provided, use Figma MCP/design context before implementation and rebuild from file structure, not from a screenshot.
139
81
 
140
- Toolcraft still owns the shell: schema, controls, canvas, panels, toolbar, file upload, sticky footer actions, and `canvasContent`.
82
+ If a video, GIF, screen recording, contact sheet, or extracted-frame sequence is provided, write a Video Reference Study before implementation.
141
83
 
142
- Do not iframe the reference, replace the route with copied original UI, or rebuild the app as a different shell.
84
+ When porting an existing app, use `transferMode: "reference-runtime-clone"` unless the user explicitly asks for redesign. Declare `referenceStudy` plus `referenceFeatureInventory`, then prove each inspected reference feature with acceptance coverage. Use `core/reference-study.md` for the detailed reference, Figma, and video study rules.
143
85
 
144
- Reference study is required before implementation. Declare `starterTransferMode.referenceStudy` and record:
86
+ ## Timeline And Animation
145
87
 
146
- - where the reference lives;
147
- - which source/runtime files, routes, assets, and handlers were inspected;
148
- - how the original was run or restored locally in the Toolcraft environment;
149
- - which runtime/browser behaviors were checked.
88
+ Before adding animation controls, write an Animation Intent Inventory. Product animation, keyframes, playback, and video export use the top Toolcraft timeline. Autonomous no-timeline animation is allowed only for non-product decorative motion with no user-facing transport and no video export.
150
89
 
151
- Use `status: "ran-original"` when the original can run as-is. Use `status: "restored-local"` when you need to reconstruct enough of the reference inside the current environment to observe behavior. Use `status: "source-inspection-only"` only when running or restoring is blocked; include the concrete blocker and compensate with stronger source evidence and acceptance coverage.
90
+ Use `core/timeline-animation.md` for timeline mode, compact/extended timeline, seamless forward loops, duration changes, keyframes, viewport interaction performance, and video export timing.
152
91
 
153
- ## Animation Intent
92
+ ## Renderer Work
154
93
 
155
- Before adding animation controls, write an Animation Intent Inventory. Classify the animation as playback timeline, keyframes timeline, custom reference timeline, or autonomous decorative output.
94
+ For custom renderers, write the Renderer Technique Decision Matrix and Render Pipeline Inventory before code. The implementation plan maps every performance-sensitive control to the render pass it invalidates.
156
95
 
157
- If the product output is animated, use the top playback timeline by default. Use no timeline only when the motion is non-product autonomous decoration with no user-facing play/pause, scrub, duration, loop, restart, progress, export-at-time behavior, product animation, or video export. In that case, declare `starterTransferMode.animationIntent.mode = "autonomous"` and list the absent transport behavior in `behaviorCoverage`. When the loop period is known or product-derived, set `panels.timeline.defaultDurationSeconds` to that period and record the same value in `starterTransferMode.animationIntent.loopDuration` with source and evidence. Reference clones that use `referenceTimeline.mode: "toolcraft-playback"` or `"toolcraft-keyframes"` record the same proof in `starterTransferMode.referenceTimeline.loopDuration`. Runtime/template fallback 8s is not evidence. Product loops are seamless forward-only cycles by default: the first and last frames stitch, direction does not reverse, and mirror/yoyo/ping-pong behavior needs explicit user intent. Use `getToolcraftTimelineLoopTime` or `getToolcraftTimelineLoopProgress` in playback renderers instead of local wall-clock or fixed-duration phase math.
158
-
159
- Do not replace `TimelinePanel` with an app-level playback, transport, or timeline panel to work around performance. Playback/keyframe timeline UI is runtime-owned. Custom timeline UI is allowed only when a reference app has non-Toolcraft timeline behavior and `starterTransferMode.referenceTimeline.mode` is `"custom-reference-timeline"` with browser-backed `referenceTimelineCoverage`.
160
-
161
- Animated preview renderers must prioritize viewport interactions. During canvas drag, pan, pinch, zoom, and radar/center, suspend or coalesce non-essential animation work, then resume from the correct timeline or autonomous time without changing the user's play/pause state.
162
-
163
- ## Canvas Sizing And Background
164
-
165
- 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`.
166
-
167
- Every product app exposes output background controls:
168
-
169
- - `appearance.background` or `scene.background` as a schema `color` control;
170
- - `export.includeBackground` as a `switch`, `checkbox`, `select`, or `segmented` control.
171
-
172
- 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.
173
-
174
- 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.
175
-
176
- Every product app needs output delivery in sticky footer `panelActions`. Still-output apps expose `Export PNG`. Animated apps expose `Export Video` and `Export PNG`. Clipboard copy is optional and never replaces export. If an odd number of footer actions leaves one action alone in the final row, that final action spans the full row.
177
-
178
- Async product actions such as Export, Download, Copy, Generate, or Apply must return the real Promise from `ToolcraftApp onPanelAction`. The controls panel uses that Promise to show the sticky footer top accent indicator while the operation is pending. Use the `reportProgress(0..1)` callback from `onPanelAction` for determinate progress; video export reports frame-based render/encode progress, and PNG export reports phase progress for render, blob, and handoff when those phases are asynchronous. Do not fire-and-forget long export work from `onPanelAction`, and do not add custom loading strips or canvas UI for export progress.
179
-
180
- Generated apps keep a controls panel so runtime `Setup` is visible from the first run. Settings import/export is mandatory runtime `Setup` behavior there; do not put Import Settings or Export Settings in sticky footer `panelActions`; runtime inserts them in the first visible headerless `Setup` controls block.
181
-
182
- If the app also uses `editable-output` canvas sizing, `Setup` contains `Export Settings`, `Import Settings`, `Aspect ratio`, `Canvas width`, `Canvas height`, optional `Resolution scale`, and optional `Timeline` in that order. Do not split the canvas size fields and settings-transfer actions into app-authored sections, and do not declare runtime Setup targets in product sections.
183
-
184
- For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` to `Setup`. The slider changes backing resolution from `1` to `2` without changing visible canvas size; DOM/SVG/vector-native previews should not use it.
185
-
186
- When `panels.timeline` is enabled, runtime appends a `Timeline` switch as the final Setup control. This is panel UI state only: off shows compact Play-only transport, on shows the extended timeline with scrubber, duration, loop, and keyframe UI. Switching it does not pause playback, remove keyframes, change export, or write product values. If `panels.timeline` is omitted, the Timeline switch must not appear.
187
-
188
- 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. Adding app-authored Canvas width/height controls elsewhere is not an alternative.
189
-
190
- 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. Include `"media"` only when runtime media state itself should survive reload, such as predefined attached files that can be removed, reordered, or transformed. Do not use settings import/export as a workaround for broken persistence.
191
-
192
- Every app with `Export PNG` must include a separate `Image Export` controls section with:
193
-
194
- - `export.image.format` as `select`, defaulting to `png`, with `png` and `jpg` baseline options;
195
- - `export.image.resolution` as `select`, defaulting to `4k`, with `2k`, `4k`, and `8k` baseline options.
196
-
197
- `Image Export` `Format` and `Resolution` are one compact workflow pair: render them in a two-column inline row by default. For still-output apps, place `Image Export` directly above sticky footer export buttons. For animated apps with both image and video export, place `Image Export` immediately before `Video Export`.
198
-
199
- Animated apps with `Export Video` must enable the top Toolcraft timeline and include a separate `Video Export` controls section with at least:
200
-
201
- - `export.video.format` as `select`, defaulting to `mp4`, with `mp4` and `webm` baseline options;
202
- - `export.video.resolution` as `select`, defaulting to `current`, with options such as `current` and `4k`.
203
-
204
- 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.
205
-
206
- 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 uses `getToolcraftVideoExportSize` for `current` and `4k` dimensions.
207
-
208
- 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 and not PNG-style 4096px long-edge sizing. `getToolcraftVideoExportSize` keeps `current` at the current canvas/output size with even encoder-safe rounding, and keeps `4k` encoder-safe by fitting inside 3840x2160, preserving aspect ratio, and returning even dimensions. Set recording canvas dimensions before `captureStream`, `MediaRecorder`, `VideoEncoder`, or equivalent setup, and reject recorder/encoder errors instead of returning corrupt blobs. 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.
96
+ Use `renderer-technique.md` and `core/performance.md` for renderer strategy, cache keys, workload fixtures, render scale, and optimization evidence.
209
97
 
210
98
  ## Verification Tiers
211
99
 
212
- Before every edit, classify the change by blast radius and write the planned checks in the implementation note or plan:
100
+ Before every edit, classify blast radius and record the planned checks:
213
101
 
214
102
  ```md
215
103
  Verification tier: Tier N
@@ -218,27 +106,7 @@ Run: <commands and browser checks>
218
106
  Skip: <checks not needed for this pass and why>
219
107
  ```
220
108
 
221
- Use these tiers:
222
-
223
- | Tier | Use When | Required Checks |
224
- | --- | --- | --- |
225
- | Tier 0 — docs/copy | Documentation, comments, copy, labels, or titles change without schema targets, values, runtime behavior, renderer output, or layout mechanics. | Targeted docs/typecheck or targeted app test. Browser is not required unless visual text fitting is the risk. |
226
- | Tier 1 — local control presentation | One control or panel visual state changes: spacing, hover, focus, disabled, marker visibility, label fit, or component variant display. Runtime state shape and product renderer are unchanged. | Targeted unit/component test plus one focused browser check for the affected control or panel. |
227
- | Tier 2 — schema/product behavior | Controls, sections, defaults, persistence, panel actions, export actions, acceptance rows, or product behavior mapping changes. | `pnpm verify:quick` plus relevant browser acceptance. Run perf only when the changed control affects renderer workload or responsiveness. |
228
- | Tier 3 — renderer/canvas/runtime feature | Custom renderer, animation loop, canvas sizing, upload/media, timeline, layers, toolbar, export bytes, WebGL/Canvas/SVG output, zoom, radar, history, heavy control behavior changes, or a post-generation iteration that touches renderer workload or viewport stability. | `pnpm verify:quick`, targeted browser acceptance, and targeted performance scenarios only for touched workload/viewport/export paths. |
229
- | Tier 4 — final delivery/template architecture | Fresh generated app completion, folder export, commit-ready delivery, dependency changes, runtime/template/contract/CLI changes, broad refactors, or major post-generation iterations that rewrite renderer, canvas, animation, timeline/keyframes, layers, media, export, or control mapping. | Fresh folders run `pnpm install` once, then `pnpm verify:final`; for the first working product version also run and pass a browser performance checkpoint with the current AI agent's controlled browser when available, using `pnpm verify:perf` only as fallback, then start `pnpm dev` to provide the local URL. |
230
-
231
- Choose the tier by blast radius, not by line count. If uncertain, move one tier higher, not automatically to Tier 4.
232
-
233
- Do not rerun `pnpm install` after every edit. Run it after fresh export, dependency changes, lockfile changes, or a missing package error.
234
-
235
- Use `pnpm verify:ui` when a tier calls for the browser acceptance suite without the performance suite. Use the agent-controlled browser for focused checks when available; use a focused named Playwright test only as fallback when no agent browser is available or the relevant CI/non-agent check is already known.
236
-
237
- Run a full performance checkpoint only when the first working version of the app exists, or when the user explicitly asks to optimize performance, fix lag, remove jank, speed up animation, stabilize drag/zoom, or otherwise complains about performance. Prefer the agent-controlled browser; use `pnpm verify:perf` only as the Playwright fallback.
238
-
239
- Feature loops after the first working version do not run the full performance suite by default. Renderer, canvas, animation, export, timeline, layers, `canvas.renderScale`, bug fixes, and performance-sensitive controls still need targeted functional/browser checks first, plus targeted performance scenarios only when they directly exercise the touched path. Record any skipped full performance run and reason in the worklog.
240
-
241
- The app is not complete when required checks are failed, incomplete, pending, blocked, or listed as skipped. First working product delivery must record `pnpm verify:final` and the browser performance checkpoint as passed in the worklog, including runner `agent-browser` or `playwright-fallback`. After the first working version, skipping the full performance suite is valid only when the worklog explicitly says the full performance checkpoint is not required for a post-first-working non-performance edit.
109
+ Use `pnpm verify:ui` when browser acceptance is required without the performance suite. Run `pnpm verify:final` for first working product delivery, folder export, runtime/template/contract/CLI changes, broad refactors, and final gates. Full performance checkpoints run only for the first working product version or explicit performance complaints; otherwise run targeted performance checks only for touched paths.
242
110
 
243
111
  For final delivery, run:
244
112
 
@@ -246,7 +114,3 @@ For final delivery, run:
246
114
  pnpm verify:final
247
115
  pnpm dev
248
116
  ```
249
-
250
- Browser verification must use the real Toolcraft shell plus renderer output. `pnpm verify:final` runs the full static, build, and browser functional gate. The default `pnpm test:browser` / `pnpm verify:ui` gate excludes every Playwright test whose name contains `browser perf:`, including performance audit and budget scenarios. The browser performance checkpoint is intentionally separate and only runs for the two full-performance triggers; `pnpm verify:perf` is the Playwright fallback command for that checkpoint. `pnpm dev` is intentionally separate because it keeps the local server running.
251
-
252
- 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 only while assigning the app's first saved port. After that, normal dev/preview starts use the saved port; if that port already serves the same app, report the existing URL instead of creating a second server. A launch is valid only after the selected port serves the current app root through the Toolcraft server identity endpoint and the app title marker from `index.html`; a random listener on that port is not enough. 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, starts on the same port again, and verifies the same app identity before saving/reporting the port.
@@ -1,48 +1,14 @@
1
1
  # Component Rules
2
2
 
3
- ## Control Decision Catalog
4
-
5
- Choose controls by product value model before UI appearance.
6
-
7
- - Exact owner: if the value model belongs to a built-in, use that built-in.
8
- - Best fit: if multiple built-ins can work, choose one and record the reason.
9
- - Custom escape hatch: use custom controls only after documenting checked built-ins and why the closest one is insufficient.
10
-
11
- If a built-in owner is discovered after a custom workaround, replace the workaround with the built-in.
12
-
13
- Common exact-owner choices:
3
+ > Reading route: start with `workflow.md`. Core generated-app rules live in `core/*`; this file is a focused component reference for the topic below.
14
4
 
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
- - Use `fontPicker` for typography that includes font family, weight, size, text case, text color/opacity, letter spacing, or line height.
17
- - Use `colorOpacity` when one product entity owns both color and opacity.
18
- - Use `rangeSlider` or `rangeInput` for lower/upper bounds or from/to ranges.
19
- - Use `curves` for editable tone, response, easing, remapping, opacity, depth, mask, or channel curves.
20
- - Use `vector` for position, offset, direction, focus, anchor, light direction, or color-balance pads.
21
- - Use `fileDrop` for source material uploads.
22
- - Use `imagePicker` for choosing one visual option from a set.
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
- - Use `actions` for local section commands that affect only the nearby entity, such as randomize palette, normalize weights, sort glyphs, clear selection, duplicate item, or reset current stop.
25
- - Use `collectionActions` for repeatable product entities whose actual item list can grow or shrink, such as colors, glyphs, symbols, points, rules, variants, object entries, or typography style entries. Use it instead of a count slider when the user edits the actual set. The item list must be runtime state that changes preview/export, not panel-only row chrome. The collection control shows the collection `label` on the left and remove/add icon buttons on the right. Homogeneous repeated items do not show visible per-item labels like `Color 1`, `Color 2`, `Item 1`, or `Item 2` when the collection label already names the group. Item controls should use built-ins such as `color`, `colorOpacity`, `text`, `select`, `segmented`, `slider`, `switch`, `checkbox`, `rangeInput`, or `fontPicker` before any custom renderer. Use `fontPicker` as the item control when each item is a text style or typography entity; do not split its owned fields into neighboring collection controls.
26
- - 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.
30
- - Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
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. If the default source is a file, image, or background image, declare it in `media.defaultAssets` with `sourceTarget` matching the `fileDrop` control so it renders as an attached file rather than a hidden renderer constant.
33
-
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.
5
+ ## Control Decision Catalog
35
6
 
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`.
7
+ Use `core/control-selection.md` for the built-in fit check, exact control owners, compound-control ownership, actions, collection actions, vector ownership, and the custom control gate.
37
8
 
38
9
  ## Dividers
39
10
 
40
- - Full-width dividers belong only to panel sections.
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.
42
- - If a section contains exactly one control, whether simple or compound, render only the parent section dividers.
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.
44
- - Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers.
45
- - `collectionActions` is a compound control when it shares a section with generated item controls, so it follows the same content-width divider rules. Place it at the start of the controlled section. Generated item controls still follow normal density rules: plain colors use equal 50% columns when they fit, while color+opacity items stay stacked.
11
+ Use `core/layout.md` for section dividers and compound-control divider rules. Component-specific exceptions are documented in the relevant component sections below.
46
12
 
47
13
  ## Sliders
48
14
 
@@ -102,71 +68,21 @@ Standalone `select` controls render stacked and full-width: label above, dropdow
102
68
 
103
69
  Use a two-column inline row only for related short `select` pairs that tune one workflow or entity, such as export `Format` and `Resolution`. If either label or selected value clips, truncates, or loses internal padding, stack the pair and record the fit reason.
104
70
 
105
- ## Sliders
106
-
107
- 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.
71
+ ## Slider Responsiveness
108
72
 
109
- 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.
73
+ Use `core/performance.md` for live slider responsiveness, renderer optimization, and browser evidence requirements.
110
74
 
111
75
  ## Sections
112
76
 
113
- 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.
114
-
115
- 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.
116
-
117
- Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created `Setup` renders as the first visible headerless controls block with no title, reset action, collapse button, or collapsed state; 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.
118
-
119
- 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.
120
-
121
- Section expand/collapse uses the standard runtime height/opacity animation. Do not replace it with instant custom section visibility.
122
-
123
- Section collapsed/expanded state persists as a per-app runtime UI preference. It is not undo/redo state, not settings import/export state, and `Reset controls` must not clear it. Runtime `Setup` is not collapsible; sticky footer `Export` sections are not collapsible.
124
-
125
- Ordinary section headers expose the runtime section reset action before the collapse button. It dispatches `controls.resetTargets` and restores only that section's control targets to their schema `defaultValue`.
126
-
127
- Runtime `Setup` and ordinary controls-panel body sections use 8px top spacing and 24px bottom spacing for their control content. Sticky footer action sections keep their dedicated spacing.
77
+ Use `core/layout.md` for section grouping, dependency cohesion, headers, reset, collapse persistence, spacing, dividers, labels, inline rows, and color-row fit. Keep this page focused on component-specific behavior.
128
78
 
129
79
  ## Colors
130
80
 
131
- First identify the semantic entity the color belongs to: background, object, connector, glow, tone mapping, brand, export, or a named product object.
132
-
133
- Keep color inside a section when it configures the same entity as nearby controls. Use a standalone color section only when color is the whole semantic section.
134
-
135
- 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.
136
-
137
- 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`.
138
-
139
- 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.
140
-
141
- 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.
142
-
143
- 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.
144
-
145
- 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.
146
-
147
- 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.
81
+ Use `core/layout.md` for semantic color grouping, color labels, row fit, and color/opacity layout. Use `core/setup-export.md` for the required output `Background` section and export background behavior.
148
82
 
149
83
  ## File Upload
150
84
 
151
- Use `fileDrop` for source material uploads in the controls panel. Do not place upload UI on the canvas.
152
-
153
- 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.
154
-
155
- In single-layer apps, the runtime shows uploaded image preview and clear button in the file control. Clearing removes the attached source from the renderer and canvas. Global Reset controls and section reset restore `media.defaultAssets` for that fileDrop target; when no default asset exists, reset removes uploaded source material and returns the fileDrop target to `defaultValue`. If users can delete/reorder/transform predefined attached files and that state should survive reload, include `"media"` in schema persistence.
156
-
157
- In image mode, the runtime owns image transform actions directly below the uploader: `90° Right`, `Flip horizontal`, and `Flip vertical`. They render through the built-in actions-control in one three-column row with compact visible labels: `90°`, `Flip H`, `Flip V`; keep a 6px vertical gap between the uploader and action row. Do not create a custom image action button grid. With exactly one uploaded image, those actions are visible immediately. With multiple uploaded images, the user selects a thumbnail first; until then the actions are hidden, and once shown they apply only to the selected image. Product preview/export must consume `state.mediaAssets[].transform` rather than keeping separate image transform state.
158
-
159
- The FileDrop panel preview is not product canvas rendering. It keeps a stable preview frame across rotate/flip actions and contains the transformed bitmap inside that frame, so horizontal or vertical uploads are never cropped by the control preview. Canvas/product renderers may still use cover/crop when the uploaded image is source material.
160
-
161
- 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.
162
-
163
- 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.
164
-
165
- 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.
166
-
167
- When uploaded images are used as canvas/background source material, use `editable-output`, draw them with cover/crop behavior, scale proportionally until the current canvas bounds are fully covered, leave canvas dimensions and Setup controls unchanged, and crop overflow at the canvas bounds.
168
-
169
- In multi-layer apps, deletion and visibility belong to the Layers panel; `fileDrop` stays an upload target.
85
+ Use `core/media-upload.md` for `fileDrop` ownership, image/file modes, multiple uploads, sorting, transform actions, canvas source images, default assets, and layer ownership.
170
86
 
171
87
  ## Image Picker
172
88
 
@@ -256,23 +172,7 @@ Use `code` / `CodeTextarea` as the base multiline content editor for any potenti
256
172
 
257
173
  ## Labels
258
174
 
259
- Visible control labels should be short UI names, usually one to three words. Do not put explanations, formulas, units, parenthetical hints, or usage instructions in field labels.
260
-
261
- Short labels must still be semantically sufficient with nearby context. `Animation` / `Speed` is fine because the section names the entity; `Settings` / `Speed` should become `Animation speed`, and mixed visual buckets should use labels such as `Symbol color` or `Background opacity`.
262
-
263
- Visible control labels can get a runtime-owned filled Phosphor question tooltip icon. Put a concise product-specific explanation in `description` only when it adds meaning beyond the label. Do not write recaps like `Adjusts Opacity`, and do not build custom help icons beside built-in labels.
264
-
265
- Do not add `description` to obvious color clusters. If a section title already names the palette/color context, sequential labels such as `Color 1`, `Color 2`, or simple palette controls such as `Spread` do not need help icons. Keep the whole obvious group clean unless the tooltip explains a non-obvious product behavior.
266
-
267
- For compound controls such as `fontPicker`, `description` must not enumerate owned fields like font, weight, size, case, color, opacity, letter spacing, or line height. The component already labels those fields.
268
-
269
- If a source label is unavoidably long, keep the visible label concise and rely on native `title` for the full text.
270
-
271
- Switch and checkbox labels name the setting context, not the action. Do not prefix them with `Enable` or `Disable`; use `CRT`, `Glow`, `Loop`, or `Guides` instead of `Enable CRT` or `Disable guides`. If the section title already names the setting context, do not repeat that title as the visible toggle label; use a short contextual label such as `Include` or, only for icon-only visual toggles, `label: false` with the meaning in `target` and `description`.
272
-
273
- 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.
274
-
275
- 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.
175
+ Use `core/layout.md` for label naming, help tooltip eligibility, switch/checkbox naming, toggle rows, and label parity. Component pages should add `description` only for non-obvious product behavior that the core layout rules allow.
276
176
 
277
177
  ## Layers
278
178
 
@@ -284,87 +184,11 @@ When Layers are enabled, browser tests must use the real LayersPanel UI: select,
284
184
 
285
185
  ## Timeline
286
186
 
287
- Before choosing timeline mode for an animated product, write an Animation Intent Inventory:
288
-
289
- - `timeline-playback`: user-facing play, pause, scrub, duration, loop, restart, progress, export-at-time, or video export.
290
- - `timeline-keyframes`: editable diamonds, rows, easing, or keyframe evaluation.
291
- - `autonomous`: decorative or self-running output with no user-facing transport and no video export.
292
-
293
- Product output animation uses the top Toolcraft timeline. Use no timeline only for non-product autonomous decorative/self-running motion without video export, and declare `starterTransferMode.animationIntent.mode = "autonomous"` with coverage proving no play/pause, scrub, duration, loop, export-at-time, product animation, or video export behavior.
294
-
295
- Use playback timeline for play, pause, scrub, duration, loop, restart, export-at-time, or video export.
296
-
297
- When `panels.timeline` is enabled for a new Toolcraft app, `starterTransferMode.animationIntent` must match it: `mode: "timeline-playback"` for playback, or `mode: "timeline-keyframes"` for keyframes.
298
-
299
- Playback renderers must read `state.timeline.currentTimeSeconds`, `state.timeline.durationSeconds`, `state.timeline.isPlaying`, and loop state from the runtime. The full animation cycle must span `state.timeline.durationSeconds`; do not hard-code a separate local animation duration such as 3s or 8s inside the renderer.
300
-
301
- Product animation loop means a seamless forward-only cycle by default. Motion advances in one direction, the first and last frames stitch without a visible jump, and mirror, yoyo, ping-pong, or reverse loops are allowed only when the user explicitly requests that behavior as a product mode.
302
-
303
- When the product has a known loop duration, declare it as `panels.timeline.defaultDurationSeconds`; the runtime timeline starts from that loop duration instead of an unrelated 8s default. Timeline animation intent must also declare `loopDuration` with `source`, `seconds`, and `evidence`. Valid sources are `reference`, `user-request`, and `product-derived`; runtime/template fallback 8s is not a valid source. `panels.timeline.defaultDurationSeconds` must match `animationIntent.loopDuration.seconds` so the initial timeline UI shows the declared product loop. Renderers may compute an initial loop duration default during app initialization or reset, but they must not watch `state.timeline.durationSeconds` and dispatch `timeline.setDuration` back to a computed local value. Once the user edits the timeline duration, that runtime value is the loop duration source of truth and renderer progress must map into it. Use `getToolcraftTimelineLoopTime` or `getToolcraftTimelineLoopProgress` to derive loop phase from `state.timeline.currentTimeSeconds` and `state.timeline.durationSeconds`; do not hand-roll wall-clock, fixed-duration, mirror, yoyo, ping-pong, or reverse phase math. Changing duration must preserve seamless forward-loop semantics: one complete cycle maps from `0` to `state.timeline.durationSeconds`, the first and last frames still stitch, direction does not reverse, and the renderer must not switch to wall-clock time or a fixed local duration.
304
-
305
- For reference-runtime-clone apps that map reference transport to the Toolcraft timeline, the same duration proof lives on `starterTransferMode.referenceTimeline.loopDuration`. `referenceTimeline.mode: "toolcraft-playback"` or `"toolcraft-keyframes"` must declare `loopDuration` with source, seconds, and evidence, and `panels.timeline.defaultDurationSeconds` must match it. Do not let a reference clone inherit the runtime/template 8s default unless the reference or user request actually proves an 8s loop.
306
-
307
- Use keyframes timeline for diamonds, editable rows, easing, or keyframe evaluation. In keyframes mode, Toolcraft infers capable controls; do not manually hide diamonds on controls that can be keyframed.
308
-
309
- Keyframe state stores typed control values. `valueLabel` is display-only for the timeline UI; renderers and tests must never parse it as the source of truth. Custom renderers must read keyframed settings through `evaluateToolcraftTimelineValues`, `evaluateToolcraftTimelineValue`, `useToolcraftEvaluatedValues`, or `useToolcraftEvaluatedValue` instead of reading raw `state.values` for keyframed targets.
310
-
311
- Playback-only timelines stay collapsed and must not show control diamonds or expanded keyframe rows.
312
-
313
- When non-looping playback reaches the end, pressing Play again must restart from time 0. Do not require users to scrub back manually before replaying.
314
-
315
- App-wide Play, Pause, Animate, and Restart controls do not belong in the right panel.
316
-
317
- Right-panel animation controls may tune renderer parameters such as mode, intensity, speed, or stagger only after the animation intent is declared. They must not replace top timeline transport.
318
-
319
- Do not replace `TimelinePanel` with an app-level playback, transport, or timeline panel to avoid runtime performance issues. Keep the runtime panel design and fix the Toolcraft runtime clock/state path. Use custom timeline UI only for explicit `custom-reference-timeline` transfers with browser-backed reference timeline coverage.
187
+ Use `core/timeline-animation.md` for animation intent, playback/keyframe timeline choice, forward seamless loop rules, duration mapping, keyframe evaluation, viewport interaction performance, and video export timing.
320
188
 
321
189
  ## Panel Actions
322
190
 
323
- Use `panelActions` only for sticky footer product actions such as Generate, Apply, Export, Copy, or Download.
324
-
325
- Generated apps keep a controls panel so runtime `Setup` is visible from the first run. Settings import/export is mandatory runtime `Setup` behavior there; do not add Import Settings or Export Settings to sticky footer `panelActions`; the runtime inserts them in the first visible headerless `Setup` controls block and imports/exports control values, canvas size, and timeline state.
326
-
327
- Do not gate settings import/export by complexity thresholds, app size, or prompt wording. Use schema `settingsTransfer` only to customize exported JSON identity or file name.
328
-
329
- When editable-output canvas sizing is enabled, the first `Setup` runtime section contains `Export Settings`, `Import Settings`, `Aspect ratio`, `Canvas width`, `Canvas height`, optional `Resolution scale`, and optional `Timeline` in that order. Do not split these into separate app-authored sections, rename the controls, rebuild the block by hand, or declare runtime Setup targets in product sections. App-authored controls targeting `runtime.settingsTransfer`, `canvas.aspectRatio`, `canvas.size.width`, `canvas.size.height`, `canvas.renderScale`, or `panels.timeline.extended` are invalid and never suppress the mandatory runtime controls.
330
-
331
- If only `Export Settings` and `Import Settings` appear in that section, the schema is not using `editable-output` canvas sizing. 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.
332
-
333
- 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.
334
-
335
- 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. A full browser performance checkpoint is required for the first working product version and explicit performance complaints; use the agent-controlled browser first and `pnpm verify:perf` only as fallback. 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.
336
-
337
- When `panels.timeline` is enabled, runtime adds a `Timeline` switch as the last Setup control. It is a runtime presentation preference only: off shows compact Play-only transport, on shows the extended TimelinePanel with scrubber, duration, loop, and keyframe UI. It does not change playback, keyframes, export, product values, settings transfer, or Reset controls. `persistence.include: ["panels"]` may restore it. If `panels.timeline` is omitted, the Timeline switch must not be shown.
338
-
339
- 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.
340
-
341
- Still-output product apps include one primary `Export PNG` action.
342
-
343
- Animated product apps include `Export Video` as the primary action and `Export PNG` as the secondary action.
344
-
345
- Export-labeled footer actions use `icon: "upload-simple"`, matching the runtime `Export Settings` button. Do not use `download`, `download-simple`, or `export` icons for `Export PNG` or `Export Video`.
346
-
347
- Every product app with `Export PNG` includes a separate `Image Export` section. That section must contain:
348
-
349
- - `export.image.format` as a `select`, with default value `png` and baseline options `png` and `jpg`;
350
- - `export.image.resolution` as a `select`, with default value `4k` and baseline options `2k`, `4k`, and `8k`.
351
-
352
- Place `Image Export` directly above sticky footer export buttons for still-output apps. For animated apps with both PNG and video export, place `Image Export` immediately before `Video Export`. `Format` and `Resolution` are one compact workflow pair: render them in a two-column inline row by default. Do not use `segmented` for this pair; it must visually match the Video Export dropdown structure.
353
-
354
- Animated product apps with `Export Video` must enable the top Toolcraft timeline and include a separate `Video Export` section. That section must contain:
355
-
356
- - `export.video.format` as a `select`, with default value `mp4` and baseline options `mp4` and `webm`;
357
- - `export.video.resolution` as a `select`, with default value `current` and options such as `current` and `4k`.
358
-
359
- Place `Video Export` as the final authored controls section directly above sticky footer export buttons. `Format` and `Resolution` are one compact workflow pair: render them in a two-column inline row by default. Use stacked rows only when a label or selected value would clip, truncate, or lose internal padding, and record that fallback reason in the spec or worklog. Do not use `segmented` for this pair unless the product has a deliberately tiny fixed output menu and browser tests prove every cell keeps padding.
360
-
361
- Do not put video export format/resolution controls inside effect, renderer, animation, or output-background sections. `MOV` and `ProRes` are not baseline browser outputs; use them only with an explicit encoder/transcoder and dedicated acceptance plus performance coverage. Video exporters use `getToolcraftVideoExportSize`; do not hand-roll `4096` long-edge sizing for video. The `current` video option uses the current canvas/output size with even encoder-safe rounding. The `4k` video option fits inside 3840x2160, preserves aspect ratio, and returns even encoder-safe dimensions. Recorder/encoder errors must reject the export Promise instead of producing a corrupt blob.
362
-
363
- Add `Copy PNG` only when clipboard output is part of the product. Copy never replaces export. If two footer actions are needed, secondary/outline goes left and primary goes right. Footer actions must be one compact horizontal group, not stacked full-width rows. If an odd number of actions leaves one action alone in the final row, that final action spans the full row.
364
-
365
- Async footer actions return the real Promise from `ToolcraftApp onPanelAction`. Export, download, copy, generate, and apply must not run as fire-and-forget work; the runtime uses the returned Promise to show the sticky footer top accent indicator only while the operation is pending. Use `reportProgress(0..1)` from `onPanelAction` for determinate progress. Video export reports frame-based render/encode progress, and PNG export reports phase progress when render/blob/handoff are asynchronous.
366
-
367
- Do not place product action buttons on the canvas or in the renderer.
191
+ Use `core/setup-export.md` for mandatory runtime Setup, background, Image Export, Video Export, sticky product actions, export icons, and async progress. Use `core/control-selection.md` for choosing `actions` versus sticky `panelActions`.
368
192
 
369
193
  ## Canvas Handles
370
194