@pixel-point/toolcraft 0.0.16 → 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-performance-iteration-execution.test.mjs +2 -2
- package/src/generate-test-demand-only-lifecycle.mjs +14 -3
- package/src/generate-test-dependency-sandbox.mjs +42 -5
- package/src/generate-test-doc-assertions.mjs +109 -69
- package/src/generate-test-doc-assertions.test.mjs +65 -0
- package/src/generate-test-doc-policy-assertions.mjs +92 -0
- package/src/generate-test-file-assertions.mjs +36 -118
- package/src/generate-test-output-assertions.mjs +95 -0
- package/src/generate-test-package-assertions.mjs +6 -0
- package/src/generate-test-performance-boundary-assertions.mjs +45 -0
- package/src/generate-test-product-fixture.mjs +1 -0
- package/src/generate-test-product-fixture.test.mjs +18 -0
- package/src/generate-test-product-iteration.mjs +1 -0
- package/src/generate-test-product-iteration.test.mjs +18 -0
- package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
- package/src/generate-test-standalone-assertions.mjs +10 -0
- package/src/generate-test-standalone-delivery-stages.mjs +14 -6
- package/src/generate.test.mjs +14 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +59 -12
- package/templates/runtime/contracts/component-contracts.choices.ts +58 -12
- 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 +21 -6
- package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +4 -1
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +4 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +15 -10
- package/templates/runtime/contracts/component-contracts.test.ts +2 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +43 -4
- package/templates/runtime/contracts/decision-contracts.ts +10 -10
- package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
- package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
- package/templates/runtime/export/artifact-download.test.ts +71 -0
- package/templates/runtime/export/artifact-download.ts +80 -0
- package/templates/runtime/export/artifact-export-request.ts +23 -0
- package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
- package/templates/runtime/export/artifact-export-settings.ts +96 -0
- package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
- package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
- package/templates/runtime/export/artifact-frame-state.ts +44 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
- package/templates/runtime/export/artifact-scene-frame.ts +118 -0
- package/templates/runtime/export/export-background.ts +46 -0
- package/templates/runtime/export/export-error.ts +43 -0
- package/templates/runtime/export/export.test.ts +49 -226
- package/templates/runtime/export/image-artifact-export.test.ts +111 -0
- package/templates/runtime/export/image-artifact-export.ts +145 -0
- package/templates/runtime/export/index.ts +44 -3
- package/templates/runtime/export/product-export-renderer.test.ts +77 -0
- package/templates/runtime/export/product-export-renderer.ts +63 -0
- package/templates/runtime/export/video-artifact-export.test.ts +166 -0
- package/templates/runtime/export/video-artifact-export.ts +164 -0
- package/templates/runtime/export/video-encoding-backend.ts +124 -0
- package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
- package/templates/runtime/export/video-encoding-policy.ts +86 -0
- package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
- package/templates/runtime/export/video-frame-schedule.ts +42 -0
- package/templates/runtime/index.ts +3 -0
- package/templates/runtime/model-import/model-import-limits.ts +15 -4
- package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
- package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +15 -304
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +41 -27
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +39 -11
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
- 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.footer-actions.test.tsx +7 -12
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +18 -84
- 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 +43 -87
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +68 -0
- 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/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
- package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
- package/templates/runtime/scene/scene-bounds.test.ts +23 -0
- package/templates/runtime/scene/scene-bounds.ts +32 -19
- package/templates/runtime/schema/app-capabilities.test.ts +4 -0
- package/templates/runtime/schema/canvas-render-scale.ts +62 -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-id.test.ts +1 -1
- package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +62 -3
- 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-background.test.ts +1 -1
- package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
- package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
- package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
- package/templates/runtime/schema/define-toolcraft.ts +1 -1
- package/templates/runtime/schema/runtime-setup-section.ts +24 -5
- package/templates/runtime/schema/schema-resolvers.ts +0 -56
- package/templates/runtime/schema/types.ts +72 -17
- package/templates/runtime/state/canvas-background-state.ts +6 -0
- package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
- package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
- package/templates/starter/AGENTS.md +18 -15
- package/templates/starter/NOTICE.md +6 -0
- package/templates/starter/docs/toolcraft/README.md +2 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +23 -35
- package/templates/starter/docs/toolcraft/agent-worklog.md +18 -3
- package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -5
- 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 +4 -2
- package/templates/starter/docs/toolcraft/core/performance.md +14 -3
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +16 -3
- package/templates/starter/docs/toolcraft/core/setup-export.md +49 -19
- package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
- 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/performance.md +20 -6
- package/templates/starter/docs/toolcraft/renderer-technique.md +5 -1
- package/templates/starter/docs/toolcraft/schema-reference.md +23 -25
- package/templates/starter/docs/toolcraft/workflow.md +7 -2
- package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
- package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +50 -3
- package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
- package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
- package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
- package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
- package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +5 -34
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -16
- package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
- package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
- package/templates/starter/e2e/app-performance.spec.ts +16 -9
- 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 +29 -341
- package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
- package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
- package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
- 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-render-scale-evidence.ts +37 -29
- package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +13 -3
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +72 -35
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
- package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
- package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
- package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
- package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
- package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
- package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
- package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
- package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
- package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
- package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
- package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
- package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
- package/templates/starter/e2e/performance-path-helpers.ts +210 -91
- package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
- package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
- package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
- package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
- package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
- package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/package.json +1 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +95 -103
- package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
- package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -26
- 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-evidence-boundary.test.mjs +85 -20
- package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +157 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
- package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -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-helpers.mjs +318 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +157 -206
- package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
- package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
- 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/export-artifact-coverage.ts +113 -0
- 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 +35 -12
- package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
- package/templates/starter/src/app/acceptance/types.ts +30 -8
- package/templates/starter/src/app/acceptance/validate-coverage.ts +20 -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-artifact.test.ts +131 -0
- 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.keyframes.test.ts +14 -4
- 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 +46 -1
- 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 +13 -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/starter-performance-test-utils.ts +103 -0
- package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +4 -2
- package/templates/starter/src/routes/index.tsx +2 -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/runtime/export/export.ts +0 -166
- package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
- package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx
CHANGED
|
@@ -52,6 +52,7 @@ describe("controls-panel media renderer", () => {
|
|
|
52
52
|
dispatchCommand,
|
|
53
53
|
id: "source-model",
|
|
54
54
|
mediaAssets: [],
|
|
55
|
+
name: "Model",
|
|
55
56
|
sourceAssetCoordinator,
|
|
56
57
|
});
|
|
57
58
|
|
|
@@ -102,6 +103,7 @@ describe("controls-panel media renderer", () => {
|
|
|
102
103
|
dispatchCommand: vi.fn(),
|
|
103
104
|
id: "source-files",
|
|
104
105
|
mediaAssets: [],
|
|
106
|
+
name: "Source files",
|
|
105
107
|
sourceAssetCoordinator,
|
|
106
108
|
})}
|
|
107
109
|
</>,
|
|
@@ -113,7 +115,75 @@ describe("controls-panel media renderer", () => {
|
|
|
113
115
|
undefined,
|
|
114
116
|
);
|
|
115
117
|
expect(legacyFileDrop.mock.calls[0]?.[0]).toEqual(
|
|
116
|
-
expect.objectContaining({
|
|
118
|
+
expect.objectContaining({
|
|
119
|
+
collectionActions: undefined,
|
|
120
|
+
onFolderSelect: undefined,
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("maps the collection-actions variant to compact FileDrop cardinality controls", () => {
|
|
126
|
+
const control = {
|
|
127
|
+
assetKind: "file" as const,
|
|
128
|
+
itemLabel: "bump map",
|
|
129
|
+
multiple: true,
|
|
130
|
+
target: "surface.bumpMaps",
|
|
131
|
+
type: "fileDrop" as const,
|
|
132
|
+
variant: "collection-actions",
|
|
133
|
+
};
|
|
134
|
+
const operation = { phase: "idle" as const, target: control.target };
|
|
135
|
+
const presentation = {
|
|
136
|
+
accept: "image/*",
|
|
137
|
+
allowsFileBatch: true,
|
|
138
|
+
allowsFolderSelection: false,
|
|
139
|
+
emptyDescription: "Drop bump maps",
|
|
140
|
+
emptyTitle: "Upload bump maps",
|
|
141
|
+
items: [{
|
|
142
|
+
assetKind: "file" as const,
|
|
143
|
+
fileName: "grain.png",
|
|
144
|
+
id: "bump-grain",
|
|
145
|
+
}],
|
|
146
|
+
presenter: "file-list" as const,
|
|
147
|
+
secondaryActions: [],
|
|
148
|
+
};
|
|
149
|
+
const sourceAssetCoordinator = {
|
|
150
|
+
cancelTarget: vi.fn(),
|
|
151
|
+
clearPresentationFeedback: vi.fn(),
|
|
152
|
+
dispose: vi.fn(async () => undefined),
|
|
153
|
+
getOperation: vi.fn(() => operation),
|
|
154
|
+
getPresentation: vi.fn(() => presentation),
|
|
155
|
+
hydrateBinaryMedia: vi.fn(async () => undefined),
|
|
156
|
+
hydrateModels: vi.fn(async () => undefined),
|
|
157
|
+
importBatch: vi.fn(),
|
|
158
|
+
repairModel: vi.fn(),
|
|
159
|
+
reportPresentationFeedback: vi.fn(),
|
|
160
|
+
subscribe: () => () => undefined,
|
|
161
|
+
} satisfies ToolcraftSourceAssetCoordinator;
|
|
162
|
+
|
|
163
|
+
render(
|
|
164
|
+
<>
|
|
165
|
+
{renderFileDropControl({
|
|
166
|
+
canvasSize: { height: 720, unit: "px", width: 1280 },
|
|
167
|
+
control,
|
|
168
|
+
dispatchCommand: vi.fn(),
|
|
169
|
+
id: "surface-bump-maps",
|
|
170
|
+
mediaAssets: [],
|
|
171
|
+
name: "Bump maps",
|
|
172
|
+
sourceAssetCoordinator,
|
|
173
|
+
})}
|
|
174
|
+
</>,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
expect(legacyFileDrop.mock.calls[0]?.[0]).toEqual(
|
|
178
|
+
expect.objectContaining({
|
|
179
|
+
collectionActions: {
|
|
180
|
+
addLabel: "Add bump map",
|
|
181
|
+
canAdd: true,
|
|
182
|
+
itemLabel: "bump map",
|
|
183
|
+
name: "Bump maps",
|
|
184
|
+
removeLabel: "Remove bump map",
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
117
187
|
);
|
|
118
188
|
});
|
|
119
189
|
|
|
@@ -157,6 +227,7 @@ describe("controls-panel media renderer", () => {
|
|
|
157
227
|
dispatchCommand: vi.fn(),
|
|
158
228
|
id: "source-model",
|
|
159
229
|
mediaAssets: [],
|
|
230
|
+
name: "Model",
|
|
160
231
|
sourceAssetCoordinator,
|
|
161
232
|
})}
|
|
162
233
|
</>,
|
|
@@ -226,6 +297,7 @@ describe("controls-panel media renderer", () => {
|
|
|
226
297
|
dispatchCommand: vi.fn(),
|
|
227
298
|
id: "source-model",
|
|
228
299
|
mediaAssets: [],
|
|
300
|
+
name: "Model",
|
|
229
301
|
sourceAssetCoordinator,
|
|
230
302
|
})}
|
|
231
303
|
</>,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FileDrop,
|
|
4
|
+
type ControlChangeMeta,
|
|
5
|
+
type FileDropPresentationItem,
|
|
6
|
+
} from "@repo/ui";
|
|
3
7
|
|
|
4
8
|
import type { ToolcraftCanvasSize, ToolcraftControlSchema } from "../../../schema/types";
|
|
5
9
|
import type { ToolcraftSourceAssetCoordinator } from "../../../source-assets/source-asset-coordinator";
|
|
@@ -11,6 +15,7 @@ import type {
|
|
|
11
15
|
} from "../../../state/types";
|
|
12
16
|
import { useToolcraftMediaPresentationUrls } from "../../app-shell/toolcraft-source-asset-context";
|
|
13
17
|
import { useToolcraftSelector } from "../../app-shell/use-toolcraft";
|
|
18
|
+
import { ControlsPanelFileDropItemControls } from "./controls-panel-file-drop-item-controls";
|
|
14
19
|
|
|
15
20
|
export type FileDropControlRenderArgs = {
|
|
16
21
|
canvasSize: ToolcraftCanvasSize;
|
|
@@ -19,7 +24,15 @@ export type FileDropControlRenderArgs = {
|
|
|
19
24
|
id: string;
|
|
20
25
|
layerSelectionEnabled?: boolean;
|
|
21
26
|
mediaAssets: readonly ToolcraftMediaAsset[];
|
|
27
|
+
name: string;
|
|
28
|
+
setControlValue?: (
|
|
29
|
+
target: string,
|
|
30
|
+
value: unknown,
|
|
31
|
+
label?: string,
|
|
32
|
+
meta?: ControlChangeMeta,
|
|
33
|
+
) => void;
|
|
22
34
|
sourceAssetCoordinator: ToolcraftSourceAssetCoordinator;
|
|
35
|
+
value?: unknown;
|
|
23
36
|
};
|
|
24
37
|
|
|
25
38
|
const IMAGE_TRANSFORM_OPERATIONS = new Set<ToolcraftMediaTransformOperation>([
|
|
@@ -43,8 +56,11 @@ function FileDropControlRenderer({
|
|
|
43
56
|
id,
|
|
44
57
|
layerSelectedImageAsset = null,
|
|
45
58
|
mediaAssets,
|
|
59
|
+
name,
|
|
60
|
+
setControlValue = () => undefined,
|
|
46
61
|
sourceAssetCoordinator,
|
|
47
62
|
suppressMediaCollection = false,
|
|
63
|
+
value,
|
|
48
64
|
}: Omit<
|
|
49
65
|
FileDropControlRenderArgs,
|
|
50
66
|
"canvasSize" | "layerSelectionEnabled"
|
|
@@ -112,6 +128,10 @@ function FileDropControlRenderer({
|
|
|
112
128
|
? { ...basePresentation, secondaryActions: [] }
|
|
113
129
|
: basePresentation
|
|
114
130
|
: null;
|
|
131
|
+
const usesCollectionActions = control.variant === "collection-actions";
|
|
132
|
+
const canAddCollectionItem =
|
|
133
|
+
typeof control.hardMaxItems !== "number" ||
|
|
134
|
+
(basePresentation?.items.length ?? 0) < control.hardMaxItems;
|
|
115
135
|
|
|
116
136
|
React.useEffect(() => {
|
|
117
137
|
if (selectedMediaId && !previewMediaIds.includes(selectedMediaId)) {
|
|
@@ -156,6 +176,35 @@ function FileDropControlRenderer({
|
|
|
156
176
|
|
|
157
177
|
return (
|
|
158
178
|
<FileDrop
|
|
179
|
+
collectionActions={
|
|
180
|
+
usesCollectionActions
|
|
181
|
+
? {
|
|
182
|
+
addLabel: control.addLabel ?? `Add ${control.itemLabel ?? "file"}`,
|
|
183
|
+
canAdd: canAddCollectionItem,
|
|
184
|
+
itemLabel: control.itemLabel ?? "file",
|
|
185
|
+
name,
|
|
186
|
+
removeLabel:
|
|
187
|
+
control.removeLabel ?? `Remove ${control.itemLabel ?? "file"}`,
|
|
188
|
+
...(control.itemControls
|
|
189
|
+
? {
|
|
190
|
+
renderItemContent: (
|
|
191
|
+
item: FileDropPresentationItem,
|
|
192
|
+
index: number,
|
|
193
|
+
) => (
|
|
194
|
+
<ControlsPanelFileDropItemControls
|
|
195
|
+
control={control}
|
|
196
|
+
index={index}
|
|
197
|
+
mediaId={item.id}
|
|
198
|
+
name={name}
|
|
199
|
+
setControlValue={setControlValue}
|
|
200
|
+
value={value}
|
|
201
|
+
/>
|
|
202
|
+
),
|
|
203
|
+
}
|
|
204
|
+
: {}),
|
|
205
|
+
}
|
|
206
|
+
: undefined
|
|
207
|
+
}
|
|
159
208
|
key={id}
|
|
160
209
|
onAction={
|
|
161
210
|
activeImageId
|
package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts
CHANGED
|
@@ -30,7 +30,7 @@ describe("Toolcraft controls-panel renderer registry", () => {
|
|
|
30
30
|
"canvas-handle": [],
|
|
31
31
|
collection: ["value"],
|
|
32
32
|
compound: ["value"],
|
|
33
|
-
media: ["canvas.size", "mediaAssets"],
|
|
33
|
+
media: ["canvas.size", "mediaAssets", "value"],
|
|
34
34
|
settings: [],
|
|
35
35
|
} as const;
|
|
36
36
|
|
package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts
CHANGED
|
@@ -23,7 +23,7 @@ export const TOOLCRAFT_CONTROL_RENDERER_STATE_DEPENDENCIES = {
|
|
|
23
23
|
"canvas-handle": [],
|
|
24
24
|
collection: ["value"],
|
|
25
25
|
compound: ["value"],
|
|
26
|
-
media: ["canvas.size", "mediaAssets"],
|
|
26
|
+
media: ["canvas.size", "mediaAssets", "value"],
|
|
27
27
|
settings: [],
|
|
28
28
|
} as const satisfies Record<
|
|
29
29
|
ToolcraftControlRendererKind,
|
|
@@ -38,6 +38,7 @@ export const TOOLCRAFT_CONTROL_RENDERER_REGISTRY = {
|
|
|
38
38
|
checkbox: "basic",
|
|
39
39
|
code: "basic",
|
|
40
40
|
collectionActions: "collection",
|
|
41
|
+
sourceCollection: "collection",
|
|
41
42
|
color: "compound",
|
|
42
43
|
colorOpacity: "compound",
|
|
43
44
|
curves: "compound",
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
import type { ToolcraftCollectionItemControlType } from "../../../schema/collection-item-controls";
|
|
1
2
|
import type { ToolcraftControlSchema } from "../../../schema/types";
|
|
2
3
|
import { asFontPickerValue } from "./controls-panel-font-values";
|
|
3
4
|
import { asNumber } from "./controls-panel-value-primitives";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
function assertNeverCollectionItemControl(value: never): never {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`Unsupported Toolcraft collection item control: ${String(value)}`,
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function asCollectionItems(
|
|
13
|
+
value: unknown,
|
|
14
|
+
fallback: unknown,
|
|
15
|
+
): unknown[] {
|
|
6
16
|
if (Array.isArray(value)) {
|
|
7
17
|
return [...value];
|
|
8
18
|
}
|
|
@@ -17,18 +27,25 @@ export function getCollectionMinItems(control: ToolcraftControlSchema): number {
|
|
|
17
27
|
export function getCollectionHardMaxItems(
|
|
18
28
|
control: ToolcraftControlSchema,
|
|
19
29
|
): number | null {
|
|
20
|
-
if (
|
|
30
|
+
if (
|
|
31
|
+
typeof control.hardMaxItems !== "number" ||
|
|
32
|
+
!Number.isFinite(control.hardMaxItems)
|
|
33
|
+
) {
|
|
21
34
|
return null;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
return Math.max(0, Math.floor(control.hardMaxItems));
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
export function getCollectionItemType(
|
|
40
|
+
export function getCollectionItemType(
|
|
41
|
+
control: ToolcraftControlSchema,
|
|
42
|
+
): ToolcraftCollectionItemControlType {
|
|
28
43
|
return control.itemControl?.type ?? "color";
|
|
29
44
|
}
|
|
30
45
|
|
|
31
|
-
export function getCollectionItemBaseLabel(
|
|
46
|
+
export function getCollectionItemBaseLabel(
|
|
47
|
+
control: ToolcraftControlSchema,
|
|
48
|
+
): string {
|
|
32
49
|
if (control.itemLabel) {
|
|
33
50
|
return control.itemLabel;
|
|
34
51
|
}
|
|
@@ -54,6 +71,15 @@ export function getCollectionItemName(
|
|
|
54
71
|
export function getCollectionItemDefaultValue(
|
|
55
72
|
control: ToolcraftControlSchema,
|
|
56
73
|
): unknown {
|
|
74
|
+
if (control.itemControls) {
|
|
75
|
+
return Object.fromEntries(
|
|
76
|
+
Object.entries(control.itemControls).map(([id, itemControl]) => [
|
|
77
|
+
id,
|
|
78
|
+
itemControl.defaultValue,
|
|
79
|
+
]),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
if ("itemDefaultValue" in control) {
|
|
58
84
|
return control.itemDefaultValue;
|
|
59
85
|
}
|
|
@@ -62,7 +88,9 @@ export function getCollectionItemDefaultValue(
|
|
|
62
88
|
return control.itemControl.defaultValue;
|
|
63
89
|
}
|
|
64
90
|
|
|
65
|
-
|
|
91
|
+
const itemType = getCollectionItemType(control);
|
|
92
|
+
|
|
93
|
+
switch (itemType) {
|
|
66
94
|
case "color":
|
|
67
95
|
return { hex: "#C1FF00" };
|
|
68
96
|
case "colorOpacity":
|
|
@@ -74,6 +102,8 @@ export function getCollectionItemDefaultValue(
|
|
|
74
102
|
return false;
|
|
75
103
|
case "rangeInput":
|
|
76
104
|
return { end: "100%", start: "0%" };
|
|
105
|
+
case "rangeSlider":
|
|
106
|
+
return [control.itemControl?.min ?? 0, control.itemControl?.max ?? 100];
|
|
77
107
|
case "select":
|
|
78
108
|
case "segmented":
|
|
79
109
|
return control.itemControl?.options?.[0]?.value ?? "";
|
|
@@ -81,7 +111,9 @@ export function getCollectionItemDefaultValue(
|
|
|
81
111
|
return control.itemControl?.min ?? 0;
|
|
82
112
|
case "text":
|
|
83
113
|
return "";
|
|
114
|
+
case "vector":
|
|
115
|
+
return { x: "0", y: "0" };
|
|
84
116
|
default:
|
|
85
|
-
return
|
|
117
|
+
return assertNeverCollectionItemControl(itemType);
|
|
86
118
|
}
|
|
87
119
|
}
|
|
@@ -38,6 +38,7 @@ export function formatControlValueLabel(
|
|
|
38
38
|
return `${colorOpacityValue.hex} ${colorOpacityValue.opacity}%`;
|
|
39
39
|
}
|
|
40
40
|
case "collectionActions":
|
|
41
|
+
case "sourceCollection":
|
|
41
42
|
return `${asCollectionItems(value, control.defaultValue).length} items`;
|
|
42
43
|
case "fontPicker":
|
|
43
44
|
return asFontPickerValue(value).fontId;
|
|
@@ -6,6 +6,10 @@ export { useToolcraftMediaPresentationUrls } from "./app-shell/toolcraft-media-p
|
|
|
6
6
|
export * from "./app-shell/use-toolcraft-pipeline";
|
|
7
7
|
export * from "./app-shell/use-toolcraft-pipeline-pass";
|
|
8
8
|
export * from "./canvas/canvas-shell";
|
|
9
|
+
export {
|
|
10
|
+
useToolcraftProductSceneFrame,
|
|
11
|
+
type ToolcraftProductSceneFrame,
|
|
12
|
+
} from "./canvas/product-scene-surface";
|
|
9
13
|
export * from "./controls-panel/control-renderers";
|
|
10
14
|
export * from "./controls-panel/controls-panel";
|
|
11
15
|
export * from "./layers/layers-panel";
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
resolveToolcraftExportFrame,
|
|
3
3
|
ToolcraftSceneExportError,
|
|
4
4
|
type ToolcraftExportFrame,
|
|
5
|
-
} from "../../export";
|
|
5
|
+
} from "../../export/export-frame";
|
|
6
6
|
import { getToolcraftCanvasFrame, type ToolcraftCanvasFrame } from "../../state/canvas-frame";
|
|
7
7
|
import type { ToolcraftState } from "../../state/types";
|
|
8
8
|
import { getSceneElementPresentationRect } from "../canvas/scene-element-presentation-rect";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
ResolvedToolcraftControlSchema,
|
|
3
|
+
ResolvedToolcraftControlSectionSchema,
|
|
4
4
|
} from "../../schema/types";
|
|
5
5
|
import type { ToolcraftState } from "../../state/types";
|
|
6
6
|
import {
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
} from "../controls-panel/conditions/control-conditions";
|
|
10
10
|
|
|
11
11
|
export type ToolcraftOrientationControlEntry = {
|
|
12
|
-
control:
|
|
12
|
+
control: ResolvedToolcraftControlSchema;
|
|
13
13
|
id: string;
|
|
14
|
-
section:
|
|
14
|
+
section: ResolvedToolcraftControlSectionSchema;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export function getToolcraftOrientationControlEntries(
|
|
18
|
-
sections: readonly
|
|
18
|
+
sections: readonly ResolvedToolcraftControlSectionSchema[],
|
|
19
19
|
): ToolcraftOrientationControlEntry[] {
|
|
20
20
|
return sections.flatMap((section) =>
|
|
21
21
|
Object.entries(section.controls).flatMap(([id, control]) =>
|
package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { ResolvedToolcraftControlSchema } from "../../schema/types";
|
|
6
6
|
import type { ToolcraftStoreDependency } from "../../state/toolcraft-external-store-dependencies";
|
|
7
7
|
import type { ToolcraftState } from "../../state/types";
|
|
8
8
|
import { useToolcraftDependencySelector } from "../app-shell/toolcraft-selectors";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "./orientation-gizmo-selection";
|
|
19
19
|
|
|
20
20
|
export type ToolcraftOrientationControlSelection = Readonly<{
|
|
21
|
-
control:
|
|
21
|
+
control: ResolvedToolcraftControlSchema | null;
|
|
22
22
|
id: string | null;
|
|
23
23
|
value: unknown;
|
|
24
24
|
}>;
|
|
@@ -195,4 +195,34 @@ describe("ToolcraftPanelHost runtime placement", () => {
|
|
|
195
195
|
);
|
|
196
196
|
});
|
|
197
197
|
});
|
|
198
|
+
|
|
199
|
+
it("keeps the CSS-anchored default placement during viewport resize", async () => {
|
|
200
|
+
const { container } = renderToolbar();
|
|
201
|
+
const host = container.querySelector<HTMLElement>(
|
|
202
|
+
"[data-slot='toolcraft-runtime-panel-host']",
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (!host) {
|
|
206
|
+
throw new Error("Expected the toolbar panel host to render.");
|
|
207
|
+
}
|
|
208
|
+
vi.spyOn(host, "getBoundingClientRect").mockReturnValue({
|
|
209
|
+
bottom: 900,
|
|
210
|
+
height: 100,
|
|
211
|
+
left: 1_500,
|
|
212
|
+
right: 1_700,
|
|
213
|
+
top: 800,
|
|
214
|
+
width: 200,
|
|
215
|
+
x: 1_500,
|
|
216
|
+
y: 800,
|
|
217
|
+
toJSON: () => ({}),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
fireEvent(window, new Event("resize"));
|
|
221
|
+
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(screen.getByTestId("toolbar-placement").textContent).toBe(
|
|
224
|
+
"0,0,free",
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
198
228
|
});
|
|
@@ -191,6 +191,10 @@ function usePanelSnapControls({
|
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
if (x.get() === 0 && y.get() === 0 && snapEdge === undefined) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
194
198
|
const currentPlacement = {
|
|
195
199
|
offset: { x: x.get(), y: y.get() },
|
|
196
200
|
...(snapEdge ? { snapEdge } : {}),
|
|
@@ -6,6 +6,7 @@ import { createToolcraftState } from "../state/create-template-state";
|
|
|
6
6
|
import {
|
|
7
7
|
getToolcraftSceneElementRect,
|
|
8
8
|
outwardRoundToolcraftSceneRect,
|
|
9
|
+
resolveToolcraftProductSceneBounds,
|
|
9
10
|
resolveToolcraftSceneBounds,
|
|
10
11
|
unionToolcraftSceneRects,
|
|
11
12
|
} from "./scene-bounds";
|
|
@@ -17,6 +18,28 @@ function emptyState() {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
describe("scene bounds", () => {
|
|
21
|
+
it("resolves one exact product frame before runtime contributors are added", () => {
|
|
22
|
+
expect(
|
|
23
|
+
resolveToolcraftProductSceneBounds([
|
|
24
|
+
{ height: 20, width: 40, x: -10, y: -5 },
|
|
25
|
+
{ height: 30, width: 10, x: 35, y: 15 },
|
|
26
|
+
]),
|
|
27
|
+
).toEqual({
|
|
28
|
+
bounds: { height: 50, width: 55, x: -10, y: -5 },
|
|
29
|
+
ok: true,
|
|
30
|
+
});
|
|
31
|
+
expect(resolveToolcraftProductSceneBounds([])).toMatchObject({
|
|
32
|
+
code: "empty-scene",
|
|
33
|
+
ok: false,
|
|
34
|
+
});
|
|
35
|
+
expect(
|
|
36
|
+
resolveToolcraftProductSceneBounds([
|
|
37
|
+
{ height: 20, width: 40, x: -10, y: -5 },
|
|
38
|
+
{ height: 30, width: Number.NaN, x: 35, y: 15 },
|
|
39
|
+
]),
|
|
40
|
+
).toMatchObject({ code: "scene-bounds-unavailable", ok: false });
|
|
41
|
+
});
|
|
42
|
+
|
|
20
43
|
it("projects center-anchored frames and unions negative coordinates", () => {
|
|
21
44
|
expect(
|
|
22
45
|
getToolcraftSceneElementRect({
|
|
@@ -36,8 +36,7 @@ export function createToolcraftRuntimeSceneVisibility(
|
|
|
36
36
|
|
|
37
37
|
export type ToolcraftProductSceneBoundsProvider = (
|
|
38
38
|
context: Readonly<{
|
|
39
|
-
state: ToolcraftState
|
|
40
|
-
timeRange?: Readonly<{ endSeconds: number; startSeconds: number }>;
|
|
39
|
+
state: Readonly<ToolcraftState>;
|
|
41
40
|
}>,
|
|
42
41
|
) => readonly ToolcraftSceneRect[];
|
|
43
42
|
|
|
@@ -49,6 +48,18 @@ export type ToolcraftSceneBoundsResult =
|
|
|
49
48
|
ok: false;
|
|
50
49
|
}>;
|
|
51
50
|
|
|
51
|
+
const emptySceneBoundsResult: ToolcraftSceneBoundsResult = Object.freeze({
|
|
52
|
+
code: "empty-scene",
|
|
53
|
+
message: "The scene has no visible exportable elements.",
|
|
54
|
+
ok: false,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const unavailableSceneBoundsResult: ToolcraftSceneBoundsResult = Object.freeze({
|
|
58
|
+
code: "scene-bounds-unavailable",
|
|
59
|
+
message: "Product scene bounds are unavailable or invalid.",
|
|
60
|
+
ok: false,
|
|
61
|
+
});
|
|
62
|
+
|
|
52
63
|
export function getToolcraftSceneElementRect(
|
|
53
64
|
element: ToolcraftSceneElementFrame,
|
|
54
65
|
): ToolcraftSceneRect {
|
|
@@ -159,32 +170,34 @@ export function outwardRoundToolcraftSceneRect(
|
|
|
159
170
|
};
|
|
160
171
|
}
|
|
161
172
|
|
|
162
|
-
export function
|
|
163
|
-
|
|
164
|
-
productRects: unknown = [],
|
|
165
|
-
visibility: ToolcraftRuntimeSceneVisibility = defaultToolcraftRuntimeSceneVisibility,
|
|
173
|
+
export function resolveToolcraftProductSceneBounds(
|
|
174
|
+
productRects: unknown,
|
|
166
175
|
): ToolcraftSceneBoundsResult {
|
|
167
176
|
if (
|
|
168
177
|
!Array.isArray(productRects) ||
|
|
169
178
|
!productRects.every(isFiniteSceneRect)
|
|
170
179
|
) {
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
180
|
+
return unavailableSceneBoundsResult;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const bounds = unionToolcraftSceneRects(productRects);
|
|
184
|
+
return bounds ? { bounds, ok: true } : emptySceneBoundsResult;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function resolveToolcraftSceneBounds(
|
|
188
|
+
state: ToolcraftState,
|
|
189
|
+
productRects: unknown = [],
|
|
190
|
+
visibility: ToolcraftRuntimeSceneVisibility = defaultToolcraftRuntimeSceneVisibility,
|
|
191
|
+
): ToolcraftSceneBoundsResult {
|
|
192
|
+
const productBounds = resolveToolcraftProductSceneBounds(productRects);
|
|
193
|
+
if (!productBounds.ok && productBounds.code === "scene-bounds-unavailable") {
|
|
194
|
+
return productBounds;
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
const bounds = unionToolcraftSceneRects([
|
|
179
198
|
...getRuntimeSceneRects(state, visibility),
|
|
180
|
-
...
|
|
199
|
+
...(productBounds.ok ? [productBounds.bounds] : []),
|
|
181
200
|
]);
|
|
182
201
|
|
|
183
|
-
return bounds
|
|
184
|
-
? { bounds, ok: true }
|
|
185
|
-
: {
|
|
186
|
-
code: "empty-scene",
|
|
187
|
-
message: "The scene has no visible exportable elements.",
|
|
188
|
-
ok: false,
|
|
189
|
-
};
|
|
202
|
+
return bounds ? { bounds, ok: true } : emptySceneBoundsResult;
|
|
190
203
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ResolvedToolcraftCanvasRenderScaleSchema,
|
|
3
|
+
ToolcraftCanvasRenderScaleSchema,
|
|
4
|
+
} from "./types";
|
|
5
|
+
|
|
6
|
+
export const TOOLCRAFT_CANVAS_RENDER_SCALE = Object.freeze({
|
|
7
|
+
defaultValue: 2,
|
|
8
|
+
max: 2,
|
|
9
|
+
min: 1,
|
|
10
|
+
step: 0.25,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const minimumCanvasRenderScaleStep = 0.01;
|
|
14
|
+
const canvasRenderScaleStepTolerance = 1e-9;
|
|
15
|
+
|
|
16
|
+
function resolveCanvasRenderScaleStep(value: number | undefined): number {
|
|
17
|
+
if (value === undefined) {
|
|
18
|
+
return TOOLCRAFT_CANVAS_RENDER_SCALE.step;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const maximumCanvasRenderScaleStep =
|
|
22
|
+
TOOLCRAFT_CANVAS_RENDER_SCALE.max - TOOLCRAFT_CANVAS_RENDER_SCALE.min;
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
typeof value !== "number" ||
|
|
26
|
+
!Number.isFinite(value) ||
|
|
27
|
+
value < minimumCanvasRenderScaleStep ||
|
|
28
|
+
value > maximumCanvasRenderScaleStep
|
|
29
|
+
) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Canvas render scale step must be a finite value between 0.01 and 1.",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stepCount = maximumCanvasRenderScaleStep / value;
|
|
36
|
+
|
|
37
|
+
if (Math.abs(stepCount - Math.round(stepCount)) > canvasRenderScaleStepTolerance) {
|
|
38
|
+
throw new Error("Canvas render scale step must divide the canonical range exactly.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolveToolcraftCanvasRenderScale(
|
|
45
|
+
renderScale: ToolcraftCanvasRenderScaleSchema | undefined,
|
|
46
|
+
): ResolvedToolcraftCanvasRenderScaleSchema {
|
|
47
|
+
if (!renderScale) {
|
|
48
|
+
return {
|
|
49
|
+
...TOOLCRAFT_CANVAS_RENDER_SCALE,
|
|
50
|
+
enabled: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
...TOOLCRAFT_CANVAS_RENDER_SCALE,
|
|
56
|
+
enabled: true,
|
|
57
|
+
step:
|
|
58
|
+
renderScale === true
|
|
59
|
+
? TOOLCRAFT_CANVAS_RENDER_SCALE.step
|
|
60
|
+
: resolveCanvasRenderScaleStep(renderScale.step),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const TOOLCRAFT_COLLECTION_ITEM_CONTROL_TYPES = [
|
|
2
|
+
"checkbox",
|
|
3
|
+
"color",
|
|
4
|
+
"colorOpacity",
|
|
5
|
+
"fontPicker",
|
|
6
|
+
"rangeInput",
|
|
7
|
+
"rangeSlider",
|
|
8
|
+
"segmented",
|
|
9
|
+
"select",
|
|
10
|
+
"slider",
|
|
11
|
+
"switch",
|
|
12
|
+
"text",
|
|
13
|
+
"vector",
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export type ToolcraftCollectionItemControlType =
|
|
17
|
+
(typeof TOOLCRAFT_COLLECTION_ITEM_CONTROL_TYPES)[number];
|
|
18
|
+
|
|
19
|
+
const toolcraftCollectionItemControlTypes = new Set<string>(
|
|
20
|
+
TOOLCRAFT_COLLECTION_ITEM_CONTROL_TYPES,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export function isToolcraftCollectionItemControlType(
|
|
24
|
+
value: unknown,
|
|
25
|
+
): value is ToolcraftCollectionItemControlType {
|
|
26
|
+
return (
|
|
27
|
+
typeof value === "string" && toolcraftCollectionItemControlTypes.has(value)
|
|
28
|
+
);
|
|
29
|
+
}
|