@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,68 @@
1
+ import type {
2
+ ToolcraftArtifactExportRequest,
3
+ } from "../../../export/artifact-export-request";
4
+ import type { ToolcraftExportFrame } from "../../../export/export-frame";
5
+ import {
6
+ exportToolcraftImageArtifact,
7
+ } from "../../../export/image-artifact-export";
8
+ import type {
9
+ ToolcraftProductExportRenderer,
10
+ } from "../../../export/product-export-renderer";
11
+ import {
12
+ exportToolcraftVideoArtifact,
13
+ } from "../../../export/video-artifact-export";
14
+ import type { ToolcraftRendererPipelineClient } from "../../../rendering";
15
+ import type {
16
+ ToolcraftProductSceneBoundsProvider,
17
+ ToolcraftRuntimeSceneVisibility,
18
+ } from "../../../scene";
19
+ import type { ToolcraftActionSchema } from "../../../schema/types";
20
+ import type { ToolcraftState } from "../../../state/types";
21
+
22
+ export type ToolcraftControlsSceneExport = Readonly<{
23
+ boundsProvider?: ToolcraftProductSceneBoundsProvider;
24
+ exportRenderer?: ToolcraftProductExportRenderer;
25
+ productSceneRequired: boolean;
26
+ visibility: ToolcraftRuntimeSceneVisibility;
27
+ }>;
28
+
29
+ export type ToolcraftExportActionRequest = Readonly<{
30
+ action: ToolcraftActionSchema;
31
+ renderRuntimeScene: (
32
+ canvas: HTMLCanvasElement,
33
+ frame: ToolcraftExportFrame,
34
+ state: ToolcraftState,
35
+ ) => Promise<unknown>;
36
+ rendererPipeline: ToolcraftRendererPipelineClient | null;
37
+ reportProgress: (progress: number) => void;
38
+ sceneExport: ToolcraftControlsSceneExport;
39
+ state: ToolcraftState;
40
+ }>;
41
+
42
+ function createSharedArtifactRequest(
43
+ request: ToolcraftExportActionRequest,
44
+ ): ToolcraftArtifactExportRequest {
45
+ return {
46
+ boundsProvider: request.sceneExport.boundsProvider,
47
+ exportRenderer: request.sceneExport.exportRenderer,
48
+ productSceneRequired: request.sceneExport.productSceneRequired,
49
+ renderRuntimeScene: request.renderRuntimeScene,
50
+ rendererPipeline: request.rendererPipeline,
51
+ reportProgress: request.reportProgress,
52
+ state: request.state,
53
+ visibility: request.sceneExport.visibility,
54
+ };
55
+ }
56
+
57
+ export function runToolcraftExportAction(
58
+ request: ToolcraftExportActionRequest,
59
+ ): PromiseLike<unknown> | null {
60
+ switch (request.action.role) {
61
+ case "export-image":
62
+ return exportToolcraftImageArtifact(createSharedArtifactRequest(request));
63
+ case "export-video":
64
+ return exportToolcraftVideoArtifact(createSharedArtifactRequest(request));
65
+ default:
66
+ return null;
67
+ }
68
+ }
@@ -3,67 +3,21 @@ import {
3
3
  isToolcraftTimelinePanelExtendedTarget,
4
4
  isToolcraftTimelinePanelVisibleTarget,
5
5
  } from "../../../schema/runtime-targets";
6
+ import {
7
+ doesToolcraftApplicabilityMatch,
8
+ doesToolcraftPredicateMatchValue,
9
+ getToolcraftApplicabilityTargets,
10
+ } from "../../../schema/control-applicability";
6
11
  import type {
7
12
  ToolcraftControlConditionSchema,
8
- ToolcraftControlSchema,
9
- ToolcraftControlSectionSchema,
13
+ ResolvedToolcraftControlSchema,
14
+ ResolvedToolcraftControlSectionSchema,
10
15
  } from "../../../schema/types";
11
16
  import type { ToolcraftState } from "../../../state/types";
12
17
  import { readToolcraftCanvasRuntimeTarget } from "../../../state/canvas-frame";
13
18
 
14
- function valuesEqual(first: unknown, second: unknown): boolean {
15
- if (Object.is(first, second)) {
16
- return true;
17
- }
18
-
19
- if (
20
- (typeof first !== "object" || first === null) &&
21
- (typeof second !== "object" || second === null)
22
- ) {
23
- return false;
24
- }
25
-
26
- try {
27
- return JSON.stringify(first) === JSON.stringify(second);
28
- } catch {
29
- return false;
30
- }
31
- }
32
-
33
- function valuesInclude(value: unknown, options: readonly unknown[]): boolean {
34
- return options.some((option) => valuesEqual(value, option));
35
- }
36
-
37
- function readComparableNumber(value: unknown): number | null {
38
- if (typeof value === "number" && Number.isFinite(value)) {
39
- return value;
40
- }
41
-
42
- if (typeof value === "string") {
43
- const numberValue = Number(value.trim());
44
-
45
- return Number.isFinite(numberValue) ? numberValue : null;
46
- }
47
-
48
- return null;
49
- }
50
-
51
- function compareConditionNumbers(
52
- value: unknown,
53
- expected: number | undefined,
54
- comparator: (value: number, expected: number) => boolean,
55
- ): boolean | null {
56
- if (typeof expected !== "number" || !Number.isFinite(expected)) {
57
- return null;
58
- }
59
-
60
- const numberValue = readComparableNumber(value);
61
-
62
- return numberValue === null ? false : comparator(numberValue, expected);
63
- }
64
-
65
19
  function getControlDefaultValueByTarget(
66
- sections: readonly ToolcraftControlSectionSchema[],
20
+ sections: readonly ResolvedToolcraftControlSectionSchema[],
67
21
  target: string,
68
22
  ): unknown {
69
23
  for (const section of sections) {
@@ -119,22 +73,28 @@ export function getToolcraftConditionTargets(
119
73
  }
120
74
 
121
75
  export function getToolcraftControlConditionTargets(
122
- control: ToolcraftControlSchema,
76
+ control: ResolvedToolcraftControlSchema,
123
77
  ): string[] {
124
- return getToolcraftConditionTargets(
125
- control.visibleWhen,
126
- control.disabledWhen,
127
- );
78
+ return [
79
+ ...new Set([
80
+ ...getToolcraftApplicabilityTargets(
81
+ control.applicability,
82
+ ),
83
+ ...getToolcraftConditionTargets(control.disabledWhen),
84
+ ]),
85
+ ];
128
86
  }
129
87
 
130
88
  export function getToolcraftControlVisibilityTargets(
131
- control: ToolcraftControlSchema,
89
+ control: ResolvedToolcraftControlSchema,
132
90
  ): string[] {
133
- return getToolcraftConditionTargets(control.visibleWhen);
91
+ return getToolcraftApplicabilityTargets(
92
+ control.applicability,
93
+ );
134
94
  }
135
95
 
136
96
  export function getToolcraftSectionVisibilityTargets(
137
- section: ToolcraftControlSectionSchema,
97
+ section: ResolvedToolcraftControlSectionSchema,
138
98
  ): string[] {
139
99
  return getToolcraftConditionTargets(section.visibleWhen);
140
100
  }
@@ -143,63 +103,15 @@ export function toolcraftConditionMatches(
143
103
  state: ToolcraftState,
144
104
  condition: ToolcraftControlConditionSchema,
145
105
  ): boolean {
146
- const value = getToolcraftTargetValue(state, condition.target);
147
- const matches: boolean[] = [];
148
-
149
- if ("equals" in condition) {
150
- matches.push(valuesEqual(value, condition.equals));
151
- }
152
-
153
- if ("notEquals" in condition) {
154
- matches.push(!valuesEqual(value, condition.notEquals));
155
- }
156
-
157
- if (Array.isArray(condition.oneOf)) {
158
- matches.push(valuesInclude(value, condition.oneOf));
159
- }
160
-
161
- if (Array.isArray(condition.notOneOf)) {
162
- matches.push(!valuesInclude(value, condition.notOneOf));
163
- }
164
-
165
- const greaterThan = compareConditionNumbers(
166
- value,
167
- condition.greaterThan,
168
- (numberValue, expected) => numberValue > expected,
169
- );
170
- const greaterThanOrEqual = compareConditionNumbers(
171
- value,
172
- condition.greaterThanOrEqual,
173
- (numberValue, expected) => numberValue >= expected,
106
+ return doesToolcraftPredicateMatchValue(
107
+ condition,
108
+ getToolcraftTargetValue(state, condition.target),
174
109
  );
175
- const lessThan = compareConditionNumbers(
176
- value,
177
- condition.lessThan,
178
- (numberValue, expected) => numberValue < expected,
179
- );
180
- const lessThanOrEqual = compareConditionNumbers(
181
- value,
182
- condition.lessThanOrEqual,
183
- (numberValue, expected) => numberValue <= expected,
184
- );
185
-
186
- for (const match of [
187
- greaterThan,
188
- greaterThanOrEqual,
189
- lessThan,
190
- lessThanOrEqual,
191
- ]) {
192
- if (match !== null) {
193
- matches.push(match);
194
- }
195
- }
196
-
197
- return matches.length > 0 && matches.every(Boolean);
198
110
  }
199
111
 
200
112
  export function isToolcraftSectionVisible(
201
113
  state: ToolcraftState,
202
- section: ToolcraftControlSectionSchema,
114
+ section: ResolvedToolcraftControlSectionSchema,
203
115
  ): boolean {
204
116
  return section.visibleWhen
205
117
  ? toolcraftConditionMatches(state, section.visibleWhen)
@@ -208,16 +120,17 @@ export function isToolcraftSectionVisible(
208
120
 
209
121
  export function isToolcraftControlVisible(
210
122
  state: ToolcraftState,
211
- control: ToolcraftControlSchema,
123
+ control: ResolvedToolcraftControlSchema,
212
124
  ): boolean {
213
- return control.visibleWhen
214
- ? toolcraftConditionMatches(state, control.visibleWhen)
215
- : true;
125
+ return doesToolcraftApplicabilityMatch(
126
+ control.applicability,
127
+ (target) => getToolcraftTargetValue(state, target),
128
+ );
216
129
  }
217
130
 
218
131
  export function isToolcraftControlDisabled(
219
132
  state: ToolcraftState,
220
- control: ToolcraftControlSchema,
133
+ control: ResolvedToolcraftControlSchema,
221
134
  ): boolean {
222
135
  if (control.disabled) {
223
136
  return true;
@@ -29,7 +29,7 @@ import { LiveCustomControl } from "../live-custom-control";
29
29
  import type { ActionControlRunAction } from "../renderers/controls-panel-action-renderer";
30
30
  import { renderActionControl } from "../renderers/controls-panel-action-renderer";
31
31
  import { renderBasicControl } from "../renderers/controls-panel-basic-renderers";
32
- import { renderCollectionActionsControl } from "../renderers/controls-panel-collection-renderer";
32
+ import { renderCollectionControl } from "../renderers/controls-panel-collection-renderer";
33
33
  import {
34
34
  renderCompoundColorGroup,
35
35
  renderCompoundControl,
@@ -419,7 +419,7 @@ export const ControlsPanelControlGroup = React.memo(
419
419
  node = null;
420
420
  break;
421
421
  case "collection":
422
- node = renderCollectionActionsControl({
422
+ node = renderCollectionControl({
423
423
  control,
424
424
  name,
425
425
  setControlValue,
@@ -448,7 +448,10 @@ export const ControlsPanelControlGroup = React.memo(
448
448
  id,
449
449
  layerSelectionEnabled: !mediaPreviewEnabled,
450
450
  mediaAssets: mediaPreviewEnabled ? (selection.mediaAssets ?? []) : [],
451
+ name,
452
+ setControlValue,
451
453
  sourceAssetCoordinator,
454
+ value,
452
455
  });
453
456
  break;
454
457
  case "settings":
@@ -3,7 +3,8 @@
3
3
  import * as React from "react";
4
4
 
5
5
  import type {
6
- ToolcraftControlSectionSchema,
6
+ ResolvedToolcraftControlSectionSchema,
7
+ ResolvedToolcraftControlSchema,
7
8
  ToolcraftControlSchema,
8
9
  } from "../../../schema/types";
9
10
  import {
@@ -12,7 +13,7 @@ import {
12
13
  } from "../../../schema/runtime-targets";
13
14
  import { getToolcraftControlRendererKind } from "../renderers/controls-panel-renderer-registry";
14
15
 
15
- export type ControlEntry = [string, ToolcraftControlSchema];
16
+ export type ControlEntry = [string, ResolvedToolcraftControlSchema];
16
17
 
17
18
  export type ControlRenderGroup =
18
19
  | { entries: readonly ControlEntry[]; kind: "colorGroup" }
@@ -41,6 +42,7 @@ const hiddenDiscreteMarkerCount = 2;
41
42
  const sectionedCompoundControlTypes = new Set([
42
43
  "channelMixer",
43
44
  "collectionActions",
45
+ "sourceCollection",
44
46
  "fontPicker",
45
47
  "gradient",
46
48
  "palette",
@@ -87,7 +89,7 @@ export function getControlName(
87
89
 
88
90
  export function getControlsRecord(
89
91
  entries: readonly ControlEntry[],
90
- ): Record<string, ToolcraftControlSchema> {
92
+ ): Record<string, ResolvedToolcraftControlSchema> {
91
93
  return Object.fromEntries(entries);
92
94
  }
93
95
 
@@ -151,7 +153,7 @@ export function getControlRenderGroupIds(
151
153
  }
152
154
 
153
155
  export function countControlsByType(
154
- sections: readonly ToolcraftControlSectionSchema[],
156
+ sections: readonly ResolvedToolcraftControlSectionSchema[],
155
157
  type: string,
156
158
  ): number {
157
159
  return sections.reduce(
@@ -186,8 +188,8 @@ export function isColorOnlySection(entries: readonly ControlEntry[]): boolean {
186
188
  }
187
189
 
188
190
  export function getRenderedControlsSectionTitle(
189
- section: ToolcraftControlSectionSchema,
190
- ): ToolcraftControlSectionSchema["title"] {
191
+ section: ResolvedToolcraftControlSectionSchema,
192
+ ): ResolvedToolcraftControlSectionSchema["title"] {
191
193
  return isStickyFooterActionSection(section) ? undefined : section.title;
192
194
  }
193
195
 
@@ -196,7 +198,7 @@ export function isRuntimeSetupSection({
196
198
  section,
197
199
  }: {
198
200
  entries: readonly ControlEntry[];
199
- section: ToolcraftControlSectionSchema;
201
+ section: ResolvedToolcraftControlSectionSchema;
200
202
  }): boolean {
201
203
  return (
202
204
  section.title === "Setup" &&
@@ -205,7 +207,7 @@ export function isRuntimeSetupSection({
205
207
  }
206
208
 
207
209
  function isStickyFooterActionSection(
208
- section: ToolcraftControlSectionSchema,
210
+ section: ResolvedToolcraftControlSectionSchema,
209
211
  ): boolean {
210
212
  return (
211
213
  section.actionGroup !== undefined &&
@@ -10,7 +10,7 @@ import {
10
10
  TooltipTrigger,
11
11
  } from "@repo/ui";
12
12
 
13
- import type { ToolcraftControlSectionSchema } from "../../../schema/types";
13
+ import type { ResolvedToolcraftControlSectionSchema } from "../../../schema/types";
14
14
  import type { ToolcraftStoreDependency } from "../../../state/toolcraft-external-store-dependencies";
15
15
  import type {
16
16
  ToolcraftCommand,
@@ -56,7 +56,7 @@ export type ControlsPanelSectionGroupsArgs = {
56
56
  headerKeyframeTarget: string | null;
57
57
  keyframesSupported: boolean;
58
58
  runAction: ActionControlRunAction;
59
- section: ToolcraftControlSectionSchema;
59
+ section: ResolvedToolcraftControlSectionSchema;
60
60
  setControlValue: ControlsPanelSetControlValue;
61
61
  vectorPadShape: "compact" | "square";
62
62
  };
@@ -66,7 +66,7 @@ export type ControlsPanelSectionProps = Omit<
66
66
  "headerKeyframeTarget"
67
67
  > & {
68
68
  "data-toolcraft-panel-section": "";
69
- actionGroup: ToolcraftControlSectionSchema["actionGroup"];
69
+ actionGroup: ResolvedToolcraftControlSectionSchema["actionGroup"];
70
70
  collapsed: boolean;
71
71
  onCollapsedChange: (sectionCollapseKey: string, collapsed: boolean) => void;
72
72
  sectionCollapseKey: string;
@@ -0,0 +1,63 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+
4
+ import { ControlsPanelCollectionItemFields } from "./controls-panel-collection-item-fields";
5
+
6
+ describe("ControlsPanelCollectionItemFields", () => {
7
+ it("commits one complete record while preserving sibling and unknown keys", () => {
8
+ const onChange = vi.fn();
9
+
10
+ render(
11
+ <ControlsPanelCollectionItemFields
12
+ controls={{
13
+ strength: {
14
+ defaultValue: 0.5,
15
+ label: "Strength",
16
+ max: 1,
17
+ min: 0,
18
+ step: 0.1,
19
+ type: "slider",
20
+ },
21
+ invert: {
22
+ defaultValue: false,
23
+ label: "Invert",
24
+ type: "switch",
25
+ },
26
+ tint: {
27
+ defaultValue: { hex: "#FFFFFF", opacity: 100 },
28
+ label: "Tint",
29
+ type: "colorOpacity",
30
+ },
31
+ }}
32
+ id="surface.layers:0"
33
+ onChange={onChange}
34
+ target="surface.layers"
35
+ value={{
36
+ invert: true,
37
+ rendererToken: "keep",
38
+ strength: 0.4,
39
+ tint: { hex: "#EEDDCC", opacity: 80 },
40
+ }}
41
+ />,
42
+ );
43
+
44
+ fireEvent.click(screen.getByRole("button", { name: "Edit Strength value" }));
45
+ const editor = screen.getByRole("textbox", { name: "Strength value" });
46
+ editor.textContent = "0.7";
47
+ fireEvent.keyDown(editor, { key: "Enter" });
48
+
49
+ expect(onChange).toHaveBeenCalledWith(
50
+ {
51
+ invert: true,
52
+ rendererToken: "keep",
53
+ strength: 0.7,
54
+ tint: { hex: "#EEDDCC", opacity: 80 },
55
+ },
56
+ "Strength",
57
+ undefined,
58
+ );
59
+ expect(screen.getByText("Invert")).toBeTruthy();
60
+ expect(screen.getByText("Tint")).toBeTruthy();
61
+ expect(screen.queryByText(/Item 1/u)).toBeNull();
62
+ });
63
+ });
@@ -0,0 +1,138 @@
1
+ import * as React from "react";
2
+ import type { ControlChangeMeta } from "@repo/ui";
3
+
4
+ import type {
5
+ ToolcraftCollectionItemControlSchema,
6
+ ToolcraftCollectionItemControlsSchema,
7
+ ToolcraftControlSchema,
8
+ } from "../../../schema/types";
9
+ import { renderBasicControl } from "./controls-panel-basic-renderers";
10
+ import { renderCompoundControl } from "./controls-panel-compound-renderers";
11
+
12
+ type ItemFieldChange = (nextValue: unknown, meta?: ControlChangeMeta) => void;
13
+
14
+ function renderWithoutKeyframeAction({
15
+ children,
16
+ }: {
17
+ children: React.ReactNode;
18
+ }): React.ReactNode {
19
+ return children;
20
+ }
21
+
22
+ function getFieldName(
23
+ id: string,
24
+ control: ToolcraftCollectionItemControlSchema,
25
+ ): string {
26
+ if (typeof control.label === "string") {
27
+ return control.label;
28
+ }
29
+
30
+ return id
31
+ .replace(/([a-z0-9])([A-Z])/gu, "$1 $2")
32
+ .replace(/[-_.]+/gu, " ")
33
+ .replace(/^./u, (character) => character.toUpperCase());
34
+ }
35
+
36
+ function asItemRecord(value: unknown): Record<string, unknown> {
37
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
38
+ return {};
39
+ }
40
+
41
+ return Object.fromEntries(Object.entries(value));
42
+ }
43
+
44
+ export function ControlsPanelCollectionItemField({
45
+ colorLabelMode,
46
+ control,
47
+ id,
48
+ name,
49
+ onChange,
50
+ target,
51
+ value,
52
+ }: {
53
+ colorLabelMode: "authored" | "hidden";
54
+ control: ToolcraftCollectionItemControlSchema;
55
+ id: string;
56
+ name: string;
57
+ onChange: ItemFieldChange;
58
+ target: string;
59
+ value: unknown;
60
+ }): React.JSX.Element {
61
+ const fieldSchema: ToolcraftControlSchema = { ...control, target };
62
+ const shouldShowColorFieldLabel = () =>
63
+ colorLabelMode === "authored" && control.label !== false;
64
+ const rendered =
65
+ renderBasicControl({
66
+ commit: onChange,
67
+ control: fieldSchema,
68
+ id,
69
+ name,
70
+ usesHeaderKeyframeAction: false,
71
+ value,
72
+ vectorPadShape: "compact",
73
+ withKeyframeLabelAction: renderWithoutKeyframeAction,
74
+ }) ??
75
+ renderCompoundControl({
76
+ commit: onChange,
77
+ commitWithLabel: () => onChange,
78
+ control: fieldSchema,
79
+ id,
80
+ name,
81
+ sectionHasOnlyColorFields: false,
82
+ shouldShowColorFieldLabel,
83
+ usesHeaderKeyframeAction: false,
84
+ value,
85
+ withKeyframeLabelAction: renderWithoutKeyframeAction,
86
+ });
87
+
88
+ if (rendered == null) {
89
+ throw new Error(
90
+ `Unsupported Toolcraft collection item field: ${control.type}`,
91
+ );
92
+ }
93
+
94
+ return <>{rendered}</>;
95
+ }
96
+
97
+ export function ControlsPanelCollectionItemFields({
98
+ controls,
99
+ id,
100
+ onChange,
101
+ target,
102
+ value,
103
+ }: {
104
+ controls: ToolcraftCollectionItemControlsSchema;
105
+ id: string;
106
+ onChange: (
107
+ nextValue: Readonly<Record<string, unknown>>,
108
+ fieldName: string,
109
+ meta?: ControlChangeMeta,
110
+ ) => void;
111
+ target: string;
112
+ value: unknown;
113
+ }): React.JSX.Element {
114
+ const record = asItemRecord(value);
115
+
116
+ return (
117
+ <div className="min-w-0 space-y-4" data-slot="collection-item-fields">
118
+ {Object.entries(controls).map(([fieldId, field]) => {
119
+ const fieldName = getFieldName(fieldId, field);
120
+
121
+ return (
122
+ <ControlsPanelCollectionItemField
123
+ colorLabelMode="authored"
124
+ control={field}
125
+ id={`${id}:${fieldId}`}
126
+ key={fieldId}
127
+ name={fieldName}
128
+ onChange={(nextValue, meta) =>
129
+ onChange({ ...record, [fieldId]: nextValue }, fieldName, meta)
130
+ }
131
+ target={target}
132
+ value={record[fieldId] ?? field.defaultValue}
133
+ />
134
+ );
135
+ })}
136
+ </div>
137
+ );
138
+ }