@pixel-point/toolcraft 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/generate-test-doc-assertions.mjs +58 -68
- package/src/generate-test-doc-assertions.test.mjs +43 -0
- package/src/generate-test-doc-policy-assertions.mjs +92 -0
- package/src/generate-test-file-assertions.mjs +26 -75
- package/src/generate-test-product-iteration.test.mjs +17 -0
- package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
- package/src/generate-test-standalone-assertions.mjs +10 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +47 -3
- package/templates/runtime/contracts/component-contracts.choices.ts +48 -3
- package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
- package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
- package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
- package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +4 -4
- package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime.ts +12 -8
- package/templates/runtime/contracts/component-contracts.test.ts +2 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +18 -0
- package/templates/runtime/contracts/decision-contracts.ts +10 -10
- package/templates/runtime/export/export-background.ts +0 -19
- package/templates/runtime/export/export.test.ts +45 -42
- package/templates/runtime/export/index.ts +44 -16
- package/templates/runtime/index.ts +2 -0
- package/templates/runtime/model-import/model-import-limits.ts +15 -4
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +11 -1
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +29 -0
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +18 -8
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
- package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
- package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
- package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
- package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
- package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
- package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +1 -1
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -4
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +10 -4
- package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
- package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
- package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
- package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
- package/templates/runtime/react/index.ts +4 -0
- package/templates/runtime/react/model-rendering/model-export.ts +1 -1
- package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
- package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
- package/templates/runtime/scene/scene-bounds.test.ts +23 -0
- package/templates/runtime/scene/scene-bounds.ts +31 -17
- package/templates/runtime/schema/app-capabilities.test.ts +4 -0
- package/templates/runtime/schema/collection-item-controls.ts +29 -0
- package/templates/runtime/schema/control-applicability.test.ts +127 -0
- package/templates/runtime/schema/control-applicability.ts +433 -0
- package/templates/runtime/schema/control-schema-normalization.ts +177 -11
- package/templates/runtime/schema/controls-panel-actions.ts +2 -2
- package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +59 -0
- package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
- package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
- package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
- package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
- package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
- package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
- package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
- package/templates/runtime/schema/runtime-setup-section.ts +24 -5
- package/templates/runtime/schema/types.ts +68 -13
- package/templates/starter/AGENTS.md +12 -10
- package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -31
- package/templates/starter/docs/toolcraft/agent-worklog.md +15 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +10 -3
- package/templates/starter/docs/toolcraft/component-rules.md +8 -6
- package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
- package/templates/starter/docs/toolcraft/core/layout.md +7 -7
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -0
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +13 -2
- package/templates/starter/docs/toolcraft/core/setup-export.md +33 -7
- package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
- package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
- package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
- package/templates/starter/docs/toolcraft/schema-reference.md +21 -19
- package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +7 -2
- package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +1 -33
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -15
- package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
- package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +20 -11
- package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
- package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
- package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +9 -2
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +29 -26
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +85 -115
- package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -72
- package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +6 -17
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +2 -0
- package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
- package/templates/starter/src/app/acceptance/conditions.ts +1 -1
- package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
- package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
- package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
- package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
- package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
- package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
- package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
- package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
- package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
- package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
- package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
- package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
- package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
- package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
- package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
- package/templates/starter/src/app/acceptance/output-export.ts +22 -10
- package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
- package/templates/starter/src/app/acceptance/types.ts +23 -8
- package/templates/starter/src/app/acceptance/validate-coverage.ts +19 -2
- package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
- package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
- package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
- package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
- package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
- package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
- package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
- package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
- package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +4 -0
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
- package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
- package/templates/starter/src/app/starter-acceptance.ts +11 -2
- package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
- package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -2
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/ui/components/composites/tabs.tsx +2 -2
- package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
- package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
- package/templates/ui/components/controls/collection-actions/index.ts +2 -0
- package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
- package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
- package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
- package/templates/ui/components/controls/file-drop/index.ts +1 -0
- package/templates/ui/components/controls/index.ts +3 -0
- package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
- package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
- package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
- package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
|
@@ -26,9 +26,12 @@ The exported starter may keep `appProductReadiness.mode: "starter"` only while i
|
|
|
26
26
|
|
|
27
27
|
- `productName`;
|
|
28
28
|
- `productSummary`;
|
|
29
|
-
- `requestedBehavior
|
|
29
|
+
- `requestedBehavior`;
|
|
30
|
+
- required `exportIntent`;
|
|
30
31
|
- `viewInteraction`.
|
|
31
32
|
|
|
33
|
+
`productReadiness.exportIntent` is the typed authority for artifact delivery. Acceptance, schema actions, and export settings must correspond exactly to its resolved image and video capabilities. The evidence-bearing modes and decision sequence live in `core/setup-export.md`; do not infer export intent from animation or timeline state.
|
|
34
|
+
|
|
32
35
|
`viewInteraction` classifies the product as `non-spatial`, `orbit`,
|
|
33
36
|
`fixed-camera`, or `timeline-camera`. Editable spatial scenes default to orbit;
|
|
34
37
|
fixed/timeline modes require explicit request/reference evidence.
|
|
@@ -96,7 +99,7 @@ Slider and range slider rows must prove live behavior. Browser tests should drag
|
|
|
96
99
|
|
|
97
100
|
## Infinity Canvas Coverage
|
|
98
101
|
|
|
99
|
-
Every editable-output app proves `infinityCanvasCoverage
|
|
102
|
+
Every editable-output app proves `infinityCanvasCoverage`. Background proof covers color, viewport, disable/restore, and finite size. Preview checks the runtime scene against its exact `sceneBoundsProvider` union and real edge pixels; app selectors fail. Raster backing follows `useToolcraftProductSceneFrame`, not dormant finite size. Image/video decode the same bounds; video unions scheduled states. Fixed recipes cover excluded, empty, unavailable, and oversized scenes. See `core/setup-export.md`.
|
|
100
103
|
|
|
101
104
|
## Render Scale Coverage
|
|
102
105
|
|
|
@@ -117,6 +120,7 @@ Required parts:
|
|
|
117
120
|
| `anchorGrid` | `anchorGrid.position` |
|
|
118
121
|
| `channelMixer` | `channelMixer.activeChannel`, `channelMixer.values`; only for RGB channel matrix behavior |
|
|
119
122
|
| `collectionActions` | `collectionActions.add`, `collectionActions.remove`, `collectionActions.items` |
|
|
123
|
+
| `sourceCollection` | `sourceCollection.items` |
|
|
120
124
|
| `colorOpacity` | `colorOpacity.hex`, `colorOpacity.opacity` |
|
|
121
125
|
| `curves` | RGB variant: `curves.activeChannel`, `curves.points`; `variant: "single"`: `curves.points` |
|
|
122
126
|
| `fontPicker` | `fontPicker.fontId`, `fontPicker.fontWeight`, `fontPicker.fontSize`, `fontPicker.letterSpacing`, `fontPicker.lineHeight`, `fontPicker.textCase`, `fontPicker.color`, `fontPicker.opacity` |
|
|
@@ -149,6 +153,8 @@ choosing a fixed camera and thereby escaping gizmo acceptance.
|
|
|
149
153
|
|
|
150
154
|
Use protected `expectToolcraftOrientationAxisDrag`/`AxisSnap`/`ModelDrag`/`CanvasMissPan`/`UndoReset` recipes. Axis drag finds blank circular background and performs the pointer drag; axis snap projects and clicks the requested endpoint from canonical pose. Evidence follows pose/output/ownership/history assertions. Arbitrary mutations, endpoint-only or generic drag, source spelling, and value changes do not qualify. Use `expectExportExcludesCanvasHandles` for export-clean proof.
|
|
151
155
|
|
|
156
|
+
`expectToolcraftOrientationAxisDrag` validates paused playback and maximum `canvas.renderScale` when those standard controls are present. Product tests prepare those states through real UI before capturing their baseline; the protected helper does not toggle playback or quality, so one drag remains one history transaction. This is functional browser proof, not measured performance evidence.
|
|
157
|
+
|
|
152
158
|
## Control Selection Gates
|
|
153
159
|
|
|
154
160
|
Acceptance must catch wrong-substitution failures. If the prompt, spec, or app behavior needs a value model owned by a built-in control, the schema must use that built-in or include a documented built-in fit check.
|
|
@@ -159,7 +165,7 @@ High-confidence wrong-substitution cases:
|
|
|
159
165
|
- typography without `fontPicker`;
|
|
160
166
|
- sibling typography controls that split case, color, opacity, size, weight, letter spacing, or line height away from `fontPicker`;
|
|
161
167
|
- color plus opacity without `colorOpacity`;
|
|
162
|
-
-
|
|
168
|
+
- source-sized repeated item sets without `sourceCollection`, or user-editable cardinality without `collectionActions`;
|
|
163
169
|
- from/to range without `rangeSlider` or `rangeInput`;
|
|
164
170
|
- curve, remap, easing, or response without `curves`;
|
|
165
171
|
- manual stable two-axis position, direction, focus, anchor, light, or vector parameters without `vector`;
|
|
@@ -181,34 +187,14 @@ A model `fileDrop` row declares `modelImportCoverage: "all-required-model-import
|
|
|
181
187
|
|
|
182
188
|
Protected browser evidence imports every format plus folder/ZIP; proves first-root choice, runtime/custom readiness, nontransparent RGBA, and authored signatures versus fallback. Metadata, changed pose, or a canvas node is not proof. Fatal input keeps the prior commit. `Fix model` requires a deterministic topology plan and verified result; appearance warnings never expose it. Processing preview is `40%`; committed preview/export is `100%`. Both share document, appearance key, pose pixels, and omit gizmo chrome. Reload proves repository-backed appearance; corrupt refs become `unavailable`. History/reset proves removal, enabled undo/redo, and default restore through import.
|
|
183
189
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
```ts
|
|
187
|
-
builtInFitCheck: {
|
|
188
|
-
capabilities: [
|
|
189
|
-
"collection",
|
|
190
|
-
"reorder",
|
|
191
|
-
"selection",
|
|
192
|
-
"commands",
|
|
193
|
-
"custom-value-model",
|
|
194
|
-
],
|
|
195
|
-
checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
|
|
196
|
-
closestBuiltIn: "fileDrop",
|
|
197
|
-
whyInsufficient:
|
|
198
|
-
"FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
|
|
199
|
-
productObservable:
|
|
200
|
-
"Changing a glyph density threshold changes which uploaded glyph renders for the same depth-map tone.",
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
The fit check declares broad typed capabilities, names real checked built-ins, the closest built-in or `"none"`, why it is insufficient, and the product-observable evidence that proves the custom control works. At least one of `custom-interaction`, `custom-value-model`, or `custom-visualization` is required; collection/command chrome alone does not justify custom UI.
|
|
190
|
+
Custom-control rows require `customControlCoverage` and typed `builtInFitCheck`; see `custom-controls.md` for the canonical shape. The fit check records capabilities, checked controls, the closest control (or `"none"`), the missing interaction, and observable proof. One `custom-*` capability is required; collection/command chrome alone is insufficient.
|
|
205
191
|
|
|
206
192
|
A fit check containing `custom-interaction` also requires `interactionId` and a
|
|
207
193
|
panel ownership entry. The fit check compares built-in controls; ownership
|
|
208
194
|
separately proves why the operation belongs in the panel instead of directly on
|
|
209
195
|
the canvas. Passing one gate never bypasses the other.
|
|
210
196
|
|
|
211
|
-
|
|
197
|
+
Collection-like custom controls must check `sourceCollection` and `collectionActions`, plus `actions` when commands exist. Arrays, `{ items: [...] }`, selection, cardinality, ordering, or item commands establish collection behavior; product nouns and unrelated controls do not.
|
|
212
198
|
|
|
213
199
|
Custom controls cannot be justified by icons, layout, styling, compactness, or custom buttons alone. `whyInsufficient` must name the product interaction or value model that built-ins cannot express.
|
|
214
200
|
|
|
@@ -226,25 +212,25 @@ Valid acceptance evidence includes:
|
|
|
226
212
|
- changed timeline playback state plus rendered frame.
|
|
227
213
|
- restored persisted value or product output after browser reload.
|
|
228
214
|
|
|
229
|
-
Product apps
|
|
215
|
+
Product apps include artifact acceptance only for the delivery enabled by `productReadiness.exportIntent`. Image-only products prove complete image coverage; image-plus-video products prove both; video-only products prove complete video coverage; explicit no-export products have no image/video artifact rows. Timeline playback, keyframes, or animation never add video acceptance. Clipboard copy can be tested as an additional behavior, but it cannot substitute for the recorded artifact intent.
|
|
230
216
|
|
|
231
217
|
Export-content proof is distinct from export mechanics: content inspects selected artifact semantics; mechanics proves lifecycle, format, dimensions, transport, and errors. Neither substitutes.
|
|
232
218
|
|
|
233
|
-
Every app with `Export PNG` must exercise the separate `Image Export` section: choose at least two `export.image.format` values, choose at least two `export.image.resolution` values, export the image, and decode the result to prove file type and actual pixel dimensions changed.
|
|
219
|
+
Every app with `Export PNG` must exercise the separate `Image Export` section: choose at least two `export.image.format` values, choose at least two `export.image.resolution` values, export the image, and decode the result to prove file type and actual pixel dimensions changed. Apps with both `Export PNG` and explicitly requested `Export Video` still need this image-export coverage; `Video Export` does not replace it.
|
|
234
220
|
|
|
235
221
|
Runtime Export acceptance must prove the sticky footer top accent indicator advances through real render/encode/download work and hides only after the artifact settles. Async non-export Download, Copy, Generate, or Apply acceptance must prove the indicator is visible while the returned `onPanelAction` Promise is pending, advances when `reportProgress(0..1)` is called, and hides after it settles.
|
|
236
222
|
|
|
237
|
-
|
|
223
|
+
Every video-enabled app exercises two video formats and resolutions. It proves real bytes, dimensions, actual MIME/container or typed capability failure, timeline duration, 30 FPS timestamps/durations from actual encoded packet metadata, decoded product motion, current even-safe sizing, and aspect-preserving 4K inside 3840x2160. Renderer/encoder/muxer errors reject; `blobSize`, metadata alone, assumed FPS, synthetic frame counts, or substituted expectations are invalid. Animated output requires distinct decoded frame hashes.
|
|
238
224
|
|
|
239
225
|
Footer action acceptance must not include Reset. Reset is already available in the controls panel header and uses schema `defaultValue`; duplicating it in sticky `panelActions` fails acceptance.
|
|
240
226
|
|
|
241
227
|
Local `actions` acceptance must click every visible action and prove the nearby entity changed through runtime state or product output. A section-level `Randomize palette` must change palette output, `Normalize weights` must change weights/output, and `Clear selection` must clear only the scoped selection. Do not accept a test that only proves the button rendered. A single-button `actions` control fails validation when the control label duplicates the button label; the label must add concise context. Visual acceptance rejects side-label actions; labels sit above a two-column button grid where each button cell is 50% width.
|
|
242
228
|
|
|
243
|
-
`collectionActions`
|
|
229
|
+
`collectionActions` keeps parts `collectionActions.add`, `collectionActions.remove`, `collectionActions.items`. Prove limits, full-default add, sibling-preserving edit, preview/export, and whole-record removal. `sourceCollection` proves source count, item edit, output, and no add/remove.
|
|
244
230
|
|
|
245
231
|
Image export proves background changes in preview/artifact/infinite viewport; Background off restores finite mode, disables Infinity, hides bounded preview background, and makes PNG transparent while JPEG/video stay opaque. Restoring it enables Infinity availability without entering that mode. Protected proof decodes real type, selected dimensions, product bounds/pixels, and pixel hash; bytes or dimensions alone are insufficient.
|
|
246
232
|
|
|
247
|
-
Hard acceptance semantics are typed, not inferred from English prose.
|
|
233
|
+
Hard acceptance semantics are typed, not inferred from English prose. Control applicability derives exact pairwise cases from the control's section-inventory peers for both `always` and `conditional` controls. Each non-matching case requires target absence. Each matching case requires target presence plus every existing product outcome on that acceptance row, under a canonical case-suffixed requirement ID. A missing predicate therefore cannot hide behind a passing default branch. The `export.includeBackground` row separately declares `backgroundOutputCoverage` for preview exclusion and transparent image alpha, plus preserved video background when the schema exposes video export. Its protected recipe verifies the preview transition, decodes a non-empty image artifact and checks background alpha, and inspects video background behavior when applicable. `expectedObservable` and `userAction` remain human-readable context and may use any language; matching words such as “hidden”, “PNG”, or “video” never satisfy these requirements by themselves.
|
|
248
234
|
|
|
249
235
|
Invalid final acceptance evidence:
|
|
250
236
|
|
|
@@ -332,6 +318,6 @@ Performance browser tests use the derived path matrix. Each path has one browser
|
|
|
332
318
|
|
|
333
319
|
## Fixtures
|
|
334
320
|
|
|
335
|
-
Use fixtures that make each behavior visible. For example, background character-size controls need visible background characters, transparency needs alpha-sensitive pixels, selected-layer controls need multiple layers, timeline controls need deterministic playback or keyframe fixtures, and mode-specific controls need fixtures for every mode branch.
|
|
321
|
+
Use fixtures that make each behavior visible. For example, background character-size controls need visible background characters, transparency needs alpha-sensitive pixels, selected-layer controls need multiple layers, timeline controls need deterministic playback or keyframe fixtures, and mode-specific controls need fixtures for every mode branch. Applicability coverage proves matching controls visible, non-matching controls absent, values preserved after switching away and back, and the accepted renderer/export outcome in every visible finite sibling branch. Count-controlled control banks prove the numeric boundary states; the test fails if inactive controls remain visible or a visible control is ignored by the renderer.
|
|
336
322
|
|
|
337
323
|
Generic hash differences are not enough for semantic controls. If a control promises a direction, test that direction.
|
|
@@ -116,6 +116,21 @@ The quoted evidence must be an exact nontrivial raw substring of `Request` with
|
|
|
116
116
|
- Verification: One bare `pnpm verify:delivery` will derive and run the protected proof.
|
|
117
117
|
- Risks: Already-exported applications retain their copied runtime until regenerated. Browser proof relies on the runtime gizmo's canonical pose/target attributes and intentionally fails closed if the real handle is absent or ambiguous.
|
|
118
118
|
|
|
119
|
+
### Iteration 6 — Executable product-control applicability
|
|
120
|
+
|
|
121
|
+
- Request: Fix starter contracts so generated products show only settings that apply to the selected type and cannot pass delivery with a visible control that the renderer ignores.
|
|
122
|
+
- Task type: Shared runtime schema, controls-panel visibility, starter acceptance, protected browser evidence, generated fixtures, CLI, and documentation.
|
|
123
|
+
- User-visible result: Every generated product control explicitly declares `always` or `conditional` applicability. Non-matching controls disappear without losing their values, while every visible finite sibling branch must prove the control's real accepted product outcome.
|
|
124
|
+
- Source/reference checked: Badge behavior was used only as failure evidence; implementation scope remained the Toolcraft runtime and starter contracts. The legacy `visibleWhen` runtime path, control-section inventory, acceptance requirement derivation, reporter, and generated image/video/material fixtures were inspected.
|
|
125
|
+
- Contract rules applied: `controls-product-coverage`, `controls-section-inventory-required`, `controls-component-layout-invariants`, `acceptance-product-observable`, and `workflow-required`.
|
|
126
|
+
- Interaction ownership: The runtime owns applicability normalization and panel presence. Product schemas own explicit applicability claims. Existing product acceptance owns actions and outcomes; the applicability layer only derives the branch cases in which those outcomes must be reproved.
|
|
127
|
+
- Decision: Normalize explicit applicability, legacy `visibleWhen`, and omitted low-level input into one resolved model with origin metadata; reject legacy/implicit origins for product controls; combine conditional predicates with AND; derive pairwise cases from semantic section peers; preserve the authored `Background` inventory ownership after runtime relocates its product controls into `Setup`; attach case-scoped evidence only after exact presence/absence and real outcome assertions pass.
|
|
128
|
+
- Alternatives rejected: Extending optional `visibleWhen`, selector-owned target lists, renderer dependency inference, acceptance prose heuristics, Cartesian branch enumeration, and Badge-specific logic.
|
|
129
|
+
- State/output mapping: Applicability reads canonical runtime target values and changes only panel presence. Hidden values remain in runtime state, persistence, transfer, and history. Matching cases reuse the control's existing preview, rendered-pixel, artifact, command, or semantic proof.
|
|
130
|
+
- Performance intent: ordinary-product-work
|
|
131
|
+
- Verification: One bare `pnpm verify:delivery` remains the generated-app delivery authority; this runtime/template contract delivery also runs the monorepo checks required by the repository entry contract.
|
|
132
|
+
- Risks: Pairwise proof depends on truthful Control Section Inventory grouping; unsupported selector domains fail acceptance instead of silently skipping cases. Legacy low-level consumers remain readable but cannot satisfy generated product acceptance.
|
|
133
|
+
|
|
119
134
|
## Decisions
|
|
120
135
|
|
|
121
136
|
### Renderer
|
|
@@ -25,7 +25,7 @@ export const appComposition = {
|
|
|
25
25
|
} satisfies ToolcraftAppComposition;
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Edit `src/app/app-composition.tsx`, not the signed route. Product composition may provide only `schema`, `canvasContent`, `controlRenderers`, `exportRenderer`, `modelPresentation`, `onPanelAction`, `renderDefaultCanvasMedia`, and optional `rendererPipelineRegistration`; the protected route owns `className` and other host layout. Model products default to `modelPresentation: { mode: "runtime" }`. Custom presentation declares checked consumers and suppresses only their model targets; `renderDefaultCanvasMedia={false}` affects generic image/file preview, not runtime models. 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.
|
|
28
|
+
Edit `src/app/app-composition.tsx`, not the signed route. Product composition may provide only `schema`, `canvasContent`, `infiniteCanvasContent`, `controlRenderers`, `exportRenderer`, `modelPresentation`, `onPanelAction`, `renderDefaultCanvasMedia`, `sceneBoundsProvider`, and optional `rendererPipelineRegistration`; the protected route owns `className` and other host layout. `infiniteCanvasContent` is editor-only product output for a full Infinity viewport backdrop; it does not inherit world transforms or participate in scene bounds/export. Model products default to `modelPresentation: { mode: "runtime" }`. Custom presentation declares checked consumers and suppresses only their model targets; `renderDefaultCanvasMedia={false}` affects generic image/file preview, not runtime models. 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.
|
|
29
29
|
|
|
30
30
|
Supporting product modules may be organized anywhere under `src`; there is no folder allowlist. The product boundary follows all product production files and rejects host/runtime surface imports, built-in control imports/re-exports, dynamic bypasses, and production dependencies on test/spec modules.
|
|
31
31
|
|
|
@@ -35,6 +35,7 @@ Allowed app extension points:
|
|
|
35
35
|
| --- | --- |
|
|
36
36
|
| Schema controls | Built-in controls, targets, defaults, visibility, panel actions. |
|
|
37
37
|
| `canvasContent` | Product output only. |
|
|
38
|
+
| `infiniteCanvasContent` | Editor-only full-viewport product environment under the Infinity world; never app UI or export content. |
|
|
38
39
|
| `controlRenderers` | True custom controls only after the built-in fit check. |
|
|
39
40
|
| `exportRenderer` | One deterministic scene-coordinate product frame shared by runtime image and video export. |
|
|
40
41
|
| `onPanelAction` | Non-export sticky footer product actions. |
|
|
@@ -51,6 +52,10 @@ Once the folder is a real product, switch `src/app/app-acceptance-data.ts` from
|
|
|
51
52
|
|
|
52
53
|
```ts
|
|
53
54
|
export const appProductReadiness = {
|
|
55
|
+
exportIntent: {
|
|
56
|
+
image: { mode: "toolcraft-default" },
|
|
57
|
+
video: { mode: "not-requested" },
|
|
58
|
+
},
|
|
54
59
|
interactionOwnership: [],
|
|
55
60
|
mode: "product",
|
|
56
61
|
productName: "Product name",
|
|
@@ -63,6 +68,8 @@ export const appProductReadiness = {
|
|
|
63
68
|
} as const;
|
|
64
69
|
```
|
|
65
70
|
|
|
71
|
+
Every product declaration requires `productReadiness.exportIntent`. It is the authority for exact image/video schema actions, settings sections, and artifact acceptance. Use `core/setup-export.md` for the single export-intent decision sequence; animation and timeline choices do not supply delivery intent.
|
|
72
|
+
|
|
66
73
|
Populate `interactionOwnership` before adding canvas handles or custom
|
|
67
74
|
interactions. One operation has one primary `canvas` or `panel` surface chosen
|
|
68
75
|
from user request, inspected reference, or product usability evidence. Different
|
|
@@ -75,7 +82,7 @@ the prompt did not separately request rotation.
|
|
|
75
82
|
|
|
76
83
|
## Controls
|
|
77
84
|
|
|
78
|
-
Before
|
|
85
|
+
Before choosing component layout, export `starterControlSectionInventory` and make the entity-first grouping decision. Every product section declares stable `entityId`, human-readable `entity`, exact targets, and `groupingReason`; one entity stays in one section through ten controls, while larger entities split only into explicit balanced workflow stages.
|
|
79
86
|
|
|
80
87
|
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.
|
|
81
88
|
|
|
@@ -99,7 +106,7 @@ When porting an existing app, use `transferMode: "reference-runtime-clone"` unle
|
|
|
99
106
|
|
|
100
107
|
## Timeline And Animation
|
|
101
108
|
|
|
102
|
-
Before adding animation controls, write an Animation Intent Inventory. Product animation, keyframes,
|
|
109
|
+
Before adding animation controls, write an Animation Intent Inventory. Product animation, keyframes, and playback use the top Toolcraft timeline. Explicitly requested video export also requires that timeline, but the timeline never authorizes video delivery. Autonomous no-timeline animation is allowed only for non-product decorative motion with no user-facing transport and no video export.
|
|
103
110
|
|
|
104
111
|
Use `core/timeline-animation.md` for timeline mode, compact/extended timeline, seamless forward loops, duration changes, keyframes, viewport interaction performance, and video export timing.
|
|
105
112
|
|
|
@@ -36,13 +36,15 @@ Range slider value editing accepts common range separators such as `20/80`, `20-
|
|
|
36
36
|
|
|
37
37
|
Discrete sliders must still drag smoothly. Heavy preview work may be coalesced, cached, or split into lightweight live feedback plus heavier refinement, but the canvas/product output must not stay unchanged until pointer release.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Every product slider and range slider declares `applicability`. Use `mode: "conditional"` when it is meaningful only in some mode, type, source, include, variant, or count state. Every predicate in `all` must match; inactive branches disappear while their values remain preserved.
|
|
40
40
|
|
|
41
|
-
Do not use schema `disabled: true` or `disabledWhen` for product sliders and range sliders. Product panels
|
|
41
|
+
Do not use schema `disabled: true` or `disabledWhen` for product sliders and range sliders. Product panels show only controls usable in the current state.
|
|
42
42
|
|
|
43
|
-
For mode/type/source/include/count branches,
|
|
43
|
+
For mode/type/source/include/count branches, declare conditional applicability instead of disabling. Example: when Texture is `Off`, texture pattern, upload, blend, and opacity predicates do not match. When Texture is `Image`, the uploader and shared texture settings match. When `Shades` is `2`, `Shade 3`, `Shade 4`, and `Shade 5` do not match. Do not keep inactive controls visible while making the renderer ignore them.
|
|
44
44
|
|
|
45
|
-
If
|
|
45
|
+
If applicability points to a selector for the same target entity or selected branch, keep the selector and dependent controls in the same semantic section. A section that exists only because one selector option is active is not a separate product section just because the branch uses a standalone control. Use one section with conditional controls; split only when the dependent branch is a separate product entity with its own workflow and acceptance evidence.
|
|
46
|
+
|
|
47
|
+
An `always` declaration is testable, not a shortcut: protected acceptance varies every supported finite sibling selector from the same Control Section Inventory and proves the control's existing product outcome in each branch. Conditional controls use the same pairwise cases; a non-matching case proves absence, and every matching case reproves the real output. This catches both missing predicates and incorrect always-visible declarations.
|
|
46
48
|
|
|
47
49
|
## Palette
|
|
48
50
|
|
|
@@ -86,7 +88,7 @@ Use `core/layout.md` for section grouping, dependency cohesion, headers, reset,
|
|
|
86
88
|
|
|
87
89
|
## Colors
|
|
88
90
|
|
|
89
|
-
Use `core/layout.md` for semantic color grouping, color labels, row fit, and color/opacity layout. Use `core/setup-export.md` for the authored background source pair, its runtime Setup placement, and export background behavior.
|
|
91
|
+
Use `core/layout.md` for semantic color grouping, color labels, row fit, and color/opacity layout. Use `core/setup-export.md` for the authored background source pair, its runtime Setup placement, and export background behavior. For repeated built-in controls, use `sourceCollection` for a source-owned array and `collectionActions` for a user-growable array; both support `checkbox`, `color`, `colorOpacity`, `fontPicker`, `rangeInput`, `rangeSlider`, `segmented`, `select`, `slider`, `switch`, `text`, and `vector` items, while unknown item types fail schema validation. Use scalar `itemControl` for one homogeneous repeated value. A `collectionActions` control may instead use `itemControls` only when two or more built-in fields form one logical target-array record that is added or removed atomically. Runtime places a line only between adjacent compound records and leaves standalone color grids unchanged. A multiple file-kind `fileDrop` with `variant: "collection-actions"` may declare `itemControls` to render built-in settings directly below each attached file and persist per-file values keyed by `mediaId`; its upload row and settings are one logical group under the same divider rule.
|
|
90
92
|
|
|
91
93
|
## File Upload
|
|
92
94
|
|
|
@@ -179,7 +181,7 @@ Use `useToolcraftModelOrbitInteraction` on the product renderer and provide a hi
|
|
|
179
181
|
|
|
180
182
|
Multiple `orientationGizmo` declarations are allowed only for model modes whose combined section/control visibility conditions are statically provable as mutually exclusive. Runtime renders at most one active canvas handle and rejects an ambiguous state instead of selecting the first declaration.
|
|
181
183
|
|
|
182
|
-
Click a signed axis endpoint to return to that view. Drag anywhere inside the circular gizmo to orbit with Blender-style Turntable behavior: horizontal movement rotates around world up, vertical movement rotates around the screen-horizontal axis, and sensitivity is 0.4 degrees per CSS pixel. Direct model drag uses the same rotation kernel. Axis snaps use angle-scaled Blender Smooth View timing with a 200ms maximum. Gizmo drag, snap, and direct model drag share target-scoped ownership, so a newer gesture, reset, undo/redo, or external target write cancels stale work. A background click is inert; pointer cancel and lost capture end the gesture. The runtime handle is fixed under canvas pan/zoom, uses one history group per completed gesture, and is excluded from export. Product code selects it through schema and must not import or render the visual gizmo directly.
|
|
184
|
+
Click a signed axis endpoint to return to that view. Drag anywhere inside the circular gizmo to orbit with Blender-style Turntable behavior: horizontal movement rotates around world up, vertical movement rotates around the screen-horizontal axis, and sensitivity is 0.4 degrees per CSS pixel. Direct model drag uses the same rotation kernel. Axis snaps use angle-scaled Blender Smooth View timing with a 200ms maximum. Gizmo drag, snap, and direct model drag share target-scoped ownership, so a newer gesture, reset, undo/redo, or external target write cancels stale work. A background click is inert; pointer cancel and lost capture end the gesture. The runtime handle is fixed under canvas pan/zoom, uses one history group per completed gesture, and is excluded from export. Product code selects it through schema and must not import or render the visual gizmo directly. When the product also enables the Toolcraft timeline or `canvas.renderScale`, orientation axis-drag proof runs with playback paused and render scale at its declared maximum. The shared pose and visible product pixels must update before pointer release in that state. Cooperative rendering may coalesce work, but it must publish a changed high-quality frame during the gesture; pointer release is not the first allowed visible commit.
|
|
183
185
|
|
|
184
186
|
## Curves
|
|
185
187
|
|
|
@@ -84,6 +84,7 @@ renamed copy of the same capability is still duplication.
|
|
|
84
84
|
- Use `tabs` for finite choices that replace the content or workflow view below the control; the runtime changes overflowing tabs to Select without changing the value.
|
|
85
85
|
- Use `segmented` for compact finite mode settings that leave the surrounding view unchanged.
|
|
86
86
|
- Use `actions` for local section commands that affect only the nearby entity.
|
|
87
|
+
- Use `sourceCollection` when source analysis or another runtime workflow owns the array length and users edit only existing built-in item values.
|
|
87
88
|
- Use `collectionActions` for repeatable product entities whose actual item list can grow or shrink.
|
|
88
89
|
- Use `panelActions` for sticky final product actions such as export, copy, generate, apply, or download.
|
|
89
90
|
|
|
@@ -91,18 +92,22 @@ renamed copy of the same capability is still duplication.
|
|
|
91
92
|
|
|
92
93
|
- `fontPicker` owns font family, weight, size, text case, text color/opacity, letter spacing, and line height.
|
|
93
94
|
- `gradient` owns gradient type, angle, draggable stop track, and Stops list.
|
|
94
|
-
- RGB `curves`, `channelMixer`, `palette`, and `collectionActions` are also compound controls.
|
|
95
|
+
- RGB `curves`, `channelMixer`, `palette`, `sourceCollection`, and `collectionActions` are also compound controls.
|
|
95
96
|
- Do not split owned fields into neighboring schema controls.
|
|
96
97
|
- If a needed owned field is missing from a built-in, extend the kit instead of composing a parallel control.
|
|
97
98
|
|
|
98
|
-
## Collection
|
|
99
|
+
## Collection Cardinality
|
|
99
100
|
|
|
101
|
+
- Use `sourceCollection` when a loaded, derived, detected, or otherwise external source owns the exact array length. It renders the declared built-in `itemControl` for every current item and never exposes add/remove commands.
|
|
100
102
|
- Use `collectionActions` when users edit the actual growable/shrinkable set: colors, glyphs, symbols, points, rules, variants, objects, style entries, or similar repeatable entities.
|
|
103
|
+
- Both controls use the same runtime-owned built-in item renderer. Product code must not copy a child control implementation to support a dynamic list.
|
|
104
|
+
- Source workflows replace the complete `sourceCollection` target array; panel edits change item values only.
|
|
105
|
+
|
|
101
106
|
- Adding/removing items must update runtime state and product preview/export.
|
|
102
107
|
- Do not use a count slider plus hidden fixed item controls when the user needs to add or remove actual entities.
|
|
103
108
|
- The collection control shows the collection label on the left and remove/add icon buttons on the right.
|
|
104
109
|
- Homogeneous repeated items do not show visible per-item labels when the collection label already names the group.
|
|
105
|
-
- Plain color items may use equal 50% columns; color+opacity items stay stacked.
|
|
110
|
+
- Plain color items may use equal 50% columns; color+opacity items stay stacked. Use `itemControl` for one homogeneous value. Use `itemControls` only when two or more built-in fields form one logical target-array record and affect that entity's outcome: `+` appends all field defaults, `−` removes the final record, and runtime places a content-width line only between records without `Item N` headings. Standalone color `itemControl` stays a divider-free two-column grid; color may still be a legitimate compound field.
|
|
106
111
|
|
|
107
112
|
## Actions
|
|
108
113
|
|
|
@@ -156,4 +161,5 @@ typed evidence-backed `fixed-camera` escape hatch.
|
|
|
156
161
|
- Remove file names, helper text, and captions that do not help distinguish items or explain state.
|
|
157
162
|
- Do not make tiny item-level action buttons below kit comfort sizes.
|
|
158
163
|
- Do not recreate built-in controls, panels, toolbar, timeline, layers, canvas shell, or runtime surfaces.
|
|
164
|
+
- Do not import deep Toolcraft control implementation modules or substitute native form controls for a schema-owned value model.
|
|
159
165
|
- A custom control with `custom-interaction` must reference its typed interaction owner. Its built-in fit check does not justify panel UI when the same operation is already owned by a canvas interaction.
|
|
@@ -4,11 +4,10 @@ Read this module before changing sections, labels, helper icons, inline rows, di
|
|
|
4
4
|
|
|
5
5
|
## Sections
|
|
6
6
|
|
|
7
|
-
- Build controls-panel sections from product entities
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- In a section larger than seven controls, every control declares `semanticGroup`. One tightly scoped product sub-entity/workflow shares one group; mixed groups are structural evidence that the section should split. Do not infer this from labels.
|
|
7
|
+
- Build controls-panel sections from logical product entities, not component types, visual control size, or target namespaces. Before writing controls, export `starterControlSectionInventory`; every section declares stable `entityId`, human-readable `entity`, exact targets, and `groupingReason`.
|
|
8
|
+
- One to seven controls is the normal section size. Eight to ten controls are allowed for one cohesive entity and require `semanticGroup` on every control. Ten controls is the hard maximum.
|
|
9
|
+
- One entity with ten or fewer controls stays in one section. Different source, settings, placement, or presentation roles do not create sections by themselves.
|
|
10
|
+
- An entity above ten controls splits into balanced workflow sections containing two to ten controls. Every split section keeps the same `entityId` and `entity` and declares a unique `workflowStage` plus concrete `splitReason`; it may not leave a one-control tail. A one-control section is valid only when that control is the entity's complete editable surface.
|
|
12
11
|
- Do not reuse the same section title for multiple sections.
|
|
13
12
|
- Bad titles: `Controls`, `Settings`, `Options`, `Sliders`, `Inputs`, `Buttons`, `Color`, `Colors`.
|
|
14
13
|
- Good titles name the edited thing: `Background`, `Object`, `Token Pattern`, `Motion`, `Tone Mapping`, `Export`.
|
|
@@ -17,8 +16,9 @@ Read this module before changing sections, labels, helper icons, inline rows, di
|
|
|
17
16
|
|
|
18
17
|
## Dependency Cohesion
|
|
19
18
|
|
|
19
|
+
- Typed `entityId` is the primary authority for section cohesion. Target-prefix checks are secondary diagnostics and never redefine an inventory entity.
|
|
20
20
|
- A selector that controls mode, type, source, variant, or include state stays with the controls it gates when they share the same product entity.
|
|
21
|
-
-
|
|
21
|
+
- Declare conditional control applicability for inactive product branches so the panel shows only usable controls while preserving hidden values.
|
|
22
22
|
- 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.
|
|
23
23
|
- A section with no visible controls is hidden automatically.
|
|
24
24
|
- Do not use `disabled: true` or `disabledWhen` for generated product controls.
|
|
@@ -50,7 +50,7 @@ Read this module before changing sections, labels, helper icons, inline rows, di
|
|
|
50
50
|
- If a section contains exactly one control, simple or compound, render only the parent section dividers.
|
|
51
51
|
- Do not add full-width borders inside a compound control.
|
|
52
52
|
- Do not put dividers only around an internal subsection such as Gradient Stops.
|
|
53
|
-
- Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers.
|
|
53
|
+
- Small compound fields such as `colorOpacity` and `rangeInput` stay inline fields without section dividers. Repeated `collectionActions` records built from `itemControls` use one content-width line only between adjacent logical records, with 18px spacing on each side and no generated item heading. A FileDrop collection slot and its per-file settings use the same group boundary. Standalone color `itemControl` grids have no item dividers.
|
|
54
54
|
|
|
55
55
|
## Labels And Help
|
|
56
56
|
|
|
@@ -51,6 +51,7 @@ Read this module before changing image upload, file upload, source material impo
|
|
|
51
51
|
- Multiple image uploads render as a sortable four-column thumbnail grid.
|
|
52
52
|
- The add-more tile is last.
|
|
53
53
|
- Per-image removal stays inside the file control.
|
|
54
|
+
- When the product explicitly requires the compact cardinality pattern used by `collectionActions`, set `variant: "collection-actions"` on an `assetKind: "file"` control with `multiple: true`. Its header `+` adds one empty FileDrop slot and `−` removes the final slot or its attached file; the wide add row and per-item remove actions are omitted so cardinality has one owner. When every attached file owns settings, declare built-in `itemControls` with a `defaultValue` for every entry: attached files render those settings directly below their upload row, empty pending slots render no settings, the parent target stores value records keyed by `mediaId`, and runtime media keeps ownership of file bytes and lifecycle. The upload row and its per-file settings form one logical group; runtime renders one content-width line only between adjacent groups and never adds `Item N` headings. Product renderers join both slices by `mediaId`; do not collapse per-file settings into a global sibling section.
|
|
54
55
|
- Dragging thumbnails updates runtime media order.
|
|
55
56
|
- Product renderers and exports consume runtime media order instead of keeping a separate product-only order.
|
|
56
57
|
|
|
@@ -60,6 +61,7 @@ Read this module before changing image upload, file upload, source material impo
|
|
|
60
61
|
- Long filenames fade/truncate at the end instead of hard-clipping.
|
|
61
62
|
- The last item has no bottom separator.
|
|
62
63
|
- The add row is part of the file control and uses the same width and hover behavior as list rows.
|
|
64
|
+
- The `variant: "collection-actions"` opt-in replaces that add row and the per-row remove buttons with the compact header `− / +` controls while preserving runtime media order.
|
|
63
65
|
- When an app contains both image and file uploaders, canvas drops route by asset kind:
|
|
64
66
|
- image files prefer visible image uploaders;
|
|
65
67
|
- non-image files prefer visible file uploaders;
|
|
@@ -18,18 +18,24 @@ Use only these app-specific extension points. Shared runtime changes happen upst
|
|
|
18
18
|
- schema controls;
|
|
19
19
|
- schema `canvas`, `panels`, `toolbar`, `panelActions`, `persistence`, `media`, `assembly`, and transfer-mode metadata;
|
|
20
20
|
- `canvasContent` for product output only;
|
|
21
|
+
- `infiniteCanvasContent` for editor-only product output that must fill the
|
|
22
|
+
complete Infinity viewport without inheriting world pan, zoom, product scene
|
|
23
|
+
bounds, or export bounds;
|
|
21
24
|
- `renderDefaultCanvasMedia={false}` only when a product renderer replaces generic image/file preview; it does not suppress runtime model layers;
|
|
22
25
|
- typed `modelPresentation`, with `{ mode: "runtime" }` as the default standard preview/export owner or `{ mode: "custom", consumers }` for declared model targets with checked consumers;
|
|
23
26
|
- `controlRenderers` only for true custom controls that pass the built-in fit check;
|
|
24
27
|
- one `exportRenderer` that draws a deterministic product frame for runtime-owned image/video export;
|
|
28
|
+
- one `sceneBoundsProvider` that returns exact-state product world-space rectangles for infinite preview and runtime-owned export;
|
|
25
29
|
- `onPanelAction` for non-export sticky product actions;
|
|
26
30
|
- optional `rendererPipelineRegistration` for one compiled executable custom-renderer pipeline shared by product work, runtime evidence, and performance assessment;
|
|
27
|
-
- runtime commands and hooks.
|
|
31
|
+
- runtime commands and hooks, including `useToolcraftProductSceneFrame` inside `canvasContent` when a raster/WebGL renderer needs the active finite or infinite frame.
|
|
28
32
|
|
|
29
33
|
## Forbidden Rebuilds
|
|
30
34
|
|
|
31
35
|
- Do not hand-compose `ToolcraftRoot`, `CanvasShell`, `ControlsPanel`, `LayersPanel`, `TimelinePanel`, `ToolbarPanel`, or panel containers in product routes.
|
|
32
36
|
- Do not render built-in control components such as `SliderControl`, `SelectControl`, `ColorControl`, `GradientControl`, `FontPickerControl`, `FileDropControl`, or `PanelActionsControl` directly in app code.
|
|
37
|
+
- Do not import anything below `src/toolcraft/ui/components/controls/**` (or the equivalent workspace package path), even when the private symbol name is not a public control. Private popovers, parsers, inputs, and state helpers are implementation details.
|
|
38
|
+
- Do not substitute native or primitive `input` types `color`, `range`, `file`, `checkbox`, or `radio`, or native `select`/`textarea`, for schema controls. Generic text inputs and product-specific primitives remain available inside a justified custom interaction.
|
|
33
39
|
- Do not recreate controls, panels, toolbar, timeline, layers, canvas shell, drag handles, section headers, section reset, history, or runtime surfaces by hand.
|
|
34
40
|
- Do not create product-owned export canvases, encoders, object-URL downloads, or direct Mediabunny integrations; runtime owns typed image/video actions end to end.
|
|
35
41
|
- If a shared behavior is wrong, fix the shared runtime/template source and regenerate or sync the copied Toolcraft source instead of patching one exported app.
|
|
@@ -37,11 +43,16 @@ Use only these app-specific extension points. Shared runtime changes happen upst
|
|
|
37
43
|
## Canvas Boundary
|
|
38
44
|
|
|
39
45
|
- `canvasContent` contains product output only: WebGL, Canvas 2D, SVG, DOM product text, shaders, generated previews, export previews, or product editing handles.
|
|
46
|
+
- `infiniteCanvasContent` follows the same product-output-only rule. Runtime
|
|
47
|
+
mounts it only in Infinity mode as a pointer-transparent viewport layer below
|
|
48
|
+
the transformed world. It is not a scene-bounds or export extension point.
|
|
40
49
|
- App UI, CTAs, upload prompts, helper copy, placeholder instructions, buttons, menus, forms, and settings do not belong in `canvasContent`.
|
|
41
50
|
- If upload/import is part of the source-material flow, the pre-content canvas stays neutral and runtime-backed. Upload affordance belongs in `fileDrop`.
|
|
42
51
|
- DOM product text rendered inside `canvasContent` must be marked with `data-toolcraft-product-output` or `data-toolcraft-product-text` so tests and performance fixtures can target product output instead of app chrome.
|
|
43
52
|
- Product editing handles must be textless overlays, write to runtime state, and stay out of export/copy output.
|
|
44
53
|
- Preserve the runtime canvas backing. Product renderers may draw their own product background, but must not hide, replace, or make the Toolcraft canvas shell/backing transparent.
|
|
54
|
+
- Runtime owns the product scene surface. In infinite mode it resolves `sceneBoundsProvider` for the exact committed state and applies that frame before mounting product output. Product code does not position another scene wrapper from `canvas.size` or DOM measurement.
|
|
55
|
+
- Canvas 2D, WebGL, and WebGPU product output reads `useToolcraftProductSceneFrame()` for backing size and world-to-local translation. Finite frames are `{ x: 0, y: 0, width: canvas.size.width, height: canvas.size.height }`; infinite frames use the provider union. `empty` and `unavailable` are explicit states and never fall back to dormant finite dimensions.
|
|
45
56
|
|
|
46
57
|
## Model Presentation Boundary
|
|
47
58
|
|
|
@@ -65,7 +76,7 @@ Use only these app-specific extension points. Shared runtime changes happen upst
|
|
|
65
76
|
- Generated applications keep their public entry surface in `src/app/app-composition.tsx` and `src/app/app-schema.ts`. Supporting product modules may live anywhere under `src`; every product production module is discovered by the same source inventory and checked by the same AST boundary.
|
|
66
77
|
- The signed framework bootstrap includes `index.html`, `src/main.tsx`, `src/router.tsx`, `src/routes/index.tsx`, `src/routes/root.tsx`, and `src/styles.css`. Do not edit or replace those host files in a generated app.
|
|
67
78
|
- Product styling is local by construction: use locally imported `*.module.css` files only. Every selector starts with a compound containing a local class. A first-compound `:is()` or `:where()` remains local only when every branch is locally anchored; `:not()` and `:has()` do not create a local anchor. Descendants may style product-owned children, but `:global`, bare/root selectors, host-attribute selectors, sibling escapes, CSS `@import`, package CSS imports, and product-created global `<style>`/`CSSStyleSheet` injection are rejected because they cross the product/runtime boundary.
|
|
68
|
-
- Product `import()` and `require()` specifiers must be statically resolvable from literals, same-file constants, templates, or string concatenation. Production and test source share this rule, so computed module loading cannot hide a runtime/control import or a protected evidence channel.
|
|
79
|
+
- Product `import()` and `require()` specifiers must be statically resolvable from literals, same-file constants, templates, or string concatenation. Production and test source share this rule, so computed module loading cannot hide a runtime/control import or a protected evidence channel. Every deep control implementation import is rejected regardless of imported symbol name, and JSX control-substitute checks follow public `Input` aliases.
|
|
69
80
|
- Product production modules must not import product tests, test-support modules, or protected browser-evidence internals, directly or through a product bridge. Runtime evidence is emitted only by the protected public acceptance/performance helpers after their assertions pass; product-owned source must not import, re-export, assemble, or forge the reserved evidence module names and payload identifiers.
|
|
70
81
|
- Product production modules must form an acyclic dependency graph. The code-health gate resolves relative imports, directory `index.*` modules, configured TypeScript path aliases, and local package exports. Type-only imports, external packages, tests, and copied Toolcraft framework internals do not create product dependency edges. When a cycle exists, the gate prints the complete shortest cycle so the ownership boundary can be corrected directly.
|
|
71
82
|
- Imported product source remains under `src`. Code health and product-boundary analysis consume the same canonical source-inventory semantics instead of maintaining independent recursive walkers. The signed Vitest reporter uses the canonical framework-ownership policy to distinguish product-owned runner results, while acceptance and performance requirements derive from typed app configuration.
|
|
@@ -31,9 +31,16 @@ Read this module before changing Setup, canvas sizing, background, image export,
|
|
|
31
31
|
- `Infinity canvas` is the one runtime-owned mode switch for an unbounded workspace. Product code does not mirror it in `state.values` or create another canvas-mode control.
|
|
32
32
|
- Turning it on removes the finite artboard boundary and clipping. `Aspect ratio`, `Canvas width`, and `Canvas height` disappear because they do not constrain the workspace.
|
|
33
33
|
- Infinity canvas suppresses the bounded product-rendered preview background so the dormant finite output does not appear as a second canvas. While Background is on, `CanvasShell` fills the complete infinite viewport with the selected `Background color`; product code must not draw a synthetic workspace rectangle.
|
|
34
|
+
- Product output that semantically represents an editor environment rather than
|
|
35
|
+
bounded scene geometry may use `ToolcraftAppComposition.infiniteCanvasContent`.
|
|
36
|
+
Runtime mounts it only in Infinity mode, below the transformed product world,
|
|
37
|
+
across the full viewport, with pointer input disabled. It does not pan, zoom,
|
|
38
|
+
contribute to `sceneBoundsProvider`, or enter image/video export.
|
|
34
39
|
- The last finite `canvas.size` remains dormant and immutable while Infinity canvas is on. Turning it off restores that exact size and centers the finite artboard; reset, undo/redo, persistence, and settings transfer preserve the same canonical `canvas.mode` behavior. Fixed proof compares exact artboard pixels and width/height control values before Infinity, after restoration, and after redo.
|
|
35
40
|
- Runtime image and model assets keep explicit center-anchored world frames. Zoom, pan, radar, and model orientation change presentation, not scene geometry or export bounds.
|
|
36
41
|
- Product `canvasContent` and custom renderer output declare one direct `ToolcraftAppComposition.sceneBoundsProvider`. It returns product world-space rectangles for the supplied exact frame state; do not use a registry, DOM measurement, or app-authored time-range envelope.
|
|
42
|
+
- Runtime resolves that provider for the live committed state and positions one product scene surface at the exact union. Product output fills that surface; it does not create another absolute wrapper or reuse dormant finite `canvas.size` in infinite mode.
|
|
43
|
+
- Canvas 2D, WebGL, and WebGPU renderers call `useToolcraftProductSceneFrame()` inside `canvasContent` and use its rect for backing dimensions plus world-to-local translation. The hook reports finite, infinite, empty, or unavailable explicitly; empty/unavailable infinite frames do not silently render through finite fallback geometry.
|
|
37
44
|
- Infinite PNG export crops to the outward-rounded union of visible product, image, and model frames. Hidden or unavailable layers, runtime media suppressed by the composition, and editor-only handles or gizmos are excluded.
|
|
38
45
|
- Prove unavailable-image exclusion with `createToolcraftUnavailableImageResourceFixture` and `expectToolcraftInfinityCanvasUnavailableImageExportEvidence`; product tests never mutate storage/state or call the reserved bridge, and evidence publishes only after deterministic cleanup restores the ready resource.
|
|
39
46
|
- Infinite video export asks the provider for every state in the runtime-owned frame schedule, unions those bounds once, and uses the result for every encoded frame, preventing frame-to-frame output size changes.
|
|
@@ -75,13 +82,34 @@ Read this module before changing Setup, canvas sizing, background, image export,
|
|
|
75
82
|
- Runtime image export reads Background directly: PNG can be transparent, while JPG remains opaque.
|
|
76
83
|
- Runtime video export keeps the selected background even when Background is off.
|
|
77
84
|
|
|
85
|
+
## Artifact Export Intent
|
|
86
|
+
|
|
87
|
+
Use this sequence as the single authority for choosing product artifact delivery:
|
|
88
|
+
|
|
89
|
+
1. Start every product with image export.
|
|
90
|
+
2. Add video export only when the user explicitly requests video delivery.
|
|
91
|
+
3. Do not infer video from animation, playback, keyframes, or timeline.
|
|
92
|
+
4. Keep image with video unless the user explicitly requests removal.
|
|
93
|
+
5. Record in `productReadiness.exportIntent`.
|
|
94
|
+
|
|
95
|
+
Product-mode readiness requires both discriminated decisions. Image uses `toolcraft-default`, `user-requested`, or `user-removed`: `user-requested` requires non-empty user-request evidence, and `user-removed` requires non-empty explicit user-removal evidence. Video uses `not-requested` or `user-requested`; `user-requested` requires non-empty explicit user-request evidence. Do not add optional modes, legacy fallbacks, or schema-derived inference. Resolved intent must correspond exactly to runtime-owned schema actions, settings sections, and artifact acceptance:
|
|
96
|
+
|
|
97
|
+
| Resolved delivery | Settings layout | Sticky export actions | Artifact acceptance |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| Image only | `Image Export` directly above sticky actions | `Export PNG` primary | Complete image coverage only |
|
|
100
|
+
| Image and video | `Image Export` immediately before `Video Export`; `Video Export` directly above sticky actions | `Export PNG` secondary, `Export Video` primary | Complete image and video coverage |
|
|
101
|
+
| Video only | `Video Export` directly above sticky actions | `Export Video` primary | Complete video coverage only |
|
|
102
|
+
| Explicit no-export | No image or video settings section | No image or video export action | No image or video artifact row |
|
|
103
|
+
|
|
104
|
+
Video-only requires non-empty evidence for both the explicit video request and explicit image removal. Explicit no-export requires non-empty image-removal evidence and video `not-requested`.
|
|
105
|
+
|
|
78
106
|
## Image Export
|
|
79
107
|
|
|
80
108
|
- Every app with `Export PNG` exposes a separate `Image Export` section.
|
|
81
109
|
- `Image Export` uses two `select` controls in one compact two-column inline row:
|
|
82
110
|
- `export.image.format`, default `png`, with baseline `PNG` and `JPG` options;
|
|
83
111
|
- `export.image.resolution`, default `4k`, with baseline `2K`, `4K`, and `8K` options.
|
|
84
|
-
-
|
|
112
|
+
- Image-only apps place `Image Export` directly above sticky footer actions.
|
|
85
113
|
- Apps with both image and video export place `Image Export` immediately before `Video Export`.
|
|
86
114
|
- Typed `export-image` actions are handled by the runtime. It resolves the current scene frame, selected format and resolution, allocates the exact backing, composites background plus visible runtime media/models, awaits `ToolcraftAppComposition.exportRenderer`, encodes the selected artifact, downloads it, and reports typed progress/failures.
|
|
87
115
|
- Product code supplies only the shared deterministic `exportRenderer.renderFrame` callback in scene coordinates. It must not allocate an export canvas, call `toBlob`/`toDataURL`, create object URLs, or download the artifact.
|
|
@@ -89,9 +117,9 @@ Read this module before changing Setup, canvas sizing, background, image export,
|
|
|
89
117
|
|
|
90
118
|
## Video Export
|
|
91
119
|
|
|
92
|
-
-
|
|
120
|
+
- Only products with video `user-requested` intent and non-empty explicit user-request evidence expose `Export Video`; animation and timeline behavior do not authorize it.
|
|
93
121
|
- Any app with `Export Video` must enable the top Toolcraft timeline.
|
|
94
|
-
- Apps with `Export Video` expose a separate `Video Export` section directly above sticky footer export
|
|
122
|
+
- Apps with `Export Video` expose a separate `Video Export` section directly above sticky footer actions. When image export is also enabled, `Image Export` sits immediately before it.
|
|
95
123
|
- `Video Export` uses two `select` controls in one compact two-column inline row by default:
|
|
96
124
|
- `export.video.format`, default `mp4`, with baseline `MP4` and `WebM` options;
|
|
97
125
|
- `export.video.resolution`, default `current`, with baseline `Current` and `4K` options.
|
|
@@ -105,10 +133,8 @@ Read this module before changing Setup, canvas sizing, background, image export,
|
|
|
105
133
|
|
|
106
134
|
## Sticky Product Actions
|
|
107
135
|
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
- Products whose requested delivery includes animation also expose `Export Video`.
|
|
111
|
-
- Clipboard copy is optional and never replaces export.
|
|
136
|
+
- Export actions in sticky `panelActions` match the resolved artifact intent exactly. Explicit no-export products have no image or video export action.
|
|
137
|
+
- Clipboard copy may be an additional product action, but it never changes or substitutes for the recorded artifact intent.
|
|
112
138
|
- Export PNG and Export Video use `icon: "upload-simple"` to match the runtime `Export Settings` action.
|
|
113
139
|
- Runtime export actions own their real Promise and report render/encode/download progress through the sticky footer indicator.
|
|
114
140
|
- Async non-export download/copy/generate/apply handlers return the real Promise from `onPanelAction` and use `reportProgress(0..1)` when determinate progress is available.
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
Use a custom control only when no built-in Toolcraft control represents the product interaction.
|
|
6
6
|
|
|
7
|
-
Built-ins come first: `slider`, `rangeSlider`, `select`, `segmented`, `switch`, `checkbox`, `color`, `colorOpacity`, `vector`, `gradient`, `curves`, `fontPicker`, `imagePicker`, `fileDrop`, `text`, `code`, `rangeInput`, `palette`, `actions`, `collectionActions`, and `panelActions`.
|
|
7
|
+
Built-ins come first: `slider`, `rangeSlider`, `select`, `segmented`, `switch`, `checkbox`, `color`, `colorOpacity`, `vector`, `gradient`, `curves`, `fontPicker`, `imagePicker`, `fileDrop`, `text`, `code`, `rangeInput`, `palette`, `actions`, `sourceCollection`, `collectionActions`, and `panelActions`.
|
|
8
8
|
|
|
9
9
|
Register custom renderers through `ToolcraftApp controlRenderers`.
|
|
10
10
|
|
|
11
|
-
Do not use `controlRenderers` to recreate a built-in control. If the product needs a slider, select, segmented mode picker, color input, gradient editor, font picker, image upload, arbitrary file upload, textarea, local action group, repeatable item add/remove, or footer action, declare the matching schema control instead of rendering the component manually.
|
|
11
|
+
Do not use `controlRenderers` to recreate a built-in control. If the product needs a slider, select, segmented mode picker, color input, gradient editor, font picker, image upload, arbitrary file upload, textarea, local action group, source-sized repeated item editor, repeatable item add/remove, or footer action, declare the matching schema control instead of rendering the component manually.
|
|
12
|
+
|
|
13
|
+
Product modules never import deep paths below `src/toolcraft/ui/components/controls/**` and never replace schema value models with native `color`, `range`, `file`, `checkbox`, `radio`, `select`, or `textarea` controls. If a built-in lacks a required variant, improve the shared runtime instead of copying its private popover, parser, history, or state mechanics.
|
|
12
14
|
|
|
13
15
|
Do not edit `ControlsPanel`, copied `src/toolcraft`, or Toolcraft internals inside a generated app.
|
|
14
16
|
|
|
@@ -40,7 +42,7 @@ builtInFitCheck: {
|
|
|
40
42
|
"commands",
|
|
41
43
|
"custom-value-model",
|
|
42
44
|
],
|
|
43
|
-
checkedBuiltIns: ["fileDrop", "collectionActions", "imagePicker"],
|
|
45
|
+
checkedBuiltIns: ["fileDrop", "sourceCollection", "collectionActions", "imagePicker"],
|
|
44
46
|
closestBuiltIn: "fileDrop",
|
|
45
47
|
whyInsufficient:
|
|
46
48
|
"FileDrop imports, previews, orders, and removes source files, but this product also needs per-glyph density thresholds stored with each item.",
|
|
@@ -51,7 +53,7 @@ builtInFitCheck: {
|
|
|
51
53
|
|
|
52
54
|
`capabilities` is required and uses broad behavior facts: `collection`, `reorder`, `selection`, `commands`, `custom-interaction`, `custom-value-model`, or `custom-visualization`. At least one of the three `custom-*` capabilities must explain why built-ins cannot own the interaction. `checkedBuiltIns` must name real Toolcraft built-in controls. `closestBuiltIn` must be one of those checked controls or `"none"` when no built-in is meaningfully close. `whyInsufficient` explains the missing interaction. `productObservable` names the output or side effect that proves the custom control is necessary.
|
|
53
55
|
|
|
54
|
-
If the custom control owns a
|
|
56
|
+
If the custom control owns a repeated runtime item set, `checkedBuiltIns` must include `sourceCollection` and `collectionActions` so the fit check distinguishes source-owned from user-owned cardinality. Include `actions` when the custom interaction also exposes commands. Decide this from the value model and workflow, such as arrays, `{ items: [...] }` objects, selected-item state, or add/remove/reorder behavior, not from entity names like masks or glyphs. This applies even when the empty state visually looks like a few icon buttons: the fit check must prove why neither built-in collection owner can represent the state and why command UI is necessary when commands exist.
|
|
55
57
|
|
|
56
58
|
Do not justify a custom control with icons, layout, styling, compactness, or custom buttons alone. If the built-in control has the right value model and mechanics, use it or improve that built-in instead.
|
|
57
59
|
|
|
@@ -21,7 +21,7 @@ Use this before writing a schema, spec, or implementation plan. It separates har
|
|
|
21
21
|
| Panels | Mixed | Panel mechanics are hard; panel presence is product-dependent |
|
|
22
22
|
| Layers | Heuristic, then invariant | Enable only for real layer behavior; fully test when enabled |
|
|
23
23
|
| Timeline | Heuristic, then invariant | Choose from Animation Intent Inventory and transport behavior |
|
|
24
|
-
| Controls | Mixed | Bind every visible control, choose one evidence-backed surface per operation, and
|
|
24
|
+
| Controls | Mixed | Bind every visible control, choose one evidence-backed surface per operation, and make artifact UI match required export intent |
|
|
25
25
|
| Renderer | Default plus invariant | Choose technique from fidelity/workload and declare spatial view interaction before renderer code |
|
|
26
26
|
| Reference analysis | Invariant | Study video references as frame-to-frame behavior before implementation |
|
|
27
27
|
| Reference clone | Invariant | Preserve reference behavior unless redesign is explicit |
|
|
@@ -36,6 +36,8 @@ The runtime shell invariant means product code exports the typed composition and
|
|
|
36
36
|
|
|
37
37
|
This catalog mirrors `TOOLCRAFT_DECISION_CONTRACT`. If runtime adds or renames a rule id, this page and `AGENTS.md` must list the same id.
|
|
38
38
|
|
|
39
|
+
`output-export-required` keeps artifact intent and implementation in exact correspondence. Product-mode readiness records `productReadiness.exportIntent`; schema actions, settings sections, and artifact acceptance expose exactly the enabled image/video capabilities. The authoritative decision sequence and evidence requirements live in `core/setup-export.md`.
|
|
40
|
+
|
|
39
41
|
[//]: # (toolcraft-contract:decision-rule-table:start)
|
|
40
42
|
| Rule ID | Level | Area |
|
|
41
43
|
| --- | --- | --- |
|
|
@@ -27,6 +27,10 @@ Do not write renderer code before the envelope, `rendererTechnique`, pipeline, a
|
|
|
27
27
|
- Prefer retained resources and stable cache keys. Source-bound resources live outside React render, survive unrelated interactions according to pass lifecycle, and release during cleanup.
|
|
28
28
|
- Separate layers when they have different semantics, invalidation, lifecycle, interaction, or export treatment. A costly layer does not force unrelated output into the same renderer.
|
|
29
29
|
- Preserve selected quality, product boundaries, backing resolution, and source fidelity. A renderer is not accepted by silently reducing them.
|
|
30
|
+
- In infinite mode, custom Canvas 2D, WebGL, and WebGPU previews use `useToolcraftProductSceneFrame()` as their backing and coordinate frame. Do not keep rendering into dormant finite `canvas.size`, derive another bounds wrapper, or measure DOM geometry; runtime already positions the product scene surface from `sceneBoundsProvider`.
|
|
31
|
+
- A preview-only environment that must stay fixed to the complete Infinity
|
|
32
|
+
viewport uses `infiniteCanvasContent`, not the bounded product renderer. Keep
|
|
33
|
+
that layer pointer-transparent and exclude it from scene bounds and export.
|
|
30
34
|
|
|
31
35
|
When assessment requires a benchmark, declare `kernelBenchmarkDecisions`, implement only the named executable candidates in `e2e/app-kernel-benchmarks.ts`, and let protected `pnpm verify:kernel` measure them at the exact combined workload vector. Candidate outputs must be deterministic and equal at full quality. Do not author timing values or add speculative benchmark metadata when generic assessment resolves the choice.
|
|
32
36
|
|