@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
|
@@ -42,14 +42,14 @@ Custom product output declares bounds on the composition:
|
|
|
42
42
|
```tsx
|
|
43
43
|
export const appComposition: ToolcraftAppComposition = {
|
|
44
44
|
canvasContent: <ProductCanvas />,
|
|
45
|
-
sceneBoundsProvider: ({ state
|
|
46
|
-
getVisibleProductSceneRect(state
|
|
45
|
+
sceneBoundsProvider: ({ state }) => [
|
|
46
|
+
getVisibleProductSceneRect(state),
|
|
47
47
|
],
|
|
48
48
|
schema: appSchema,
|
|
49
49
|
};
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
The provider returns world-space `{ x, y, width, height }` rectangles. Runtime unions them
|
|
52
|
+
The provider returns world-space `{ x, y, width, height }` rectangles for one exact state. Runtime uses their union for the live infinite product scene, unions them with visible image/model frames for export, and calls the provider for every scheduled video state before forming one stable envelope. Canvas 2D, WebGL, and WebGPU product output calls `useToolcraftProductSceneFrame()` inside `canvasContent` for active backing size and world-to-local translation. Product code does not read DOM bounds, author a time-range envelope, or reuse dormant finite size in infinite mode.
|
|
53
53
|
|
|
54
54
|
Use `canvas.renderScale: true` or `canvas.renderScale: { step }` only for non-vector raster previews such as Canvas 2D, WebGL, or WebGPU. For this field, product code may customize only the slider step; authored schema cannot set `enabled`, `min`, `defaultValue`, or `max`. Runtime resolves enabled render scale to `{ enabled: true, min: 1, defaultValue: 2, max: 2, step }`, using `0.25` when no step is authored. A custom step must be finite, between `0.01` and `1`, and evenly partition the canonical `1..2` range so `2` remains reachable; invalid input fails fast. Do not enable it for DOM/SVG/vector-native previews. Enabling it requires one browser runtime acceptance row targeting `canvas.renderScale` with `renderScaleCoverage.kind: "selected-backing-pixels"` and exact sorted states `["interaction", "steady"]`, plus `"playback"` when timeline is enabled. The fixed `canvas-render-scale-backing` recipe proves actual backing pixels rather than timing.
|
|
55
55
|
|
|
@@ -89,7 +89,7 @@ media: {
|
|
|
89
89
|
|
|
90
90
|
`sourceTarget` must match a compatible `fileDrop` control target. Runtime shows the asset as an attached file, users can remove it, and Reset restores it. Model defaults require `assetKind: "model"`, a root `fileName`, and complete local `sourceFiles`; each source uses a serializable `dataUrl` plus its bundle-relative `path`. Runtime restores default models through the same validation, analysis, repair, repository, and rendering pipeline as a user upload. Persisted empty media remains empty until Reset and must not silently resurrect defaults.
|
|
91
91
|
|
|
92
|
-
For a multi-item `fileDrop`, `recommendedMaxItems` is advisory. `hardMaxItems`, when present, must be a finite nonnegative safe integer and is enforced before decode or storage allocation. Additive imports count current assets with the same `sourceTarget` plus the incoming logical batch; replacement imports count only the incoming batch.
|
|
92
|
+
For a multi-item `fileDrop`, `recommendedMaxItems` is advisory. `hardMaxItems`, when present, must be a finite nonnegative safe integer and is enforced before decode or storage allocation. Additive imports count current assets with the same `sourceTarget` plus the incoming logical batch; replacement imports count only the incoming batch. A `fileDrop` with `assetKind: "file"`, `multiple: true`, and `variant: "collection-actions"` may declare `itemControls`; each entry uses a supported built-in collection item control and declares `defaultValue`. Attached files render these controls directly beneath their upload row, values persist at the parent target as records keyed by `mediaId`, and file bytes remain runtime-owned media.
|
|
93
93
|
|
|
94
94
|
## Model FileDrop
|
|
95
95
|
|
|
@@ -158,9 +158,9 @@ Common control fields:
|
|
|
158
158
|
| `defaultValue` | Initial value and reset value. |
|
|
159
159
|
| `label` | Short UI label, `false`, or omitted. |
|
|
160
160
|
| `description` | Product-specific help text only when it adds meaning beyond the label. |
|
|
161
|
-
| `
|
|
161
|
+
| `applicability` | Required product claim: `{ mode: "always" }` or `{ mode: "conditional", all: [...] }`. Hidden values are preserved. |
|
|
162
162
|
| `orderRole` | Makes section order testable. |
|
|
163
|
-
| `semanticGroup` | Language-independent product sub-entity/workflow grouping, required on every control in sections
|
|
163
|
+
| `semanticGroup` | Language-independent product sub-entity/workflow grouping, required on every control in sections with eight to ten controls. |
|
|
164
164
|
| `sliderValueKind` | `slider` intent: `"continuous"` or `"discrete"`. |
|
|
165
165
|
| `textValueKind` | `text`/`code` intent: `"single-line"`, `"multiline"`, or `"structured"`. |
|
|
166
166
|
| `curveIntent` | `curves` composition: `"single-value-map"` or `"color-channels"`. |
|
|
@@ -170,11 +170,9 @@ Common control fields:
|
|
|
170
170
|
| `keyframeable` | Timeline/keyframe capability override when structurally needed. |
|
|
171
171
|
| `variant` | Component-specific variant. |
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
Every authored product control declares `applicability`: use `{ mode: "always" }` only when it affects its accepted outcome in every finite sibling-selector branch, or `{ mode: "conditional", all: [...] }` when every predicate must match. Inactive controls are absent while values remain preserved. Do not use `disabled`, `disabledWhen`, or inert visible controls for availability. Legacy control `visibleWhen` remains readable only at the low-level compatibility boundary; product acceptance rejects it and omitted applicability. Section `visibleWhen` remains a separate legacy section-layout feature.
|
|
174
174
|
|
|
175
|
-
Conditions support `equals`, `notEquals`, `oneOf`, `notOneOf`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, and `lessThanOrEqual`.
|
|
176
|
-
|
|
177
|
-
Reserved runtime targets include `runtime.settingsTransfer`, `canvas.infinity`, `canvas.aspectRatio`, `canvas.size.width`, `canvas.size.height`, `canvas.renderScale`, and `panels.timeline.extended`. Product sections must not declare those controls.
|
|
175
|
+
Conditions support `equals`, `notEquals`, `oneOf`, `notOneOf`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, and `lessThanOrEqual`. Reserved runtime targets include `runtime.settingsTransfer`, `canvas.infinity`, `canvas.aspectRatio`, `canvas.size.width`, `canvas.size.height`, `canvas.renderScale`, and `panels.timeline.extended`; product sections must not declare them.
|
|
178
176
|
|
|
179
177
|
## Built-In Control Types
|
|
180
178
|
|
|
@@ -194,6 +192,7 @@ Reserved runtime targets include `runtime.settingsTransfer`, `canvas.infinity`,
|
|
|
194
192
|
| `checkbox` | `Checkbox` |
|
|
195
193
|
| `actions` | `Actions` |
|
|
196
194
|
| `collectionActions` | `CollectionActions` |
|
|
195
|
+
| `sourceCollection` | `ControlsPanelCollectionItems` |
|
|
197
196
|
| `panelActions` | `PanelActions` |
|
|
198
197
|
| `colorOpacity` | `ColorOpacity` |
|
|
199
198
|
| `palette` | `Palette` |
|
|
@@ -210,7 +209,7 @@ Reserved runtime targets include `runtime.settingsTransfer`, `canvas.infinity`,
|
|
|
210
209
|
| `settingsTransfer` | `SettingsTransfer` |
|
|
211
210
|
[//]: # (toolcraft-contract:built-in-control-table:end)
|
|
212
211
|
|
|
213
|
-
Use `component-rules.md` for component-specific fit, labels, variants, units, parser behavior, and exceptions. Use `core/control-selection.md` before deciding a custom control is needed.
|
|
212
|
+
Use `component-rules.md` for component-specific fit, labels, variants, units, parser behavior, and exceptions. `sourceCollection` renders a supported built-in `itemControl` for a source-owned array without add/remove UI; `collectionActions` owns user cardinality and its limits. Use `itemControl` for one homogeneous repeated value. For one logical repeated entity made from two or more built-in fields, use `itemControls`; for example, `{ type: "collectionActions", target: "surface.layers", defaultValue: [{ strength: 0.4, invert: false }], itemControls: { strength: { type: "slider", defaultValue: 0.5 }, invert: { type: "switch", defaultValue: false } } }`. Every field requires `defaultValue`; `itemControl` and `itemDefaultValue` are invalid competing template sources. `+` appends the complete defaults, `−` removes the final record, edits preserve sibling/product keys, and runtime places a line only between compound records without `Item N` headings. Standalone color `itemControl` remains a divider-free two-column grid. Use `core/control-selection.md` before deciding a custom control is needed.
|
|
214
213
|
|
|
215
214
|
`orientationGizmo` uses a non-degenerate `{ position: [x, y, z], up: [x, y, z] }` default, `label: false`, and `keyframeable: false`. Declare one target for the active/selected model and keep it beside at least one visible product control in the semantic model/view section; runtime renders the handle on the canvas rather than in the controls panel. Multiple declarations require statically provable mutually exclusive combined section/control visibility conditions, because runtime permits at most one active orientation handle.
|
|
216
215
|
|
|
@@ -221,6 +220,7 @@ before schema controls or renderer code:
|
|
|
221
220
|
|
|
222
221
|
```ts
|
|
223
222
|
export const starterProductReadiness: ToolcraftProductReadiness = {
|
|
223
|
+
exportIntent: { image: { mode: "toolcraft-default" }, video: { mode: "not-requested" } },
|
|
224
224
|
interactionOwnership: [],
|
|
225
225
|
mode: "product",
|
|
226
226
|
productName: "Model Studio",
|
|
@@ -290,22 +290,22 @@ exactly match schema `orientationGizmo` targets.
|
|
|
290
290
|
## Control Section Inventory
|
|
291
291
|
|
|
292
292
|
Before writing `panels.controls.sections`, export `starterControlSectionInventory` beside `starterAcceptance`:
|
|
293
|
-
|
|
294
293
|
```ts
|
|
295
294
|
export const starterControlSectionInventory = [
|
|
296
295
|
{
|
|
297
296
|
entity: "Text block",
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
entityId: "text-block",
|
|
298
|
+
groupingReason: "These controls edit the text content, typography, and visible text fill together.",
|
|
299
|
+
id: "text",
|
|
300
300
|
targets: ["text.content", "text.font"],
|
|
301
301
|
title: "Text",
|
|
302
302
|
},
|
|
303
303
|
] as const;
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
-
Every product control target appears exactly once in the inventory. Runtime `Setup`, sticky footer `Export`, settings transfer, and runtime canvas sizing targets do not need entries.
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
- Every product control target appears exactly once in the inventory. Runtime-owned `Setup`, sticky footer `Export`, settings transfer, and runtime canvas sizing targets do not need entries. The product targets authored in the standard `Background` source section remain in its `Background` inventory entry after runtime relocates those controls into `Setup`. Stable `entityId` is the primary grouping authority; target namespaces are only secondary diagnostics.
|
|
307
|
+
- Keep one logical entity in one section through ten controls. One to seven controls is the normal size; sections with eight to ten controls require `semanticGroup` on every control. Controls editing one tightly scoped product sub-entity share the same group, and this fact may not be inferred from English labels.
|
|
308
|
+
- When one entity has more than ten controls, split it into balanced workflow sections containing two to ten controls. Every split section keeps the same `entityId` and `entity`, declares a unique `workflowStage`, and provides a concrete `splitReason`. Do not create a one-control tail.
|
|
309
309
|
|
|
310
310
|
## Transfer Metadata
|
|
311
311
|
|
|
@@ -313,13 +313,15 @@ Reference and motion metadata lives in `starterTransferMode`.
|
|
|
313
313
|
|
|
314
314
|
Use `transferMode: "reference-runtime-clone"` when porting an existing app unless the user explicitly asks for redesign. Reference clones declare `referenceStudy`, `referenceFeatureInventory`, and acceptance mapping; detailed evidence requirements live in `core/reference-study.md`.
|
|
315
315
|
|
|
316
|
+
Every product readiness declaration includes required `exportIntent`. Its resolved image/video capabilities must correspond exactly to schema actions and settings sections. Use `core/setup-export.md` for the authoritative modes, evidence requirements, and decision sequence.
|
|
317
|
+
|
|
316
318
|
Video references declare `videoReferenceStudy` before implementation. Animated products declare `animationIntent`, and playback/keyframe timeline apps declare a proven loop duration when known. Detailed animation rules live in `core/timeline-animation.md`.
|
|
317
319
|
|
|
318
320
|
## Export And Actions
|
|
319
321
|
|
|
320
|
-
Product apps expose delivery through sticky `panelActions`, not canvas UI or ordinary body controls.
|
|
322
|
+
Product apps expose enabled artifact delivery through sticky `panelActions`, not canvas UI or ordinary body controls. `productReadiness.exportIntent` is required: image export defaults on and is absent only with explicit removal evidence; video export exists only with explicit user-request evidence. Animation, playback, keyframes, and timeline presence never change that intent.
|
|
321
323
|
|
|
322
|
-
Every app with `Export PNG` includes `Image Export` controls with `export.image.format` and `export.image.resolution`. Apps with `Export Video`
|
|
324
|
+
Every app with `Export PNG` includes `Image Export` controls with `export.image.format` and `export.image.resolution`. Apps with `Export Video` include `Video Export` controls with `export.video.format` and `export.video.resolution`. Image-only, image-plus-video, video-only, and explicit no-export layouts must match `core/setup-export.md` exactly.
|
|
323
325
|
|
|
324
326
|
Use the standard runtime path: declare typed `export-image`/`export-video` panel actions and their settings, provide one `ToolcraftAppComposition.exportRenderer` when product code contributes pixels, and draw one deterministic frame from the supplied state/time in scene coordinates. Call `shouldIncludeToolcraftPreviewBackground(state)` only for bounded live preview; runtime owns artifact composition, sizing, encoding, download, progress, and errors.
|
|
325
327
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { defineToolcraft } from "@repo/toolcraft-runtime";
|
|
3
|
+
|
|
4
|
+
import { deriveToolcraftBrowserRuntimeRequirements } from "./browser-runtime-evidence-requirements";
|
|
5
|
+
|
|
6
|
+
const inventory = [
|
|
7
|
+
{
|
|
8
|
+
entity: "Feature",
|
|
9
|
+
entityId: "feature",
|
|
10
|
+
groupingReason: "Mode and amount describe one feature.",
|
|
11
|
+
id: "feature",
|
|
12
|
+
targets: ["feature.mode", "feature.amount"],
|
|
13
|
+
title: "Feature",
|
|
14
|
+
},
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
test("conditional controls derive case-specific visibility and outcome evidence", () => {
|
|
18
|
+
const schema = defineToolcraft({
|
|
19
|
+
canvas: { enabled: true },
|
|
20
|
+
panels: {
|
|
21
|
+
controls: {
|
|
22
|
+
sections: [
|
|
23
|
+
{
|
|
24
|
+
controls: {
|
|
25
|
+
amount: {
|
|
26
|
+
applicability: {
|
|
27
|
+
all: [{ equals: "advanced", target: "feature.mode" }],
|
|
28
|
+
mode: "conditional",
|
|
29
|
+
},
|
|
30
|
+
defaultValue: 1,
|
|
31
|
+
target: "feature.amount",
|
|
32
|
+
type: "slider",
|
|
33
|
+
},
|
|
34
|
+
mode: {
|
|
35
|
+
applicability: { mode: "always" },
|
|
36
|
+
defaultValue: "simple",
|
|
37
|
+
options: [
|
|
38
|
+
{ label: "Simple", value: "simple" },
|
|
39
|
+
{ label: "Advanced", value: "advanced" },
|
|
40
|
+
],
|
|
41
|
+
target: "feature.mode",
|
|
42
|
+
type: "segmented",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
id: "feature",
|
|
46
|
+
title: "Feature",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
title: "Controls",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const requirements = deriveToolcraftBrowserRuntimeRequirements(
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
browser: true,
|
|
57
|
+
browserTestName: "browser: conditional amount",
|
|
58
|
+
evidence: "product-output",
|
|
59
|
+
id: "feature.amount",
|
|
60
|
+
kind: "control",
|
|
61
|
+
target: "feature.amount",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
schema,
|
|
65
|
+
inventory,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(requirements.map(({ evidenceType }) => evidenceType)).toEqual(
|
|
69
|
+
expect.arrayContaining([
|
|
70
|
+
"control-applicability-hidden",
|
|
71
|
+
"control-applicability-visible",
|
|
72
|
+
"product-observable-change",
|
|
73
|
+
]),
|
|
74
|
+
);
|
|
75
|
+
expect(
|
|
76
|
+
requirements.some(
|
|
77
|
+
(requirement) => requirement.requirementId === "feature.amount",
|
|
78
|
+
),
|
|
79
|
+
).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("finite siblings expand outcomes even when always-applicability omits the selector", () => {
|
|
83
|
+
const schema = defineToolcraft({
|
|
84
|
+
canvas: { enabled: true },
|
|
85
|
+
panels: {
|
|
86
|
+
controls: {
|
|
87
|
+
sections: [
|
|
88
|
+
{
|
|
89
|
+
controls: {
|
|
90
|
+
amount: {
|
|
91
|
+
applicability: { mode: "always" },
|
|
92
|
+
defaultValue: 1,
|
|
93
|
+
target: "feature.amount",
|
|
94
|
+
type: "slider",
|
|
95
|
+
},
|
|
96
|
+
mode: {
|
|
97
|
+
applicability: { mode: "always" },
|
|
98
|
+
defaultValue: "simple",
|
|
99
|
+
options: [
|
|
100
|
+
{ label: "Simple", value: "simple" },
|
|
101
|
+
{ label: "Advanced", value: "advanced" },
|
|
102
|
+
],
|
|
103
|
+
target: "feature.mode",
|
|
104
|
+
type: "segmented",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
id: "feature",
|
|
108
|
+
title: "Feature",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
title: "Controls",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const acceptance = {
|
|
116
|
+
browser: true,
|
|
117
|
+
browserTestName: "browser: feature amount",
|
|
118
|
+
evidence: "product-output" as const,
|
|
119
|
+
id: "feature.amount",
|
|
120
|
+
kind: "control" as const,
|
|
121
|
+
target: "feature.amount",
|
|
122
|
+
};
|
|
123
|
+
const requirements = deriveToolcraftBrowserRuntimeRequirements(
|
|
124
|
+
[acceptance],
|
|
125
|
+
schema,
|
|
126
|
+
inventory,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(requirements).toHaveLength(4);
|
|
130
|
+
expect(
|
|
131
|
+
requirements.filter(
|
|
132
|
+
({ evidenceType }) => evidenceType === "control-applicability-visible",
|
|
133
|
+
),
|
|
134
|
+
).toHaveLength(2);
|
|
135
|
+
expect(
|
|
136
|
+
requirements.filter(
|
|
137
|
+
({ evidenceType }) => evidenceType === "product-observable-change",
|
|
138
|
+
),
|
|
139
|
+
).toHaveLength(2);
|
|
140
|
+
expect(
|
|
141
|
+
requirements.every(({ requirementId }) =>
|
|
142
|
+
requirementId.includes("#applicability:feature.mode="),
|
|
143
|
+
),
|
|
144
|
+
).toBe(true);
|
|
145
|
+
|
|
146
|
+
expect(() =>
|
|
147
|
+
deriveToolcraftBrowserRuntimeRequirements(
|
|
148
|
+
[
|
|
149
|
+
{
|
|
150
|
+
...acceptance,
|
|
151
|
+
renderScaleCoverage: {
|
|
152
|
+
kind: "selected-backing-pixels",
|
|
153
|
+
states: [],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
schema,
|
|
158
|
+
inventory,
|
|
159
|
+
),
|
|
160
|
+
).toThrow(
|
|
161
|
+
"Visible applicability case for feature.amount has no product outcome requirement",
|
|
162
|
+
);
|
|
163
|
+
});
|
|
@@ -17,13 +17,18 @@ test("toolcraft Infinity canvas observation reads runtime-owned geometry", async
|
|
|
17
17
|
await page.setContent(`
|
|
18
18
|
<div data-toolcraft-canvas-world data-toolcraft-canvas-offset-x="12" data-toolcraft-canvas-offset-y="-8">
|
|
19
19
|
<div data-toolcraft-canvas-mode="infinite" style="overflow: visible">
|
|
20
|
-
<div data-scene style="position:absolute;left
|
|
20
|
+
<div data-scene style="position:absolute;left:1px;top:2px;width:3px;height:4px"></div>
|
|
21
|
+
<div
|
|
22
|
+
data-toolcraft-product-scene
|
|
23
|
+
data-toolcraft-product-scene-status="ready"
|
|
24
|
+
style="position:absolute;left:-320px;top:-200px;width:640px;height:400px"
|
|
25
|
+
></div>
|
|
21
26
|
</div>
|
|
22
27
|
</div>
|
|
23
28
|
`);
|
|
24
29
|
|
|
25
30
|
expectInfiniteCanvasObservation(
|
|
26
|
-
await observeInfinityCanvas(page
|
|
31
|
+
await observeInfinityCanvas(page),
|
|
27
32
|
{ height: 400, width: 640, x: -320, y: -200 },
|
|
28
33
|
);
|
|
29
34
|
});
|
|
@@ -14,6 +14,81 @@ import {
|
|
|
14
14
|
setProofState,
|
|
15
15
|
} from "./browser-semantic-evidence-test-helpers";
|
|
16
16
|
|
|
17
|
+
const orientationBaseline: ToolcraftOrientationBrowserObservation = {
|
|
18
|
+
outputSignature: "model-front",
|
|
19
|
+
pixelSignature: "pixels-front",
|
|
20
|
+
pose: { position: [0, 0, 5], up: [0, 1, 0] },
|
|
21
|
+
poseTarget: "view.orbit",
|
|
22
|
+
presentationCacheKey: "appearance:document-model:v2",
|
|
23
|
+
presentationDocumentId: "document-model",
|
|
24
|
+
viewportOffsetX: 0,
|
|
25
|
+
viewportOffsetY: 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const orientationDragged: ToolcraftOrientationBrowserObservation = {
|
|
29
|
+
...orientationBaseline,
|
|
30
|
+
outputSignature: "model-dragged",
|
|
31
|
+
pixelSignature: "pixels-dragged",
|
|
32
|
+
pose: { position: [1, 1, 4.8], up: [0, 1, 0] },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const orientationOptions = {
|
|
36
|
+
requirementId: "model.orientation",
|
|
37
|
+
stabilityIntervalMs: 0,
|
|
38
|
+
target: "view.orbit",
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
type OrientationProofUiState = Readonly<{
|
|
42
|
+
playbackActions?: readonly ("pause" | "play")[];
|
|
43
|
+
renderScales?: readonly Readonly<{
|
|
44
|
+
maximum: string;
|
|
45
|
+
value: string;
|
|
46
|
+
}>[];
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
async function installOrientationProofUiState(
|
|
50
|
+
page: Page,
|
|
51
|
+
state: OrientationProofUiState,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
await page.locator('[data-slot="toolcraft-runtime-app"]').evaluate(
|
|
54
|
+
(root, proofState) => {
|
|
55
|
+
root.querySelector('[data-orientation-proof-state]')?.remove();
|
|
56
|
+
const fixture = document.createElement("div");
|
|
57
|
+
fixture.dataset.orientationProofState = "";
|
|
58
|
+
|
|
59
|
+
for (const action of proofState.playbackActions ?? []) {
|
|
60
|
+
const button = document.createElement("button");
|
|
61
|
+
const label = action === "play" ? "Play playback" : "Pause playback";
|
|
62
|
+
button.type = "button";
|
|
63
|
+
button.textContent = label;
|
|
64
|
+
button.setAttribute("aria-label", label);
|
|
65
|
+
fixture.append(button);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const [index, renderScale] of (
|
|
69
|
+
proofState.renderScales ?? []
|
|
70
|
+
).entries()) {
|
|
71
|
+
const field = document.createElement("div");
|
|
72
|
+
field.dataset.toolcraftControlTarget = "canvas.renderScale";
|
|
73
|
+
field.dataset.orientationProofRenderScale = String(index);
|
|
74
|
+
const slider = document.createElement("div");
|
|
75
|
+
slider.dataset.slot = "slider";
|
|
76
|
+
slider.setAttribute("aria-valuemax", renderScale.maximum);
|
|
77
|
+
slider.setAttribute("aria-valuemin", "1");
|
|
78
|
+
slider.setAttribute("aria-valuenow", renderScale.value);
|
|
79
|
+
slider.setAttribute("role", "slider");
|
|
80
|
+
Object.assign(slider.style, { height: "20px", width: "120px" });
|
|
81
|
+
slider.textContent = "Render scale";
|
|
82
|
+
field.append(slider);
|
|
83
|
+
fixture.append(field);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
root.append(fixture);
|
|
87
|
+
},
|
|
88
|
+
state,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
17
92
|
async function installOrientationGizmoPointerProbe(
|
|
18
93
|
page: Page,
|
|
19
94
|
initial: ToolcraftOrientationBrowserObservation,
|
|
@@ -123,33 +198,107 @@ async function installModelOrbitPointerProbe(
|
|
|
123
198
|
);
|
|
124
199
|
}
|
|
125
200
|
|
|
201
|
+
test("orientation axis drag validates paused maximum-quality proof state", async ({
|
|
202
|
+
page,
|
|
203
|
+
}, testInfo) => {
|
|
204
|
+
await page.goto("/");
|
|
205
|
+
const session = await createToolcraftBrowserProofSession(page);
|
|
206
|
+
const attachmentCount = testInfo.attachments.length;
|
|
207
|
+
const observation = session.observe((root) =>
|
|
208
|
+
JSON.parse(root.getAttribute("data-proof-orientation") ?? "null"),
|
|
209
|
+
);
|
|
210
|
+
const invalidStateCases: readonly Readonly<{
|
|
211
|
+
expectedError: RegExp;
|
|
212
|
+
state: OrientationProofUiState;
|
|
213
|
+
}>[] = [
|
|
214
|
+
{
|
|
215
|
+
expectedError: /must be paused.*Play playback/iu,
|
|
216
|
+
state: {
|
|
217
|
+
playbackActions: ["pause"],
|
|
218
|
+
renderScales: [{ maximum: "2", value: "2" }],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
expectedError: /exactly one standard playback action/iu,
|
|
223
|
+
state: { playbackActions: ["play", "pause"] },
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
expectedError: /canvas\.renderScale.*maximum/iu,
|
|
227
|
+
state: {
|
|
228
|
+
playbackActions: ["play"],
|
|
229
|
+
renderScales: [{ maximum: "2", value: "1" }],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
expectedError: /exactly one canvas\.renderScale owner/iu,
|
|
234
|
+
state: {
|
|
235
|
+
playbackActions: ["play"],
|
|
236
|
+
renderScales: [
|
|
237
|
+
{ maximum: "2", value: "2" },
|
|
238
|
+
{ maximum: "2", value: "2" },
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
expectedError: /finite aria-valuenow/iu,
|
|
244
|
+
state: {
|
|
245
|
+
playbackActions: ["play"],
|
|
246
|
+
renderScales: [{ maximum: "2", value: "" }],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
expectedError: /finite aria-valuemax/iu,
|
|
251
|
+
state: {
|
|
252
|
+
playbackActions: ["play"],
|
|
253
|
+
renderScales: [{ maximum: "invalid", value: "2" }],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
for (const { expectedError, state } of invalidStateCases) {
|
|
259
|
+
await setProofState(page, "orientation", orientationBaseline);
|
|
260
|
+
await installOrientationGizmoPointerProbe(page, orientationBaseline, {
|
|
261
|
+
outcomes: { drag: orientationDragged },
|
|
262
|
+
});
|
|
263
|
+
await installOrientationProofUiState(page, state);
|
|
264
|
+
await expect(
|
|
265
|
+
expectToolcraftOrientationAxisDrag(observation, session, {
|
|
266
|
+
...orientationOptions,
|
|
267
|
+
dragDelta: { x: 18, y: 12 },
|
|
268
|
+
}),
|
|
269
|
+
).rejects.toThrow(expectedError);
|
|
270
|
+
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("orientation axis drag retains proof for products without optional playback or render scale", async ({
|
|
275
|
+
page,
|
|
276
|
+
}) => {
|
|
277
|
+
await page.goto("/");
|
|
278
|
+
const session = await createToolcraftBrowserProofSession(page);
|
|
279
|
+
await setProofState(page, "orientation", orientationBaseline);
|
|
280
|
+
const observation = session.observe((root) =>
|
|
281
|
+
JSON.parse(root.getAttribute("data-proof-orientation") ?? "null"),
|
|
282
|
+
);
|
|
283
|
+
await installOrientationGizmoPointerProbe(page, orientationBaseline, {
|
|
284
|
+
outcomes: { drag: orientationDragged },
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await expectToolcraftOrientationAxisDrag(observation, session, {
|
|
288
|
+
...orientationOptions,
|
|
289
|
+
dragDelta: { x: 18, y: 12 },
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
126
293
|
test("orientation recipes require shared pose/output changes and correct canvas ownership", async ({
|
|
127
294
|
page,
|
|
128
295
|
}, testInfo) => {
|
|
129
296
|
await page.goto("/");
|
|
130
297
|
const session = await createToolcraftBrowserProofSession(page);
|
|
131
298
|
const attachmentCount = testInfo.attachments.length;
|
|
132
|
-
const baseline
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
pose: { position: [0, 0, 5], up: [0, 1, 0] },
|
|
136
|
-
poseTarget: "view.orbit",
|
|
137
|
-
presentationCacheKey: "appearance:document-model:v2",
|
|
138
|
-
presentationDocumentId: "document-model",
|
|
139
|
-
viewportOffsetX: 0,
|
|
140
|
-
viewportOffsetY: 0,
|
|
141
|
-
};
|
|
142
|
-
const dragged: ToolcraftOrientationBrowserObservation = {
|
|
143
|
-
...baseline,
|
|
144
|
-
outputSignature: "model-dragged",
|
|
145
|
-
pixelSignature: "pixels-dragged",
|
|
146
|
-
pose: { position: [1, 1, 4.8], up: [0, 1, 0] },
|
|
147
|
-
};
|
|
148
|
-
const options = {
|
|
149
|
-
requirementId: "model.orientation",
|
|
150
|
-
stabilityIntervalMs: 0,
|
|
151
|
-
target: "view.orbit",
|
|
152
|
-
} as const;
|
|
299
|
+
const baseline = orientationBaseline;
|
|
300
|
+
const dragged = orientationDragged;
|
|
301
|
+
const options = orientationOptions;
|
|
153
302
|
|
|
154
303
|
await setProofState(page, "orientation", baseline);
|
|
155
304
|
const observation = session.observe((root) =>
|
|
@@ -175,6 +324,10 @@ test("orientation recipes require shared pose/output changes and correct canvas
|
|
|
175
324
|
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
176
325
|
|
|
177
326
|
await setProofState(page, "orientation", baseline);
|
|
327
|
+
await installOrientationProofUiState(page, {
|
|
328
|
+
playbackActions: ["play"],
|
|
329
|
+
renderScales: [{ maximum: "2", value: "2" }],
|
|
330
|
+
});
|
|
178
331
|
await installOrientationGizmoPointerProbe(page, baseline, {
|
|
179
332
|
dragCommit: "pointerup",
|
|
180
333
|
outcomes: { drag: dragged },
|
|
@@ -133,7 +133,7 @@ test("control schema derives segmented and discrete runtime evidence", () => {
|
|
|
133
133
|
);
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
test("typed
|
|
136
|
+
test("typed background coverage derives dedicated semantic evidence", () => {
|
|
137
137
|
const schema = defineToolcraft({
|
|
138
138
|
canvas: { enabled: true },
|
|
139
139
|
panels: {
|
|
@@ -141,19 +141,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
|
|
|
141
141
|
sections: [
|
|
142
142
|
{
|
|
143
143
|
controls: {
|
|
144
|
-
conditional: {
|
|
145
|
-
defaultValue: 1,
|
|
146
|
-
label: "Conditional",
|
|
147
|
-
target: "feature.conditional",
|
|
148
|
-
type: "slider",
|
|
149
|
-
visibleWhen: { equals: true, target: "feature.enabled" },
|
|
150
|
-
},
|
|
151
|
-
enabled: {
|
|
152
|
-
defaultValue: false,
|
|
153
|
-
label: "Enabled",
|
|
154
|
-
target: "feature.enabled",
|
|
155
|
-
type: "switch",
|
|
156
|
-
},
|
|
157
144
|
includeBackground: {
|
|
158
145
|
defaultValue: true,
|
|
159
146
|
label: "Include",
|
|
@@ -181,14 +168,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
|
|
|
181
168
|
});
|
|
182
169
|
const requirements = deriveToolcraftBrowserRuntimeRequirements(
|
|
183
170
|
[
|
|
184
|
-
{
|
|
185
|
-
browser: true,
|
|
186
|
-
browserTestName: "browser: conditional visibility",
|
|
187
|
-
evidence: "product-output",
|
|
188
|
-
id: "feature.conditional",
|
|
189
|
-
target: "feature.conditional",
|
|
190
|
-
visibilityCoverage: ["hidden", "visible"],
|
|
191
|
-
},
|
|
192
171
|
{
|
|
193
172
|
backgroundOutputCoverage: [
|
|
194
173
|
"preview-hidden-when-excluded",
|
|
@@ -205,17 +184,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
|
|
|
205
184
|
],
|
|
206
185
|
schema,
|
|
207
186
|
);
|
|
208
|
-
|
|
209
|
-
expect(
|
|
210
|
-
requirements
|
|
211
|
-
.filter((requirement) => requirement.requirementId === "feature.conditional")
|
|
212
|
-
.map((requirement) => requirement.evidenceType),
|
|
213
|
-
).toEqual(
|
|
214
|
-
expect.arrayContaining([
|
|
215
|
-
"conditional-control-hidden",
|
|
216
|
-
"conditional-control-visible",
|
|
217
|
-
]),
|
|
218
|
-
);
|
|
219
187
|
expect(
|
|
220
188
|
requirements
|
|
221
189
|
.filter((requirement) => requirement.requirementId === "export.includeBackground")
|