@pixel-point/toolcraft 0.0.3 → 0.0.6

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 (47) hide show
  1. package/package.json +1 -1
  2. package/scripts/prepare-pack.mjs +5 -0
  3. package/src/generate.mjs +13 -0
  4. package/src/generate.test.mjs +6 -0
  5. package/templates/runtime/contracts/component-contracts.test.ts +86 -8
  6. package/templates/runtime/contracts/component-contracts.ts +36 -8
  7. package/templates/runtime/contracts/decision-contracts.ts +2 -2
  8. package/templates/runtime/export/export.test.ts +65 -0
  9. package/templates/runtime/export/export.ts +54 -1
  10. package/templates/runtime/react/canvas-shell.test.tsx +7 -7
  11. package/templates/runtime/react/controls-panel.test.tsx +323 -6
  12. package/templates/runtime/react/controls-panel.tsx +349 -24
  13. package/templates/runtime/react/settings-transfer.test.ts +6 -0
  14. package/templates/runtime/react/settings-transfer.ts +28 -2
  15. package/templates/runtime/react/timeline-panel.test.tsx +69 -0
  16. package/templates/runtime/react/timeline-panel.tsx +98 -10
  17. package/templates/runtime/react/toolbar-panel.test.tsx +6 -6
  18. package/templates/runtime/react/toolcraft-app.integration.test.tsx +2 -2
  19. package/templates/runtime/schema/canvas-aspect-ratio-presets.ts +50 -0
  20. package/templates/runtime/schema/define-toolcraft.test.ts +122 -2
  21. package/templates/runtime/schema/define-toolcraft.ts +197 -6
  22. package/templates/runtime/schema/keyframe-capability.test.ts +7 -0
  23. package/templates/runtime/schema/keyframe-capability.ts +2 -2
  24. package/templates/runtime/schema/runtime-targets.ts +6 -0
  25. package/templates/runtime/schema/types.ts +23 -1
  26. package/templates/runtime/state/canvas-zoom.ts +1 -1
  27. package/templates/runtime/state/create-template-state.test.ts +9 -3
  28. package/templates/runtime/state/reducer.test.ts +135 -2
  29. package/templates/runtime/state/reducer.ts +236 -12
  30. package/templates/runtime/state/types.ts +1 -0
  31. package/templates/starter/AGENTS.md +6 -4
  32. package/templates/starter/docs/toolcraft/README.md +1 -1
  33. package/templates/starter/docs/toolcraft/acceptance-testing.md +4 -2
  34. package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -4
  35. package/templates/starter/docs/toolcraft/component-rules.md +24 -5
  36. package/templates/starter/docs/toolcraft/performance.md +5 -0
  37. package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
  38. package/templates/starter/docs/toolcraft/schema-reference.md +53 -9
  39. package/templates/starter/gitignore +36 -0
  40. package/templates/starter/src/app/starter-acceptance.test.ts +678 -21
  41. package/templates/starter/src/app/starter-acceptance.ts +357 -4
  42. package/templates/ui/components/control-layout/index.tsx +4 -4
  43. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +101 -18
  44. package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -1
  45. package/templates/ui/components/controls/range-slider/range-slider-value.ts +4 -1
  46. package/templates/ui/components/controls/slider/slider-value.ts +48 -5
  47. package/templates/ui/components/primitives/editable-slider-value-label.tsx +6 -1
@@ -9,7 +9,7 @@ Edit `src/app/app-schema.ts` as the public product surface.
9
9
  - Use `settingsTransfer: "auto"` for complex apps that should let users import/export control settings.
10
10
  - Bind every control to a schema `target`.
11
11
  - Use `defaultValue` for reset behavior.
12
- - Use `description` for product-specific help beside a visible label. Keep `label` short. Omit `description` instead of writing label recaps like `Adjusts Opacity`; compound controls such as `fontPicker` must not use `description` to list their own fields.
12
+ - Use `description` for product-specific help beside a visible label. Keep `label` short. Omit `description` instead of writing label recaps like `Adjusts Opacity`; also omit it for obvious color clusters such as `Color 1` / `Color 2` inside a color section. Compound controls such as `fontPicker` must not use `description` to list their own fields.
13
13
  - Use `disabled: true` only when the control is intentionally unavailable; the runtime renders the disabled visual and interaction state.
14
14
  - Use `visibleWhen` when a control or section exists only for a specific template, type, mode, variant, or count. Hidden values are preserved. A section with no visible controls is hidden automatically.
15
15
  - Use `disabledWhen` when a control belongs to the current entity but is temporarily unavailable in the selected state. The value is preserved while disabled.
@@ -45,9 +45,47 @@ export: {
45
45
  - `appearance.background` or `scene.background` as a `color` control;
46
46
  - `export.includeBackground` as a boolean/options control.
47
47
 
48
- PNG exporters should call `createToolcraftPngExportCanvas({ background, includeBackground, state, render })`, where `background` and `includeBackground` come from runtime state. `includeBackground` controls only PNG alpha; live preview, workspace canvas backing, and video export keep the product background. Video export always includes the product background, uses `getToolcraftRetinaExportSize`, and must prove exported metadata duration matches the runtime timeline duration.
48
+ PNG exporters should call `createToolcraftPngExportCanvas({ background, includeBackground, resolution, state, render })`, where `background`, `includeBackground`, and `resolution` come from runtime state. `includeBackground` controls only PNG alpha; live preview, workspace canvas backing, and video export keep the product background. For every app with `Export PNG`, `resolution` comes from `export.image.resolution`: `2k`, `4k`, and `8k` render actual 2048/4096/8192px long-edge PNGs. `current` or omitted resolution falls back to retina sizing. Video export always includes the product background, uses `getToolcraftRetinaExportSize`, and must prove exported metadata duration matches the runtime timeline duration.
49
49
 
50
- Animated apps with `Export Video` also expose a separate `Video Export` controls section. Do not mix video export settings into renderer/effect sections. Place this section as the final authored controls section directly above sticky footer export buttons. `Format` and `Resolution` are a compact semantic pair, so use an inline two-column layout by default; stack them only when labels or selected values would clip.
50
+ Every app with `Export PNG` exposes a separate `Image Export` controls section. For still-output apps it sits directly above sticky footer actions. For animated apps with both `Export PNG` and `Export Video`, it sits immediately before `Video Export`:
51
+
52
+ ```ts
53
+ {
54
+ title: "Image Export",
55
+ controls: {
56
+ imageFormat: {
57
+ defaultValue: "png",
58
+ label: "Format",
59
+ options: [
60
+ { label: "PNG", value: "png" },
61
+ { label: "JPG", value: "jpg" },
62
+ ],
63
+ target: "export.image.format",
64
+ type: "select",
65
+ },
66
+ imageResolution: {
67
+ defaultValue: "4k",
68
+ label: "Resolution",
69
+ options: [
70
+ { label: "2K", value: "2k" },
71
+ { label: "4K", value: "4k" },
72
+ { label: "8K", value: "8k" },
73
+ ],
74
+ target: "export.image.resolution",
75
+ type: "select",
76
+ },
77
+ },
78
+ layoutGroups: [
79
+ {
80
+ layout: "inline",
81
+ columns: 2,
82
+ controls: ["imageFormat", "imageResolution"],
83
+ },
84
+ ],
85
+ }
86
+ ```
87
+
88
+ Animated apps with `Export Video` also expose a separate `Video Export` controls section. Do not mix video export settings into renderer/effect sections. Place this section after `Image Export` as the final authored controls section directly above sticky footer export buttons. `Format` and `Resolution` are a compact semantic pair, so use an inline two-column layout by default; stack them only when labels or selected values would clip.
51
89
 
52
90
  ```ts
53
91
  {
@@ -96,9 +134,13 @@ Choose sizing from product context:
96
134
 
97
135
  For product output, export, copy, download, shader rendering, procedural rendering, or no single intrinsic source image, use `editable-output` unless the product explicitly needs `fixed-output`.
98
136
 
99
- A prompt-provided base/default size is only the initial `canvas.size`. It must not remove the runtime Canvas width and Canvas height controls. Use `fixed-output` only when the reference or product explicitly locks dimensions, and add runtime acceptance with `canvasSizingCoverage: "fixed-output-size"`.
137
+ A prompt-provided base/default size is only the initial `canvas.size`. It must not remove the runtime Aspect ratio, Canvas width, and Canvas height controls. Aspect presets use canonical output sizes (`16:9` is `1920x1080`; the other presets are derived around a 1080px short edge or matching portrait long edge). When no explicit product size is provided, runtime defaults to `16:9` / `1920x1080`; choose another preset only when the product meaning calls for it. Use `fixed-output` only when the reference or product explicitly locks dimensions, and add runtime acceptance with `canvasSizingCoverage: "fixed-output-size"`.
100
138
 
101
- Resolved `canvas.size` exists for every canvas app, but visible `Canvas width` and `Canvas height` inputs are mandatory only for `editable-output` sizing. They do not depend on `settingsTransfer`: when settings transfer is off, the runtime prepends a technical `Setup` canvas size section without a visible heading; when settings transfer is on, the controls merge into the first technical `Setup` runtime settings section without a visible heading. Do not hand-build a duplicate size selector.
139
+ Resolved `canvas.size` exists for every canvas app, but visible `Aspect ratio`, `Canvas width`, and `Canvas height` controls are mandatory only for `editable-output` sizing. They do not depend on `settingsTransfer`: when settings transfer is off, the runtime prepends a technical `Setup` canvas size section without a visible heading; when settings transfer is on, the controls merge into the first technical `Setup` runtime settings section without a visible heading. Do not hand-build a duplicate size selector.
140
+
141
+ When the user manually edits `Canvas width` or `Canvas height`, the runtime treats that as an exact custom output size. It keeps the typed dimension, keeps the other dimension unchanged, switches `Aspect ratio` to `Custom`, and shows the reduced current ratio in the custom ratio inputs. Only selecting an aspect preset may resize both dimensions from a canonical preset.
142
+
143
+ For non-vector raster, Canvas 2D, WebGL, or WebGPU previews, set `canvas.renderScale: true`. Runtime appends `Resolution scale` after canvas sizing in the first technical `Setup` section. The slider ranges from `1x` to `2x`, defaults to `2x`, and changes the renderer backing pixels without changing the visible CSS size or product output dimensions. Adding or enabling this slider requires a full `pnpm verify:perf` checkpoint. Performance fixes must preserve the selected scale and keep the canvas responsive while dragging sliders or other high-frequency controls at that scale. Diagnose whether lag comes from renderer technique, React update frequency, decoded media, shader/program setup, buffer uploads, layout work, stale async renders, or animation scheduling before reducing quality. Do not silently downsample, stretch a lower-resolution backing canvas, blur output, or clamp `canvas.renderScale` below the user's chosen value. Do not enable it for DOM/SVG/vector-native previews; use native vector rendering for those.
102
144
 
103
145
  ## Panels
104
146
 
@@ -123,7 +165,7 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
123
165
  | `color` | Hex color picker | `defaultValue: { hex }`, `target`, `label` |
124
166
  | `colorOpacity` | Hex color picker plus opacity percent input | `defaultValue: { hex, opacity }`, `target`, `label` |
125
167
  | `curves` | RGB or single curve editor | `defaultValue`, `target`, `variant: "single"`, `interpolation: "smooth" \| "monotone"` |
126
- | `fileDrop` | Upload/drop input | `accept`, `target` |
168
+ | `fileDrop` | Upload/drop input | `accept`, `multiple`, `target` |
127
169
  | `fontPicker` | Font preview select with popup, category search, weight, size, text case, text color/opacity, letter spacing, and line height; product text must consume `fontId`, `fontWeight`, `fontSize`, `letterSpacing`, `lineHeight`, `textCase`, `color`, and `opacity` | `defaultValue: { fontId, fontWeight, fontSize, letterSpacing, lineHeight, textCase, color, opacity }`, `target` |
128
170
  | `gradient` | Gradient editor | `defaultValue: { angle, gradientType, stops }`, `target` |
129
171
  | `imagePicker` | Image choice grid | `items`, `defaultValue`, `target` |
@@ -140,6 +182,8 @@ Use built-ins before custom controls. Unknown `type` values render nothing unles
140
182
 
141
183
  `text` defaults to `commitMode: "content"` and applies while typing for real content such as prompts, names, titles, tokens, and short text. 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 always commit on blur or Enter. `code` / `CodeTextarea` is a content editor, applies while typing, and is capped at 12 visible lines. Long content scrolls inside the textarea instead of making the controls panel taller.
142
184
 
185
+ For `slider` and `rangeSlider`, `unit` is a measurement or scale suffix, not the entity being counted. Use units such as `%`, `px`, `°`, `x`, `s`, `ms`, `fps`, `rows`, or `cols` only when they clarify the number. Do not add repeated nouns such as `letters`, `shapes`, `words`, `symbols`, `items`, `particles`, or `layers` when the label or section already names that entity. If the value needs a noun, rename the label or section. Word or acronym units, when truly needed, render with a space (`5 cols`, `17 fps`); compact symbol/CSS units stay tight (`70%`, `24px`).
186
+
143
187
  ## Control Selection Inventory
144
188
 
145
189
  Before writing schema controls, map product needs to built-ins by value model, not visual similarity.
@@ -203,9 +247,9 @@ Control labels are judged with their nearest visible context. Short property lab
203
247
 
204
248
  If a target prefix has to be split across sections, the spec must name the workflow reason. Otherwise the acceptance validator treats the split as a sectioning error.
205
249
 
206
- Switch and checkbox labels name the setting context only. Do not prefix them with `Enable` or `Disable`; use `CRT`, `Glow`, `Loop`, or `Guides` instead. If the nearest section title already names the context, do not duplicate it as the visible toggle label. Use `label: false` for a visual-only toggle and keep the product meaning in `target` and `description`.
250
+ Switch and checkbox labels name the setting context only. Do not prefix them with `Enable` or `Disable`; use `CRT`, `Glow`, `Loop`, or `Guides` instead. If the nearest section title already names the context, do not duplicate it as the visible toggle label. Use a short contextual label such as `Include` or, only for icon-only visual toggles, `label: false` with the product meaning in `target` and `description`.
207
251
 
208
- Inline two-column groups are preferred when controls tune one close product meaning and labels/values fit. Short numeric text pairs can be inline. Related short `select` pairs can be inline, especially workflow pairs such as `Format` + `Resolution`, `Codec` + `Profile`, or `Width unit` + `Height unit`. Use stacked one-control rows only as a fit fallback when a label, selected value, or option text would clip, truncate, or lose padding; record that fallback reason in the spec or worklog. A short numeric/text field may also pair with one related plain `color` field when both configure the same entity, such as `Mask size` and `Color` inside `Mask`. `colorOpacity` never renders in inline two-column groups; if either color control has opacity, keep the controls stacked. Color controls show visible field labels in mixed sections that contain any non-color control. Omit visible color labels only in color-only sections or in a section-title-owned toggle + parameter row, such as hidden `export.includeBackground` plus `appearance.background` inside `Background`. Mixed inline rows require visible labels on every field except this hidden-label toggle + parameter pattern. Color fields in other mixed rows must not be unlabeled. Two adjacent `switch` or `checkbox` controls for the same product entity must share one inline row when both visible labels fit without truncation; the runtime auto-pairs safe adjacent toggles by target entity, and schemas should stack them only when either label is too long. A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and both controls edit the same entity; hide the toggle label when the section title already supplies the context. Schema `slider` and `rangeSlider` controls always stay stacked at full width; the only built-in exception is the paired letter-spacing and line-height footer sliders inside `fontPicker`. Do not place sibling controls for case, color, opacity, size, weight, letter spacing, or line height when the same text entity already uses `fontPicker`.
252
+ Inline two-column groups are preferred when controls tune one close product meaning and labels/values fit. Short numeric text pairs can be inline. Related short `select` pairs can be inline, especially workflow pairs such as `Format` + `Resolution`, `Codec` + `Profile`, or `Width unit` + `Height unit`. Use stacked one-control rows only as a fit fallback when a label, selected value, or option text would clip, truncate, or lose padding; record that fallback reason in the spec or worklog. A short numeric/text field may also pair with one related plain `color` field when both configure the same entity, such as `Mask size` and `Color` inside `Mask`. `colorOpacity` never renders in inline two-column groups; if either color control has opacity, keep the controls stacked. Color controls show visible field labels in mixed sections that contain any non-color control. Omit visible color labels only in color-only sections. Mixed inline rows require visible labels on every field, except the required Background row where the color control uses `label: false` because the section title owns the context. Color fields in other mixed rows must not be unlabeled. Two adjacent `switch` or `checkbox` controls for the same product entity must share one inline row when both visible labels fit without truncation; the runtime auto-pairs safe adjacent toggles by target entity, and schemas should stack them only when either label is too long. A single `switch` or `checkbox` may share an inline row with one related parameter control when the toggle label fits and both controls edit the same entity; shorten the toggle label when the section title already supplies context. Toggle plus parameter rows are equal-width two-column rows: each control occupies one half, never intrinsic toggle width plus remaining space. The required Background row uses `Include` plus unlabeled background color. Schema `slider` and `rangeSlider` controls always stay stacked at full width; the only built-in exception is the paired letter-spacing and line-height footer sliders inside `fontPicker`. Do not place sibling controls for case, color, opacity, size, weight, letter spacing, or line height when the same text entity already uses `fontPicker`.
209
253
 
210
254
  `rangeSlider` is always a full-width two-thumb control. Do not include it in `layoutGroups`. Its `defaultValue` must start with different lower and upper values, such as `[20, 80]`, so the two handles do not collapse into one apparent slider. Manual range labels accept built-in separators such as slash, hyphen, spaces, and dashes.
211
255
 
@@ -258,7 +302,7 @@ When enabled, the runtime inserts a technical `Setup` settings-transfer section
258
302
 
259
303
  After adding, removing, or reorganizing controls, sections, timeline, or layers, recalculate settings-transfer eligibility. If the threshold is reached, use `"auto"` / `true` or add an explicit `runtime.settingsTransfer` opt-out acceptance row with product evidence.
260
304
 
261
- When settings transfer is enabled and the canvas uses `editable-output` sizing, the first technical `Setup` runtime section is mandatory and contains `Export Settings`, `Import Settings`, `Canvas width`, and `Canvas height` in that order. Do not split these into separate sections or recreate them manually.
305
+ When settings transfer is enabled and the canvas uses `editable-output` sizing, 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 sections or recreate them manually.
262
306
 
263
307
  A settings-transfer section with only `Export Settings` and `Import Settings` means the canvas is not `editable-output` or the app already declares its own `canvas.size.width` / `canvas.size.height` controls. For product-output apps, treat that as a schema decision to review.
264
308
 
@@ -0,0 +1,36 @@
1
+ # Dependencies
2
+ node_modules
3
+
4
+ # Build output
5
+ dist
6
+ dist-ssr
7
+ .vite
8
+ .turbo
9
+ .output
10
+ .next
11
+ *.tsbuildinfo
12
+
13
+ # Test output
14
+ coverage
15
+ test-results
16
+ playwright-report
17
+
18
+ # Local environment
19
+ .env
20
+ .env.*
21
+ !.env.example
22
+
23
+ # Logs
24
+ logs
25
+ *.log
26
+ npm-debug.log*
27
+ pnpm-debug.log*
28
+ yarn-debug.log*
29
+
30
+ # OS and editor files
31
+ .DS_Store
32
+ Thumbs.db
33
+ .idea
34
+ .vscode/*
35
+ !.vscode/extensions.json
36
+ !.vscode/settings.json