@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.
Files changed (313) hide show
  1. package/package.json +1 -1
  2. package/src/generate-performance-iteration-execution.test.mjs +2 -2
  3. package/src/generate-test-demand-only-lifecycle.mjs +14 -3
  4. package/src/generate-test-dependency-sandbox.mjs +42 -5
  5. package/src/generate-test-doc-assertions.mjs +109 -69
  6. package/src/generate-test-doc-assertions.test.mjs +65 -0
  7. package/src/generate-test-doc-policy-assertions.mjs +92 -0
  8. package/src/generate-test-file-assertions.mjs +36 -118
  9. package/src/generate-test-output-assertions.mjs +95 -0
  10. package/src/generate-test-package-assertions.mjs +6 -0
  11. package/src/generate-test-performance-boundary-assertions.mjs +45 -0
  12. package/src/generate-test-product-fixture.mjs +1 -0
  13. package/src/generate-test-product-fixture.test.mjs +18 -0
  14. package/src/generate-test-product-iteration.mjs +1 -0
  15. package/src/generate-test-product-iteration.test.mjs +18 -0
  16. package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
  17. package/src/generate-test-standalone-assertions.mjs +10 -0
  18. package/src/generate-test-standalone-delivery-stages.mjs +14 -6
  19. package/src/generate.test.mjs +14 -0
  20. package/templates/runtime/contracts/component-contracts.choices.test.ts +59 -12
  21. package/templates/runtime/contracts/component-contracts.choices.ts +58 -12
  22. package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
  23. package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
  24. package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
  25. package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
  26. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +21 -6
  27. package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
  28. package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +4 -1
  29. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +4 -1
  30. package/templates/runtime/contracts/component-contracts.runtime.ts +15 -10
  31. package/templates/runtime/contracts/component-contracts.test.ts +2 -0
  32. package/templates/runtime/contracts/decision-contracts.test.ts +43 -4
  33. package/templates/runtime/contracts/decision-contracts.ts +10 -10
  34. package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
  35. package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
  36. package/templates/runtime/export/artifact-download.test.ts +71 -0
  37. package/templates/runtime/export/artifact-download.ts +80 -0
  38. package/templates/runtime/export/artifact-export-request.ts +23 -0
  39. package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
  40. package/templates/runtime/export/artifact-export-settings.ts +96 -0
  41. package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
  42. package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
  43. package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
  44. package/templates/runtime/export/artifact-frame-state.ts +44 -0
  45. package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
  46. package/templates/runtime/export/artifact-scene-frame.ts +118 -0
  47. package/templates/runtime/export/export-background.ts +46 -0
  48. package/templates/runtime/export/export-error.ts +43 -0
  49. package/templates/runtime/export/export.test.ts +49 -226
  50. package/templates/runtime/export/image-artifact-export.test.ts +111 -0
  51. package/templates/runtime/export/image-artifact-export.ts +145 -0
  52. package/templates/runtime/export/index.ts +44 -3
  53. package/templates/runtime/export/product-export-renderer.test.ts +77 -0
  54. package/templates/runtime/export/product-export-renderer.ts +63 -0
  55. package/templates/runtime/export/video-artifact-export.test.ts +166 -0
  56. package/templates/runtime/export/video-artifact-export.ts +164 -0
  57. package/templates/runtime/export/video-encoding-backend.ts +124 -0
  58. package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
  59. package/templates/runtime/export/video-encoding-policy.ts +86 -0
  60. package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
  61. package/templates/runtime/export/video-frame-schedule.ts +42 -0
  62. package/templates/runtime/index.ts +3 -0
  63. package/templates/runtime/model-import/model-import-limits.ts +15 -4
  64. package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
  65. package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
  66. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +15 -304
  67. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +41 -27
  68. package/templates/runtime/react/app-shell/toolcraft-app.tsx +39 -11
  69. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
  70. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
  71. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
  72. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
  73. package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
  74. package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
  75. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
  76. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
  77. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
  78. package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
  79. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
  80. package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
  81. package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
  82. package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
  83. package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
  84. package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
  85. package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
  86. package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
  87. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +18 -84
  88. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
  89. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
  90. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +43 -87
  91. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +68 -0
  92. package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
  93. package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
  94. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
  95. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
  96. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
  97. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
  98. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
  99. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
  100. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
  101. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
  102. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
  103. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
  104. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
  105. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
  106. package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
  107. package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
  108. package/templates/runtime/react/index.ts +4 -0
  109. package/templates/runtime/react/model-rendering/model-export.ts +1 -1
  110. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
  111. package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
  112. package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
  113. package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
  114. package/templates/runtime/scene/scene-bounds.test.ts +23 -0
  115. package/templates/runtime/scene/scene-bounds.ts +32 -19
  116. package/templates/runtime/schema/app-capabilities.test.ts +4 -0
  117. package/templates/runtime/schema/canvas-render-scale.ts +62 -0
  118. package/templates/runtime/schema/collection-item-controls.ts +29 -0
  119. package/templates/runtime/schema/control-applicability.test.ts +127 -0
  120. package/templates/runtime/schema/control-applicability.ts +433 -0
  121. package/templates/runtime/schema/control-schema-normalization.ts +177 -11
  122. package/templates/runtime/schema/controls-panel-actions.ts +2 -2
  123. package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
  124. package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
  125. package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +62 -3
  126. package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
  127. package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
  128. package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
  129. package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
  130. package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
  131. package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
  132. package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
  133. package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
  134. package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
  135. package/templates/runtime/schema/define-toolcraft.ts +1 -1
  136. package/templates/runtime/schema/runtime-setup-section.ts +24 -5
  137. package/templates/runtime/schema/schema-resolvers.ts +0 -56
  138. package/templates/runtime/schema/types.ts +72 -17
  139. package/templates/runtime/state/canvas-background-state.ts +6 -0
  140. package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
  141. package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
  142. package/templates/starter/AGENTS.md +18 -15
  143. package/templates/starter/NOTICE.md +6 -0
  144. package/templates/starter/docs/toolcraft/README.md +2 -1
  145. package/templates/starter/docs/toolcraft/acceptance-testing.md +23 -35
  146. package/templates/starter/docs/toolcraft/agent-worklog.md +18 -3
  147. package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -5
  148. package/templates/starter/docs/toolcraft/component-rules.md +8 -6
  149. package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
  150. package/templates/starter/docs/toolcraft/core/layout.md +7 -7
  151. package/templates/starter/docs/toolcraft/core/media-upload.md +4 -2
  152. package/templates/starter/docs/toolcraft/core/performance.md +14 -3
  153. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +16 -3
  154. package/templates/starter/docs/toolcraft/core/setup-export.md +49 -19
  155. package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
  156. package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
  157. package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
  158. package/templates/starter/docs/toolcraft/performance.md +20 -6
  159. package/templates/starter/docs/toolcraft/renderer-technique.md +5 -1
  160. package/templates/starter/docs/toolcraft/schema-reference.md +23 -25
  161. package/templates/starter/docs/toolcraft/workflow.md +7 -2
  162. package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
  163. package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
  164. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +50 -3
  165. package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
  166. package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
  167. package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
  168. package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
  169. package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
  170. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +5 -34
  171. package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -16
  172. package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
  173. package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
  174. package/templates/starter/e2e/app-performance.spec.ts +16 -9
  175. package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
  176. package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
  177. package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +29 -341
  178. package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
  179. package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
  180. package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
  181. package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
  182. package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
  183. package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
  184. package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
  185. package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +13 -3
  186. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +72 -35
  187. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
  188. package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
  189. package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
  190. package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
  191. package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
  192. package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
  193. package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
  194. package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
  195. package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
  196. package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
  197. package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
  198. package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
  199. package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
  200. package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
  201. package/templates/starter/e2e/performance-path-helpers.ts +210 -91
  202. package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
  203. package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
  204. package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
  205. package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
  206. package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
  207. package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
  208. package/templates/starter/gitignore +1 -0
  209. package/templates/starter/package.json +1 -0
  210. package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
  211. package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
  212. package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
  213. package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
  214. package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
  215. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +95 -103
  216. package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
  217. package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
  218. package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -26
  219. package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
  220. package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
  221. package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
  222. package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
  223. package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
  224. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +157 -0
  225. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
  226. package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
  227. package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
  228. package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
  229. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
  230. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +157 -206
  231. package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
  232. package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
  233. package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
  234. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
  235. package/templates/starter/src/app/acceptance/conditions.ts +1 -1
  236. package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
  237. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
  238. package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
  239. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
  240. package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
  241. package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
  242. package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
  243. package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
  244. package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
  245. package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
  246. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
  247. package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
  248. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
  249. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
  250. package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
  251. package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
  252. package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
  253. package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
  254. package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
  255. package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
  256. package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
  257. package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
  258. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
  259. package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
  260. package/templates/starter/src/app/acceptance/output-export.ts +35 -12
  261. package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
  262. package/templates/starter/src/app/acceptance/types.ts +30 -8
  263. package/templates/starter/src/app/acceptance/validate-coverage.ts +20 -2
  264. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
  265. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
  266. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
  267. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
  268. package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
  269. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
  270. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
  271. package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
  272. package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
  273. package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
  274. package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
  275. package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
  276. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
  277. package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
  278. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
  279. package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
  280. package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
  281. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  282. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
  283. package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +46 -1
  284. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
  285. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
  286. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
  287. package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
  288. package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
  289. package/templates/starter/src/app/starter-acceptance.ts +13 -2
  290. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
  291. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
  292. package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
  293. package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
  294. package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
  295. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +4 -2
  296. package/templates/starter/src/routes/index.tsx +2 -0
  297. package/templates/ui/components/composites/tabs.tsx +2 -2
  298. package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
  299. package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
  300. package/templates/ui/components/controls/collection-actions/index.ts +2 -0
  301. package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
  302. package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
  303. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
  304. package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
  305. package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
  306. package/templates/ui/components/controls/file-drop/index.ts +1 -0
  307. package/templates/ui/components/controls/index.ts +3 -0
  308. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
  309. package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
  310. package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
  311. package/templates/runtime/export/export.ts +0 -166
  312. package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
  313. package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
@@ -0,0 +1,46 @@
1
+ import type { ToolcraftState } from "../state/types";
2
+
3
+ export type ToolcraftPreviewBackgroundOptions = {
4
+ includeBackgroundTarget?: string;
5
+ state: ToolcraftState;
6
+ };
7
+
8
+ export function shouldIncludeToolcraftPreviewBackground({
9
+ includeBackgroundTarget = "export.includeBackground",
10
+ state,
11
+ }: ToolcraftPreviewBackgroundOptions): boolean {
12
+ if (state.canvas.mode === "infinite") {
13
+ return false;
14
+ }
15
+
16
+ const includeBackgroundValue = state.values[includeBackgroundTarget];
17
+
18
+ if (typeof includeBackgroundValue === "boolean") {
19
+ return includeBackgroundValue;
20
+ }
21
+
22
+ if (typeof includeBackgroundValue === "string") {
23
+ const normalizedValue = includeBackgroundValue.trim().toLowerCase();
24
+
25
+ if (
26
+ normalizedValue === "false" ||
27
+ normalizedValue === "off" ||
28
+ normalizedValue === "no" ||
29
+ normalizedValue === "transparent" ||
30
+ normalizedValue === "exclude"
31
+ ) {
32
+ return false;
33
+ }
34
+
35
+ if (
36
+ normalizedValue === "true" ||
37
+ normalizedValue === "on" ||
38
+ normalizedValue === "yes" ||
39
+ normalizedValue === "include"
40
+ ) {
41
+ return true;
42
+ }
43
+ }
44
+
45
+ return true;
46
+ }
@@ -0,0 +1,43 @@
1
+ import { ToolcraftSceneExportError } from "./export-frame";
2
+
3
+ export type ToolcraftArtifactExportFailure = Readonly<{
4
+ code:
5
+ | "artifact-download-failed"
6
+ | "canvas-context-unavailable"
7
+ | "image-encode-failed"
8
+ | "invalid-export-setting"
9
+ | "product-frame-render-failed"
10
+ | "runtime-scene-render-failed"
11
+ | "video-artifact-too-large"
12
+ | "video-encode-failed"
13
+ | "video-encoder-unavailable";
14
+ message: string;
15
+ target?: string;
16
+ }>;
17
+
18
+ export class ToolcraftArtifactExportError extends Error {
19
+ readonly feedback: ToolcraftArtifactExportFailure;
20
+
21
+ constructor(
22
+ feedback: ToolcraftArtifactExportFailure,
23
+ options?: ErrorOptions,
24
+ ) {
25
+ super(feedback.message, options);
26
+ this.name = "ToolcraftArtifactExportError";
27
+ this.feedback = feedback;
28
+ }
29
+ }
30
+
31
+ export function normalizeToolcraftExportError(
32
+ error: unknown,
33
+ feedback: ToolcraftArtifactExportFailure,
34
+ ): ToolcraftArtifactExportError | ToolcraftSceneExportError {
35
+ if (
36
+ error instanceof ToolcraftArtifactExportError ||
37
+ error instanceof ToolcraftSceneExportError
38
+ ) {
39
+ return error;
40
+ }
41
+
42
+ return new ToolcraftArtifactExportError(feedback, { cause: error });
43
+ }
@@ -1,16 +1,26 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, expectTypeOf, it } from "vitest";
2
2
 
3
+ import * as toolcraftRuntimeRoot from "../index";
3
4
  import { defineToolcraft } from "../schema/define-toolcraft";
4
5
  import type { ToolcraftState } from "../state/types";
6
+ import type {
7
+ ToolcraftArtifactSize,
8
+ ToolcraftExportFrame,
9
+ ToolcraftPreviewBackgroundOptions,
10
+ ToolcraftProductExportFrameContext,
11
+ ToolcraftProductExportFrameRenderer,
12
+ ToolcraftProductExportRenderer,
13
+ ToolcraftRetinaExportSize,
14
+ } from "../index";
15
+ import {
16
+ shouldIncludeToolcraftPreviewBackground,
17
+ } from "./export-background";
5
18
  import {
6
- createToolcraftPngExportCanvas,
7
19
  getToolcraftImageExportSize,
8
20
  getToolcraftRetinaExportPixelRatio,
9
21
  getToolcraftRetinaExportSize,
10
22
  getToolcraftVideoExportSize,
11
- shouldIncludeToolcraftExportBackground,
12
- shouldIncludeToolcraftPreviewBackground,
13
- } from "./export";
23
+ } from "./export-sizing";
14
24
 
15
25
  function createState(schema = defineToolcraft({ canvas: { enabled: true }, panels: {} })): ToolcraftState {
16
26
  return {
@@ -48,49 +58,42 @@ function createState(schema = defineToolcraft({ canvas: { enabled: true }, panel
48
58
  };
49
59
  }
50
60
 
51
- function createMockCanvas() {
52
- const context = {
53
- clearRect: vi.fn(),
54
- fillRect: vi.fn(),
55
- fillStyle: "",
56
- restore: vi.fn(),
57
- save: vi.fn(),
58
- scale: vi.fn(),
59
- translate: vi.fn(),
60
- };
61
- const canvas = {
62
- getContext: vi.fn(() => context),
63
- height: 0,
64
- width: 0,
65
- } as unknown as HTMLCanvasElement;
66
-
67
- return { canvas, context };
68
- }
69
-
70
61
  describe("Toolcraft export helpers", () => {
71
- it("normalizes png export background to include by default", () => {
72
- const schema = defineToolcraft({
73
- canvas: { enabled: true },
74
- panels: {},
75
- });
76
-
77
- expect(schema.export.png.background).toBe("include");
78
- });
79
-
80
- it("preserves transparent png export background option", () => {
81
- const schema = defineToolcraft({
82
- canvas: { enabled: true },
83
- export: {
84
- png: {
85
- background: "transparent",
86
- },
87
- },
88
- panels: {},
89
- });
62
+ it("keeps the runtime root limited to product-safe export primitives", () => {
63
+ expect(toolcraftRuntimeRoot).toMatchObject({
64
+ getToolcraftImageExportSize: expect.any(Function),
65
+ getToolcraftRetinaExportPixelRatio: expect.any(Function),
66
+ getToolcraftRetinaExportSize: expect.any(Function),
67
+ getToolcraftVideoExportSize: expect.any(Function),
68
+ shouldIncludeToolcraftPreviewBackground: expect.any(Function),
69
+ TOOLCRAFT_MAX_EXPORT_EDGE_PX: 8192,
70
+ TOOLCRAFT_MAX_EXPORT_PIXELS: 8192 * 8192,
71
+ toolcraftImageExportFormatTarget: "export.image.format",
72
+ toolcraftImageExportResolutionTarget: "export.image.resolution",
73
+ toolcraftVideoExportFormatTarget: "export.video.format",
74
+ toolcraftVideoExportResolutionTarget: "export.video.resolution",
75
+ validateToolcraftArtifactSize: expect.any(Function),
76
+ });
77
+ expect(toolcraftRuntimeRoot).not.toHaveProperty("exportToolcraftImageArtifact");
78
+ expect(toolcraftRuntimeRoot).not.toHaveProperty("exportToolcraftVideoArtifact");
79
+ expect(toolcraftRuntimeRoot).not.toHaveProperty(
80
+ "createToolcraftVideoEncoderBackend",
81
+ );
82
+ expect(toolcraftRuntimeRoot).not.toHaveProperty(
83
+ "createToolcraftVideoFrameSchedule",
84
+ );
90
85
 
91
- expect(schema.export.png.background).toBe("transparent");
92
- expect(shouldIncludeToolcraftExportBackground({ format: "png", schema })).toBe(false);
93
- expect(shouldIncludeToolcraftExportBackground({ format: "video", schema })).toBe(true);
86
+ expectTypeOf<ToolcraftArtifactSize>().toMatchTypeOf<
87
+ Readonly<{ height: number; width: number }>
88
+ >();
89
+ expectTypeOf<ToolcraftExportFrame>().toMatchTypeOf<
90
+ Readonly<{ height: number; width: number; x: number; y: number }>
91
+ >();
92
+ expectTypeOf<ToolcraftPreviewBackgroundOptions>().toHaveProperty("state");
93
+ expectTypeOf<ToolcraftProductExportFrameContext>().toHaveProperty("frame");
94
+ expectTypeOf<ToolcraftProductExportFrameRenderer>().toBeFunction();
95
+ expectTypeOf<ToolcraftProductExportRenderer>().toHaveProperty("renderFrame");
96
+ expectTypeOf<ToolcraftRetinaExportSize>().toHaveProperty("pixelRatio");
94
97
  });
95
98
 
96
99
  it("uses include-background runtime state for live preview background", () => {
@@ -121,24 +124,6 @@ describe("Toolcraft export helpers", () => {
121
124
  expect(shouldIncludeToolcraftPreviewBackground({ state })).toBe(true);
122
125
  });
123
126
 
124
- it("keeps video export background independent from preview include state", () => {
125
- const schema = defineToolcraft({
126
- canvas: { enabled: true },
127
- export: {
128
- png: {
129
- background: "transparent",
130
- },
131
- },
132
- panels: {},
133
- });
134
- const state = createState(schema);
135
-
136
- state.values["export.includeBackground"] = false;
137
-
138
- expect(shouldIncludeToolcraftPreviewBackground({ state })).toBe(false);
139
- expect(shouldIncludeToolcraftExportBackground({ format: "video", schema })).toBe(true);
140
- });
141
-
142
127
  it("uses at least 2x pixel ratio for retina export", () => {
143
128
  expect(getToolcraftRetinaExportPixelRatio(1)).toBe(2);
144
129
  expect(getToolcraftRetinaExportPixelRatio(1.5)).toBe(2);
@@ -266,166 +251,4 @@ describe("Toolcraft export helpers", () => {
266
251
  });
267
252
  });
268
253
 
269
- it("creates a transparent retina png canvas when png background is disabled", () => {
270
- const schema = defineToolcraft({
271
- canvas: { enabled: true },
272
- export: {
273
- png: {
274
- background: "transparent",
275
- },
276
- },
277
- panels: {},
278
- });
279
- const state = createState(schema);
280
- const { canvas, context } = createMockCanvas();
281
- const render = vi.fn();
282
-
283
- const exportedCanvas = createToolcraftPngExportCanvas({
284
- canvasFactory: () => canvas,
285
- devicePixelRatio: 2,
286
- render,
287
- state,
288
- });
289
-
290
- expect(exportedCanvas.width).toBe(400);
291
- expect(exportedCanvas.height).toBe(200);
292
- expect(context.clearRect).toHaveBeenCalledWith(0, 0, 400, 200);
293
- expect(context.fillRect).not.toHaveBeenCalled();
294
- expect(context.scale).toHaveBeenCalledWith(2, 2);
295
- expect(render).toHaveBeenCalledWith(
296
- expect.objectContaining({
297
- cssHeight: 100,
298
- cssWidth: 200,
299
- includeBackground: false,
300
- pixelHeight: 200,
301
- pixelRatio: 2,
302
- pixelWidth: 400,
303
- }),
304
- );
305
- });
306
-
307
- it("fills the png background when png background is enabled", () => {
308
- const state = createState();
309
- const { canvas, context } = createMockCanvas();
310
-
311
- createToolcraftPngExportCanvas({
312
- background: "#112233",
313
- canvasFactory: () => canvas,
314
- devicePixelRatio: 2,
315
- render: vi.fn(),
316
- state,
317
- });
318
-
319
- expect(context.fillStyle).toBe("#112233");
320
- expect(context.fillRect).toHaveBeenCalledWith(0, 0, 400, 200);
321
- });
322
-
323
- it("creates a png canvas at the selected image export resolution", () => {
324
- const state = createState();
325
- const { canvas, context } = createMockCanvas();
326
-
327
- createToolcraftPngExportCanvas({
328
- canvasFactory: () => canvas,
329
- render: vi.fn(),
330
- resolution: "4k",
331
- state,
332
- });
333
-
334
- expect(canvas.width).toBe(4096);
335
- expect(canvas.height).toBe(2048);
336
- expect(context.scale).toHaveBeenCalledWith(20.48, 20.48);
337
- });
338
-
339
- it("allows runtime controls to disable the png background", () => {
340
- const state = createState();
341
- const { canvas, context } = createMockCanvas();
342
-
343
- createToolcraftPngExportCanvas({
344
- background: "#112233",
345
- canvasFactory: () => canvas,
346
- devicePixelRatio: 2,
347
- includeBackground: false,
348
- render: vi.fn(),
349
- state,
350
- });
351
-
352
- expect(context.fillRect).not.toHaveBeenCalled();
353
- });
354
-
355
- it("allows runtime controls to include the png background for transparent schema defaults", () => {
356
- const schema = defineToolcraft({
357
- canvas: { enabled: true },
358
- export: {
359
- png: {
360
- background: "transparent",
361
- },
362
- },
363
- panels: {},
364
- });
365
- const state = createState(schema);
366
- const { canvas, context } = createMockCanvas();
367
-
368
- createToolcraftPngExportCanvas({
369
- background: "#445566",
370
- canvasFactory: () => canvas,
371
- devicePixelRatio: 2,
372
- includeBackground: true,
373
- render: vi.fn(),
374
- state,
375
- });
376
-
377
- expect(context.fillStyle).toBe("#445566");
378
- expect(context.fillRect).toHaveBeenCalledWith(0, 0, 400, 200);
379
- });
380
-
381
- it("renders an infinite scene through its resolved world crop", () => {
382
- const state = createState();
383
- state.canvas.mode = "infinite";
384
- const { canvas, context } = createMockCanvas();
385
- const render = vi.fn();
386
-
387
- createToolcraftPngExportCanvas({
388
- canvasFactory: () => canvas,
389
- devicePixelRatio: 2,
390
- frame: { height: 40, width: 80, x: -30, y: 10 },
391
- render,
392
- state,
393
- });
394
-
395
- expect(canvas.width).toBe(160);
396
- expect(canvas.height).toBe(80);
397
- expect(context.scale).toHaveBeenCalledWith(2, 2);
398
- expect(context.translate).toHaveBeenCalledWith(30, -10);
399
- expect(render).toHaveBeenCalledWith(
400
- expect.objectContaining({
401
- cssHeight: 40,
402
- cssWidth: 80,
403
- frame: { height: 40, width: 80, x: -30, y: 10 },
404
- }),
405
- );
406
- });
407
-
408
- it("rejects infinite and oversized exports before canvas allocation", () => {
409
- const state = createState();
410
- state.canvas.mode = "infinite";
411
- const { canvas } = createMockCanvas();
412
-
413
- expect(() =>
414
- createToolcraftPngExportCanvas({
415
- canvasFactory: () => canvas,
416
- render: vi.fn(),
417
- state,
418
- }),
419
- ).toThrow("no visible exportable elements");
420
- expect(() =>
421
- createToolcraftPngExportCanvas({
422
- canvasFactory: () => canvas,
423
- frame: { height: 5000, width: 5000, x: 0, y: 0 },
424
- render: vi.fn(),
425
- state,
426
- }),
427
- ).toThrow("8192 px per edge");
428
- expect(canvas.width).toBe(0);
429
- expect(canvas.height).toBe(0);
430
- });
431
254
  });
@@ -0,0 +1,111 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ import { defineToolcraft } from "../schema/define-toolcraft";
4
+ import { createToolcraftState } from "../state/create-template-state";
5
+ import { createToolcraftRuntimeSceneVisibility } from "../scene";
6
+ import {
7
+ toolcraftImageExportFormatTarget,
8
+ toolcraftImageExportResolutionTarget,
9
+ } from "./artifact-export-settings";
10
+ import { exportToolcraftImageArtifact } from "./image-artifact-export";
11
+
12
+ function createState(format: "jpg" | "png" = "png") {
13
+ return createToolcraftState(
14
+ defineToolcraft({ canvas: { enabled: true }, panels: {} }),
15
+ {
16
+ values: {
17
+ [toolcraftImageExportFormatTarget]: format,
18
+ [toolcraftImageExportResolutionTarget]: "2k",
19
+ },
20
+ },
21
+ );
22
+ }
23
+
24
+ function createCanvas(blob: Blob | null) {
25
+ const context = {
26
+ clearRect: vi.fn(),
27
+ fillRect: vi.fn(),
28
+ fillStyle: "",
29
+ restore: vi.fn(),
30
+ save: vi.fn(),
31
+ scale: vi.fn(),
32
+ setTransform: vi.fn(),
33
+ translate: vi.fn(),
34
+ };
35
+ const canvas = {
36
+ getContext: vi.fn(() => context),
37
+ height: 0,
38
+ toBlob: vi.fn((callback: BlobCallback) => callback(blob)),
39
+ width: 0,
40
+ } as unknown as HTMLCanvasElement;
41
+ return { canvas, context };
42
+ }
43
+
44
+ const visibility = createToolcraftRuntimeSceneVisibility({
45
+ renderDefaultImages: true,
46
+ suppressedModelTargets: [],
47
+ });
48
+
49
+ describe("exportToolcraftImageArtifact", () => {
50
+ it("renders and downloads the selected real image format and dimensions", async () => {
51
+ const { canvas } = createCanvas(
52
+ new Blob(["jpeg"], { type: "image/jpeg" }),
53
+ );
54
+ const downloadArtifact = vi.fn(() => ({ fileName: "scene.jpg" }));
55
+
56
+ const result = await exportToolcraftImageArtifact({
57
+ boundsProvider: undefined,
58
+ canvasFactory: () => canvas,
59
+ downloadArtifact,
60
+ exportRenderer: {
61
+ baseFileName: "scene",
62
+ renderFrame: vi.fn(),
63
+ },
64
+ productSceneRequired: true,
65
+ renderRuntimeScene: async () => undefined,
66
+ rendererPipeline: null,
67
+ reportProgress: vi.fn(),
68
+ state: createState("jpg"),
69
+ visibility,
70
+ });
71
+
72
+ expect(result).toEqual({
73
+ byteLength: 4,
74
+ extension: ".jpg",
75
+ height: 1152,
76
+ mediaType: "image/jpeg",
77
+ width: 2048,
78
+ });
79
+ expect(canvas.width).toBe(1);
80
+ expect(canvas.height).toBe(1);
81
+ expect(canvas.toBlob).toHaveBeenCalledWith(
82
+ expect.any(Function),
83
+ "image/jpeg",
84
+ 0.95,
85
+ );
86
+ expect(downloadArtifact).toHaveBeenCalledWith(
87
+ expect.objectContaining({ extension: ".jpg", rawBaseFileName: "scene" }),
88
+ );
89
+ });
90
+
91
+ it("fails visibly when canvas encoding returns no blob", async () => {
92
+ const { canvas } = createCanvas(null);
93
+
94
+ await expect(
95
+ exportToolcraftImageArtifact({
96
+ boundsProvider: undefined,
97
+ canvasFactory: () => canvas,
98
+ downloadArtifact: vi.fn(),
99
+ exportRenderer: undefined,
100
+ productSceneRequired: false,
101
+ renderRuntimeScene: async () => undefined,
102
+ rendererPipeline: null,
103
+ reportProgress: vi.fn(),
104
+ state: createState(),
105
+ visibility,
106
+ }),
107
+ ).rejects.toMatchObject({
108
+ feedback: { code: "image-encode-failed" },
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,145 @@
1
+ import {
2
+ getToolcraftRuntimeBackgroundColor,
3
+ isToolcraftRuntimeBackgroundEnabled,
4
+ } from "../state/canvas-background-state";
5
+ import {
6
+ downloadToolcraftArtifact,
7
+ type ToolcraftArtifactDownloadRequest,
8
+ type ToolcraftArtifactDownloadResult,
9
+ type ToolcraftArtifactFileExtension,
10
+ } from "./artifact-download";
11
+ import type { ToolcraftArtifactExportRequest } from "./artifact-export-request";
12
+ import { resolveToolcraftImageExportSettings } from "./artifact-export-settings";
13
+ import { renderToolcraftArtifactFrame } from "./artifact-frame-renderer";
14
+ import { resolveToolcraftImageArtifactFrame } from "./artifact-scene-frame";
15
+ import { createToolcraftArtifactFrameState } from "./artifact-frame-state";
16
+ import {
17
+ ToolcraftSceneExportError,
18
+ validateToolcraftArtifactSize,
19
+ } from "./export-frame";
20
+ import { ToolcraftArtifactExportError } from "./export-error";
21
+ import { getToolcraftImageExportSize } from "./export-sizing";
22
+
23
+ export type ToolcraftImageArtifactExportResult = Readonly<{
24
+ byteLength: number;
25
+ extension: ".jpg" | ".png";
26
+ height: number;
27
+ mediaType: "image/jpeg" | "image/png";
28
+ width: number;
29
+ }>;
30
+
31
+ export type ToolcraftImageArtifactExportRequest = ToolcraftArtifactExportRequest &
32
+ Readonly<{
33
+ canvasFactory?: () => HTMLCanvasElement;
34
+ downloadArtifact?: (
35
+ request: ToolcraftArtifactDownloadRequest,
36
+ ) => ToolcraftArtifactDownloadResult;
37
+ }>;
38
+
39
+ function encodeCanvas(
40
+ canvas: HTMLCanvasElement,
41
+ mediaType: "image/jpeg" | "image/png",
42
+ ): Promise<Blob> {
43
+ return new Promise((resolve, reject) => {
44
+ try {
45
+ canvas.toBlob(
46
+ (blob) => {
47
+ if (blob) {
48
+ resolve(blob);
49
+ } else {
50
+ reject(
51
+ new ToolcraftArtifactExportError({
52
+ code: "image-encode-failed",
53
+ message: "Toolcraft could not encode the image export.",
54
+ }),
55
+ );
56
+ }
57
+ },
58
+ mediaType,
59
+ mediaType === "image/jpeg" ? 0.95 : undefined,
60
+ );
61
+ } catch (error) {
62
+ reject(
63
+ new ToolcraftArtifactExportError(
64
+ {
65
+ code: "image-encode-failed",
66
+ message: "Toolcraft could not encode the image export.",
67
+ },
68
+ { cause: error },
69
+ ),
70
+ );
71
+ }
72
+ });
73
+ }
74
+
75
+ export async function exportToolcraftImageArtifact(
76
+ request: ToolcraftImageArtifactExportRequest,
77
+ ): Promise<ToolcraftImageArtifactExportResult> {
78
+ const settings = resolveToolcraftImageExportSettings(request.state);
79
+ const frameState = createToolcraftArtifactFrameState(
80
+ request.state,
81
+ request.state.timeline.currentTimeSeconds,
82
+ );
83
+ const frame = resolveToolcraftImageArtifactFrame({
84
+ boundsProvider: request.boundsProvider,
85
+ productSceneRequired: request.productSceneRequired,
86
+ state: frameState,
87
+ visibility: request.visibility,
88
+ });
89
+ const size = getToolcraftImageExportSize({
90
+ frame,
91
+ resolution: settings.resolution,
92
+ state: frameState,
93
+ });
94
+ const sizeValidation = validateToolcraftArtifactSize(size);
95
+ if (!sizeValidation.ok) {
96
+ throw new ToolcraftSceneExportError(sizeValidation);
97
+ }
98
+
99
+ const canvas = (request.canvasFactory ?? (() => document.createElement("canvas")))();
100
+ const mediaType = settings.format === "jpg" ? "image/jpeg" : "image/png";
101
+ const extension: ToolcraftArtifactFileExtension =
102
+ settings.format === "jpg" ? ".jpg" : ".png";
103
+ const includeBackground =
104
+ mediaType === "image/jpeg" || isToolcraftRuntimeBackgroundEnabled(frameState);
105
+ try {
106
+ canvas.width = size.width;
107
+ canvas.height = size.height;
108
+ request.reportProgress(0.1);
109
+
110
+ await renderToolcraftArtifactFrame({
111
+ backgroundColor:
112
+ getToolcraftRuntimeBackgroundColor(frameState) ?? "#000000",
113
+ canvas,
114
+ frame,
115
+ includeBackground,
116
+ pixelRatio: size.pixelRatio,
117
+ renderProductFrame: request.exportRenderer?.renderFrame ?? null,
118
+ renderRuntimeScene: request.renderRuntimeScene,
119
+ rendererPipeline: request.rendererPipeline,
120
+ state: frameState,
121
+ });
122
+ request.reportProgress(0.75);
123
+
124
+ const blob = await encodeCanvas(canvas, mediaType);
125
+ request.reportProgress(0.9);
126
+ (request.downloadArtifact ?? downloadToolcraftArtifact)({
127
+ blob,
128
+ extension,
129
+ rawBaseFileName:
130
+ request.exportRenderer?.baseFileName ?? "toolcraft-export",
131
+ });
132
+ request.reportProgress(1);
133
+
134
+ return {
135
+ byteLength: blob.size,
136
+ extension,
137
+ height: size.height,
138
+ mediaType,
139
+ width: size.width,
140
+ };
141
+ } finally {
142
+ canvas.width = 1;
143
+ canvas.height = 1;
144
+ }
145
+ }
@@ -1,3 +1,44 @@
1
- export * from "./export";
2
- export * from "./export-frame";
3
- export * from "./export-sizing";
1
+ export {
2
+ toolcraftImageExportFormatTarget,
3
+ toolcraftImageExportResolutionTarget,
4
+ toolcraftVideoExportFormatTarget,
5
+ toolcraftVideoExportResolutionTarget,
6
+ } from "./artifact-export-settings";
7
+ export type {
8
+ ToolcraftImageExportFormat,
9
+ ToolcraftImageExportPresetResolution,
10
+ ToolcraftResolvedImageExportSettings,
11
+ ToolcraftResolvedVideoExportSettings,
12
+ ToolcraftVideoExportFormat,
13
+ ToolcraftVideoExportPresetResolution,
14
+ } from "./artifact-export-settings";
15
+ export { shouldIncludeToolcraftPreviewBackground } from "./export-background";
16
+ export type { ToolcraftPreviewBackgroundOptions } from "./export-background";
17
+ export {
18
+ TOOLCRAFT_MAX_EXPORT_EDGE_PX,
19
+ TOOLCRAFT_MAX_EXPORT_PIXELS,
20
+ validateToolcraftArtifactSize,
21
+ } from "./export-frame";
22
+ export type {
23
+ ToolcraftArtifactSize,
24
+ ToolcraftExportFrame,
25
+ } from "./export-frame";
26
+ export {
27
+ getToolcraftImageExportSize,
28
+ getToolcraftRetinaExportPixelRatio,
29
+ getToolcraftRetinaExportSize,
30
+ getToolcraftVideoExportSize,
31
+ } from "./export-sizing";
32
+ export type {
33
+ ToolcraftExportSizeOptions,
34
+ ToolcraftImageExportResolution,
35
+ ToolcraftImageExportSizeOptions,
36
+ ToolcraftRetinaExportSize,
37
+ ToolcraftVideoExportResolution,
38
+ ToolcraftVideoExportSizeOptions,
39
+ } from "./export-sizing";
40
+ export type {
41
+ ToolcraftProductExportFrameContext,
42
+ ToolcraftProductExportFrameRenderer,
43
+ ToolcraftProductExportRenderer,
44
+ } from "./product-export-renderer";