@cueframe/kernel 0.2.2 → 0.2.4

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 (335) hide show
  1. package/dist/_members/animate/index.d.ts +8 -2
  2. package/dist/_members/animate/layout/FlexLayout.d.ts +69 -0
  3. package/dist/_members/animate/layout/fit.d.ts +44 -0
  4. package/dist/_members/animate/layout/index.d.ts +3 -0
  5. package/dist/_members/animate/layout/solveFlex.d.ts +70 -0
  6. package/dist/_members/animate/media/MediaTileScene.d.ts +65 -0
  7. package/dist/_members/animate/media/recipes.d.ts +61 -0
  8. package/dist/_members/animate/scene/FocusPass.d.ts +76 -0
  9. package/dist/_members/animate/scene/UnlitPlane.d.ts +45 -0
  10. package/dist/_members/animate/scene/__oracle__/cssProjection.d.ts +24 -0
  11. package/dist/_members/animate/scene/assets.d.ts +21 -0
  12. package/dist/_members/animate/scene/backdrop.d.ts +7 -0
  13. package/dist/_members/animate/scene/coordinates.d.ts +58 -0
  14. package/dist/_members/animate/scene/deviceOpacity.d.ts +18 -0
  15. package/dist/_members/animate/scene/errors.d.ts +14 -0
  16. package/dist/_members/animate/scene/finishing.d.ts +69 -0
  17. package/dist/_members/animate/scene/finishingMath.d.ts +60 -0
  18. package/dist/_members/animate/scene/finishingPass.d.ts +35 -0
  19. package/dist/_members/animate/scene/focusBlur.d.ts +56 -0
  20. package/dist/_members/animate/scene/focusResources.d.ts +190 -0
  21. package/dist/_members/animate/scene/focusShaders.d.ts +68 -0
  22. package/dist/_members/animate/scene/fontReadiness.d.ts +27 -0
  23. package/dist/_members/animate/scene/grainNoise.d.ts +118 -0
  24. package/dist/_members/animate/scene/index.d.ts +19 -0
  25. package/dist/_members/animate/scene/internal.d.ts +29 -0
  26. package/dist/_members/animate/scene/planeFit.d.ts +46 -0
  27. package/dist/_members/animate/scene/preparedWorldText.d.ts +25 -0
  28. package/dist/_members/animate/scene/rasterCache.d.ts +30 -0
  29. package/dist/_members/animate/scene/rasterReadiness.d.ts +33 -0
  30. package/dist/_members/animate/scene/readiness.d.ts +69 -0
  31. package/dist/_members/animate/scene/redraw.d.ts +10 -0
  32. package/dist/_members/animate/scene/render.d.ts +9 -0
  33. package/dist/_members/animate/scene/scaling.d.ts +13 -0
  34. package/dist/_members/animate/scene/text.d.ts +40 -0
  35. package/dist/_members/animate/scene/textLayout.d.ts +78 -0
  36. package/dist/_members/animate/scene/textureImageSize.d.ts +4 -0
  37. package/dist/_members/animate/scene/useSceneImageTexture.d.ts +21 -0
  38. package/dist/_members/animate/scene/useWorldTextTexture.d.ts +21 -0
  39. package/dist/_members/animate/scene/v2/MeasuredPlaneGeometry.d.ts +7 -0
  40. package/dist/_members/animate/scene/v2/PhotographicStudioRig.d.ts +11 -0
  41. package/dist/_members/animate/scene/v2/PreparedSceneShotV1.d.ts +12 -0
  42. package/dist/_members/animate/scene/v2/SceneShotV2.d.ts +59 -0
  43. package/dist/_members/animate/scene/v2/builders.d.ts +170 -0
  44. package/dist/_members/animate/scene/v2/finishes.d.ts +152 -0
  45. package/dist/_members/animate/scene/v2/lighting.d.ts +119 -0
  46. package/dist/_members/animate/scene/v2/observePmremTargets.d.ts +4 -0
  47. package/dist/_members/animate/scene/v2/preparationFacts.d.ts +32 -0
  48. package/dist/_members/animate/scene/v2/preparedSurface.d.ts +17 -0
  49. package/dist/_members/animate/scene/v2/projectedResolution.d.ts +2 -0
  50. package/dist/_members/animate/scene/v2/renderTargetObserver.d.ts +5 -0
  51. package/dist/_members/animate/scene/v2/runtimeAllocations.d.ts +55 -0
  52. package/dist/_members/animate/scene/v2/shadow.d.ts +23 -0
  53. package/dist/_members/animate/scene/v2/slabGeometry.d.ts +134 -0
  54. package/dist/_members/animate/scene/v2/slots.d.ts +73 -0
  55. package/dist/_members/animate/scene/v2/videoBinding.d.ts +39 -0
  56. package/dist/_members/animate/scene/worldBounds.d.ts +19 -0
  57. package/dist/_members/animate/scene/worldTextRaster.d.ts +87 -0
  58. package/dist/_members/animate/scene/worldTextTexture.d.ts +32 -0
  59. package/dist/_members/animate/stage/PerspectiveStage.d.ts +73 -0
  60. package/dist/_members/animate/stage/camera.d.ts +143 -0
  61. package/dist/_members/animate/stage/dof.d.ts +9 -0
  62. package/dist/_members/animate/stage/index.d.ts +6 -0
  63. package/dist/_members/animate/stage/presets.d.ts +17 -0
  64. package/dist/_members/animate/three/DeviceMesh.d.ts +256 -0
  65. package/dist/_members/animate/three/SceneCameraRig.d.ts +12 -0
  66. package/dist/_members/animate/three/StageCameraRig.d.ts +7 -0
  67. package/dist/_members/animate/three/StudioRig.d.ts +228 -0
  68. package/dist/_members/animate/three/ThreeCanvas.d.ts +22 -0
  69. package/dist/_members/animate/three/defaultScreen.d.ts +23 -0
  70. package/dist/_members/animate/three/index.d.ts +7 -1
  71. package/dist/_members/animate/three/textureLifecycle.d.ts +29 -0
  72. package/dist/_members/animate/three/toneMapping.d.ts +113 -0
  73. package/dist/_members/animate/three/useImageTexture.d.ts +17 -0
  74. package/dist/_members/authoring/index.d.ts +2 -0
  75. package/dist/_members/authoring/scene/index.d.ts +6 -0
  76. package/dist/_members/authoring/scene/materialize.d.ts +47 -0
  77. package/dist/_members/authoring/scene/validate.d.ts +80 -0
  78. package/dist/_members/authoring/scene/wrapper.d.ts +41 -0
  79. package/dist/_members/component-runtime/allowedImports.d.ts +3 -0
  80. package/dist/_members/component-runtime/allowedImports.d.ts.map +1 -0
  81. package/dist/_members/component-runtime/bake.d.ts +8 -0
  82. package/dist/_members/component-runtime/bake.d.ts.map +1 -1
  83. package/dist/_members/component-runtime/browserFirewall.d.ts +7 -1
  84. package/dist/_members/component-runtime/browserFirewall.d.ts.map +1 -1
  85. package/dist/_members/component-runtime/browserOptions.d.ts +59 -3
  86. package/dist/_members/component-runtime/browserOptions.d.ts.map +1 -1
  87. package/dist/_members/component-runtime/canonicalIdentity.d.ts +14 -0
  88. package/dist/_members/component-runtime/canonicalIdentity.d.ts.map +1 -1
  89. package/dist/_members/component-runtime/contracts.d.ts +341 -1
  90. package/dist/_members/component-runtime/contracts.d.ts.map +1 -1
  91. package/dist/_members/component-runtime/fixtures/imageTileDriftWall.d.ts +5 -0
  92. package/dist/_members/component-runtime/fixtures/imageTileDriftWall.d.ts.map +1 -0
  93. package/dist/_members/component-runtime/fixtures/mediaTileMixed.d.ts +5 -0
  94. package/dist/_members/component-runtime/fixtures/mediaTileMixed.d.ts.map +1 -0
  95. package/dist/_members/component-runtime/fixtures/mediaTileVideo.d.ts +8 -0
  96. package/dist/_members/component-runtime/fixtures/mediaTileVideo.d.ts.map +1 -0
  97. package/dist/_members/component-runtime/importPolicy.d.ts +3 -2
  98. package/dist/_members/component-runtime/importPolicy.d.ts.map +1 -1
  99. package/dist/_members/component-runtime/index.d.ts +3 -0
  100. package/dist/_members/component-runtime/index.d.ts.map +1 -1
  101. package/dist/_members/component-runtime/livePreviewProxy.d.ts +7 -0
  102. package/dist/_members/component-runtime/livePreviewProxy.d.ts.map +1 -0
  103. package/dist/_members/component-runtime/localRoot.d.ts +37 -0
  104. package/dist/_members/component-runtime/localRoot.d.ts.map +1 -0
  105. package/dist/_members/component-runtime/mediaResolution.d.ts +25 -0
  106. package/dist/_members/component-runtime/mediaResolution.d.ts.map +1 -0
  107. package/dist/_members/component-runtime/render.d.ts +151 -13
  108. package/dist/_members/component-runtime/render.d.ts.map +1 -1
  109. package/dist/_members/component-runtime/renderDiagnostics.d.ts +14 -0
  110. package/dist/_members/component-runtime/renderDiagnostics.d.ts.map +1 -0
  111. package/dist/_members/component-runtime/sceneAdmission.d.ts +47 -0
  112. package/dist/_members/component-runtime/sceneAdmission.d.ts.map +1 -0
  113. package/dist/_members/component-runtime/sourceWorkspace.d.ts +8 -0
  114. package/dist/_members/component-runtime/sourceWorkspace.d.ts.map +1 -1
  115. package/dist/_members/component-runtime/webglProbe.d.ts +191 -0
  116. package/dist/_members/component-runtime/webglProbe.d.ts.map +1 -0
  117. package/dist/_members/compose/primitiveRegistry.d.ts +2 -6
  118. package/dist/_members/composition/fonts/bundledFaces.d.ts +150 -0
  119. package/dist/_members/composition/fonts/captionFaces.d.ts +268 -0
  120. package/dist/_members/composition/fonts.d.ts +11 -4
  121. package/dist/_members/composition/graphics-plan/schemas.d.ts +4 -4
  122. package/dist/_members/composition/ops/types.d.ts +1 -1
  123. package/dist/_members/composition/primitive-catalog/index.d.ts +4 -0
  124. package/dist/_members/composition/primitive-catalog/types.d.ts +1 -0
  125. package/dist/_members/composition/projection/behind-readiness.d.ts +3 -0
  126. package/dist/_members/composition/projection/brandKit.d.ts +6 -0
  127. package/dist/_members/composition/projection/composition-floor.d.ts +2 -1
  128. package/dist/_members/composition/scene/animation.d.ts +25 -0
  129. package/dist/_members/composition/scene/budget.d.ts +29 -0
  130. package/dist/_members/composition/scene/budgetPolicy.d.mts +13 -0
  131. package/dist/_members/composition/scene/clipRules.d.ts +119 -0
  132. package/dist/_members/composition/scene/constraints.d.ts +61 -0
  133. package/dist/_members/composition/scene/device.d.ts +21 -0
  134. package/dist/_members/composition/scene/examples.d.ts +11 -0
  135. package/dist/_members/composition/scene/index.d.ts +11 -0
  136. package/dist/_members/composition/scene/photographicRig.d.ts +242 -0
  137. package/dist/_members/composition/scene/preparedRender.d.ts +239 -0
  138. package/dist/_members/composition/scene/resolution.d.ts +178 -0
  139. package/dist/_members/composition/scene/schema.d.ts +351 -0
  140. package/dist/_members/composition/scene/videoSchedule.d.ts +43 -0
  141. package/dist/_members/composition/validators/brandKit.d.ts +1 -0
  142. package/dist/_members/composition/validators/render.d.ts +2 -0
  143. package/dist/_members/composition-ops/index.d.ts +45 -0
  144. package/dist/_members/composition-ops/opRecorder.d.ts +1 -1
  145. package/dist/_members/core/render/compositionRender.d.ts +41 -9
  146. package/dist/_members/core/render/index.d.ts +9 -2
  147. package/dist/_members/core/render/sceneCaptureIdentity.d.ts +14 -0
  148. package/dist/_members/core/render/sceneShotPlan.d.ts +142 -0
  149. package/dist/_members/core/render/sceneShotPreparation.d.ts +57 -0
  150. package/dist/_members/core/render/slabDescriptor.d.ts +21 -0
  151. package/dist/_members/core/render/trackProjection.d.ts +33 -0
  152. package/dist/_members/core/render/types.d.ts +142 -0
  153. package/dist/_members/errors/registry.d.ts +6 -0
  154. package/dist/_members/mcp/adapter.d.ts +5 -0
  155. package/dist/_members/mcp/generated/harness.d.ts +1 -1
  156. package/dist/_members/mcp/generated/sceneContract.d.ts +155 -0
  157. package/dist/_members/mcp/sceneShot.d.ts +38 -0
  158. package/dist/_members/mcp/sceneShotExample.d.ts +22 -0
  159. package/dist/_members/media-model/api.d.ts +1 -0
  160. package/dist/_members/media-model/media-kind.d.ts +1 -1
  161. package/dist/_members/media-model/testing/mediaStoreContract.d.ts +1 -1
  162. package/dist/_members/primitives/effects/index.d.ts +1 -0
  163. package/dist/_members/primitives/effects/stage-camera/config.d.ts +50 -0
  164. package/dist/_members/primitives/effects/stage-camera/index.d.ts +31 -0
  165. package/dist/_members/primitives/layouts/flex-layout/config.d.ts +73 -0
  166. package/dist/_members/primitives/layouts/flex-layout/index.d.ts +4 -0
  167. package/dist/_members/primitives/layouts/index.d.ts +1 -0
  168. package/dist/_members/primitives/layouts/picture-in-picture/config.d.ts +1 -1
  169. package/dist/_members/primitives/lib/frameScale.d.ts +4 -0
  170. package/dist/_members/primitives/lib/index.d.ts +1 -0
  171. package/dist/_members/primitives/lib/palette.d.ts +30 -30
  172. package/dist/_members/primitives/lib/text/contrast.d.ts +2 -1
  173. package/dist/_members/primitives/lib/text/spec.d.ts +0 -8
  174. package/dist/_members/primitives/lib/types.d.ts +2 -0
  175. package/dist/_members/primitives/scenes/device-showcase/config.d.ts +41 -0
  176. package/dist/_members/primitives/scenes/device-showcase/index.d.ts +36 -0
  177. package/dist/_members/primitives/scenes/hero-device-assemble/config.d.ts +1 -0
  178. package/dist/_members/primitives/scenes/hero-device-assemble/index.d.ts +2 -1
  179. package/dist/_members/primitives/scenes/index.d.ts +1 -0
  180. package/dist/_members/primitives/ui-blocks/badge-pop/config.d.ts +1 -1
  181. package/dist/_members/primitives/ui-blocks/device-mockup-zoom/config.d.ts +1 -0
  182. package/dist/_members/primitives/ui-blocks/device-mockup-zoom/index.d.ts +2 -1
  183. package/dist/_members/primitives/ui-blocks/terminal-simulator/config.d.ts +1 -1
  184. package/dist/_members/primitives/ui-blocks/toast-notification/config.d.ts +1 -1
  185. package/dist/_members/render-harness/cardAssetAdmission.d.ts.map +1 -1
  186. package/dist/_members/render-harness/cardRender.d.ts.map +1 -1
  187. package/dist/_members/render-harness/css-tree-subpaths.d.ts +11 -0
  188. package/dist/_members/render-harness/extractSegments.d.ts +1 -0
  189. package/dist/_members/render-harness/extractSegments.d.ts.map +1 -1
  190. package/dist/_members/render-harness/imageHeader.d.ts +14 -0
  191. package/dist/_members/render-harness/imageHeader.d.ts.map +1 -0
  192. package/dist/_members/render-harness/index.d.ts +6 -0
  193. package/dist/_members/render-harness/index.d.ts.map +1 -1
  194. package/dist/_members/render-harness/materializeSources.d.ts +46 -2
  195. package/dist/_members/render-harness/materializeSources.d.ts.map +1 -1
  196. package/dist/_members/render-harness/nodeScenePreparation.d.ts +97 -0
  197. package/dist/_members/render-harness/nodeScenePreparation.d.ts.map +1 -0
  198. package/dist/_members/render-harness/remotion-runtime.d.ts +16 -2
  199. package/dist/_members/render-harness/renderInputAssets.d.ts +1 -1
  200. package/dist/_members/render-harness/renderInputAssets.d.ts.map +1 -1
  201. package/dist/_members/render-harness/resolveAssets.d.ts +2 -2
  202. package/dist/_members/render-harness/resolveAssets.d.ts.map +1 -1
  203. package/dist/_members/render-harness/sceneShotAssets.d.ts +17 -0
  204. package/dist/_members/render-harness/sceneShotAssets.d.ts.map +1 -0
  205. package/dist/animate/preparation-facts.d.ts +1 -0
  206. package/dist/animate/preparation-facts.js +22 -0
  207. package/dist/animate.js +336 -1005
  208. package/dist/authoring/build-tools.js +7 -6
  209. package/dist/authoring/context.js +5 -4
  210. package/dist/authoring/prompts.js +1 -1
  211. package/dist/authoring/scene.d.ts +1 -0
  212. package/dist/authoring/scene.js +50 -0
  213. package/dist/authoring/tools.js +6 -5
  214. package/dist/authoring.js +49 -29
  215. package/dist/chunk-2L7PYYJK.js +23 -0
  216. package/dist/chunk-5DJ3IEJR.js +4424 -0
  217. package/dist/chunk-6BVVCRRD.js +26488 -0
  218. package/dist/{chunk-DQKSCHFC.js → chunk-6EWRIAZA.js} +187 -66
  219. package/dist/{chunk-YQ7FFW3G.js → chunk-6VWICHOZ.js} +26 -5
  220. package/dist/{chunk-C5SEPOWD.js → chunk-7AZLFN26.js} +1798 -702
  221. package/dist/{chunk-6ID2J64H.js → chunk-7W6ZIKWV.js} +162 -25
  222. package/dist/{chunk-RETQFTGY.js → chunk-A74FZCPY.js} +42 -5
  223. package/dist/chunk-BVA3537Z.js +63 -0
  224. package/dist/{chunk-DHKQ47Q6.js → chunk-DAARYRNT.js} +6 -1
  225. package/dist/chunk-DQLYWNLC.js +2063 -0
  226. package/dist/{chunk-HXKTZPRH.js → chunk-E7GIZEPS.js} +1 -1
  227. package/dist/chunk-EBIBLL7E.js +3619 -0
  228. package/dist/{chunk-M2TLRXEO.js → chunk-EXY4JJPH.js} +1 -1
  229. package/dist/{chunk-AWT3P5ZN.js → chunk-FVDFPJHT.js} +1 -1
  230. package/dist/{chunk-EMGFZ3PR.js → chunk-FWSJLHSV.js} +5 -1
  231. package/dist/{chunk-TMIBBD5E.js → chunk-I5QWBM3M.js} +280 -113
  232. package/dist/{chunk-IBMJ33EL.js → chunk-I5RX2QZT.js} +1 -1
  233. package/dist/{chunk-4FOOBSEE.js → chunk-J3DIUG24.js} +1 -0
  234. package/dist/{chunk-AD5HWBCE.js → chunk-JX53CVJV.js} +14 -5
  235. package/dist/{chunk-LNBVTDFR.js → chunk-L2AHS3AW.js} +1 -1
  236. package/dist/{chunk-BDMTUEEZ.js → chunk-LT4PWSR2.js} +2 -2
  237. package/dist/chunk-MH3UPKWR.js +3415 -0
  238. package/dist/{chunk-RFXR7ATW.js → chunk-MN6D3U5N.js} +13 -1
  239. package/dist/{chunk-IJ7PVJTK.js → chunk-ODUWCQI3.js} +3 -2
  240. package/dist/chunk-QAGGJHAF.js +16 -0
  241. package/dist/chunk-QS36DMFG.js +332 -0
  242. package/dist/{chunk-G5BFI32U.js → chunk-RAXHCRVV.js} +1 -1
  243. package/dist/{chunk-BIBYZJL3.js → chunk-RL5SO653.js} +3 -3
  244. package/dist/{chunk-ZSZABOE5.js → chunk-S5H6GH6M.js} +1 -1
  245. package/dist/{chunk-EEXCDFY5.js → chunk-SD25VQDE.js} +1 -1
  246. package/dist/chunk-TARKDVP6.js +430 -0
  247. package/dist/{chunk-XVBOSPZI.js → chunk-TBNTAQJA.js} +6 -6
  248. package/dist/{chunk-IHFREGFA.js → chunk-TCHPRGC5.js} +18 -2
  249. package/dist/{chunk-ZCAQHNPI.js → chunk-UKGNHZS4.js} +1 -1
  250. package/dist/{chunk-ZN6P3KOJ.js → chunk-UN6NY2E6.js} +3 -3
  251. package/dist/{chunk-WQSGVRFU.js → chunk-UY77TMKD.js} +31 -44
  252. package/dist/chunk-VKATKWWR.js +1462 -0
  253. package/dist/{chunk-U5LBYS3N.js → chunk-VLJOIUHL.js} +341 -274
  254. package/dist/{chunk-CX2IIQ4R.js → chunk-W2245F6G.js} +29 -29
  255. package/dist/{chunk-GEGRJKKO.js → chunk-W6B7BMLO.js} +1 -1
  256. package/dist/{chunk-XHUYWVYK.js → chunk-WBNFSG6T.js} +1 -1
  257. package/dist/chunk-WIHXXF3X.js +221 -0
  258. package/dist/{chunk-7KEUL3FI.js → chunk-XF2QMKRG.js} +9 -1
  259. package/dist/{chunk-HFC3MKXA.js → chunk-Y4HPN4BC.js} +1 -0
  260. package/dist/{chunk-D43J23QX.js → chunk-YFSBFCXO.js} +1018 -42
  261. package/dist/{chunk-KKEY5MIW.js → chunk-ZS3A3QUM.js} +16 -16
  262. package/dist/{chunk-LQUWKBST.js → chunk-ZVG446DJ.js} +139 -10
  263. package/dist/component-runtime/browserOptions.js +14 -3
  264. package/dist/component-runtime/contracts.js +35 -3
  265. package/dist/component-runtime/identity/browser.js +1 -1
  266. package/dist/component-runtime/identity.js +2 -2
  267. package/dist/component-runtime/importPolicy.js +2 -2
  268. package/dist/component-runtime/mediaResolution.d.ts +1 -0
  269. package/dist/component-runtime/mediaResolution.js +11 -0
  270. package/dist/component-runtime/render.js +38 -5
  271. package/dist/component-runtime.js +118 -14
  272. package/dist/compose.js +192 -33
  273. package/dist/composition/fonts.js +64 -67
  274. package/dist/composition/graphics-plan.js +3 -2
  275. package/dist/composition/ops.js +4 -3
  276. package/dist/composition/primitive-catalog.js +3 -1
  277. package/dist/composition/projection.js +10 -3
  278. package/dist/composition/scene/budget-policy.d.ts +1 -0
  279. package/dist/composition/scene/budget-policy.js +17 -0
  280. package/dist/composition/scene/schema.json +2757 -0
  281. package/dist/composition/scene.d.ts +1 -0
  282. package/dist/composition/scene.js +275 -0
  283. package/dist/composition/validators.js +3 -2
  284. package/dist/composition-ops.js +103 -13
  285. package/dist/composition.js +12 -5
  286. package/dist/{context-4GSDBFT3.js → context-IUPQ54V4.js} +5 -4
  287. package/dist/core/render.js +54 -3
  288. package/dist/core/select.js +2 -2
  289. package/dist/core.js +6 -3
  290. package/dist/director/ai-sdk.js +3 -3
  291. package/dist/director/domain.js +2 -2
  292. package/dist/director.js +3 -3
  293. package/dist/errors.js +1 -1
  294. package/dist/mcp/adapter.js +4 -4
  295. package/dist/mcp/core.js +1 -1
  296. package/dist/mcp/curation.js +1 -1
  297. package/dist/mcp/server.js +4 -4
  298. package/dist/mcp.js +4 -4
  299. package/dist/media-model/media-kind.js +2 -0
  300. package/dist/media-model/testing.js +16 -11
  301. package/dist/media-model.js +1 -1
  302. package/dist/nle-export.js +1 -27
  303. package/dist/primitives/backgrounds.js +2 -2
  304. package/dist/primitives/captions.js +21 -11
  305. package/dist/primitives/effects.js +14 -4
  306. package/dist/primitives/graphics.json +3 -0
  307. package/dist/primitives/hero-text.js +4 -3
  308. package/dist/primitives/layouts.js +9 -2
  309. package/dist/primitives/lib/frameScale.d.ts +1 -0
  310. package/dist/primitives/lib/frameScale.js +11 -0
  311. package/dist/primitives/lib/index.js +18 -10
  312. package/dist/primitives/lib/palette.js +1 -1
  313. package/dist/primitives/lib/text/contrast.js +2 -1
  314. package/dist/primitives/lib/text/typeface.js +1 -1
  315. package/dist/primitives/lib.js +18 -10
  316. package/dist/primitives/params-required.json +3 -0
  317. package/dist/primitives/scenes.js +12 -3
  318. package/dist/primitives/text.js +5 -5
  319. package/dist/primitives/transitions.js +2 -2
  320. package/dist/primitives/ui-blocks.js +7 -6
  321. package/dist/primitives/version-map.json +5 -2
  322. package/dist/primitives.js +23 -16
  323. package/dist/render-harness/cardAssetAdmission.js +2 -2
  324. package/dist/render-harness/renderInputAssets.js +2 -2
  325. package/dist/render-harness.js +874 -124
  326. package/package.json +48 -14
  327. package/dist/_members/animate/three/drei.d.ts +0 -6
  328. package/dist/_members/component-runtime/fixtures/webgpuTiledRefraction.d.ts +0 -9
  329. package/dist/_members/component-runtime/fixtures/webgpuTiledRefraction.d.ts.map +0 -1
  330. package/dist/chunk-5OTQ7M43.js +0 -718
  331. package/dist/chunk-7REW2MW3.js +0 -202
  332. package/dist/chunk-CGHTMZNC.js +0 -6
  333. package/dist/chunk-KVMVYRXX.js +0 -9527
  334. package/dist/chunk-WYQX4QIK.js +0 -544
  335. /package/dist/{chunk-H5LMJKZU.js → chunk-JVDUSOYG.js} +0 -0
@@ -0,0 +1,3415 @@
1
+ import {
2
+ SCENE_CONTEXT_GL_BUDGET_BYTES,
3
+ SCENE_MAX_TRIANGLES,
4
+ SCENE_SURFACE_TEXTURE_BUDGET_BYTES
5
+ } from "./chunk-2L7PYYJK.js";
6
+
7
+ // ../composition/src/scene/schema.ts
8
+ import { z } from "zod";
9
+ var SCENE_SHOT_SPEC_VERSION = 2;
10
+ var SCENE_MAX_LAYERS = 32;
11
+ var SCENE_MAX_GROUP_DEPTH = 4;
12
+ var SCENE_MAX_KEYFRAMES_PER_CHANNEL = 256;
13
+ var SCENE_MAX_KEYFRAMES_PER_SHOT = 2048;
14
+ var SCENE_MAX_SURFACES = 32;
15
+ var SCENE_FOV_MIN = Math.PI / 10;
16
+ var SCENE_FOV_MAX = 4 * Math.PI / 9;
17
+ var SCENE_LAYER_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
18
+ var SCENE_ASSET_SLOT_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
19
+ var SCENE_BRAND_COLOR_TOKENS = [
20
+ "background",
21
+ "surface",
22
+ "text",
23
+ "textMuted",
24
+ "accent",
25
+ "accentStrong",
26
+ "accentSoft",
27
+ "success",
28
+ "warning",
29
+ "danger"
30
+ ];
31
+ var SCENE_BRAND_FONT_TOKENS = ["heading", "body", "caption"];
32
+ var COLOR_REFERENCE_VALUES = SCENE_BRAND_COLOR_TOKENS.map(
33
+ (token) => `$brand:colors.${token}`
34
+ );
35
+ var FONT_REFERENCE_VALUES = SCENE_BRAND_FONT_TOKENS.map(
36
+ (token) => `$brand:fonts.${token}`
37
+ );
38
+ var SceneHexColor = z.string().regex(/^#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, "Expected #rrggbb or #rrggbbaa.");
39
+ var SceneBrandColorRef = z.enum(COLOR_REFERENCE_VALUES);
40
+ var SceneColor = z.union([SceneHexColor, SceneBrandColorRef]);
41
+ var SceneBrandFontRef = z.enum(FONT_REFERENCE_VALUES);
42
+ var SCENE_FONT_FAMILY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 _.\-]*$/;
43
+ var SceneAdmittedFontFamily = z.string().min(1).max(128).regex(SCENE_FONT_FAMILY_PATTERN);
44
+ var SceneFontFamily = z.union([SceneAdmittedFontFamily, SceneBrandFontRef]);
45
+ var SceneLayerId = z.string().min(1).max(64).regex(SCENE_LAYER_ID_PATTERN);
46
+ var SceneAssetSlot = z.string().min(1).max(64).regex(
47
+ SCENE_ASSET_SLOT_PATTERN,
48
+ 'Expected a slot name: a letter or underscore followed by letters, digits, "_" or "-".'
49
+ ).describe("Image asset slot name bound by the clip assets map; never a URL.");
50
+ var SceneUnit = z.number().min(0).max(1);
51
+ var SceneVec3Schema = z.tuple([z.number(), z.number(), z.number()]);
52
+ var JsonValueSchema = z.lazy(
53
+ () => z.union([
54
+ z.null(),
55
+ z.boolean(),
56
+ z.number(),
57
+ z.string(),
58
+ z.array(JsonValueSchema),
59
+ z.record(z.string(), JsonValueSchema)
60
+ ])
61
+ );
62
+ var SceneReferenceSizeV2 = z.object({
63
+ width: z.number().int().min(16).max(8192),
64
+ height: z.number().int().min(16).max(8192)
65
+ }).strict();
66
+ var SceneCameraInputV2 = z.object({
67
+ position: SceneVec3Schema.optional(),
68
+ rotation: SceneVec3Schema.optional(),
69
+ fov: z.number().min(SCENE_FOV_MIN).max(SCENE_FOV_MAX).optional()
70
+ }).strict();
71
+ var SceneCameraV2 = z.object({
72
+ position: SceneVec3Schema,
73
+ rotation: SceneVec3Schema,
74
+ fov: z.number().min(SCENE_FOV_MIN).max(SCENE_FOV_MAX)
75
+ }).strict();
76
+ var SceneTransformV2 = z.object({
77
+ position: SceneVec3Schema.default([0, 0, 0]),
78
+ rotation: SceneVec3Schema.default([0, 0, 0]),
79
+ scale: z.number().positive().default(1)
80
+ }).strict().default({ position: [0, 0, 0], rotation: [0, 0, 0], scale: 1 });
81
+ var SceneGradientStopV2 = z.object({ at: SceneUnit, color: SceneColor }).strict();
82
+ var SceneGradientStopsV2 = z.array(SceneGradientStopV2).min(2).max(8).superRefine((stops, ctx) => {
83
+ for (let index = 1; index < stops.length; index += 1) {
84
+ if (stops[index].at <= stops[index - 1].at) {
85
+ ctx.addIssue({
86
+ code: "custom",
87
+ path: [index, "at"],
88
+ message: "Gradient stops must be strictly increasing."
89
+ });
90
+ }
91
+ }
92
+ });
93
+ var SceneBackdropV2 = z.discriminatedUnion("kind", [
94
+ z.object({ kind: z.literal("transparent") }).strict(),
95
+ z.object({ kind: z.literal("solid"), color: SceneColor }).strict(),
96
+ z.object({
97
+ kind: z.literal("linear"),
98
+ angle: z.number().default(0),
99
+ stops: SceneGradientStopsV2
100
+ }).strict(),
101
+ z.object({
102
+ kind: z.literal("radial"),
103
+ at: z.tuple([SceneUnit, SceneUnit]).default([0.5, 0.5]),
104
+ stops: SceneGradientStopsV2
105
+ }).strict()
106
+ ]);
107
+ var SceneImageSurfaceV2 = z.object({ source: z.literal("image"), assetSlot: SceneAssetSlot }).strict();
108
+ var SceneColorSurfaceV2 = z.object({ source: z.literal("color"), color: SceneColor }).strict();
109
+ var SceneComponentSurfaceV2 = z.object({
110
+ source: z.literal("component"),
111
+ component: z.object({
112
+ version: z.literal(1),
113
+ componentId: z.string().min(1).max(256),
114
+ sourceVersionId: z.string().regex(/^[a-f0-9]{64}$/)
115
+ }).strict(),
116
+ captureFrame: z.number().int().min(0),
117
+ layout: z.object({
118
+ width: z.number().int().positive(),
119
+ height: z.number().int().positive()
120
+ }).strict(),
121
+ bakeScale: z.union([z.literal(2), z.literal(3)]).optional().describe("Optional authored scale. Preparation defaults omission to 2 and may auto-raise it to 3."),
122
+ params: z.record(z.string(), JsonValueSchema).optional()
123
+ }).strict();
124
+ var SceneVideoSurfaceV2 = z.object({
125
+ source: z.literal("video"),
126
+ mediaId: z.string().min(1).max(256),
127
+ startFrame: z.number().int().min(0).default(0),
128
+ endFrame: z.number().int().positive().optional()
129
+ }).strict().superRefine((surface, ctx) => {
130
+ if (surface.endFrame !== void 0 && surface.endFrame <= surface.startFrame) {
131
+ ctx.addIssue({
132
+ code: "custom",
133
+ path: ["endFrame"],
134
+ message: "endFrame must be greater than startFrame (the exclusive source-frame trim)."
135
+ });
136
+ }
137
+ });
138
+ var SceneSurfaceBindingV2 = z.discriminatedUnion("source", [
139
+ SceneImageSurfaceV2,
140
+ SceneColorSurfaceV2,
141
+ SceneComponentSurfaceV2,
142
+ SceneVideoSurfaceV2
143
+ ]);
144
+ var PHYSICAL_ONLY_FIELDS = [
145
+ "roughness",
146
+ "metalness",
147
+ "clearcoat",
148
+ "clearcoatRoughness",
149
+ "anisotropy",
150
+ "anisotropyRotation",
151
+ "ior",
152
+ "reflectivity"
153
+ ];
154
+ var SceneMaterialOverrideV2 = z.object({
155
+ role: z.string().min(1).max(64),
156
+ type: z.enum(["physical", "unlit"]),
157
+ color: SceneColor.optional(),
158
+ roughness: SceneUnit.optional(),
159
+ metalness: SceneUnit.optional(),
160
+ clearcoat: SceneUnit.optional(),
161
+ clearcoatRoughness: SceneUnit.optional(),
162
+ anisotropy: SceneUnit.optional(),
163
+ anisotropyRotation: z.number().optional(),
164
+ ior: z.number().min(1).max(2.333).optional(),
165
+ reflectivity: SceneUnit.optional()
166
+ }).strict().superRefine((material, ctx) => {
167
+ if (material.type !== "unlit") return;
168
+ for (const field of PHYSICAL_ONLY_FIELDS) {
169
+ if (material[field] !== void 0) {
170
+ ctx.addIssue({
171
+ code: "custom",
172
+ path: [field],
173
+ message: `${field} is physical-only and cannot be set on an unlit material.`
174
+ });
175
+ }
176
+ }
177
+ });
178
+ var OptionalMaterials = z.array(SceneMaterialOverrideV2).max(16).optional();
179
+ var OptionalSurfaces = z.record(z.string(), SceneSurfaceBindingV2).optional();
180
+ var SceneTextRunV2 = z.object({
181
+ text: z.string().min(1).max(512).regex(/^[^\r\n]+$/, "A run may not contain a line break."),
182
+ color: SceneColor.optional(),
183
+ emphasis: z.boolean().optional()
184
+ }).strict();
185
+ var SceneTextLineV2 = z.object({ runs: z.array(SceneTextRunV2).min(1).max(16) }).strict();
186
+ var textFields = {
187
+ id: SceneLayerId,
188
+ kind: z.literal("text"),
189
+ opacity: SceneUnit.default(1),
190
+ lines: z.array(SceneTextLineV2).min(1).max(24),
191
+ fontFamily: SceneFontFamily,
192
+ fontWeight: z.number().int().min(100).max(900),
193
+ fontStyle: z.enum(["normal", "italic"]),
194
+ fontSize: z.number().min(8).max(800),
195
+ lineHeight: z.number().min(0.6).max(3).default(1.2),
196
+ letterSpacing: z.number().min(-20).max(40).default(0),
197
+ align: z.enum(["left", "center", "right"]).default("center"),
198
+ maxWidth: z.number().positive().optional(),
199
+ color: SceneColor
200
+ };
201
+ var SceneGroupV2 = z.lazy(
202
+ () => z.object({
203
+ kind: z.literal("group"),
204
+ id: SceneLayerId,
205
+ transform: SceneTransformV2,
206
+ opacity: SceneUnit.default(1),
207
+ children: z.array(SceneObjectV2).min(1).max(16)
208
+ }).strict()
209
+ );
210
+ var SceneLaptopV2 = z.object({
211
+ kind: z.literal("laptop"),
212
+ id: SceneLayerId,
213
+ transform: SceneTransformV2,
214
+ opacity: SceneUnit.default(1),
215
+ width: z.number().min(200).max(4e3),
216
+ openAngle: z.number().min(0).max(Math.PI),
217
+ materials: OptionalMaterials,
218
+ surfaces: OptionalSurfaces
219
+ }).strict();
220
+ var ScenePhoneV2 = z.object({
221
+ kind: z.literal("phone"),
222
+ id: SceneLayerId,
223
+ transform: SceneTransformV2,
224
+ opacity: SceneUnit.default(1),
225
+ width: z.number().min(80).max(2e3),
226
+ materials: OptionalMaterials,
227
+ surfaces: OptionalSurfaces
228
+ }).strict();
229
+ var SceneSlabV2 = z.object({
230
+ kind: z.literal("slab"),
231
+ id: SceneLayerId,
232
+ transform: SceneTransformV2,
233
+ opacity: SceneUnit.default(1),
234
+ width: z.number().min(40).max(8e3),
235
+ height: z.number().min(40).max(8e3),
236
+ thickness: z.number().min(4).max(200).default(32),
237
+ cornerRadius: z.number().min(0).default(32),
238
+ edgeRadius: z.number().min(0).default(15),
239
+ finish: z.enum(["satin", "brushed"]).default("satin"),
240
+ materials: OptionalMaterials,
241
+ surfaces: OptionalSurfaces
242
+ }).strict().superRefine((slab, ctx) => {
243
+ if (slab.cornerRadius > Math.min(slab.width, slab.height) / 2) {
244
+ ctx.addIssue({
245
+ code: "custom",
246
+ path: ["cornerRadius"],
247
+ message: "cornerRadius cannot exceed half the shorter face side."
248
+ });
249
+ }
250
+ if (slab.edgeRadius > slab.thickness / 2) {
251
+ ctx.addIssue({
252
+ code: "custom",
253
+ path: ["edgeRadius"],
254
+ message: "edgeRadius cannot exceed thickness/2."
255
+ });
256
+ }
257
+ });
258
+ var ScenePageV2 = z.object({
259
+ kind: z.literal("page"),
260
+ id: SceneLayerId,
261
+ transform: SceneTransformV2,
262
+ opacity: SceneUnit.default(1),
263
+ width: z.number().min(40).max(8e3),
264
+ height: z.number().min(40).max(8e3),
265
+ fit: z.enum(["contain", "cover"]).default("contain"),
266
+ transparency: z.enum(["straight", "premultiplied", "opaque"]).default("straight"),
267
+ depthWrite: z.boolean().default(true),
268
+ backface: z.enum(["hidden", "visible"]).default("hidden"),
269
+ materials: OptionalMaterials,
270
+ surfaces: z.record(z.string(), SceneSurfaceBindingV2)
271
+ }).strict();
272
+ var SceneWorldTextV2 = z.object({ ...textFields, space: z.literal("world"), transform: SceneTransformV2 }).strict();
273
+ var SceneScreenTextV2 = z.object({
274
+ ...textFields,
275
+ space: z.literal("screen"),
276
+ x: z.number(),
277
+ y: z.number(),
278
+ rotation: z.number().default(0),
279
+ scale: z.number().positive().default(1)
280
+ }).strict();
281
+ var SceneObjectV2 = z.lazy(
282
+ () => z.union([
283
+ SceneGroupV2,
284
+ SceneLaptopV2,
285
+ ScenePhoneV2,
286
+ SceneSlabV2,
287
+ ScenePageV2,
288
+ SceneWorldTextV2,
289
+ SceneScreenTextV2
290
+ ])
291
+ );
292
+ var SceneEasingV2 = z.discriminatedUnion("kind", [
293
+ z.object({ kind: z.literal("linear") }).strict(),
294
+ z.object({ kind: z.literal("hold") }).strict(),
295
+ z.object({
296
+ kind: z.literal("bezier"),
297
+ x1: SceneUnit,
298
+ y1: z.number().min(-4).max(5),
299
+ x2: SceneUnit,
300
+ y2: z.number().min(-4).max(5)
301
+ }).strict()
302
+ ]);
303
+ var SceneKeyframeV2 = z.object({ at: SceneUnit, value: z.number(), easing: SceneEasingV2.optional() }).strict();
304
+ var SceneKeyframesV2 = z.array(SceneKeyframeV2).min(1).max(SCENE_MAX_KEYFRAMES_PER_CHANNEL).superRefine((keyframes, ctx) => {
305
+ if (keyframes[0]?.easing !== void 0) {
306
+ ctx.addIssue({
307
+ code: "custom",
308
+ path: [0, "easing"],
309
+ message: "The first keyframe has no arriving segment; easing is not authorable here."
310
+ });
311
+ }
312
+ for (let index = 1; index < keyframes.length; index += 1) {
313
+ if (keyframes[index].at <= keyframes[index - 1].at) {
314
+ ctx.addIssue({
315
+ code: "custom",
316
+ path: [index, "at"],
317
+ message: "Keyframe times must be strictly increasing."
318
+ });
319
+ }
320
+ }
321
+ });
322
+ var SceneChannelTargetV2 = z.discriminatedUnion(
323
+ "kind",
324
+ [
325
+ z.object({ kind: z.literal("camera") }).strict(),
326
+ z.object({ kind: z.literal("layer"), layerId: SceneLayerId }).strict(),
327
+ z.object({ kind: z.literal("environment") }).strict(),
328
+ z.object({ kind: z.literal("focus") }).strict()
329
+ ]
330
+ );
331
+ var SceneChannelV2 = z.object({
332
+ target: SceneChannelTargetV2,
333
+ property: z.string().min(1).max(64),
334
+ keyframes: SceneKeyframesV2
335
+ }).strict();
336
+ var SCENE_CAMERA_PROPERTIES = [
337
+ "positionX",
338
+ "positionY",
339
+ "positionZ",
340
+ "rotationX",
341
+ "rotationY",
342
+ "rotationZ",
343
+ "fov"
344
+ ];
345
+ var SCENE_WORLD_OBJECT_PROPERTIES = [
346
+ "positionX",
347
+ "positionY",
348
+ "positionZ",
349
+ "rotationX",
350
+ "rotationY",
351
+ "rotationZ",
352
+ "scale",
353
+ "opacity"
354
+ ];
355
+ var SCENE_LAPTOP_PROPERTIES = [
356
+ ...SCENE_WORLD_OBJECT_PROPERTIES,
357
+ "openAngle"
358
+ ];
359
+ var SCENE_SCREEN_TEXT_PROPERTIES = ["x", "y", "rotation", "scale", "opacity"];
360
+ var SCENE_ENVIRONMENT_PROPERTIES = ["intensity"];
361
+ var SCENE_FOCUS_PROPERTIES = ["distance", "range", "maxBlur"];
362
+ var SCENE_CHANNEL_PROPERTIES = [
363
+ .../* @__PURE__ */ new Set([
364
+ ...SCENE_CAMERA_PROPERTIES,
365
+ ...SCENE_WORLD_OBJECT_PROPERTIES,
366
+ ...SCENE_LAPTOP_PROPERTIES,
367
+ ...SCENE_SCREEN_TEXT_PROPERTIES,
368
+ ...SCENE_ENVIRONMENT_PROPERTIES,
369
+ ...SCENE_FOCUS_PROPERTIES
370
+ ])
371
+ ];
372
+ var SceneFocusV2 = z.object({
373
+ distance: z.number().positive(),
374
+ range: z.number().positive(),
375
+ maxBlur: z.number().min(0).max(64)
376
+ }).strict();
377
+ var ScenePostV2 = z.object({
378
+ grain: z.number().min(0).max(0.5).default(0),
379
+ vignette: SceneUnit.default(0),
380
+ focus: SceneFocusV2.nullable().default(null)
381
+ }).strict().default({ grain: 0, vignette: 0, focus: null });
382
+ var SceneGeneratedStudioRigV2 = z.object({
383
+ kind: z.literal("studio").optional(),
384
+ lightingProfile: z.enum(["studio", "studio-edge"]).default("studio"),
385
+ preset: z.enum(["soft", "key", "rim"]).default("soft"),
386
+ intensity: z.number().min(0).default(1),
387
+ floorY: z.number().default(-420),
388
+ shadowOpacity: SceneUnit.default(0.5)
389
+ }).strict();
390
+ var ScenePhotographicStudioRigV1 = z.object({ kind: z.literal("photographic-studio-v1") }).strict();
391
+ var SceneRigV2 = z.union([
392
+ SceneGeneratedStudioRigV2,
393
+ ScenePhotographicStudioRigV1
394
+ ]);
395
+ var SceneConstraintV2 = z.object({
396
+ kind: z.literal("minSeparation"),
397
+ objects: z.array(SceneLayerId).min(2),
398
+ gap: z.number().positive()
399
+ }).strict();
400
+ function flattenSceneObjects(nodes, path = ["layers"], groupDepth = 0, output = []) {
401
+ nodes.forEach((node, index) => {
402
+ const nodePath = [...path, index];
403
+ const nodeGroupDepth = node.kind === "group" ? groupDepth + 1 : groupDepth;
404
+ output.push({ node, path: nodePath, groupDepth: nodeGroupDepth });
405
+ if (node.kind === "group") {
406
+ flattenSceneObjects(node.children, [...nodePath, "children"], nodeGroupDepth, output);
407
+ }
408
+ });
409
+ return output;
410
+ }
411
+ var SCENE_PRESET_SLOTS = Object.freeze({
412
+ laptop: ["screen"],
413
+ phone: ["screen"],
414
+ slab: ["front"],
415
+ page: ["content"]
416
+ });
417
+ var SCENE_PRESET_MATERIAL_ROLES = Object.freeze({
418
+ laptop: ["base", "lid", "keyboard", "trackpad", "bezel", "screen", "glass"],
419
+ phone: ["shell", "screen", "glass"],
420
+ slab: ["body", "face"],
421
+ page: ["base"]
422
+ });
423
+ function allowedLayerProperties(node) {
424
+ if (node.kind === "text" && node.space === "screen") return SCENE_SCREEN_TEXT_PROPERTIES;
425
+ if (node.kind === "laptop") return SCENE_LAPTOP_PROPERTIES;
426
+ return SCENE_WORLD_OBJECT_PROPERTIES;
427
+ }
428
+ function channelBound(target, property) {
429
+ if (target === "camera" && property === "fov") return { min: SCENE_FOV_MIN, max: SCENE_FOV_MAX };
430
+ if (target === "layer" && property === "scale") return { exclusiveMin: 0 };
431
+ if (target === "layer" && property === "opacity") return { min: 0, max: 1 };
432
+ if (target === "layer" && property === "openAngle") return { min: 0, max: Math.PI };
433
+ if (target === "environment" && property === "intensity") return { min: 0 };
434
+ if (target === "focus" && property === "maxBlur") return { min: 0, max: 64 };
435
+ if (target === "focus") return { exclusiveMin: 0 };
436
+ return {};
437
+ }
438
+ function violatesBound(bound, value) {
439
+ return bound.min !== void 0 && value < bound.min || bound.exclusiveMin !== void 0 && value <= bound.exclusiveMin || bound.max !== void 0 && value > bound.max;
440
+ }
441
+ var SceneShotSpecV2Base = z.object({
442
+ version: z.literal(SCENE_SHOT_SPEC_VERSION),
443
+ referenceSize: SceneReferenceSizeV2,
444
+ camera: SceneCameraInputV2.optional(),
445
+ environment: SceneRigV2.default({
446
+ lightingProfile: "studio",
447
+ preset: "soft",
448
+ intensity: 1,
449
+ floorY: -420,
450
+ shadowOpacity: 0.5
451
+ }),
452
+ backdrop: SceneBackdropV2.default({ kind: "transparent" }),
453
+ layers: z.array(SceneObjectV2).min(1),
454
+ animation: z.array(SceneChannelV2).default([]),
455
+ post: ScenePostV2,
456
+ constraints: z.array(SceneConstraintV2).default([])
457
+ }).strict().transform((spec2) => {
458
+ const fov = spec2.camera?.fov ?? 40 * Math.PI / 180;
459
+ return {
460
+ ...spec2,
461
+ camera: {
462
+ position: spec2.camera?.position ?? [0, 0, spec2.referenceSize.height / (2 * Math.tan(fov / 2))],
463
+ rotation: spec2.camera?.rotation ?? [0, 0, 0],
464
+ fov
465
+ }
466
+ };
467
+ });
468
+ var SceneShotSpecV2Schema = SceneShotSpecV2Base.superRefine(
469
+ (spec2, ctx) => {
470
+ const flat = flattenSceneObjects(spec2.layers);
471
+ if (flat.length > SCENE_MAX_LAYERS) {
472
+ ctx.addIssue({
473
+ code: "custom",
474
+ path: ["layers"],
475
+ message: `The flattened scene has ${flat.length} layers; the limit is ${SCENE_MAX_LAYERS}.`
476
+ });
477
+ }
478
+ const nodesById = /* @__PURE__ */ new Map();
479
+ let surfaceCount = 0;
480
+ for (const entry of flat) {
481
+ const { node, path, groupDepth } = entry;
482
+ if (groupDepth > SCENE_MAX_GROUP_DEPTH) {
483
+ ctx.addIssue({
484
+ code: "custom",
485
+ path,
486
+ message: `Group depth ${groupDepth} exceeds the limit ${SCENE_MAX_GROUP_DEPTH}.`
487
+ });
488
+ }
489
+ if (nodesById.has(node.id)) {
490
+ ctx.addIssue({
491
+ code: "custom",
492
+ path: [...path, "id"],
493
+ message: `Duplicate layer id "${node.id}".`
494
+ });
495
+ } else {
496
+ nodesById.set(node.id, node);
497
+ }
498
+ if ("surfaces" in node && node.surfaces !== void 0) {
499
+ const preset = node.kind;
500
+ const legalSlots = SCENE_PRESET_SLOTS[preset] ?? [];
501
+ for (const slot of Object.keys(node.surfaces)) {
502
+ surfaceCount += 1;
503
+ if (!legalSlots.includes(slot)) {
504
+ ctx.addIssue({
505
+ code: "custom",
506
+ path: [...path, "surfaces", slot],
507
+ message: `Unknown ${node.kind} surface slot "${slot}"; legal slots: ${legalSlots.join(", ") || "(none)"}.`
508
+ });
509
+ }
510
+ }
511
+ if (node.kind === "page" && node.surfaces.content === void 0) {
512
+ ctx.addIssue({
513
+ code: "custom",
514
+ path: [...path, "surfaces", "content"],
515
+ message: "A page requires its content surface."
516
+ });
517
+ }
518
+ }
519
+ if ("materials" in node && node.materials !== void 0) {
520
+ const preset = node.kind;
521
+ const legalRoles = SCENE_PRESET_MATERIAL_ROLES[preset] ?? [];
522
+ const seenRoles = /* @__PURE__ */ new Set();
523
+ node.materials.forEach((material, index) => {
524
+ if (!legalRoles.includes(material.role)) {
525
+ ctx.addIssue({
526
+ code: "custom",
527
+ path: [...path, "materials", index, "role"],
528
+ message: `Unknown ${node.kind} material role "${material.role}"; legal roles: ${legalRoles.join(", ")}.`
529
+ });
530
+ }
531
+ if (seenRoles.has(material.role)) {
532
+ ctx.addIssue({
533
+ code: "custom",
534
+ path: [...path, "materials", index, "role"],
535
+ message: `Duplicate material override for role "${material.role}".`
536
+ });
537
+ }
538
+ seenRoles.add(material.role);
539
+ });
540
+ }
541
+ }
542
+ if (surfaceCount > SCENE_MAX_SURFACES) {
543
+ ctx.addIssue({
544
+ code: "custom",
545
+ path: ["layers"],
546
+ message: `The flattened scene binds ${surfaceCount} surfaces; the limit is ${SCENE_MAX_SURFACES}.`
547
+ });
548
+ }
549
+ const channelKeys = /* @__PURE__ */ new Set();
550
+ let keyframeCount = 0;
551
+ spec2.animation.forEach((channel, index) => {
552
+ keyframeCount += channel.keyframes.length;
553
+ let allowed;
554
+ let targetNode;
555
+ if (channel.target.kind === "camera") {
556
+ allowed = SCENE_CAMERA_PROPERTIES;
557
+ } else if (channel.target.kind === "environment") {
558
+ allowed = spec2.environment.kind === "photographic-studio-v1" ? [] : SCENE_ENVIRONMENT_PROPERTIES;
559
+ } else if (channel.target.kind === "focus") {
560
+ allowed = SCENE_FOCUS_PROPERTIES;
561
+ if (spec2.post.focus === null) {
562
+ ctx.addIssue({
563
+ code: "custom",
564
+ path: ["animation", index, "target"],
565
+ message: "A focus channel requires post.focus."
566
+ });
567
+ }
568
+ } else {
569
+ targetNode = nodesById.get(channel.target.layerId);
570
+ allowed = targetNode === void 0 ? [] : allowedLayerProperties(targetNode);
571
+ if (targetNode === void 0) {
572
+ ctx.addIssue({
573
+ code: "custom",
574
+ path: ["animation", index, "target", "layerId"],
575
+ message: `Unknown layer "${channel.target.layerId}".`
576
+ });
577
+ }
578
+ }
579
+ if (!allowed.includes(channel.property)) {
580
+ ctx.addIssue({
581
+ code: "custom",
582
+ path: ["animation", index, "property"],
583
+ message: `"${channel.property}" cannot be animated on this target; legal properties: ${allowed.join(", ") || "(none)"}.`
584
+ });
585
+ }
586
+ const targetKey = channel.target.kind === "layer" ? `layer:${channel.target.layerId}` : channel.target.kind;
587
+ const channelKey2 = `${targetKey}|${channel.property}`;
588
+ if (channelKeys.has(channelKey2)) {
589
+ ctx.addIssue({
590
+ code: "custom",
591
+ path: ["animation", index],
592
+ message: `Duplicate channel for ${targetKey}.${channel.property}.`
593
+ });
594
+ }
595
+ channelKeys.add(channelKey2);
596
+ const bound = channelBound(channel.target.kind, channel.property);
597
+ channel.keyframes.forEach((keyframe, keyframeIndex) => {
598
+ if (violatesBound(bound, keyframe.value)) {
599
+ ctx.addIssue({
600
+ code: "custom",
601
+ path: ["animation", index, "keyframes", keyframeIndex, "value"],
602
+ message: `${channel.property} is outside its authored bounds.`
603
+ });
604
+ }
605
+ });
606
+ });
607
+ if (keyframeCount > SCENE_MAX_KEYFRAMES_PER_SHOT) {
608
+ ctx.addIssue({
609
+ code: "custom",
610
+ path: ["animation"],
611
+ message: `The shot has ${keyframeCount} keyframes; the limit is ${SCENE_MAX_KEYFRAMES_PER_SHOT}.`
612
+ });
613
+ }
614
+ spec2.constraints.forEach((constraint, constraintIndex) => {
615
+ const seenObjects = /* @__PURE__ */ new Set();
616
+ constraint.objects.forEach((objectId, objectIndex) => {
617
+ if (!nodesById.has(objectId)) {
618
+ ctx.addIssue({
619
+ code: "custom",
620
+ path: ["constraints", constraintIndex, "objects", objectIndex],
621
+ message: `Unknown constrained object "${objectId}".`
622
+ });
623
+ }
624
+ if (seenObjects.has(objectId)) {
625
+ ctx.addIssue({
626
+ code: "custom",
627
+ path: ["constraints", constraintIndex, "objects", objectIndex],
628
+ message: `Duplicate constrained object "${objectId}".`
629
+ });
630
+ }
631
+ seenObjects.add(objectId);
632
+ });
633
+ });
634
+ }
635
+ );
636
+ var SCENE_OBJECT_LIGHT_FIELDS = [
637
+ "light",
638
+ "lights",
639
+ "lighting",
640
+ "lightingProfile",
641
+ "intensity",
642
+ "lightIntensity",
643
+ "lightPosition",
644
+ "lightColor",
645
+ "lightColour",
646
+ "slabEdgeLighting",
647
+ "slabEdgeBoost",
648
+ "slabSideLighting",
649
+ "slabSideIntensity",
650
+ "edgeLight",
651
+ "edgeLighting",
652
+ "rimLight",
653
+ "castShadow",
654
+ "shadowOpacity"
655
+ ];
656
+ function throwRefusal(code, path, message, input) {
657
+ throw new z.ZodError([{ code: "custom", path, message: `${code}: ${message}`, input }]);
658
+ }
659
+ function preflightObjectLightFields(value, path = ["layers"]) {
660
+ if (!Array.isArray(value)) return;
661
+ value.forEach((candidate, index) => {
662
+ if (candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) return;
663
+ const node = candidate;
664
+ for (const field of SCENE_OBJECT_LIGHT_FIELDS) {
665
+ if (Object.prototype.hasOwnProperty.call(node, field)) {
666
+ throwRefusal(
667
+ "scene_object_light_field_unsupported",
668
+ [...path, index, field],
669
+ `No object carries "${field}". Choose a shot-level environment lighting profile.`,
670
+ node[field]
671
+ );
672
+ }
673
+ }
674
+ if (node.kind === "group") {
675
+ preflightObjectLightFields(node.children, [...path, index, "children"]);
676
+ }
677
+ });
678
+ }
679
+ function recordAt(value) {
680
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
681
+ }
682
+ function rejectNonFiniteNumbers(value, path = []) {
683
+ if (typeof value === "number") {
684
+ if (!Number.isFinite(value)) {
685
+ throw new z.ZodError([
686
+ {
687
+ code: "custom",
688
+ path,
689
+ message: "Expected a finite number.",
690
+ input: value
691
+ }
692
+ ]);
693
+ }
694
+ return;
695
+ }
696
+ if (Array.isArray(value)) {
697
+ value.forEach((entry, index) => rejectNonFiniteNumbers(entry, [...path, index]));
698
+ return;
699
+ }
700
+ const record2 = recordAt(value);
701
+ if (record2 === null) return;
702
+ for (const [key, entry] of Object.entries(record2)) {
703
+ rejectNonFiniteNumbers(entry, [...path, key]);
704
+ }
705
+ }
706
+ function rejectUnknownFields(value, allowed, path) {
707
+ const record2 = recordAt(value);
708
+ if (record2 === null) return;
709
+ const unknown = Object.keys(record2).find((key) => !allowed.includes(key));
710
+ if (unknown !== void 0) {
711
+ throw new z.ZodError([
712
+ {
713
+ code: "custom",
714
+ path: [...path, unknown],
715
+ message: `Unrecognized field "${unknown}".`,
716
+ input: record2[unknown]
717
+ }
718
+ ]);
719
+ }
720
+ }
721
+ function requireFields(value, required, path) {
722
+ const record2 = recordAt(value);
723
+ if (record2 === null) return;
724
+ const missing = required.find((key) => record2[key] === void 0);
725
+ if (missing !== void 0) {
726
+ throw new z.ZodError([
727
+ {
728
+ code: "custom",
729
+ path: [...path, missing],
730
+ message: `Required field "${missing}" is missing.`,
731
+ input: void 0
732
+ }
733
+ ]);
734
+ }
735
+ }
736
+ function preflightColor(value, path) {
737
+ if (typeof value === "string" && value.startsWith("$brand:") && !COLOR_REFERENCE_VALUES.includes(value)) {
738
+ throw new z.ZodError([
739
+ {
740
+ code: "custom",
741
+ path,
742
+ message: `scene_brand_token_unresolved: "${value}" is not a scene colour token; supported roles: ${SCENE_BRAND_COLOR_TOKENS.join(", ")}.`,
743
+ input: value
744
+ }
745
+ ]);
746
+ }
747
+ }
748
+ function preflightTransform(value, path) {
749
+ rejectUnknownFields(value, ["position", "rotation", "scale"], path);
750
+ }
751
+ function preflightMaterial(value, path) {
752
+ rejectUnknownFields(
753
+ value,
754
+ [
755
+ "role",
756
+ "type",
757
+ "color",
758
+ "roughness",
759
+ "metalness",
760
+ "clearcoat",
761
+ "clearcoatRoughness",
762
+ "anisotropy",
763
+ "anisotropyRotation",
764
+ "ior",
765
+ "reflectivity"
766
+ ],
767
+ path
768
+ );
769
+ preflightColor(recordAt(value)?.color, [...path, "color"]);
770
+ }
771
+ function preflightSurface(value, path) {
772
+ const surface = recordAt(value);
773
+ if (surface === null) return;
774
+ requireFields(surface, ["source"], path);
775
+ switch (surface.source) {
776
+ case "image":
777
+ rejectUnknownFields(surface, ["source", "assetSlot"], path);
778
+ requireFields(surface, ["assetSlot"], path);
779
+ break;
780
+ case "color":
781
+ rejectUnknownFields(surface, ["source", "color"], path);
782
+ requireFields(surface, ["color"], path);
783
+ preflightColor(surface.color, [...path, "color"]);
784
+ break;
785
+ case "component":
786
+ rejectUnknownFields(
787
+ surface,
788
+ ["source", "component", "captureFrame", "layout", "bakeScale", "params"],
789
+ path
790
+ );
791
+ requireFields(surface, ["component", "captureFrame", "layout"], path);
792
+ requireFields(
793
+ surface.component,
794
+ ["version", "componentId", "sourceVersionId"],
795
+ [...path, "component"]
796
+ );
797
+ requireFields(surface.layout, ["width", "height"], [...path, "layout"]);
798
+ rejectUnknownFields(
799
+ surface.component,
800
+ ["version", "componentId", "sourceVersionId"],
801
+ [...path, "component"]
802
+ );
803
+ rejectUnknownFields(surface.layout, ["width", "height"], [...path, "layout"]);
804
+ break;
805
+ case "video":
806
+ rejectUnknownFields(surface, ["source", "mediaId", "startFrame", "endFrame"], path);
807
+ requireFields(surface, ["mediaId"], path);
808
+ break;
809
+ default:
810
+ break;
811
+ }
812
+ }
813
+ function preflightText(value, path) {
814
+ const common = [
815
+ "kind",
816
+ "space",
817
+ "id",
818
+ "opacity",
819
+ "lines",
820
+ "fontFamily",
821
+ "fontWeight",
822
+ "fontStyle",
823
+ "fontSize",
824
+ "lineHeight",
825
+ "letterSpacing",
826
+ "align",
827
+ "maxWidth",
828
+ "color"
829
+ ];
830
+ rejectUnknownFields(
831
+ value,
832
+ value.space === "screen" ? [...common, "x", "y", "rotation", "scale"] : [...common, "transform"],
833
+ path
834
+ );
835
+ requireFields(
836
+ value,
837
+ [
838
+ "kind",
839
+ "space",
840
+ "id",
841
+ "lines",
842
+ "fontFamily",
843
+ "fontWeight",
844
+ "fontStyle",
845
+ "fontSize",
846
+ "color",
847
+ ...value.space === "screen" ? ["x", "y"] : []
848
+ ],
849
+ path
850
+ );
851
+ preflightColor(value.color, [...path, "color"]);
852
+ if (typeof value.fontFamily === "string" && value.fontFamily.startsWith("$brand:") && !FONT_REFERENCE_VALUES.includes(value.fontFamily)) {
853
+ throw new z.ZodError([
854
+ {
855
+ code: "custom",
856
+ path: [...path, "fontFamily"],
857
+ message: `scene_brand_token_unresolved: "${value.fontFamily}" is not a scene font token; supported roles: ${SCENE_BRAND_FONT_TOKENS.join(", ")}.`,
858
+ input: value.fontFamily
859
+ }
860
+ ]);
861
+ }
862
+ if (value.space === "world") preflightTransform(value.transform, [...path, "transform"]);
863
+ if (!Array.isArray(value.lines)) return;
864
+ value.lines.forEach((line, lineIndex) => {
865
+ const linePath = [...path, "lines", lineIndex];
866
+ rejectUnknownFields(line, ["runs"], linePath);
867
+ const runs = recordAt(line)?.runs;
868
+ if (!Array.isArray(runs)) return;
869
+ runs.forEach((run, runIndex) => {
870
+ const runPath = [...linePath, "runs", runIndex];
871
+ rejectUnknownFields(run, ["text", "color", "emphasis"], runPath);
872
+ preflightColor(recordAt(run)?.color, [...runPath, "color"]);
873
+ });
874
+ });
875
+ }
876
+ function preflightObjects(value, path = ["layers"]) {
877
+ if (!Array.isArray(value)) return;
878
+ value.forEach((candidate, index) => {
879
+ const node = recordAt(candidate);
880
+ if (node === null) return;
881
+ const nodePath = [...path, index];
882
+ if (typeof node.kind !== "string" || !["group", "laptop", "phone", "slab", "page", "text"].includes(node.kind)) {
883
+ throw new z.ZodError([
884
+ {
885
+ code: "custom",
886
+ path: [...nodePath, "kind"],
887
+ message: "Expected a supported scene object kind.",
888
+ input: node.kind
889
+ }
890
+ ]);
891
+ }
892
+ if (node.kind === "text" && node.space !== "world" && node.space !== "screen") {
893
+ throw new z.ZodError([
894
+ {
895
+ code: "custom",
896
+ path: [...nodePath, "space"],
897
+ message: 'Text space must be "world" or "screen".',
898
+ input: node.space
899
+ }
900
+ ]);
901
+ }
902
+ switch (node.kind) {
903
+ case "group":
904
+ rejectUnknownFields(node, ["kind", "id", "transform", "opacity", "children"], nodePath);
905
+ preflightTransform(node.transform, [...nodePath, "transform"]);
906
+ requireFields(node, ["id", "children"], nodePath);
907
+ preflightObjects(node.children, [...nodePath, "children"]);
908
+ return;
909
+ case "laptop":
910
+ rejectUnknownFields(
911
+ node,
912
+ ["kind", "id", "transform", "opacity", "width", "openAngle", "materials", "surfaces"],
913
+ nodePath
914
+ );
915
+ requireFields(node, ["id", "width", "openAngle"], nodePath);
916
+ break;
917
+ case "phone":
918
+ rejectUnknownFields(
919
+ node,
920
+ ["kind", "id", "transform", "opacity", "width", "materials", "surfaces"],
921
+ nodePath
922
+ );
923
+ requireFields(node, ["id", "width"], nodePath);
924
+ break;
925
+ case "slab":
926
+ rejectUnknownFields(
927
+ node,
928
+ [
929
+ "kind",
930
+ "id",
931
+ "transform",
932
+ "opacity",
933
+ "width",
934
+ "height",
935
+ "thickness",
936
+ "cornerRadius",
937
+ "edgeRadius",
938
+ "finish",
939
+ "materials",
940
+ "surfaces"
941
+ ],
942
+ nodePath
943
+ );
944
+ requireFields(node, ["id", "width", "height"], nodePath);
945
+ break;
946
+ case "page":
947
+ rejectUnknownFields(
948
+ node,
949
+ [
950
+ "kind",
951
+ "id",
952
+ "transform",
953
+ "opacity",
954
+ "width",
955
+ "height",
956
+ "fit",
957
+ "transparency",
958
+ "depthWrite",
959
+ "backface",
960
+ "materials",
961
+ "surfaces"
962
+ ],
963
+ nodePath
964
+ );
965
+ requireFields(node, ["id", "width", "height", "surfaces"], nodePath);
966
+ break;
967
+ case "text":
968
+ preflightText(node, nodePath);
969
+ return;
970
+ default:
971
+ return;
972
+ }
973
+ preflightTransform(node.transform, [...nodePath, "transform"]);
974
+ if (Array.isArray(node.materials)) {
975
+ node.materials.forEach(
976
+ (material, materialIndex) => preflightMaterial(material, [...nodePath, "materials", materialIndex])
977
+ );
978
+ }
979
+ const surfaces = recordAt(node.surfaces);
980
+ if (surfaces !== null) {
981
+ for (const [slot, surface] of Object.entries(surfaces)) {
982
+ preflightSurface(surface, [...nodePath, "surfaces", slot]);
983
+ }
984
+ }
985
+ });
986
+ }
987
+ function preflightDocument(document) {
988
+ rejectNonFiniteNumbers(document);
989
+ rejectUnknownFields(
990
+ document,
991
+ [
992
+ "version",
993
+ "referenceSize",
994
+ "camera",
995
+ "environment",
996
+ "backdrop",
997
+ "layers",
998
+ "animation",
999
+ "post",
1000
+ "constraints"
1001
+ ],
1002
+ []
1003
+ );
1004
+ rejectUnknownFields(document.referenceSize, ["width", "height"], ["referenceSize"]);
1005
+ rejectUnknownFields(document.camera, ["position", "rotation", "fov"], ["camera"]);
1006
+ const environment = recordAt(document.environment);
1007
+ if (environment !== null) {
1008
+ rejectUnknownFields(
1009
+ environment,
1010
+ environment.kind === "photographic-studio-v1" ? ["kind"] : ["kind", "lightingProfile", "preset", "intensity", "floorY", "shadowOpacity"],
1011
+ ["environment"]
1012
+ );
1013
+ }
1014
+ const backdrop = recordAt(document.backdrop);
1015
+ if (backdrop !== null) {
1016
+ if (backdrop.kind === "transparent") rejectUnknownFields(backdrop, ["kind"], ["backdrop"]);
1017
+ if (backdrop.kind === "solid") {
1018
+ rejectUnknownFields(backdrop, ["kind", "color"], ["backdrop"]);
1019
+ preflightColor(backdrop.color, ["backdrop", "color"]);
1020
+ }
1021
+ if (backdrop.kind === "linear") {
1022
+ rejectUnknownFields(backdrop, ["kind", "angle", "stops"], ["backdrop"]);
1023
+ }
1024
+ if (backdrop.kind === "radial") {
1025
+ rejectUnknownFields(backdrop, ["kind", "at", "stops"], ["backdrop"]);
1026
+ }
1027
+ if (Array.isArray(backdrop.stops)) {
1028
+ backdrop.stops.forEach((stop, index) => {
1029
+ rejectUnknownFields(stop, ["at", "color"], ["backdrop", "stops", index]);
1030
+ preflightColor(recordAt(stop)?.color, ["backdrop", "stops", index, "color"]);
1031
+ });
1032
+ }
1033
+ }
1034
+ preflightObjects(document.layers);
1035
+ if (Array.isArray(document.animation)) {
1036
+ document.animation.forEach((candidate, index) => {
1037
+ const channel = recordAt(candidate);
1038
+ if (channel === null) return;
1039
+ const channelPath = ["animation", index];
1040
+ rejectUnknownFields(channel, ["target", "property", "keyframes"], channelPath);
1041
+ const target = recordAt(channel.target);
1042
+ rejectUnknownFields(
1043
+ target,
1044
+ target?.kind === "layer" ? ["kind", "layerId"] : ["kind"],
1045
+ [...channelPath, "target"]
1046
+ );
1047
+ if (!Array.isArray(channel.keyframes)) return;
1048
+ channel.keyframes.forEach((candidateKeyframe, keyframeIndex) => {
1049
+ const keyframePath = [...channelPath, "keyframes", keyframeIndex];
1050
+ const keyframe = recordAt(candidateKeyframe);
1051
+ rejectUnknownFields(keyframe, ["at", "value", "easing"], keyframePath);
1052
+ const easing = recordAt(keyframe?.easing);
1053
+ if (easing !== null) {
1054
+ rejectUnknownFields(
1055
+ easing,
1056
+ easing.kind === "bezier" ? ["kind", "x1", "y1", "x2", "y2"] : ["kind"],
1057
+ [...keyframePath, "easing"]
1058
+ );
1059
+ }
1060
+ });
1061
+ });
1062
+ }
1063
+ const post = recordAt(document.post);
1064
+ rejectUnknownFields(post, ["grain", "vignette", "focus"], ["post"]);
1065
+ rejectUnknownFields(recordAt(post?.focus), ["distance", "range", "maxBlur"], ["post", "focus"]);
1066
+ if (Array.isArray(document.constraints)) {
1067
+ document.constraints.forEach(
1068
+ (constraint, index) => rejectUnknownFields(constraint, ["kind", "objects", "gap"], ["constraints", index])
1069
+ );
1070
+ }
1071
+ }
1072
+ function runSceneShotPreflight(value) {
1073
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1074
+ return;
1075
+ }
1076
+ const document = value;
1077
+ if (document.version !== SCENE_SHOT_SPEC_VERSION) {
1078
+ throwRefusal(
1079
+ "scene_spec_version_unsupported",
1080
+ ["version"],
1081
+ `Scene version ${String(document.version)} is unsupported; this runtime accepts version 2.`,
1082
+ document.version
1083
+ );
1084
+ }
1085
+ const environment = document.environment;
1086
+ if (environment !== void 0 && environment !== null && typeof environment === "object") {
1087
+ const rig = environment;
1088
+ const kind = rig.kind;
1089
+ if (kind !== void 0 && kind !== "studio" && kind !== "photographic-studio-v1") {
1090
+ throwRefusal(
1091
+ "scene_rig_unsupported",
1092
+ ["environment", "kind"],
1093
+ `Unsupported rig ${JSON.stringify(kind)}; use "studio" or "photographic-studio-v1".`,
1094
+ kind
1095
+ );
1096
+ }
1097
+ if (kind === "photographic-studio-v1") {
1098
+ const unsupported = Object.keys(rig).find((key) => key !== "kind");
1099
+ if (unsupported !== void 0) {
1100
+ throwRefusal(
1101
+ "scene_photographic_rig_field_unsupported",
1102
+ ["environment", unsupported],
1103
+ "photographic-studio-v1 is fixed and accepts only its kind.",
1104
+ rig[unsupported]
1105
+ );
1106
+ }
1107
+ } else if (rig.lightingProfile !== void 0 && rig.lightingProfile !== "studio" && rig.lightingProfile !== "studio-edge") {
1108
+ throwRefusal(
1109
+ "scene_lighting_profile_unsupported",
1110
+ ["environment", "lightingProfile"],
1111
+ 'Supported profiles are "studio" and "studio-edge".',
1112
+ rig.lightingProfile
1113
+ );
1114
+ }
1115
+ }
1116
+ preflightObjectLightFields(document.layers);
1117
+ preflightDocument(document);
1118
+ }
1119
+ var SceneShotSpecV2Preflight = z.unknown().superRefine((value, ctx) => {
1120
+ try {
1121
+ runSceneShotPreflight(value);
1122
+ } catch (error) {
1123
+ if (!(error instanceof z.ZodError)) throw error;
1124
+ for (const issue of error.issues) ctx.addIssue(issue);
1125
+ }
1126
+ });
1127
+ var SceneShotSpecV2 = SceneShotSpecV2Preflight.pipe(
1128
+ SceneShotSpecV2Schema
1129
+ );
1130
+ function parseSceneShotSpecV2(value) {
1131
+ return SceneShotSpecV2.parse(value);
1132
+ }
1133
+ function visitSceneObjects(nodes, visitor) {
1134
+ for (const node of nodes) {
1135
+ visitor(node);
1136
+ if (node.kind === "group") visitSceneObjects(node.children, visitor);
1137
+ }
1138
+ }
1139
+ function sceneShotImageSlots(spec2) {
1140
+ const slots = [];
1141
+ visitSceneObjects(spec2.layers, (node) => {
1142
+ if (!("surfaces" in node) || node.surfaces === void 0) return;
1143
+ for (const binding of Object.values(node.surfaces)) {
1144
+ if (binding.source === "image" && !slots.includes(binding.assetSlot)) {
1145
+ slots.push(binding.assetSlot);
1146
+ }
1147
+ }
1148
+ });
1149
+ return slots;
1150
+ }
1151
+ function sceneShotVideoSlots(spec2) {
1152
+ const slots = [];
1153
+ visitSceneObjects(spec2.layers, (node) => {
1154
+ if (!("surfaces" in node) || node.surfaces === void 0) return;
1155
+ for (const binding of Object.values(node.surfaces)) if (binding.source === "video" && !slots.includes(binding.mediaId)) slots.push(binding.mediaId);
1156
+ });
1157
+ return slots;
1158
+ }
1159
+ function sceneShotSurfaceCount(spec2) {
1160
+ let count = 0;
1161
+ visitSceneObjects(spec2.layers, (node) => {
1162
+ if ("surfaces" in node && node.surfaces !== void 0) {
1163
+ count += Object.keys(node.surfaces).length;
1164
+ }
1165
+ });
1166
+ return count;
1167
+ }
1168
+ var sceneShotJsonSchema = z.toJSONSchema(SceneShotSpecV2Schema, {
1169
+ target: "draft-2020-12",
1170
+ io: "input"
1171
+ });
1172
+ function sceneShotJsonSchemaText() {
1173
+ return `${JSON.stringify(sceneShotJsonSchema, null, 2)}
1174
+ `;
1175
+ }
1176
+
1177
+ // ../composition/src/scene/animation.ts
1178
+ function contentFrame(frame, durationInFrames) {
1179
+ if (!Number.isFinite(frame) || !Number.isInteger(durationInFrames) || durationInFrames <= 1) return 0;
1180
+ return Math.max(0, Math.min(durationInFrames - 1, Math.floor(frame)));
1181
+ }
1182
+ function normalizedTime(frame, durationInFrames) {
1183
+ return durationInFrames <= 1 ? 0 : contentFrame(frame, durationInFrames) / (durationInFrames - 1);
1184
+ }
1185
+ function cubicBezierEase(x1, y1, x2, y2) {
1186
+ return (input) => {
1187
+ const t2 = Math.max(0, Math.min(1, input));
1188
+ if (t2 === 0 || t2 === 1) return t2;
1189
+ let lo = 0, hi = 1, u = t2;
1190
+ for (let i = 0; i < 40; i += 1) {
1191
+ u = (lo + hi) / 2;
1192
+ const x = 3 * (1 - u) ** 2 * u * x1 + 3 * (1 - u) * u ** 2 * x2 + u ** 3;
1193
+ if (x < t2) lo = u;
1194
+ else hi = u;
1195
+ }
1196
+ return 3 * (1 - u) ** 2 * u * y1 + 3 * (1 - u) * u ** 2 * y2 + u ** 3;
1197
+ };
1198
+ }
1199
+ function evaluateChannel(keyframes, t2) {
1200
+ const first = keyframes[0];
1201
+ if (!first) throw new Error("Scene channel carries no keyframes");
1202
+ if (t2 <= first.at) return first.value;
1203
+ for (let i = 1; i < keyframes.length; i += 1) {
1204
+ const next = keyframes[i];
1205
+ if (t2 > next.at) continue;
1206
+ const previous = keyframes[i - 1];
1207
+ const easing = next.easing;
1208
+ if (easing?.kind === "hold") return t2 === next.at ? next.value : previous.value;
1209
+ let progress = (t2 - previous.at) / (next.at - previous.at);
1210
+ if (easing?.kind === "bezier") progress = cubicBezierEase(easing.x1, easing.y1, easing.x2, easing.y2)(progress);
1211
+ return previous.value + (next.value - previous.value) * progress;
1212
+ }
1213
+ return keyframes[keyframes.length - 1].value;
1214
+ }
1215
+ var AXES = {
1216
+ positionX: ["position", 0],
1217
+ positionY: ["position", 1],
1218
+ positionZ: ["position", 2],
1219
+ rotationX: ["rotation", 0],
1220
+ rotationY: ["rotation", 1],
1221
+ rotationZ: ["rotation", 2]
1222
+ };
1223
+ var AXIS_PROPERTIES = Object.keys(AXES);
1224
+ var channelKey = (channel) => `${channel.target.kind === "layer" ? `layer:${channel.target.layerId}` : channel.target.kind}|${channel.property}`;
1225
+ var identityMatrix = () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1226
+ function multiply(a, b) {
1227
+ const out = new Array(16).fill(0);
1228
+ for (let column = 0; column < 4; column += 1) for (let row = 0; row < 4; row += 1) {
1229
+ for (let k = 0; k < 4; k += 1) out[column * 4 + row] += a[k * 4 + row] * b[column * 4 + k];
1230
+ }
1231
+ return out;
1232
+ }
1233
+ function sceneTransformMatrix(transform) {
1234
+ const [x, y, z2] = transform.position, [rx, ry, rz] = transform.rotation;
1235
+ const cx = Math.cos(rx), sx = Math.sin(rx), cy = Math.cos(ry), sy = Math.sin(ry), cz = Math.cos(rz), sz = Math.sin(rz), s = transform.scale;
1236
+ const t2 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z2, 1];
1237
+ const mx = [1, 0, 0, 0, 0, cx, sx, 0, 0, -sx, cx, 0, 0, 0, 0, 1];
1238
+ const my = [cy, 0, -sy, 0, 0, 1, 0, 0, sy, 0, cy, 0, 0, 0, 0, 1];
1239
+ const mz = [cz, sz, 0, 0, -sz, cz, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1240
+ const ms = [s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1];
1241
+ return multiply(multiply(t2, mx), multiply(my, multiply(mz, ms)));
1242
+ }
1243
+ function setAxis(target, property, value) {
1244
+ const [field, index] = AXES[property];
1245
+ target[field][index] = value;
1246
+ }
1247
+ function evaluateScene(spec2, requestedFrame, durationInFrames) {
1248
+ const u = normalizedTime(requestedFrame, durationInFrames), frame = contentFrame(requestedFrame, durationInFrames);
1249
+ const channels = new Map(spec2.animation.map((channel) => [channelKey(channel), channel]));
1250
+ const sample = (target, property, base) => {
1251
+ const channel = channels.get(`${target}|${property}`);
1252
+ return channel ? evaluateChannel(channel.keyframes, u) : base;
1253
+ };
1254
+ const camera = structuredClone(spec2.camera);
1255
+ for (const property of AXIS_PROPERTIES) {
1256
+ const [field, index] = AXES[property];
1257
+ setAxis(camera, property, sample("camera", property, camera[field][index]));
1258
+ }
1259
+ camera.fov = sample("camera", "fov", camera.fov);
1260
+ const walk = (objects, parentMatrix, parentOpacity) => objects.map((source) => {
1261
+ const object = structuredClone(source);
1262
+ const localOpacity = sample(`layer:${object.id}`, "opacity", object.opacity), worldOpacity = parentOpacity * localOpacity;
1263
+ if (object.kind === "text" && object.space === "screen") {
1264
+ object.x = sample(`layer:${object.id}`, "x", object.x);
1265
+ object.y = sample(`layer:${object.id}`, "y", object.y);
1266
+ object.rotation = sample(`layer:${object.id}`, "rotation", object.rotation);
1267
+ object.scale = sample(`layer:${object.id}`, "scale", object.scale);
1268
+ object.opacity = localOpacity;
1269
+ return { ...object, worldMatrix: null, worldOpacity };
1270
+ }
1271
+ for (const property of AXIS_PROPERTIES) {
1272
+ const [field, index] = AXES[property];
1273
+ setAxis(object.transform, property, sample(`layer:${object.id}`, property, object.transform[field][index]));
1274
+ }
1275
+ object.transform.scale = sample(`layer:${object.id}`, "scale", object.transform.scale);
1276
+ object.opacity = localOpacity;
1277
+ if (object.kind === "laptop") object.openAngle = sample(`layer:${object.id}`, "openAngle", object.openAngle);
1278
+ const worldMatrix = multiply(parentMatrix, sceneTransformMatrix(object.transform));
1279
+ return object.kind === "group" ? { ...object, children: walk(object.children, worldMatrix, worldOpacity), worldMatrix, worldOpacity } : { ...object, worldMatrix, worldOpacity };
1280
+ });
1281
+ const environment = structuredClone(spec2.environment);
1282
+ if (environment.kind !== "photographic-studio-v1") environment.intensity = sample("environment", "intensity", environment.intensity);
1283
+ const focus = spec2.post.focus ? structuredClone(spec2.post.focus) : null;
1284
+ if (focus) {
1285
+ focus.distance = sample("focus", "distance", focus.distance);
1286
+ focus.range = sample("focus", "range", focus.range);
1287
+ focus.maxBlur = sample("focus", "maxBlur", focus.maxBlur);
1288
+ }
1289
+ return { u, contentFrame: frame, camera, environment, focus, layers: walk(spec2.layers, identityMatrix(), 1) };
1290
+ }
1291
+
1292
+ // ../composition/src/scene/clipRules.ts
1293
+ import { ZodError } from "zod";
1294
+ function slotOwner(spec2, slot) {
1295
+ const find = (layers) => {
1296
+ for (const layer of layers) {
1297
+ if ("surfaces" in layer && layer.surfaces !== void 0) {
1298
+ for (const binding of Object.values(layer.surfaces)) {
1299
+ if (binding.source === "image" && binding.assetSlot === slot) return layer.id;
1300
+ }
1301
+ }
1302
+ if (layer.kind === "group") {
1303
+ const nested = find(layer.children);
1304
+ if (nested !== void 0) return nested;
1305
+ }
1306
+ }
1307
+ return void 0;
1308
+ };
1309
+ return find(spec2.layers);
1310
+ }
1311
+ function isSceneShotSource(declaration) {
1312
+ return declaration?.manifest.sceneShot?.version === SCENE_SHOT_SPEC_VERSION && declaration.manifest.graphics.api === "webgl";
1313
+ }
1314
+ var SOURCE_VERSION_ID = /^[a-f0-9]{64}$/;
1315
+ function readSceneSourceRef(props) {
1316
+ const ref = props.componentRef;
1317
+ if (!ref || typeof ref !== "object") return null;
1318
+ const { version, sourceVersionId } = ref;
1319
+ if (version !== 1 || typeof sourceVersionId !== "string" || !SOURCE_VERSION_ID.test(sourceVersionId)) return null;
1320
+ return { sourceVersionId };
1321
+ }
1322
+ function isTimelineClip(content) {
1323
+ return "source" in content;
1324
+ }
1325
+ function isSceneShotClip(content, resolveSource) {
1326
+ if (!isTimelineClip(content) || content.source.kind !== "component") return false;
1327
+ const ref = readSceneSourceRef(content.source.props);
1328
+ if (!ref) return false;
1329
+ const declaration = resolveSource(content.source.componentId, ref.sourceVersionId);
1330
+ return isSceneShotSource(declaration) && declaration.componentId === content.source.componentId;
1331
+ }
1332
+ var FRAME_EPSILON = 1e-6;
1333
+ function offFrameLattice(seconds, fps) {
1334
+ const frames = seconds * fps;
1335
+ return Math.abs(frames - Math.round(frames)) > FRAME_EPSILON;
1336
+ }
1337
+ function latticeDetail(seconds, fps) {
1338
+ return `${seconds} s is not on the ${fps} fps frame lattice; use a whole number of frames (n/${fps} s).`;
1339
+ }
1340
+ var SCENE_TRANSITION_KINDS = ["cut", "fade"];
1341
+ function sceneTransitionDurationIssues(transition, fps) {
1342
+ if (offFrameLattice(transition.duration, fps)) {
1343
+ return [{
1344
+ path: "transitionIn.duration",
1345
+ detail: latticeDetail(transition.duration, fps),
1346
+ code: "validation_error"
1347
+ }];
1348
+ }
1349
+ if (transition.type === "fade" && Math.round(transition.duration * fps) < 1) {
1350
+ return [{
1351
+ path: "transitionIn.duration",
1352
+ detail: `${transition.duration} s is zero frames at ${fps} fps; a fade needs at least one frame. Lengthen the fade or author the boundary as a cut.`,
1353
+ code: "validation_error"
1354
+ }];
1355
+ }
1356
+ return [];
1357
+ }
1358
+ var SCENE_PROPS_KEYS = /* @__PURE__ */ new Set(["componentRef", "scene"]);
1359
+ var COMPETING_PROP_DETAIL = {
1360
+ transition: "a scene clip takes its boundary from `transitionIn` (cut or fade); `props.transition` would be a second owner of the same boundary. Remove it.",
1361
+ sequenceGroup: "scene clips do not join legacy sequence groups; the composition already orders them on the dedicated track. Remove `props.sequenceGroup`.",
1362
+ fadeIn: 'internal fades are refused on scene clips; express a fade with `transitionIn: { type: "fade", duration }`. Remove `props.fadeIn`.',
1363
+ fadeOut: "internal fades are refused on scene clips; the next clip's `transitionIn` fades this one out. Remove `props.fadeOut`."
1364
+ };
1365
+ function sceneSpecIssues(value, path) {
1366
+ try {
1367
+ return { spec: parseSceneShotSpecV2(value), issues: [] };
1368
+ } catch (error) {
1369
+ if (!(error instanceof ZodError)) throw error;
1370
+ const issues = error.issues.map((issue) => ({
1371
+ path: issue.path.length > 0 ? `${path}.${issue.path.join(".")}` : path,
1372
+ detail: issue.message,
1373
+ code: "validation_error"
1374
+ }));
1375
+ return { spec: null, issues };
1376
+ }
1377
+ }
1378
+ function bindingIssues(spec2, assets, path, mediaKindById) {
1379
+ const issues = [];
1380
+ const slots = sceneShotImageSlots(spec2);
1381
+ for (const slot of slots) {
1382
+ if (typeof assets[slot] === "string" && assets[slot].length > 0) continue;
1383
+ issues.push({
1384
+ path: `${path}.${slot}`,
1385
+ detail: `slot '${slot}' is referenced by layer '${slotOwner(spec2, slot)}' but not bound; bind it to an image media id.`,
1386
+ code: "validation_error"
1387
+ });
1388
+ }
1389
+ for (const [name, mediaId] of Object.entries(assets)) {
1390
+ if (!SCENE_ASSET_SLOT_PATTERN.test(name)) {
1391
+ issues.push({ path: `${path}.${name}`, detail: `'${name}' is not a valid slot name.`, code: "validation_error" });
1392
+ continue;
1393
+ }
1394
+ if (!slots.includes(name)) {
1395
+ issues.push({
1396
+ path: `${path}.${name}`,
1397
+ detail: `no layer references slot '${name}'; remove the binding or reference the slot from a layer.`,
1398
+ code: "validation_error"
1399
+ });
1400
+ continue;
1401
+ }
1402
+ if (!mediaKindById) continue;
1403
+ const kind = mediaKindById[mediaId];
1404
+ if (kind === void 0) {
1405
+ issues.push({ path: `${path}.${name}`, detail: `media '${mediaId}' is not in this project.`, code: "validation_error" });
1406
+ } else if (kind !== "image") {
1407
+ issues.push({
1408
+ path: `${path}.${name}`,
1409
+ detail: `slot '${name}' needs image media (PNG, JPEG or WebP); '${mediaId}' is ${kind}.`,
1410
+ code: "validation_error"
1411
+ });
1412
+ }
1413
+ }
1414
+ return issues;
1415
+ }
1416
+ function sceneClipShapeIssues(clip, ctx, options = {}) {
1417
+ const issues = [];
1418
+ const source = clip.source;
1419
+ if (source.kind !== "component") {
1420
+ return [{ path: "source.kind", detail: `a scene shot is a 'component' clip; got '${source.kind}'.`, code: "invalid_composition" }];
1421
+ }
1422
+ if (source.zPlane !== void 0 && source.zPlane !== "front") {
1423
+ issues.push({
1424
+ path: "source.zPlane",
1425
+ detail: `scene shots render on the front plane only; '${source.zPlane}' is refused. Remove zPlane or set it to 'front'.`,
1426
+ code: "validation_error"
1427
+ });
1428
+ }
1429
+ for (const field of ["region", "position", "scale", "rotation", "fit"]) {
1430
+ if (clip[field] !== void 0) {
1431
+ issues.push({
1432
+ path: field,
1433
+ detail: `scene shots are full-frame: \`${field}\` is refused. Place and size content inside the scene (layer transforms), not on the clip.`,
1434
+ code: "validation_error"
1435
+ });
1436
+ }
1437
+ }
1438
+ if (clip.effects !== void 0 && clip.effects.length > 0) {
1439
+ issues.push({
1440
+ path: "effects",
1441
+ detail: "outer per-clip effects are refused on scene shots; use the scene's post (grain, vignette, focus) instead.",
1442
+ code: "validation_error"
1443
+ });
1444
+ }
1445
+ if (clip.animate !== void 0 && Object.keys(clip.animate).length > 0) {
1446
+ issues.push({
1447
+ path: "animate",
1448
+ detail: "outer transform animation is refused on scene shots; animate the camera or layers through `scene.animation`.",
1449
+ code: "validation_error"
1450
+ });
1451
+ }
1452
+ for (const key of Object.keys(source.props)) {
1453
+ if (SCENE_PROPS_KEYS.has(key)) continue;
1454
+ issues.push({
1455
+ path: `source.props.${key}`,
1456
+ detail: COMPETING_PROP_DETAIL[key] ?? `scene shot props carry only \`componentRef\` and \`scene\`; \`${key}\` is refused.`,
1457
+ code: "validation_error"
1458
+ });
1459
+ }
1460
+ if (clip.transitionIn !== void 0) {
1461
+ if (!SCENE_TRANSITION_KINDS.includes(clip.transitionIn.type)) {
1462
+ issues.push({
1463
+ path: "transitionIn.type",
1464
+ detail: `scene shots accept 'cut' or 'fade' transitions; '${clip.transitionIn.type}' is refused.`,
1465
+ code: "validation_error"
1466
+ });
1467
+ }
1468
+ issues.push(...sceneTransitionDurationIssues(clip.transitionIn, ctx.fps));
1469
+ }
1470
+ if (offFrameLattice(clip.startTime, ctx.fps)) {
1471
+ issues.push({ path: "startTime", detail: latticeDetail(clip.startTime, ctx.fps), code: "validation_error" });
1472
+ }
1473
+ if (offFrameLattice(clip.duration, ctx.fps)) {
1474
+ issues.push({ path: "duration", detail: latticeDetail(clip.duration, ctx.fps), code: "validation_error" });
1475
+ }
1476
+ const parsed = sceneSpecIssues(source.props.scene, "source.props.scene");
1477
+ issues.push(...parsed.issues);
1478
+ if (parsed.issues.length === 0) {
1479
+ issues.push(...bindingIssues(parsed.spec, source.assets ?? {}, "source.assets", options.checkMedia ? ctx.mediaKindById : void 0));
1480
+ }
1481
+ return issues;
1482
+ }
1483
+ function sceneClipOverlapIssue(clip, track) {
1484
+ const end = clip.startTime + clip.duration;
1485
+ for (const other of track.contents) {
1486
+ if (other.id === clip.id) continue;
1487
+ const otherEnd = other.startTime + other.duration;
1488
+ if (clip.startTime < otherEnd && other.startTime < end) {
1489
+ return {
1490
+ path: "startTime",
1491
+ detail: `[${clip.startTime}, ${end}) overlaps '${other.id}' [${other.startTime}, ${otherEnd}) on track '${track.id}'; scene shots on one track cannot overlap.`,
1492
+ code: "invalid_composition"
1493
+ };
1494
+ }
1495
+ }
1496
+ return null;
1497
+ }
1498
+ function sceneDedicatedTrackIssue(track, resolveSource, path) {
1499
+ if (track.kind !== "overlay") {
1500
+ return {
1501
+ path,
1502
+ detail: `track '${track.id}' is kind '${track.kind}'; scene shots live on a dedicated overlay track. Pass newTrack: true or name an overlay track that holds only scene shots.`,
1503
+ code: "invalid_composition"
1504
+ };
1505
+ }
1506
+ for (const content of track.contents) {
1507
+ if (!isTimelineClip(content) || isSceneShotClip(content, resolveSource)) continue;
1508
+ return {
1509
+ path,
1510
+ detail: `track '${track.id}' holds '${content.id}' (source '${content.source.kind}'); scene shots need a dedicated track holding only scene shots and gaps. Pass newTrack: true or choose such a track.`,
1511
+ code: "invalid_composition"
1512
+ };
1513
+ }
1514
+ return null;
1515
+ }
1516
+ function collectSceneClipIssues(composition, ctx) {
1517
+ const issues = [];
1518
+ for (const track of composition.tracks) {
1519
+ const clips = track.contents.filter(isTimelineClip);
1520
+ const scenes = clips.filter((clip) => isSceneShotClip(clip, ctx.resolveSource));
1521
+ if (scenes.length === 0) continue;
1522
+ const sceneIds = new Set(scenes.map((clip) => clip.id));
1523
+ for (const clip of clips) {
1524
+ if (sceneIds.has(clip.id)) continue;
1525
+ issues.push({
1526
+ clipId: clip.id,
1527
+ path: "source.kind",
1528
+ message: `clip '${clip.id}' (source '${clip.source.kind}') shares track '${track.id}' with scene shot '${scenes[0].id}'; scene tracks hold only scene shots and gaps. Move it to another overlay track.`
1529
+ });
1530
+ }
1531
+ if (track.kind !== "overlay") {
1532
+ issues.push({
1533
+ clipId: scenes[0].id,
1534
+ path: "trackId",
1535
+ message: `scene shot '${scenes[0].id}' sits on track '${track.id}' of kind '${track.kind}'; scene shots live on a dedicated overlay track.`
1536
+ });
1537
+ }
1538
+ for (const clip of scenes) {
1539
+ for (const issue of sceneClipShapeIssues(clip, ctx)) {
1540
+ issues.push({ clipId: clip.id, path: issue.path, message: `scene shot '${clip.id}' ${issue.path}: ${issue.detail}` });
1541
+ }
1542
+ const overlap = sceneClipOverlapIssue(clip, track);
1543
+ if (overlap) issues.push({ clipId: clip.id, path: overlap.path, message: `scene shot '${clip.id}' ${overlap.path}: ${overlap.detail}` });
1544
+ }
1545
+ }
1546
+ return issues;
1547
+ }
1548
+
1549
+ // ../composition/src/scene/constraints.ts
1550
+ var SceneConstraintRefusal = class extends Error {
1551
+ code;
1552
+ path;
1553
+ detail;
1554
+ constructor(code, path, message, detail) {
1555
+ super(message);
1556
+ this.name = "SceneConstraintRefusal";
1557
+ this.code = code;
1558
+ this.path = path;
1559
+ this.detail = detail;
1560
+ }
1561
+ };
1562
+ var dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
1563
+ var cross = (a, b) => [
1564
+ a[1] * b[2] - a[2] * b[1],
1565
+ a[2] * b[0] - a[0] * b[2],
1566
+ a[0] * b[1] - a[1] * b[0]
1567
+ ];
1568
+ var length = (value) => Math.hypot(value[0], value[1], value[2]);
1569
+ function applyLinear(basis, value) {
1570
+ return [
1571
+ basis[0][0] * value[0] + basis[1][0] * value[1] + basis[2][0] * value[2],
1572
+ basis[0][1] * value[0] + basis[1][1] * value[1] + basis[2][1] * value[2],
1573
+ basis[0][2] * value[0] + basis[1][2] * value[1] + basis[2][2] * value[2]
1574
+ ];
1575
+ }
1576
+ function add(a, b) {
1577
+ return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
1578
+ }
1579
+ function sceneRotationAxesV2(rotation) {
1580
+ const [rotationX, rotationY, rotationZ] = rotation;
1581
+ const cx = Math.cos(rotationX);
1582
+ const sx = Math.sin(rotationX);
1583
+ const cy = Math.cos(rotationY);
1584
+ const sy = Math.sin(rotationY);
1585
+ const cz = Math.cos(rotationZ);
1586
+ const sz = Math.sin(rotationZ);
1587
+ return [
1588
+ [cy * cz, cx * sz + sx * sy * cz, sx * sz - cx * sy * cz],
1589
+ [-cy * sz, cx * cz - sx * sy * sz, sx * cz + cx * sy * sz],
1590
+ [sy, -sx * cy, cx * cy]
1591
+ ];
1592
+ }
1593
+ function affineFor(transform) {
1594
+ const axes = sceneRotationAxesV2(transform.rotation);
1595
+ const scale = transform.scale;
1596
+ return {
1597
+ basis: [
1598
+ [axes[0][0] * scale, axes[0][1] * scale, axes[0][2] * scale],
1599
+ [axes[1][0] * scale, axes[1][1] * scale, axes[1][2] * scale],
1600
+ [axes[2][0] * scale, axes[2][1] * scale, axes[2][2] * scale]
1601
+ ],
1602
+ translation: transform.position
1603
+ };
1604
+ }
1605
+ function multiplyAffine(a, b) {
1606
+ return {
1607
+ basis: [
1608
+ applyLinear(a.basis, b.basis[0]),
1609
+ applyLinear(a.basis, b.basis[1]),
1610
+ applyLinear(a.basis, b.basis[2])
1611
+ ],
1612
+ translation: add(applyLinear(a.basis, b.translation), a.translation)
1613
+ };
1614
+ }
1615
+ function composedAffine(transforms) {
1616
+ let result = {
1617
+ basis: [
1618
+ [1, 0, 0],
1619
+ [0, 1, 0],
1620
+ [0, 0, 1]
1621
+ ],
1622
+ translation: [0, 0, 0]
1623
+ };
1624
+ for (const transform of transforms) result = multiplyAffine(result, affineFor(transform));
1625
+ return result;
1626
+ }
1627
+ function finiteVec3(value) {
1628
+ return value.length === 3 && value.every(Number.isFinite);
1629
+ }
1630
+ function validTransform(transform) {
1631
+ return finiteVec3(transform.position) && finiteVec3(transform.rotation) && Number.isFinite(transform.scale) && transform.scale > 0;
1632
+ }
1633
+ function sceneOrientedBoundsV2(body, path = "constraint.body") {
1634
+ const { center, halfExtents } = body.bounds;
1635
+ if (!finiteVec3(center) || !finiteVec3(halfExtents) || halfExtents.some((extent) => extent < 0) || halfExtents.every((extent) => extent === 0) || !Number.isFinite(body.opacity) || body.transforms.some((transform) => !validTransform(transform)) || body.transforms.length === 0) {
1636
+ throw new SceneConstraintRefusal(
1637
+ "scene_constraint_bounds_invalid",
1638
+ path,
1639
+ "A constraint body needs finite non-empty local bounds, finite opacity and a valid evaluated transform chain.",
1640
+ { center, halfExtents, transformCount: body.transforms.length }
1641
+ );
1642
+ }
1643
+ const affine = composedAffine(body.transforms);
1644
+ const lengths = affine.basis.map(length);
1645
+ if (lengths.some((axisLength) => !Number.isFinite(axisLength) || axisLength <= 0)) {
1646
+ throw new SceneConstraintRefusal(
1647
+ "scene_constraint_bounds_invalid",
1648
+ path,
1649
+ "The evaluated transform chain produced a degenerate oriented bound.",
1650
+ { lengths }
1651
+ );
1652
+ }
1653
+ const transformedCenter = add(applyLinear(affine.basis, center), affine.translation);
1654
+ const transformedHalfExtents = [
1655
+ halfExtents[0] * lengths[0],
1656
+ halfExtents[1] * lengths[1],
1657
+ halfExtents[2] * lengths[2]
1658
+ ];
1659
+ if (!finiteVec3(transformedCenter) || !finiteVec3(transformedHalfExtents)) {
1660
+ throw new SceneConstraintRefusal(
1661
+ "scene_constraint_bounds_invalid",
1662
+ path,
1663
+ "The evaluated transform chain overflowed the oriented bound.",
1664
+ { center: transformedCenter, halfExtents: transformedHalfExtents }
1665
+ );
1666
+ }
1667
+ return {
1668
+ center: transformedCenter,
1669
+ axes: [
1670
+ affine.basis[0].map((value) => value / lengths[0]),
1671
+ affine.basis[1].map((value) => value / lengths[1]),
1672
+ affine.basis[2].map((value) => value / lengths[2])
1673
+ ],
1674
+ halfExtents: transformedHalfExtents
1675
+ };
1676
+ }
1677
+ function sceneSeparatingAxisGapV2(a, b) {
1678
+ const centreDelta = [
1679
+ b.center[0] - a.center[0],
1680
+ b.center[1] - a.center[1],
1681
+ b.center[2] - a.center[2]
1682
+ ];
1683
+ let bestGap = -Infinity;
1684
+ const consider = (candidate) => {
1685
+ const axisLength = length(candidate);
1686
+ if (axisLength < 1e-12) return;
1687
+ const axis = candidate.map((value) => value / axisLength);
1688
+ const radiusA = a.halfExtents[0] * Math.abs(dot(a.axes[0], axis)) + a.halfExtents[1] * Math.abs(dot(a.axes[1], axis)) + a.halfExtents[2] * Math.abs(dot(a.axes[2], axis));
1689
+ const radiusB = b.halfExtents[0] * Math.abs(dot(b.axes[0], axis)) + b.halfExtents[1] * Math.abs(dot(b.axes[1], axis)) + b.halfExtents[2] * Math.abs(dot(b.axes[2], axis));
1690
+ bestGap = Math.max(bestGap, Math.abs(dot(centreDelta, axis)) - radiusA - radiusB);
1691
+ };
1692
+ for (const axis of a.axes) consider(axis);
1693
+ for (const axis of b.axes) consider(axis);
1694
+ for (const axisA of a.axes) {
1695
+ for (const axisB of b.axes) consider(cross(axisA, axisB));
1696
+ }
1697
+ if (!Number.isFinite(bestGap)) {
1698
+ throw new SceneConstraintRefusal(
1699
+ "scene_constraint_bounds_invalid",
1700
+ "constraint.bounds",
1701
+ "The separating-axis calculation produced a non-finite gap.",
1702
+ { separatingAxisGap: bestGap }
1703
+ );
1704
+ }
1705
+ return bestGap;
1706
+ }
1707
+ function evaluateSceneSeparationV2(constraints, frames) {
1708
+ if (constraints.length === 0) return { pairs: [] };
1709
+ if (frames.length === 0) {
1710
+ throw new SceneConstraintRefusal(
1711
+ "scene_constraint_frames_missing",
1712
+ "constraints",
1713
+ "Separation constraints require the complete evaluated content-frame set.",
1714
+ {}
1715
+ );
1716
+ }
1717
+ const pairs = [];
1718
+ constraints.forEach((constraint, constraintIndex) => {
1719
+ frames.forEach(({ frame, resolveBody }) => {
1720
+ const bodies = constraint.objects.map((authoredId, objectIndex) => {
1721
+ const body = resolveBody(authoredId);
1722
+ const path = `constraints.${constraintIndex}.objects.${objectIndex}`;
1723
+ if (body === null || body === void 0) {
1724
+ throw new SceneConstraintRefusal(
1725
+ "scene_constraint_bounds_missing",
1726
+ path,
1727
+ `No evaluated conservative bound was supplied for authored object "${authoredId}" at content frame ${frame}.`,
1728
+ { authoredId, frame }
1729
+ );
1730
+ }
1731
+ return sceneOrientedBoundsV2(body, path);
1732
+ });
1733
+ for (let first = 0; first < bodies.length; first += 1) {
1734
+ for (let second = first + 1; second < bodies.length; second += 1) {
1735
+ const objectPair = [
1736
+ constraint.objects[first],
1737
+ constraint.objects[second]
1738
+ ];
1739
+ const separatingAxisGap = sceneSeparatingAxisGapV2(
1740
+ bodies[first],
1741
+ bodies[second]
1742
+ );
1743
+ if (!Number.isFinite(separatingAxisGap) || !Number.isFinite(constraint.gap)) {
1744
+ throw new SceneConstraintRefusal(
1745
+ "scene_constraint_bounds_invalid",
1746
+ `constraints.${constraintIndex}.gap`,
1747
+ "The required or evaluated separation gap is not finite.",
1748
+ { separatingAxisGap, requiredGap: constraint.gap }
1749
+ );
1750
+ }
1751
+ const result = {
1752
+ constraintIndex,
1753
+ frame,
1754
+ objects: objectPair,
1755
+ separatingAxisGap,
1756
+ requiredGap: constraint.gap
1757
+ };
1758
+ pairs.push(result);
1759
+ if (separatingAxisGap < constraint.gap) {
1760
+ throw new SceneConstraintRefusal(
1761
+ "scene_min_separation_violated",
1762
+ `constraints.${constraintIndex}.gap`,
1763
+ `Objects "${objectPair[0]}" and "${objectPair[1]}" have separating-axis gap ${separatingAxisGap} at content frame ${frame}; required ${constraint.gap}.`,
1764
+ { ...result }
1765
+ );
1766
+ }
1767
+ }
1768
+ }
1769
+ });
1770
+ });
1771
+ return { pairs };
1772
+ }
1773
+
1774
+ // ../composition/src/scene/examples.ts
1775
+ var t = {
1776
+ position: [0, 0, 0],
1777
+ rotation: [0, 0, 0],
1778
+ scale: 1
1779
+ };
1780
+ var spec = parseSceneShotSpecV2({
1781
+ version: 2,
1782
+ referenceSize: { width: 1920, height: 1080 },
1783
+ environment: { kind: "photographic-studio-v1" },
1784
+ backdrop: { kind: "solid", color: "$brand:colors.background" },
1785
+ layers: [
1786
+ {
1787
+ kind: "group",
1788
+ id: "cards",
1789
+ transform: t,
1790
+ children: [
1791
+ {
1792
+ kind: "slab",
1793
+ id: "hero",
1794
+ transform: t,
1795
+ width: 720,
1796
+ height: 900,
1797
+ finish: "satin",
1798
+ surfaces: {
1799
+ front: {
1800
+ source: "component",
1801
+ component: {
1802
+ version: 1,
1803
+ componentId: "hero-card",
1804
+ sourceVersionId: "0".repeat(64)
1805
+ },
1806
+ captureFrame: 12,
1807
+ layout: { width: 720, height: 900 }
1808
+ }
1809
+ }
1810
+ },
1811
+ {
1812
+ kind: "page",
1813
+ id: "page",
1814
+ transform: { ...t, position: [760, 0, -20] },
1815
+ width: 640,
1816
+ height: 800,
1817
+ surfaces: { content: { source: "image", assetSlot: "page" } }
1818
+ }
1819
+ ]
1820
+ }
1821
+ ],
1822
+ animation: [
1823
+ {
1824
+ target: { kind: "layer", layerId: "cards" },
1825
+ property: "rotationY",
1826
+ keyframes: [
1827
+ { at: 0, value: 0 },
1828
+ { at: 1, value: Math.PI * 6, easing: { kind: "linear" } }
1829
+ ]
1830
+ }
1831
+ ]
1832
+ });
1833
+ var SCENE_SHOT_EXAMPLES = [
1834
+ {
1835
+ id: "world-objects",
1836
+ title: "World objects",
1837
+ summary: "Nested satin card and fitted page.",
1838
+ slots: sceneShotImageSlots(spec),
1839
+ spec
1840
+ }
1841
+ ];
1842
+ var sceneShotExample = (id) => SCENE_SHOT_EXAMPLES.find((example) => example.id === id) ?? null;
1843
+ function assertSceneShotExamplesValid() {
1844
+ for (const example of SCENE_SHOT_EXAMPLES) SceneShotSpecV2.parse(example.spec);
1845
+ }
1846
+
1847
+ // ../composition/src/scene/budget.ts
1848
+ var STUDIO_SHADOW_MAP_BUDGET = 4096;
1849
+ var SCENE_TESSELLATION_VERSION = 1;
1850
+ var SCENE_SLAB_BODY_TRIANGLES = 5484;
1851
+ var SCENE_SLAB_CONTENT_FACE_TRIANGLES = 2;
1852
+ var SCENE_SLAB_CONTENT_OFFSET = 0.6;
1853
+ var SCENE_PRESET_TRIANGLES = Object.freeze({
1854
+ laptop: 1954,
1855
+ phone: 3136,
1856
+ slab: 5486,
1857
+ page: 2
1858
+ });
1859
+ var BYTES_PER_TEXEL = Object.freeze({
1860
+ rgba8: 4,
1861
+ rgba16f: 8,
1862
+ rgba32f: 16,
1863
+ depth24: 4
1864
+ });
1865
+ function positiveInteger(value, field) {
1866
+ if (!Number.isSafeInteger(value) || value < 1) {
1867
+ throw new Error(`scene_allocation_declaration_invalid: ${field}`);
1868
+ }
1869
+ return value;
1870
+ }
1871
+ function sceneFullMipLevels(width, height) {
1872
+ positiveInteger(width, "width");
1873
+ positiveInteger(height, "height");
1874
+ return Math.floor(Math.log2(Math.max(width, height))) + 1;
1875
+ }
1876
+ function sceneMipTexels(width, height, mipLevels) {
1877
+ positiveInteger(width, "width");
1878
+ positiveInteger(height, "height");
1879
+ positiveInteger(mipLevels, "mipLevels");
1880
+ const maximum = sceneFullMipLevels(width, height);
1881
+ if (mipLevels > maximum) {
1882
+ throw new Error(`scene_allocation_declaration_invalid: mipLevels exceeds full chain ${maximum}`);
1883
+ }
1884
+ let texels = 0;
1885
+ for (let level = 0; level < mipLevels; level += 1) {
1886
+ texels += Math.max(1, Math.floor(width / 2 ** level)) * Math.max(1, Math.floor(height / 2 ** level));
1887
+ }
1888
+ if (!Number.isSafeInteger(texels)) throw new Error("scene_allocation_declaration_invalid: texel count");
1889
+ return texels;
1890
+ }
1891
+ function sceneTextureBytes(allocation2) {
1892
+ const faces = positiveInteger(allocation2.faces ?? 1, "faces");
1893
+ const layers = positiveInteger(allocation2.layers ?? 1, "layers");
1894
+ const samples = positiveInteger(allocation2.samples ?? 1, "samples");
1895
+ const bytes = sceneMipTexels(allocation2.width, allocation2.height, allocation2.mipLevels) * BYTES_PER_TEXEL[allocation2.format] * faces * layers * samples;
1896
+ if (!Number.isSafeInteger(bytes)) throw new Error("scene_allocation_declaration_invalid: byte count");
1897
+ return bytes;
1898
+ }
1899
+ function sceneTriangleCount(presets) {
1900
+ const total = presets.reduce((sum, preset) => {
1901
+ if (!Object.prototype.hasOwnProperty.call(SCENE_PRESET_TRIANGLES, preset)) {
1902
+ throw new Error(`scene_triangle_preset_unsupported: ${String(preset)}`);
1903
+ }
1904
+ return sum + SCENE_PRESET_TRIANGLES[preset];
1905
+ }, 0);
1906
+ if (!Number.isSafeInteger(total)) throw new Error("scene_triangle_declaration_invalid");
1907
+ return total;
1908
+ }
1909
+
1910
+ // ../composition/src/scene/device.ts
1911
+ function sceneDeviceDimensionsV1(kind, width) {
1912
+ return kind === "phone" ? { w: width, h: Math.round(width * 2.05), t: Math.round(width * 0.03), d: 0, screenInset: Math.round(width * 0.035), radius: Math.round(width * 0.13) } : { w: width, h: Math.round(width * 0.66), t: Math.round(width * 0.011), d: Math.round(width * 0.68), screenInset: Math.round(width * 0.02), radius: Math.round(width * 0.012) };
1913
+ }
1914
+ function sceneLaptopLayoutV1(dim) {
1915
+ const baseY = -dim.h * 0.32;
1916
+ return { baseY, hingeFromBaseY: dim.t / 2, hingeZ: -dim.d / 2, hingeY: baseY + dim.t / 2, lidCentreY: dim.h / 2, bottomY: baseY - dim.t / 2 };
1917
+ }
1918
+ var SCENE_DEVICE_SCREEN_OFFSET_V1 = 0.6;
1919
+ function sceneLaptopScreenQuadV1(node) {
1920
+ const dim = sceneDeviceDimensionsV1("laptop", node.width);
1921
+ const layout = sceneLaptopLayoutV1(dim);
1922
+ const theta = Math.PI / 2 - node.openAngle;
1923
+ const c = Math.cos(theta);
1924
+ const s = Math.sin(theta);
1925
+ const w = dim.w * 0.86 / 2;
1926
+ const h = dim.h * 0.86 / 2;
1927
+ const z2 = dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1;
1928
+ const point = (x, y) => [x, layout.hingeY + (layout.lidCentreY + y) * c - z2 * s, layout.hingeZ + (layout.lidCentreY + y) * s + z2 * c];
1929
+ return [point(-w, h), point(w, h), point(w, -h), point(-w, -h)];
1930
+ }
1931
+
1932
+ // ../composition/src/scene/photographicRig.ts
1933
+ var PHOTOGRAPHIC_STUDIO_RIG_ID = "photographic-studio-v1";
1934
+ var PHOTOGRAPHIC_STUDIO_HDRI_PATH = "environments/brown_photostudio_02.hdr";
1935
+ var PHOTOGRAPHIC_STUDIO_HDRI_SHA256 = "469abc5e6035b6c450bae8adbb448f8d1fa7c51d9e384e501516c28352c91b58";
1936
+ var PHOTOGRAPHIC_STUDIO_HDRI_BYTES = 1648130;
1937
+ var PHOTOGRAPHIC_STUDIO_PMREM_VERSION = "three-0.183.2-fromEquirectangular-cubeuv256-v1";
1938
+ var PHOTOGRAPHIC_STUDIO_LIGHTING_VERSION = 1;
1939
+ var PHOTOGRAPHIC_STUDIO_EDGE_INTENSITY = 26;
1940
+ var PHOTOGRAPHIC_STUDIO_EDGE_LIGHTS = Object.freeze([
1941
+ Object.freeze([2e3, 350, -700]),
1942
+ Object.freeze([-2e3, 350, -700])
1943
+ ]);
1944
+ var PHOTOGRAPHIC_STUDIO_GPU_BYTES = Object.freeze({
1945
+ sourceRgba16f: 1024 * 512 * 8,
1946
+ pmremTargetRgba16f: 768 * 1024 * 8,
1947
+ pmremTransientRgba16f: 768 * 1024 * 8,
1948
+ preparationPeak: 1024 * 512 * 8 + 2 * 768 * 1024 * 8,
1949
+ retained: 768 * 1024 * 8
1950
+ });
1951
+ var PHOTOGRAPHIC_STUDIO_RIG_FACTS = Object.freeze({
1952
+ rigId: PHOTOGRAPHIC_STUDIO_RIG_ID,
1953
+ hdri: Object.freeze({
1954
+ path: PHOTOGRAPHIC_STUDIO_HDRI_PATH,
1955
+ sha256: PHOTOGRAPHIC_STUDIO_HDRI_SHA256,
1956
+ bytes: PHOTOGRAPHIC_STUDIO_HDRI_BYTES
1957
+ }),
1958
+ pmremVersion: PHOTOGRAPHIC_STUDIO_PMREM_VERSION,
1959
+ lightingVersion: PHOTOGRAPHIC_STUDIO_LIGHTING_VERSION,
1960
+ studioPreset: "soft",
1961
+ hemisphere: Object.freeze({ skyColor: "#ffffff", groundColor: "#202024", intensity: 0.7 }),
1962
+ studioLights: Object.freeze([
1963
+ Object.freeze({ role: "key", position: Object.freeze([700, 1100, 1300]), intensity: 1.6, castShadow: false }),
1964
+ Object.freeze({ role: "fill", position: Object.freeze([-1100, 400, 900]), intensity: 0.9, castShadow: false }),
1965
+ Object.freeze({ role: "rim", position: Object.freeze([0, 1300, -1900]), intensity: 1, castShadow: false })
1966
+ ]),
1967
+ edgeLights: Object.freeze(
1968
+ PHOTOGRAPHIC_STUDIO_EDGE_LIGHTS.map(
1969
+ (position) => Object.freeze({ position, intensity: PHOTOGRAPHIC_STUDIO_EDGE_INTENSITY, castShadow: false })
1970
+ )
1971
+ ),
1972
+ environmentIntensity: 1,
1973
+ floor: "none",
1974
+ shadows: "none",
1975
+ gpuBytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES
1976
+ });
1977
+ function photographicStudioPreparationFactsV1(worldScale) {
1978
+ return {
1979
+ identity: {
1980
+ rigId: PHOTOGRAPHIC_STUDIO_RIG_FACTS.rigId,
1981
+ hdriSha256: PHOTOGRAPHIC_STUDIO_RIG_FACTS.hdri.sha256,
1982
+ pmremVersion: PHOTOGRAPHIC_STUDIO_RIG_FACTS.pmremVersion,
1983
+ lightingVersion: PHOTOGRAPHIC_STUDIO_RIG_FACTS.lightingVersion,
1984
+ worldScale,
1985
+ hemisphere: PHOTOGRAPHIC_STUDIO_RIG_FACTS.hemisphere,
1986
+ lightLayout: PHOTOGRAPHIC_STUDIO_RIG_FACTS.studioLights,
1987
+ edgeLightLayout: PHOTOGRAPHIC_STUDIO_RIG_FACTS.edgeLights,
1988
+ environmentIntensity: PHOTOGRAPHIC_STUDIO_RIG_FACTS.environmentIntensity,
1989
+ floor: PHOTOGRAPHIC_STUDIO_RIG_FACTS.floor,
1990
+ shadows: PHOTOGRAPHIC_STUDIO_RIG_FACTS.shadows,
1991
+ retainedGpuBytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.retained,
1992
+ preparationPeakGpuBytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.preparationPeak
1993
+ },
1994
+ allocations: [
1995
+ { owner: "rig:photographic-studio-v1", name: "encoded-hdri", domain: "cpu", resource: "encoded-image", format: "rgbe", bytes: PHOTOGRAPHIC_STUDIO_HDRI_BYTES, lifetime: "preparation" },
1996
+ { owner: "rig:photographic-studio-v1", name: "encoded-hdri-working-copy", domain: "cpu", resource: "encoded-image", format: "rgbe", bytes: PHOTOGRAPHIC_STUDIO_HDRI_BYTES, lifetime: "preparation" },
1997
+ { owner: "rig:photographic-studio-v1", name: "decoded-hdri", domain: "cpu", resource: "decoded-image", format: "rgba16f", bytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.sourceRgba16f, lifetime: "preparation" },
1998
+ { owner: "rig:photographic-studio-v1", name: "rgbe-source", domain: "gl", resource: "pmrem-source", format: "rgba16f", bytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.sourceRgba16f, lifetime: "preparation" },
1999
+ { owner: "rig:photographic-studio-v1", name: "pmrem-transient", domain: "gl", resource: "pmrem-target", format: "rgba16f", bytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.pmremTransientRgba16f, lifetime: "preparation" },
2000
+ { owner: "rig:photographic-studio-v1", name: "pmrem-environment", domain: "gl", resource: "pmrem-target", format: "rgba16f", bytes: PHOTOGRAPHIC_STUDIO_GPU_BYTES.pmremTargetRgba16f, lifetime: "context" }
2001
+ ]
2002
+ };
2003
+ }
2004
+ var GRAIN_LATTICE_BYTES = 24672;
2005
+ function sceneFocusPreparationAllocationsV1(width, height, samples = 4) {
2006
+ if (!Number.isSafeInteger(width) || width <= 0 || !Number.isSafeInteger(height) || height <= 0)
2007
+ throw new Error("scene_focus_allocation_dimensions_invalid");
2008
+ if (samples !== 4) throw new Error("scene_focus_allocation_samples_invalid");
2009
+ const pixels = width * height;
2010
+ return [
2011
+ { owner: "post:focus-resources", name: "world-color-resolve", domain: "gl", resource: "texture", format: "rgba16f", bytes: pixels * 8, lifetime: "context" },
2012
+ { owner: "post:focus-resources", name: "world-depth", domain: "gl", resource: "texture", format: "depth32u", bytes: pixels * 4, lifetime: "context" },
2013
+ { owner: "post:focus-resources", name: "gathered-color", domain: "gl", resource: "texture", format: "rgba16f", bytes: pixels * 8, lifetime: "context" },
2014
+ { owner: "post:focus-resources", name: "grain-lattice", domain: "gl", resource: "texture", format: "rgba32f", bytes: GRAIN_LATTICE_BYTES, lifetime: "context" },
2015
+ { owner: "post:focus-resources", name: "grain-lattice-array", domain: "cpu", resource: "decoded-image", format: "rgba32f", bytes: GRAIN_LATTICE_BYTES, lifetime: "context" },
2016
+ { owner: "post:focus-resources", name: "world-color-msaa", domain: "gl", resource: "renderbuffer", format: "rgba16f-msaa-4", bytes: pixels * 8 * 4, lifetime: "context" },
2017
+ { owner: "post:focus-resources", name: "world-depth-msaa", domain: "gl", resource: "renderbuffer", format: "depth32u-msaa-4", bytes: pixels * 4 * 4, lifetime: "context" }
2018
+ ];
2019
+ }
2020
+ function photographicSceneRigFactsV1(worldScale, width, height) {
2021
+ const rig = photographicStudioPreparationFactsV1(worldScale);
2022
+ return { identity: rig.identity, allocations: [...rig.allocations, ...sceneFocusPreparationAllocationsV1(width, height, 4)] };
2023
+ }
2024
+
2025
+ // ../composition/src/scene/videoSchedule.ts
2026
+ var SceneVideoAdmissionRefusal = class extends Error {
2027
+ constructor(code, message) {
2028
+ super(`${code}: ${message} ${code.startsWith("video_") && (code === "video_presentation_index_missing" || code === "video_variable_frame_rate") ? "Render or transcode this asset to CFR with the existing media tools, then prepare the scene again." : ""}`.trim());
2029
+ this.code = code;
2030
+ this.name = "SceneVideoAdmissionRefusal";
2031
+ }
2032
+ code;
2033
+ corrective = "Render or transcode this asset to CFR with the existing media tools, then prepare the scene again.";
2034
+ };
2035
+ function positiveInteger2(value, field) {
2036
+ if (!Number.isSafeInteger(value) || value <= 0) throw new SceneVideoAdmissionRefusal("video_rate_invalid", `${field} must be a positive safe integer.`);
2037
+ return BigInt(value);
2038
+ }
2039
+ function gcd(a, b) {
2040
+ while (b !== 0n) [a, b] = [b, a % b];
2041
+ return a;
2042
+ }
2043
+ function canonicalSceneVideoRateV1(value, field = "rate") {
2044
+ const num = positiveInteger2(value.num, `${field}.num`);
2045
+ const den = positiveInteger2(value.den, `${field}.den`);
2046
+ const divisor = gcd(num, den);
2047
+ return { num: Number(num / divisor), den: Number(den / divisor) };
2048
+ }
2049
+ function canonicalBigRate(num, den, field) {
2050
+ const divisor = gcd(num, den);
2051
+ const n = num / divisor;
2052
+ const d = den / divisor;
2053
+ if (n > BigInt(Number.MAX_SAFE_INTEGER) || d > BigInt(Number.MAX_SAFE_INTEGER)) throw new SceneVideoAdmissionRefusal("video_rate_invalid", `${field} exceeds exact safe-integer transport.`);
2054
+ return { num: Number(n), den: Number(d) };
2055
+ }
2056
+ function verifySceneVideoPresentationIndexV1(index) {
2057
+ if (!index || !Array.isArray(index.pts) || index.pts.length < 2) {
2058
+ throw new SceneVideoAdmissionRefusal("video_presentation_index_missing", "At least two verified presentation timestamps are required.");
2059
+ }
2060
+ if (!index.pts.every(Number.isSafeInteger)) {
2061
+ throw new SceneVideoAdmissionRefusal("video_presentation_index_missing", "Presentation timestamps must be exact integers in the verified stream time base.");
2062
+ }
2063
+ const step = index.pts[1] - index.pts[0];
2064
+ if (step <= 0 || index.pts.some((pts, i) => i > 0 && pts - index.pts[i - 1] !== step)) {
2065
+ throw new SceneVideoAdmissionRefusal("video_variable_frame_rate", "Presentation timestamps do not form one constant positive interval.");
2066
+ }
2067
+ const timeBase = canonicalSceneVideoRateV1(index.timeBase, "timeBase");
2068
+ return {
2069
+ sourceRate: canonicalBigRate(BigInt(timeBase.den), BigInt(timeBase.num) * BigInt(step), "sourceRate"),
2070
+ sourceFrameCount: index.pts.length
2071
+ };
2072
+ }
2073
+ function prepareSceneVideoScheduleV1(input) {
2074
+ const { sourceRate, sourceFrameCount } = verifySceneVideoPresentationIndexV1(input.presentationIndex);
2075
+ const shotRate = canonicalSceneVideoRateV1(input.shotRate, "shotRate");
2076
+ if (!Number.isSafeInteger(input.contentDuration) || input.contentDuration <= 0) throw new SceneVideoAdmissionRefusal("video_rate_invalid", "contentDuration must be a positive safe integer.");
2077
+ if (!Number.isSafeInteger(input.startFrame) || input.endFrame !== void 0 && !Number.isSafeInteger(input.endFrame)) {
2078
+ throw new SceneVideoAdmissionRefusal("video_trim_not_integer", "Video trims are presentation-frame indices.");
2079
+ }
2080
+ const endFrame = input.endFrame ?? sourceFrameCount;
2081
+ if (input.startFrame < 0 || endFrame <= input.startFrame || endFrame > sourceFrameCount) {
2082
+ throw new SceneVideoAdmissionRefusal("video_trim_out_of_range", "Expected 0 <= startFrame < endFrame <= sourceFrameCount.");
2083
+ }
2084
+ return { sourceRate, shotRate, contentDuration: input.contentDuration, startFrame: input.startFrame, endFrame, sourceFrameCount, addressing: "midpoint" };
2085
+ }
2086
+ function sceneVideoSourceIndexV1(frame, schedule) {
2087
+ if (!Number.isSafeInteger(frame) || frame < 0) throw new SceneVideoAdmissionRefusal("video_rate_invalid", "content frame must be a nonnegative safe integer.");
2088
+ const c = BigInt(Math.min(frame, schedule.contentDuration - 1));
2089
+ const numerator = c * BigInt(schedule.sourceRate.num) * BigInt(schedule.shotRate.den);
2090
+ const denominator = BigInt(schedule.sourceRate.den) * BigInt(schedule.shotRate.num);
2091
+ const selected = BigInt(schedule.startFrame) + numerator / denominator;
2092
+ return Number(selected < BigInt(schedule.endFrame) ? selected : BigInt(schedule.endFrame - 1));
2093
+ }
2094
+ function sceneVideoMidpointAddressV1(frame, schedule) {
2095
+ const sourceIndex = sceneVideoSourceIndexV1(frame, schedule);
2096
+ return {
2097
+ sourceIndex,
2098
+ mediaFrame: sourceIndex * 2 + 1,
2099
+ playbackRate: schedule.shotRate.num * schedule.sourceRate.den / (schedule.shotRate.den * 2 * schedule.sourceRate.num)
2100
+ };
2101
+ }
2102
+
2103
+ // ../composition/src/scene/resolution.ts
2104
+ var RESOLUTION_BOUND = 1;
2105
+ var ALLOWED_BAKE_SCALES = [2, 3];
2106
+ var DEFAULT_BAKE_SCALE = 2;
2107
+ function sceneCameraPlanesV2(referenceHeight, fov, scale) {
2108
+ const perspective = referenceHeight / (2 * Math.tan(fov / 2));
2109
+ const p = perspective * scale;
2110
+ return { near: p / 100, far: p * 40 };
2111
+ }
2112
+ var SceneResolutionRefusal = class extends Error {
2113
+ code;
2114
+ path;
2115
+ detail;
2116
+ constructor(code, message, path, detail) {
2117
+ super(message);
2118
+ this.name = "SceneResolutionRefusal";
2119
+ this.code = code;
2120
+ this.path = path;
2121
+ this.detail = detail;
2122
+ }
2123
+ };
2124
+ var IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
2125
+ function mul(a, b) {
2126
+ const o = new Array(16).fill(0);
2127
+ for (let c = 0; c < 4; c += 1) {
2128
+ for (let r = 0; r < 4; r += 1) {
2129
+ let s = 0;
2130
+ for (let k = 0; k < 4; k += 1) s += a[k * 4 + r] * b[c * 4 + k];
2131
+ o[c * 4 + r] = s;
2132
+ }
2133
+ }
2134
+ return o;
2135
+ }
2136
+ var translate = (x, y, z2) => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z2, 1];
2137
+ var scaleM = (s) => [s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1];
2138
+ var rotX = (a) => {
2139
+ const c = Math.cos(a);
2140
+ const s = Math.sin(a);
2141
+ return [1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1];
2142
+ };
2143
+ var rotY = (a) => {
2144
+ const c = Math.cos(a);
2145
+ const s = Math.sin(a);
2146
+ return [c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1];
2147
+ };
2148
+ var rotZ = (a) => {
2149
+ const c = Math.cos(a);
2150
+ const s = Math.sin(a);
2151
+ return [c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
2152
+ };
2153
+ var applyM = (m, p) => [
2154
+ m[0] * p[0] + m[4] * p[1] + m[8] * p[2] + m[12],
2155
+ m[1] * p[0] + m[5] * p[1] + m[9] * p[2] + m[13],
2156
+ m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14]
2157
+ ];
2158
+ function trs(t2, scale) {
2159
+ return mul(
2160
+ mul(
2161
+ translate(t2.position[0] * scale, t2.position[1] * scale, t2.position[2] * scale),
2162
+ mul(rotX(t2.rotation[0]), mul(rotY(t2.rotation[1]), rotZ(t2.rotation[2])))
2163
+ ),
2164
+ scaleM(t2.scale)
2165
+ );
2166
+ }
2167
+ function projectToOutputPixels(world, camera, output, scale) {
2168
+ const eye = [camera.position[0] * scale, camera.position[1] * scale, camera.position[2] * scale];
2169
+ const R = mul(rotX(camera.rotation[0]), mul(rotY(camera.rotation[1]), rotZ(camera.rotation[2])));
2170
+ const d = [world[0] - eye[0], world[1] - eye[1], world[2] - eye[2]];
2171
+ const cam = [
2172
+ R[0] * d[0] + R[1] * d[1] + R[2] * d[2],
2173
+ R[4] * d[0] + R[5] * d[1] + R[6] * d[2],
2174
+ R[8] * d[0] + R[9] * d[1] + R[10] * d[2]
2175
+ ];
2176
+ if (cam[2] >= -1e-9) return null;
2177
+ const P = output.height / (2 * Math.tan(camera.fov / 2));
2178
+ return [output.width / 2 + P * cam[0] / -cam[2], output.height / 2 - P * cam[1] / -cam[2]];
2179
+ }
2180
+ var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
2181
+ function projectedSlotEdges({ slot, chain, camera, output, referenceSize, projectionQuad, path, frame }) {
2182
+ const scale = output.width / referenceSize.width;
2183
+ let m = IDENTITY;
2184
+ for (const t2 of chain) m = mul(m, trs(t2, scale));
2185
+ const hw = slot.width * scale / 2;
2186
+ const hh = slot.height * scale / 2;
2187
+ const supplied = projectionQuad;
2188
+ const local = supplied ? {
2189
+ tl: supplied[0].map((v) => v * scale),
2190
+ tr: supplied[1].map((v) => v * scale),
2191
+ br: supplied[2].map((v) => v * scale),
2192
+ bl: supplied[3].map((v) => v * scale)
2193
+ } : { tl: [-hw, hh, 0], tr: [hw, hh, 0], br: [hw, -hh, 0], bl: [-hw, -hh, 0] };
2194
+ const p = {};
2195
+ for (const key of ["tl", "tr", "br", "bl"]) {
2196
+ const q = projectToOutputPixels(applyM(m, local[key]), camera, output, scale);
2197
+ if (!q) return { behindEye: true, corner: key };
2198
+ p[key] = q;
2199
+ }
2200
+ const widthPx = (dist(p.tl, p.tr) + dist(p.bl, p.br)) / 2;
2201
+ const heightPx = (dist(p.tl, p.bl) + dist(p.tr, p.br)) / 2;
2202
+ if (!Number.isFinite(widthPx) || !Number.isFinite(heightPx) || widthPx <= 0 || heightPx <= 0) {
2203
+ throw new SceneResolutionRefusal(
2204
+ "scene_surface_resolution_insufficient",
2205
+ "Projected slot edges are non-finite or degenerate.",
2206
+ path ?? "surface.projection",
2207
+ { widthPx, heightPx, ...frame === void 0 ? {} : { frame } }
2208
+ );
2209
+ }
2210
+ return { behindEye: false, widthPx, heightPx, corners: p };
2211
+ }
2212
+ function checkSurfaceResolution(args) {
2213
+ const { surfaceId, slotName, slot, layout, bakeScale, contentDuration, output, referenceSize, evaluate } = args;
2214
+ const raster = { width: layout.width * bakeScale, height: layout.height * bakeScale };
2215
+ let worst = Infinity;
2216
+ let worstAt = null;
2217
+ for (let f = 0; f < contentDuration; f += 1) {
2218
+ const { camera, chain, projectionQuad } = evaluate(f);
2219
+ const e = projectedSlotEdges({ slot, chain, camera, output, referenceSize, projectionQuad, path: `layers.${surfaceId}.surfaces.${slotName}`, frame: f });
2220
+ if (e.behindEye) {
2221
+ throw new SceneResolutionRefusal(
2222
+ "scene_surface_behind_eye",
2223
+ `"${surfaceId}" slot "${slotName}" puts corner ${e.corner} at or behind the eye plane at content frame ${f}; projected magnification is unbounded there, so this is refused rather than clamped.`,
2224
+ `layers.${surfaceId}.surfaces.${slotName}`,
2225
+ { frame: f, corner: e.corner }
2226
+ );
2227
+ }
2228
+ const densityU = raster.width / e.widthPx;
2229
+ const densityV = raster.height / e.heightPx;
2230
+ const d = Math.min(densityU, densityV);
2231
+ if (d < worst) {
2232
+ worst = d;
2233
+ worstAt = { frame: f, densityU, densityV, axis: densityU <= densityV ? "U" : "V", widthPx: e.widthPx, heightPx: e.heightPx };
2234
+ }
2235
+ }
2236
+ if (!worstAt) {
2237
+ throw new SceneResolutionRefusal(
2238
+ "scene_surface_resolution_insufficient",
2239
+ `"${surfaceId}" slot "${slotName}" has a content duration of ${contentDuration} frames, so the check has no frame to evaluate.`,
2240
+ `layers.${surfaceId}.surfaces.${slotName}`,
2241
+ { contentDuration }
2242
+ );
2243
+ }
2244
+ return { raster, bakeScale, worstDensity: worst, worstAt, meetsBound: worst >= RESOLUTION_BOUND, bound: RESOLUTION_BOUND };
2245
+ }
2246
+ function correctiveLayout(args, worst) {
2247
+ return {
2248
+ width: Math.ceil(args.layout.width / worst.worstDensity),
2249
+ height: Math.ceil(args.layout.height / worst.worstDensity)
2250
+ };
2251
+ }
2252
+ function selectSceneBakeScale(args, declared) {
2253
+ const results = {};
2254
+ for (const s of ALLOWED_BAKE_SCALES) results[s] = checkSurfaceResolution({ ...args, bakeScale: s });
2255
+ const sufficient = ALLOWED_BAKE_SCALES.filter((s) => results[s].meetsBound);
2256
+ if (declared !== void 0) {
2257
+ if (results[declared].meetsBound) return { selected: declared, reason: "declared", results };
2258
+ const needed = sufficient.length ? Math.min(...sufficient) : null;
2259
+ const worst = results[declared];
2260
+ throw new SceneResolutionRefusal(
2261
+ "scene_surface_resolution_insufficient",
2262
+ `"${args.surfaceId}" slot "${args.slotName}" declares bakeScale ${declared}x, which gives ${worst.worstDensity.toFixed(3)} source texels per output pixel on the ${worst.worstAt.axis} axis at content frame ${worst.worstAt.frame} (bound ${RESOLUTION_BOUND}). ` + (needed ? `Declare bakeScale ${needed}x.` : `No supported bake scale (${ALLOWED_BAKE_SCALES.join("x, ")}x) reaches the bound; increase the declared layout from ${args.layout.width}x${args.layout.height} to at least ${correctiveLayout(args, worst).width}x${correctiveLayout(args, worst).height}, or move the surface further from the camera.`),
2263
+ `layers.${args.surfaceId}.surfaces.${args.slotName}`,
2264
+ { declared, worst, neededScale: needed, correctiveLayout: correctiveLayout(args, worst) }
2265
+ );
2266
+ }
2267
+ if (!sufficient.length) {
2268
+ const largest = Math.max(...ALLOWED_BAKE_SCALES);
2269
+ const best = results[largest];
2270
+ throw new SceneResolutionRefusal(
2271
+ "scene_surface_resolution_insufficient",
2272
+ `"${args.surfaceId}" slot "${args.slotName}" reaches only ${best.worstDensity.toFixed(3)} source texels per output pixel on the ${best.worstAt.axis} axis at content frame ${best.worstAt.frame} even at ${largest}x (bound ${RESOLUTION_BOUND}). Increase the declared layout from ${args.layout.width}x${args.layout.height} to at least ${correctiveLayout(args, best).width}x${correctiveLayout(args, best).height}, or move the surface further from the camera.`,
2273
+ `layers.${args.surfaceId}.surfaces.${args.slotName}`,
2274
+ { worst: best, neededScale: null, correctiveLayout: correctiveLayout(args, best) }
2275
+ );
2276
+ }
2277
+ const selected = Math.min(...sufficient);
2278
+ return {
2279
+ selected,
2280
+ reason: selected === DEFAULT_BAKE_SCALE ? "default-sufficient" : "raised-by-resolution-check",
2281
+ results
2282
+ };
2283
+ }
2284
+
2285
+ // ../composition/src/scene/preparedRender.ts
2286
+ var ScenePreparedPlanRefusal = class extends Error {
2287
+ constructor(path, message) {
2288
+ super(message);
2289
+ this.path = path;
2290
+ this.name = "ScenePreparedPlanRefusal";
2291
+ }
2292
+ path;
2293
+ };
2294
+ function record(value, path) {
2295
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new ScenePreparedPlanRefusal(path, `${path} must be an object.`);
2296
+ return value;
2297
+ }
2298
+ function exactKeys(value, keys, path) {
2299
+ const unknown = Object.keys(value).find((key) => !keys.includes(key));
2300
+ if (unknown) throw new ScenePreparedPlanRefusal(`${path}.${unknown}`, `Unknown sealed plan field ${path}.${unknown}.`);
2301
+ const missing = keys.find((key) => value[key] === void 0);
2302
+ if (missing) throw new ScenePreparedPlanRefusal(`${path}.${missing}`, `Missing sealed plan field ${path}.${missing}.`);
2303
+ }
2304
+ function finiteNumber(value, path, options = {}) {
2305
+ if (typeof value !== "number" || !Number.isFinite(value) || options.integer && !Number.isSafeInteger(value) || options.positive && value <= 0 || options.nonnegative && value < 0)
2306
+ throw new ScenePreparedPlanRefusal(path, `${path} is invalid.`);
2307
+ return value;
2308
+ }
2309
+ function stringValue(value, path) {
2310
+ if (typeof value !== "string" || value.length === 0) throw new ScenePreparedPlanRefusal(path, `${path} must be a non-empty string.`);
2311
+ return value;
2312
+ }
2313
+ function booleanValue(value, path) {
2314
+ if (typeof value !== "boolean") throw new ScenePreparedPlanRefusal(path, `${path} must be boolean.`);
2315
+ return value;
2316
+ }
2317
+ function arrayValue(value, path) {
2318
+ if (!Array.isArray(value)) throw new ScenePreparedPlanRefusal(path, `${path} must be an array.`);
2319
+ return value;
2320
+ }
2321
+ function enumValue(value, values, path) {
2322
+ if (typeof value !== "string" || !values.includes(value)) throw new ScenePreparedPlanRefusal(path, `${path} is invalid.`);
2323
+ return value;
2324
+ }
2325
+ function sha256Value(value, path) {
2326
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) throw new ScenePreparedPlanRefusal(path, `${path} must be a lowercase SHA-256 digest.`);
2327
+ return value;
2328
+ }
2329
+ function allocation(value, path) {
2330
+ const row = record(value, path);
2331
+ exactKeys(row, ["owner", "name", "domain", "resource", "format", "bytes", "lifetime"], path);
2332
+ stringValue(row.owner, `${path}.owner`);
2333
+ stringValue(row.name, `${path}.name`);
2334
+ stringValue(row.format, `${path}.format`);
2335
+ if (!["gl", "cpu"].includes(row.domain)) throw new ScenePreparedPlanRefusal(`${path}.domain`, "Invalid allocation domain.");
2336
+ if (!["texture", "renderbuffer", "geometry", "pmrem-source", "pmrem-target", "encoded-image", "decoded-image", "encoded-video", "decoded-video"].includes(row.resource))
2337
+ throw new ScenePreparedPlanRefusal(`${path}.resource`, "Invalid allocation resource.");
2338
+ if (!["preparation", "context"].includes(row.lifetime)) throw new ScenePreparedPlanRefusal(`${path}.lifetime`, "Invalid allocation lifetime.");
2339
+ finiteNumber(row.bytes, `${path}.bytes`, {
2340
+ integer: true,
2341
+ nonnegative: true
2342
+ });
2343
+ }
2344
+ function allocationArray(value, path) {
2345
+ if (!Array.isArray(value)) throw new ScenePreparedPlanRefusal(path, `${path} must be an array.`);
2346
+ value.forEach((entry, index) => allocation(entry, `${path}.${index}`));
2347
+ }
2348
+ function vec3(value, path) {
2349
+ if (!Array.isArray(value) || value.length !== 3) throw new ScenePreparedPlanRefusal(path, `${path} must be a vec3.`);
2350
+ value.forEach((entry, index) => finiteNumber(entry, `${path}.${index}`));
2351
+ }
2352
+ function slotDescriptor(value, path) {
2353
+ const slot = record(value, path);
2354
+ exactKeys(slot, ["slot", "attachment", "dimensions", "projectionQuad", "uvOrigin", "fit", "alpha", "depth", "required", "bakeScales"], path);
2355
+ stringValue(slot.slot, `${path}.slot`);
2356
+ const attachment = record(slot.attachment, `${path}.attachment`);
2357
+ exactKeys(attachment, ["partId", "materialRole"], `${path}.attachment`);
2358
+ stringValue(attachment.partId, `${path}.attachment.partId`);
2359
+ stringValue(attachment.materialRole, `${path}.attachment.materialRole`);
2360
+ const dimensions = record(slot.dimensions, `${path}.dimensions`);
2361
+ exactKeys(dimensions, ["width", "height"], `${path}.dimensions`);
2362
+ finiteNumber(dimensions.width, `${path}.dimensions.width`, {
2363
+ positive: true
2364
+ });
2365
+ finiteNumber(dimensions.height, `${path}.dimensions.height`, {
2366
+ positive: true
2367
+ });
2368
+ const quad = arrayValue(slot.projectionQuad, `${path}.projectionQuad`);
2369
+ if (quad.length !== 4) throw new ScenePreparedPlanRefusal(`${path}.projectionQuad`, "Projection quad must contain four points.");
2370
+ quad.forEach((point, index) => vec3(point, `${path}.projectionQuad.${index}`));
2371
+ enumValue(slot.uvOrigin, ["top-left", "bottom-left"], `${path}.uvOrigin`);
2372
+ enumValue(slot.fit, ["contain", "cover", "stretch"], `${path}.fit`);
2373
+ enumValue(slot.alpha, ["opaque", "straight", "premultiplied"], `${path}.alpha`);
2374
+ enumValue(slot.depth, ["write", "test-only"], `${path}.depth`);
2375
+ booleanValue(slot.required, `${path}.required`);
2376
+ const scales = arrayValue(slot.bakeScales, `${path}.bakeScales`);
2377
+ if (scales.length !== 2 || scales[0] !== 2 || scales[1] !== 3) throw new ScenePreparedPlanRefusal(`${path}.bakeScales`, "Bake scales must be exactly [2,3].");
2378
+ }
2379
+ function descriptor(value, path) {
2380
+ const row = record(value, path);
2381
+ exactKeys(
2382
+ row,
2383
+ ["kind", "builderVersion", "tessellationVersion", "budgetTriangles", "parts", "surfaceSlots", "materialRoles", "localBounds", "allocations"],
2384
+ path
2385
+ );
2386
+ enumValue(row.kind, ["laptop", "phone", "slab", "page", "text"], `${path}.kind`);
2387
+ finiteNumber(row.builderVersion, `${path}.builderVersion`, {
2388
+ integer: true,
2389
+ positive: true
2390
+ });
2391
+ finiteNumber(row.tessellationVersion, `${path}.tessellationVersion`, {
2392
+ integer: true,
2393
+ positive: true
2394
+ });
2395
+ finiteNumber(row.budgetTriangles, `${path}.budgetTriangles`, {
2396
+ integer: true,
2397
+ nonnegative: true
2398
+ });
2399
+ const presetBudget = SCENE_PRESET_TRIANGLES[row.kind];
2400
+ if (presetBudget !== void 0 && row.budgetTriangles !== presetBudget)
2401
+ throw new ScenePreparedPlanRefusal(`${path}.budgetTriangles`, "Builder budget differs from the frozen preset ceiling.");
2402
+ const parts = arrayValue(row.parts, `${path}.parts`);
2403
+ const slots = arrayValue(row.surfaceSlots, `${path}.surfaceSlots`);
2404
+ const roles = arrayValue(row.materialRoles, `${path}.materialRoles`);
2405
+ if (!roles.length) throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, "At least one material role is required.");
2406
+ roles.forEach((role, index) => stringValue(role, `${path}.materialRoles.${index}`));
2407
+ if (new Set(roles).size !== roles.length) throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, "Material roles must be unique.");
2408
+ parts.forEach((entry, index) => {
2409
+ const part = record(entry, `${path}.parts.${index}`);
2410
+ exactKeys(part, ["partId", "materialRole", "triangles", "mounted"], `${path}.parts.${index}`);
2411
+ stringValue(part.partId, `${path}.parts.${index}.partId`);
2412
+ stringValue(part.materialRole, `${path}.parts.${index}.materialRole`);
2413
+ finiteNumber(part.triangles, `${path}.parts.${index}.triangles`, {
2414
+ integer: true,
2415
+ nonnegative: true
2416
+ });
2417
+ if (typeof part.mounted !== "boolean") throw new ScenePreparedPlanRefusal(`${path}.parts.${index}.mounted`, "mounted must be boolean.");
2418
+ });
2419
+ slots.forEach((entry, index) => slotDescriptor(entry, `${path}.surfaceSlots.${index}`));
2420
+ const bounds = record(row.localBounds, `${path}.localBounds`);
2421
+ exactKeys(bounds, ["center", "halfExtents"], `${path}.localBounds`);
2422
+ vec3(bounds.center, `${path}.localBounds.center`);
2423
+ vec3(bounds.halfExtents, `${path}.localBounds.halfExtents`);
2424
+ allocationArray(row.allocations, `${path}.allocations`);
2425
+ const halfExtents = bounds.halfExtents;
2426
+ if (halfExtents.some((entry) => entry < 0) || halfExtents.every((entry) => entry === 0))
2427
+ throw new ScenePreparedPlanRefusal(`${path}.localBounds.halfExtents`, "Bounds must be nonnegative and nondegenerate.");
2428
+ const partRows = parts;
2429
+ if (new Set(partRows.map((part) => part.partId)).size !== partRows.length) throw new ScenePreparedPlanRefusal(`${path}.parts`, "Part ids must be unique.");
2430
+ for (const part of partRows) if (!roles.includes(part.materialRole)) throw new ScenePreparedPlanRefusal(`${path}.parts`, "Part material role is undeclared.");
2431
+ const slotRows = slots;
2432
+ if (new Set(slotRows.map((slot) => slot.slot)).size !== slotRows.length)
2433
+ throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots`, "Slot ids must be unique.");
2434
+ for (const slot of slotRows) {
2435
+ const attachment = slot.attachment;
2436
+ const part = partRows.find((entry) => entry.partId === attachment.partId);
2437
+ if (!part || part.materialRole !== attachment.materialRole)
2438
+ throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots`, "Slot attachment does not match a declared part.");
2439
+ }
2440
+ }
2441
+ function validateSlabDescriptor(value, slab, path) {
2442
+ const builder = value;
2443
+ if (builder.builderVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.builderVersion`, "Slab builder version must be 1.");
2444
+ if (builder.tessellationVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.tessellationVersion`, "Slab tessellation version must be 1.");
2445
+ const parts = builder.parts;
2446
+ const bound = slab.surfaces?.front !== void 0;
2447
+ if (parts.length !== 2 || parts[0]?.partId !== "body" || parts[0]?.materialRole !== "body" || parts[0]?.mounted !== true || !Number.isSafeInteger(parts[0]?.triangles) || parts[0]?.triangles <= 0 || parts[1]?.partId !== "front" || parts[1]?.materialRole !== "face" || parts[1]?.triangles !== SCENE_SLAB_CONTENT_FACE_TRIANGLES || parts[1]?.mounted !== bound)
2448
+ throw new ScenePreparedPlanRefusal(`${path}.parts`, "Slab parts disagree with the production descriptor contract.");
2449
+ if (canonicalScenePreparedJsonV1(builder.materialRoles) !== canonicalScenePreparedJsonV1(["body", "face"]))
2450
+ throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, "Slab material roles are not canonical.");
2451
+ const slots = builder.surfaceSlots;
2452
+ if (slots.length !== 1 || slots[0]?.slot !== "front") throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots`, "Slab must declare exactly its front slot.");
2453
+ const z2 = slab.thickness / 2 + SCENE_SLAB_CONTENT_OFFSET;
2454
+ const expectedSlot = {
2455
+ slot: "front",
2456
+ attachment: { partId: "front", materialRole: "face" },
2457
+ dimensions: { width: slab.width, height: slab.height },
2458
+ projectionQuad: [
2459
+ [-slab.width / 2, slab.height / 2, z2],
2460
+ [slab.width / 2, slab.height / 2, z2],
2461
+ [slab.width / 2, -slab.height / 2, z2],
2462
+ [-slab.width / 2, -slab.height / 2, z2]
2463
+ ],
2464
+ uvOrigin: "top-left",
2465
+ fit: "contain",
2466
+ alpha: "straight",
2467
+ depth: "test-only",
2468
+ required: false,
2469
+ bakeScales: [2, 3]
2470
+ };
2471
+ if (canonicalScenePreparedJsonV1(slots[0]) !== canonicalScenePreparedJsonV1(expectedSlot))
2472
+ throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots.0`, "Slab front slot facts are not canonical.");
2473
+ const expectedBounds = { center: [0, 0, 0], halfExtents: [slab.width / 2, slab.height / 2, slab.thickness / 2] };
2474
+ if (canonicalScenePreparedJsonV1(builder.localBounds) !== canonicalScenePreparedJsonV1(expectedBounds))
2475
+ throw new ScenePreparedPlanRefusal(`${path}.localBounds`, "Slab local bounds are not canonical.");
2476
+ if (!builder.allocations.length) throw new ScenePreparedPlanRefusal(`${path}.allocations`, "Slab production allocations are missing.");
2477
+ }
2478
+ var geometryAllocationPairV1 = (owner, name, bytes) => [
2479
+ { owner, name, domain: "gl", resource: "geometry", format: "three-buffer-attributes-and-index", bytes, lifetime: "context" },
2480
+ { owner, name: `${name}-arrays`, domain: "cpu", resource: "geometry", format: "three-typed-arrays", bytes, lifetime: "context" }
2481
+ ];
2482
+ function sceneW5PresetAllocationsV1(object) {
2483
+ const owner = `${object.kind}:${object.id}`;
2484
+ if (object.kind === "page") return geometryAllocationPairV1(owner, "content-geometry", 140);
2485
+ const rows = geometryAllocationPairV1(owner, "shell-geometry", object.kind === "phone" ? 300672 : 93312);
2486
+ if (object.kind === "laptop") rows.push(...geometryAllocationPairV1(owner, "base-geometry", 93312));
2487
+ for (const name of object.kind === "phone" ? ["screen", "glass"] : ["keyboard", "trackpad", "bezel", "screen", "glass"]) rows.push(...geometryAllocationPairV1(owner, `${name}-geometry`, 140));
2488
+ return rows;
2489
+ }
2490
+ function sceneW5WorldTextAllocationsV1(object, referenceWidth, outputWidth) {
2491
+ const width = object.maxWidth ?? referenceWidth, height = object.lines.length * object.fontSize * object.lineHeight, scale = outputWidth / referenceWidth;
2492
+ const rw = Math.max(1, Math.ceil(width * scale)), rh = Math.max(1, Math.ceil(height * scale)), cpu = rw * rh * 4;
2493
+ let mips = 0;
2494
+ for (let w = rw, h = rh; ; w = Math.max(1, Math.floor(w / 2)), h = Math.max(1, Math.floor(h / 2))) {
2495
+ mips += w * h * 4;
2496
+ if (w === 1 && h === 1) break;
2497
+ }
2498
+ return [
2499
+ ...geometryAllocationPairV1(`text:${object.id}`, "plane-geometry", 140),
2500
+ { owner: `text:${object.id}`, name: "raster-canvas", domain: "cpu", resource: "decoded-image", format: "rgba8", bytes: cpu, lifetime: "context" },
2501
+ { owner: `text:${object.id}`, name: "raster-mip-chain", domain: "gl", resource: "texture", format: "rgba8-mip-chain", bytes: mips, lifetime: "context" }
2502
+ ];
2503
+ }
2504
+ function sceneW5GeneratedRigAllocationsV1(edge, width, height) {
2505
+ const shadow = edge ** 2 * 4, pixels = width * height;
2506
+ return [
2507
+ { owner: "studio:rim", name: "shadow-color", domain: "gl", resource: "texture", format: "rgba8", bytes: shadow, lifetime: "context" },
2508
+ { owner: "studio:rim", name: "shadow-depth", domain: "gl", resource: "renderbuffer", format: "depth24-charged-rgba32", bytes: shadow, lifetime: "context" },
2509
+ { owner: "studio:environment", name: "pmrem-cubeuv", domain: "gl", resource: "pmrem-target", format: "rgba16f", bytes: 6 * 1024 * 1024, lifetime: "context" },
2510
+ { owner: "studio:environment", name: "pmrem-ping-pong", domain: "gl", resource: "pmrem-target", format: "rgba16f", bytes: 6 * 1024 * 1024, lifetime: "preparation" },
2511
+ { owner: "studio:environment", name: "pmrem-depth", domain: "gl", resource: "renderbuffer", format: "depth24-charged-rgba32", bytes: 3 * 1024 * 1024, lifetime: "preparation" },
2512
+ { owner: "post:focus-resources", name: "world-color-resolve", domain: "gl", resource: "texture", format: "rgba16f", bytes: pixels * 8, lifetime: "context" },
2513
+ { owner: "post:focus-resources", name: "world-depth", domain: "gl", resource: "texture", format: "depth32u", bytes: pixels * 4, lifetime: "context" },
2514
+ { owner: "post:focus-resources", name: "gathered-color", domain: "gl", resource: "texture", format: "rgba16f", bytes: pixels * 8, lifetime: "context" },
2515
+ { owner: "post:focus-resources", name: "grain-lattice", domain: "gl", resource: "texture", format: "rgba32f", bytes: 24672, lifetime: "context" },
2516
+ { owner: "post:focus-resources", name: "grain-lattice-array", domain: "cpu", resource: "decoded-image", format: "rgba32f", bytes: 24672, lifetime: "context" },
2517
+ { owner: "post:focus-resources", name: "world-color-msaa", domain: "gl", resource: "renderbuffer", format: "rgba16f-msaa-4", bytes: pixels * 32, lifetime: "context" },
2518
+ { owner: "post:focus-resources", name: "world-depth-msaa", domain: "gl", resource: "renderbuffer", format: "depth32u-msaa-4", bytes: pixels * 16, lifetime: "context" }
2519
+ ];
2520
+ }
2521
+ function validateW5Descriptor(value, object, path) {
2522
+ const builder = value;
2523
+ if (builder.builderVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.builderVersion`, `${object.kind} builder version must be 1.`);
2524
+ if (builder.tessellationVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.tessellationVersion`, `${object.kind} tessellation version must be 1.`);
2525
+ const parts = builder.parts;
2526
+ const slots = builder.surfaceSlots;
2527
+ const roles = builder.materialRoles;
2528
+ if (object.kind === "page") {
2529
+ if (builder.budgetTriangles !== SCENE_PRESET_TRIANGLES.page) throw new ScenePreparedPlanRefusal(`${path}.budgetTriangles`, "Page triangle budget is not canonical.");
2530
+ if (canonicalScenePreparedJsonV1(parts) !== canonicalScenePreparedJsonV1([{ partId: "base", materialRole: "base", triangles: 2, mounted: true }])) throw new ScenePreparedPlanRefusal(`${path}.parts`, "Page parts are not canonical.");
2531
+ if (canonicalScenePreparedJsonV1(roles) !== canonicalScenePreparedJsonV1(["base"])) throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, "Page material roles are not canonical.");
2532
+ const expected = { slot: "content", attachment: { partId: "base", materialRole: "base" }, dimensions: { width: object.width, height: object.height }, projectionQuad: [[-object.width / 2, object.height / 2, 0], [object.width / 2, object.height / 2, 0], [object.width / 2, -object.height / 2, 0], [-object.width / 2, -object.height / 2, 0]], uvOrigin: "top-left", fit: object.fit, alpha: object.transparency, depth: object.depthWrite ? "write" : "test-only", required: true, bakeScales: [2, 3] };
2533
+ if (canonicalScenePreparedJsonV1(slots) !== canonicalScenePreparedJsonV1([expected])) throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots`, "Page content slot is not canonical.");
2534
+ if (canonicalScenePreparedJsonV1(builder.localBounds) !== canonicalScenePreparedJsonV1({ center: [0, 0, 0], halfExtents: [object.width / 2, object.height / 2, 0] })) throw new ScenePreparedPlanRefusal(`${path}.localBounds`, "Page bounds are not canonical.");
2535
+ const expectedAllocations = sceneW5PresetAllocationsV1(object);
2536
+ if (canonicalScenePreparedJsonV1(builder.allocations) !== canonicalScenePreparedJsonV1(expectedAllocations)) throw new ScenePreparedPlanRefusal(`${path}.allocations`, "Page allocations are not canonical.");
2537
+ return;
2538
+ }
2539
+ const dim = sceneDeviceDimensionsV1(object.kind, object.width);
2540
+ const screenW = object.kind === "laptop" ? dim.w * 0.86 : dim.w - 2 * dim.screenInset;
2541
+ const screenH = object.kind === "laptop" ? dim.h * 0.86 : dim.h - 2 * dim.screenInset;
2542
+ const expectedSlot = { slot: "screen", attachment: { partId: "screen", materialRole: "screen" }, dimensions: { width: screenW, height: screenH }, projectionQuad: object.kind === "laptop" ? sceneLaptopScreenQuadV1(object) : [[-screenW / 2, screenH / 2, dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1], [screenW / 2, screenH / 2, dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1], [screenW / 2, -screenH / 2, dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1], [-screenW / 2, -screenH / 2, dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1]], uvOrigin: "top-left", fit: "contain", alpha: "straight", depth: "test-only", required: false, bakeScales: [2, 3] };
2543
+ if (canonicalScenePreparedJsonV1(slots) !== canonicalScenePreparedJsonV1([expectedSlot])) throw new ScenePreparedPlanRefusal(`${path}.surfaceSlots`, `${object.kind} screen slot is not canonical.`);
2544
+ const expectedParts = object.kind === "phone" ? [["shell", "shell", 3132], ["screen", "screen", 2], ["glass", "glass", 2]] : [["base", "shell", 972], ["lid", "shell", 972], ["keyboard", "keyboard", 2], ["trackpad", "trackpad", 2], ["bezel", "bezel", 2], ["screen", "screen", 2], ["glass", "glass", 2]];
2545
+ if (parts.length !== expectedParts.length || expectedParts.some((p, i) => parts[i]?.partId !== p[0] || parts[i]?.materialRole !== p[1] || parts[i]?.triangles !== p[2] || parts[i]?.mounted !== true)) throw new ScenePreparedPlanRefusal(`${path}.parts`, `${object.kind} parts are not canonical.`);
2546
+ const expectedRoles = object.kind === "phone" ? ["shell", "screen", "glass"] : ["shell", "keyboard", "trackpad", "bezel", "screen", "glass"];
2547
+ if (canonicalScenePreparedJsonV1(roles) !== canonicalScenePreparedJsonV1(expectedRoles)) throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, `${object.kind} material roles are not canonical.`);
2548
+ if (builder.budgetTriangles !== SCENE_PRESET_TRIANGLES[object.kind]) throw new ScenePreparedPlanRefusal(`${path}.budgetTriangles`, `${object.kind} triangle budget is not canonical.`);
2549
+ const expectedBounds = object.kind === "phone" ? { center: [0, 0, 0], halfExtents: [dim.w / 2, dim.h / 2, dim.t / 2 + SCENE_DEVICE_SCREEN_OFFSET_V1 * 2] } : (() => {
2550
+ const layout = sceneLaptopLayoutV1(dim), quad = sceneLaptopScreenQuadV1(object), ys = [layout.bottomY, ...quad.map((p) => p[1])], zs = [-dim.d / 2, dim.d / 2, ...quad.map((p) => p[2])];
2551
+ return { center: [0, (Math.max(...ys) + Math.min(...ys)) / 2, (Math.max(...zs) + Math.min(...zs)) / 2], halfExtents: [dim.w / 2, (Math.max(...ys) - Math.min(...ys)) / 2, (Math.max(...zs) - Math.min(...zs)) / 2] };
2552
+ })();
2553
+ if (canonicalScenePreparedJsonV1(builder.localBounds) !== canonicalScenePreparedJsonV1(expectedBounds)) throw new ScenePreparedPlanRefusal(`${path}.localBounds`, `${object.kind} bounds are not canonical.`);
2554
+ const allocations = sceneW5PresetAllocationsV1(object);
2555
+ if (canonicalScenePreparedJsonV1(builder.allocations) !== canonicalScenePreparedJsonV1(allocations)) throw new ScenePreparedPlanRefusal(`${path}.allocations`, `${object.kind} allocations are not canonical.`);
2556
+ }
2557
+ function validateWorldTextDescriptor(value, object, referenceWidth, outputWidth, path) {
2558
+ const builder = value;
2559
+ const width = object.maxWidth ?? referenceWidth;
2560
+ const height = object.lines.length * object.fontSize * object.lineHeight;
2561
+ if (builder.builderVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.builderVersion`, "World text builder version must be 1.");
2562
+ if (builder.tessellationVersion !== 1) throw new ScenePreparedPlanRefusal(`${path}.tessellationVersion`, "World text tessellation version must be 1.");
2563
+ if (builder.budgetTriangles !== 2) throw new ScenePreparedPlanRefusal(`${path}.budgetTriangles`, "World text triangle contribution must be 2.");
2564
+ if (canonicalScenePreparedJsonV1(builder.parts) !== canonicalScenePreparedJsonV1([{ partId: "quad", materialRole: "glyphs", triangles: 2, mounted: true }])) throw new ScenePreparedPlanRefusal(`${path}.parts`, "World text parts are not canonical.");
2565
+ if (canonicalScenePreparedJsonV1(builder.surfaceSlots) !== canonicalScenePreparedJsonV1([]) || canonicalScenePreparedJsonV1(builder.materialRoles) !== canonicalScenePreparedJsonV1(["glyphs"])) throw new ScenePreparedPlanRefusal(`${path}.materialRoles`, "World text roles and slots are not canonical.");
2566
+ if (canonicalScenePreparedJsonV1(builder.localBounds) !== canonicalScenePreparedJsonV1({ center: [0, 0, 0], halfExtents: [width / 2, height / 2, 0] })) throw new ScenePreparedPlanRefusal(`${path}.localBounds`, "World text bounds are not canonical.");
2567
+ const allocations = builder.allocations;
2568
+ const expectedAllocations = sceneW5WorldTextAllocationsV1(object, referenceWidth, outputWidth);
2569
+ if (canonicalScenePreparedJsonV1(allocations) !== canonicalScenePreparedJsonV1(expectedAllocations)) throw new ScenePreparedPlanRefusal(`${path}.allocations`, "World text allocations are not canonical.");
2570
+ }
2571
+ function imageFacts(value, path, pngOnly = false) {
2572
+ const image = record(value, path);
2573
+ exactKeys(image, ["handle", "mimeType", "byteLength", "width", "height", "decodedSha256"], path);
2574
+ stringValue(image.handle, `${path}.handle`);
2575
+ const mimes = pngOnly ? ["image/png"] : ["image/png", "image/jpeg", "image/webp"];
2576
+ enumValue(image.mimeType, mimes, `${path}.mimeType`);
2577
+ finiteNumber(image.byteLength, `${path}.byteLength`, {
2578
+ integer: true,
2579
+ positive: true
2580
+ });
2581
+ finiteNumber(image.width, `${path}.width`, { integer: true, positive: true });
2582
+ finiteNumber(image.height, `${path}.height`, {
2583
+ integer: true,
2584
+ positive: true
2585
+ });
2586
+ sha256Value(image.decodedSha256, `${path}.decodedSha256`);
2587
+ }
2588
+ function videoFacts(value, path) {
2589
+ const video = record(value, path);
2590
+ exactKeys(video, ["handle", "mimeType", "byteLength", "width", "height", "encodedSha256", "presentationIndex"], path);
2591
+ stringValue(video.handle, `${path}.handle`);
2592
+ enumValue(video.mimeType, ["video/mp4", "video/webm"], `${path}.mimeType`);
2593
+ for (const key of ["byteLength", "width", "height"]) finiteNumber(video[key], `${path}.${key}`, { integer: true, positive: true });
2594
+ sha256Value(video.encodedSha256, `${path}.encodedSha256`);
2595
+ const index = record(video.presentationIndex, `${path}.presentationIndex`);
2596
+ exactKeys(index, ["pts", "timeBase"], `${path}.presentationIndex`);
2597
+ const pts = arrayValue(index.pts, `${path}.presentationIndex.pts`);
2598
+ pts.forEach((entry, i) => finiteNumber(entry, `${path}.presentationIndex.pts.${i}`, { integer: true }));
2599
+ const timeBase = record(index.timeBase, `${path}.presentationIndex.timeBase`);
2600
+ exactKeys(timeBase, ["num", "den"], `${path}.presentationIndex.timeBase`);
2601
+ finiteNumber(timeBase.num, `${path}.presentationIndex.timeBase.num`, { integer: true, positive: true });
2602
+ finiteNumber(timeBase.den, `${path}.presentationIndex.timeBase.den`, { integer: true, positive: true });
2603
+ return video;
2604
+ }
2605
+ function resolutionResult(value, path, scale) {
2606
+ const result = record(value, path);
2607
+ exactKeys(result, ["raster", "bakeScale", "worstDensity", "worstAt", "meetsBound", "bound"], path);
2608
+ if (result.bakeScale !== scale) throw new ScenePreparedPlanRefusal(`${path}.bakeScale`, "Resolution result scale disagrees with its key.");
2609
+ const raster = record(result.raster, `${path}.raster`);
2610
+ exactKeys(raster, ["width", "height"], `${path}.raster`);
2611
+ finiteNumber(raster.width, `${path}.raster.width`, {
2612
+ integer: true,
2613
+ positive: true
2614
+ });
2615
+ finiteNumber(raster.height, `${path}.raster.height`, {
2616
+ integer: true,
2617
+ positive: true
2618
+ });
2619
+ finiteNumber(result.worstDensity, `${path}.worstDensity`, {
2620
+ nonnegative: true
2621
+ });
2622
+ finiteNumber(result.bound, `${path}.bound`, { positive: true });
2623
+ booleanValue(result.meetsBound, `${path}.meetsBound`);
2624
+ if (result.meetsBound !== result.worstDensity >= result.bound)
2625
+ throw new ScenePreparedPlanRefusal(`${path}.meetsBound`, "Resolution bound result is inconsistent.");
2626
+ const worst = record(result.worstAt, `${path}.worstAt`);
2627
+ exactKeys(worst, ["frame", "densityU", "densityV", "axis", "widthPx", "heightPx"], `${path}.worstAt`);
2628
+ finiteNumber(worst.frame, `${path}.worstAt.frame`, {
2629
+ integer: true,
2630
+ nonnegative: true
2631
+ });
2632
+ for (const key of ["densityU", "densityV", "widthPx", "heightPx"]) finiteNumber(worst[key], `${path}.worstAt.${key}`, { positive: true });
2633
+ enumValue(worst.axis, ["U", "V"], `${path}.worstAt.axis`);
2634
+ const expectedWorst = Math.min(worst.densityU, worst.densityV);
2635
+ if (result.worstDensity !== expectedWorst || worst.axis !== (worst.densityU <= worst.densityV ? "U" : "V"))
2636
+ throw new ScenePreparedPlanRefusal(`${path}.worstAt`, "Worst resolution evidence is inconsistent.");
2637
+ }
2638
+ var SCENE_CAPTURE_IDENTITY_FIELDS_V1 = [
2639
+ "componentId",
2640
+ "sourceVersionId",
2641
+ "params",
2642
+ "assetBindings",
2643
+ "layoutWidth",
2644
+ "layoutHeight",
2645
+ "bakeScale",
2646
+ "captureFrame",
2647
+ "durationInFrames",
2648
+ "fps",
2649
+ "seed",
2650
+ "brandFacts",
2651
+ "fontFacts",
2652
+ "mediaFacts",
2653
+ "rasterWidth",
2654
+ "rasterHeight",
2655
+ "runtimeIdentity",
2656
+ "dependencyIdentity",
2657
+ "browserProduct",
2658
+ "browserVersion",
2659
+ "browserExecutableSha256",
2660
+ "headless",
2661
+ "rasterAffectingFlags",
2662
+ "os",
2663
+ "arch",
2664
+ "deviceScaleFactor",
2665
+ "colorProfile",
2666
+ "requestedGlBackend",
2667
+ "actualGlBackend",
2668
+ "actualRenderer",
2669
+ "gpuIdentity"
2670
+ ];
2671
+ function captureIdentity(value, path) {
2672
+ const identity = record(value, path);
2673
+ exactKeys(identity, SCENE_CAPTURE_IDENTITY_FIELDS_V1, path);
2674
+ for (const key of [
2675
+ "componentId",
2676
+ "sourceVersionId",
2677
+ "seed",
2678
+ "runtimeIdentity",
2679
+ "dependencyIdentity",
2680
+ "browserProduct",
2681
+ "browserVersion",
2682
+ "os",
2683
+ "arch",
2684
+ "colorProfile",
2685
+ "requestedGlBackend",
2686
+ "actualGlBackend",
2687
+ "actualRenderer"
2688
+ ])
2689
+ stringValue(identity[key], `${path}.${key}`);
2690
+ sha256Value(identity.sourceVersionId, `${path}.sourceVersionId`);
2691
+ sha256Value(identity.browserExecutableSha256, `${path}.browserExecutableSha256`);
2692
+ for (const key of ["layoutWidth", "layoutHeight", "captureFrame", "durationInFrames", "rasterWidth", "rasterHeight"])
2693
+ finiteNumber(identity[key], `${path}.${key}`, {
2694
+ integer: true,
2695
+ ...key === "captureFrame" ? { nonnegative: true } : { positive: true }
2696
+ });
2697
+ if (identity.bakeScale !== 2 && identity.bakeScale !== 3) throw new ScenePreparedPlanRefusal(`${path}.bakeScale`, "Invalid capture bake scale.");
2698
+ if (identity.deviceScaleFactor !== identity.bakeScale)
2699
+ throw new ScenePreparedPlanRefusal(`${path}.deviceScaleFactor`, "Captured DPR disagrees with bake scale.");
2700
+ if (identity.rasterWidth !== identity.layoutWidth * identity.bakeScale || identity.rasterHeight !== identity.layoutHeight * identity.bakeScale)
2701
+ throw new ScenePreparedPlanRefusal(`${path}.rasterWidth`, "Capture raster disagrees with layout and scale.");
2702
+ booleanValue(identity.headless, `${path}.headless`);
2703
+ if (identity.gpuIdentity !== null) stringValue(identity.gpuIdentity, `${path}.gpuIdentity`);
2704
+ const fps = record(identity.fps, `${path}.fps`);
2705
+ exactKeys(fps, ["num", "den"], `${path}.fps`);
2706
+ finiteNumber(fps.num, `${path}.fps.num`, { integer: true, positive: true });
2707
+ finiteNumber(fps.den, `${path}.fps.den`, { integer: true, positive: true });
2708
+ for (const key of ["params", "assetBindings", "brandFacts", "fontFacts", "mediaFacts", "rasterAffectingFlags"])
2709
+ canonicalScenePreparedJsonV1(identity[key], `${path}.${key}`);
2710
+ }
2711
+ function canonicalCaptureIdentityV1(value) {
2712
+ const ordered = Object.fromEntries(SCENE_CAPTURE_IDENTITY_FIELDS_V1.map((field) => [field, JSON.parse(canonicalScenePreparedJsonV1(value[field]))]));
2713
+ return JSON.stringify(ordered);
2714
+ }
2715
+ function expectedSceneEntries(scene) {
2716
+ const objects = [];
2717
+ const surfaces = [];
2718
+ const visit = (layers, prefix = "layers", parentId = null) => layers.forEach((object, index) => {
2719
+ const path = `${prefix}.${index}`;
2720
+ objects.push({ object, parentId, path });
2721
+ if ("surfaces" in object && object.surfaces)
2722
+ for (const [slot, binding] of Object.entries(object.surfaces))
2723
+ surfaces.push({
2724
+ object,
2725
+ path: `${path}.surfaces.${slot}`,
2726
+ slot,
2727
+ binding
2728
+ });
2729
+ if (object.kind === "group") visit(object.children, `${path}.children`, object.id);
2730
+ });
2731
+ visit(scene.layers);
2732
+ return { objects, surfaces };
2733
+ }
2734
+ function fullMipBytes(width, height) {
2735
+ let bytes = 0;
2736
+ for (let w = width, h = height; ; w = Math.max(1, Math.floor(w / 2)), h = Math.max(1, Math.floor(h / 2))) {
2737
+ bytes += w * h * 4;
2738
+ if (w === 1 && h === 1) return bytes;
2739
+ }
2740
+ }
2741
+ function validateNestedPlan(plan, scene) {
2742
+ const output = plan.output;
2743
+ const camera = record(plan.cameraIdentity, "plan.cameraIdentity");
2744
+ exactKeys(camera, ["position", "fov", "near", "far"], "plan.cameraIdentity");
2745
+ vec3(camera.position, "plan.cameraIdentity.position");
2746
+ finiteNumber(camera.fov, "plan.cameraIdentity.fov", { positive: true });
2747
+ finiteNumber(camera.near, "plan.cameraIdentity.near", { positive: true });
2748
+ finiteNumber(camera.far, "plan.cameraIdentity.far", { positive: true });
2749
+ if (camera.far <= camera.near) throw new ScenePreparedPlanRefusal("plan.cameraIdentity.far", "Camera far must exceed near.");
2750
+ const motion = record(plan.motion, "plan.motion");
2751
+ exactKeys(motion, ["channels", "contentDurationInFrames", "finalContentFrame", "cameraClipPlanes"], "plan.motion");
2752
+ if (canonicalScenePreparedJsonV1(motion.channels, "plan.motion.channels") !== canonicalScenePreparedJsonV1(scene.animation, "plan.scene.animation"))
2753
+ throw new ScenePreparedPlanRefusal("plan.motion.channels", "Motion channels must equal the parsed authored animation.");
2754
+ finiteNumber(motion.contentDurationInFrames, "plan.motion.contentDurationInFrames", { integer: true, positive: true });
2755
+ finiteNumber(motion.finalContentFrame, "plan.motion.finalContentFrame", {
2756
+ integer: true,
2757
+ nonnegative: true
2758
+ });
2759
+ if (motion.contentDurationInFrames !== output.durationInFrames || motion.finalContentFrame !== motion.contentDurationInFrames - 1)
2760
+ throw new ScenePreparedPlanRefusal("plan.motion.finalContentFrame", "Output duration, motion duration and final hold disagree.");
2761
+ const planes = arrayValue(motion.cameraClipPlanes, "plan.motion.cameraClipPlanes");
2762
+ if (planes.length !== motion.contentDurationInFrames)
2763
+ throw new ScenePreparedPlanRefusal("plan.motion.cameraClipPlanes", "Clip-plane schedule must cover every content frame.");
2764
+ planes.forEach((entry, index) => {
2765
+ const plane = record(entry, `plan.motion.cameraClipPlanes.${index}`);
2766
+ exactKeys(plane, ["near", "far"], `plan.motion.cameraClipPlanes.${index}`);
2767
+ finiteNumber(plane.near, `plan.motion.cameraClipPlanes.${index}.near`, {
2768
+ positive: true
2769
+ });
2770
+ finiteNumber(plane.far, `plan.motion.cameraClipPlanes.${index}.far`, {
2771
+ positive: true
2772
+ });
2773
+ if (plane.far <= plane.near)
2774
+ throw new ScenePreparedPlanRefusal(`plan.motion.cameraClipPlanes.${index}.far`, "Camera far must exceed near.");
2775
+ });
2776
+ if (camera.near !== planes[0].near || camera.far !== planes[0].far)
2777
+ throw new ScenePreparedPlanRefusal("plan.cameraIdentity.near", "Camera identity clip planes disagree with frame zero.");
2778
+ const frame0 = evaluateScene(scene, 0, output.durationInFrames).camera;
2779
+ if (canonicalScenePreparedJsonV1(camera.position) !== canonicalScenePreparedJsonV1(frame0.position) || camera.fov !== frame0.fov)
2780
+ throw new ScenePreparedPlanRefusal("plan.cameraIdentity", "Camera identity disagrees with evaluated frame zero.");
2781
+ allocationArray(plan.rigAllocations, "plan.rigAllocations");
2782
+ canonicalScenePreparedJsonV1(plan.rigIdentity, "plan.rigIdentity");
2783
+ if (scene.environment.kind === "photographic-studio-v1") {
2784
+ const expectedRig = photographicSceneRigFactsV1(
2785
+ output.height / scene.referenceSize.height,
2786
+ output.width,
2787
+ output.height
2788
+ );
2789
+ if (canonicalScenePreparedJsonV1(plan.rigIdentity) !== canonicalScenePreparedJsonV1(expectedRig.identity))
2790
+ throw new ScenePreparedPlanRefusal("plan.rigIdentity", "Photographic rig identity differs from the fixed production rig.");
2791
+ if (canonicalScenePreparedJsonV1(plan.rigAllocations) !== canonicalScenePreparedJsonV1(expectedRig.allocations))
2792
+ throw new ScenePreparedPlanRefusal(
2793
+ "plan.rigAllocations",
2794
+ "Photographic rig allocations must match the fixed environment and output-sized 4-sample Focus resources."
2795
+ );
2796
+ } else {
2797
+ const rig = record(plan.rigIdentity, "plan.rigIdentity");
2798
+ exactKeys(rig, ["lightingProfile", "preset", "intensity", "floorY", "shadowOpacity", "shadowMapEdge", "environmentVersion"], "plan.rigIdentity");
2799
+ for (const key of ["lightingProfile", "preset", "intensity", "floorY", "shadowOpacity"]) if (rig[key] !== scene.environment[key]) throw new ScenePreparedPlanRefusal(`plan.rigIdentity.${key}`, "Generated studio identity disagrees with the authored rig.");
2800
+ if (rig.environmentVersion !== 1) throw new ScenePreparedPlanRefusal("plan.rigIdentity.environmentVersion", "Generated studio environment version must be 1.");
2801
+ if (rig.shadowMapEdge !== 2048 && rig.shadowMapEdge !== 4096) throw new ScenePreparedPlanRefusal("plan.rigIdentity.shadowMapEdge", "Generated studio shadow edge must be 2048 or 4096.");
2802
+ const expected2 = sceneW5GeneratedRigAllocationsV1(rig.shadowMapEdge, output.width, output.height);
2803
+ if (canonicalScenePreparedJsonV1(plan.rigAllocations) !== canonicalScenePreparedJsonV1(expected2)) throw new ScenePreparedPlanRefusal("plan.rigAllocations", "Generated studio allocations disagree with the exact production rig and FocusResources allocation contract.");
2804
+ }
2805
+ const fonts = arrayValue(plan.fonts, "plan.fonts");
2806
+ fonts.forEach((entry, index) => {
2807
+ const path = `plan.fonts.${index}`;
2808
+ const face = record(entry, path);
2809
+ exactKeys(face, face.url === void 0 ? ["family", "weight", "style", "paths"] : ["family", "weight", "style", "url", "paths"], path);
2810
+ stringValue(face.family, `${path}.family`);
2811
+ finiteNumber(face.weight, `${path}.weight`, {
2812
+ integer: true,
2813
+ positive: true
2814
+ });
2815
+ enumValue(face.style, ["normal", "italic"], `${path}.style`);
2816
+ if (face.url !== void 0) stringValue(face.url, `${path}.url`);
2817
+ const paths = arrayValue(face.paths, `${path}.paths`);
2818
+ if (!paths.length) throw new ScenePreparedPlanRefusal(`${path}.paths`, "A font face needs at least one authored path.");
2819
+ paths.forEach((entry2, i) => stringValue(entry2, `${path}.paths.${i}`));
2820
+ });
2821
+ const expected = expectedSceneEntries(scene);
2822
+ const expectedText = new Map(
2823
+ expected.objects.filter(({ object }) => object.kind === "text").map(({ object, path }) => {
2824
+ const text = object;
2825
+ return [
2826
+ path,
2827
+ {
2828
+ family: text.fontFamily,
2829
+ weight: text.fontWeight,
2830
+ style: text.fontStyle
2831
+ }
2832
+ ];
2833
+ })
2834
+ );
2835
+ const coveredText = /* @__PURE__ */ new Set();
2836
+ fonts.forEach((entry, index) => {
2837
+ const face = entry;
2838
+ for (const fontPath of face.paths) {
2839
+ const expectedFace = expectedText.get(fontPath);
2840
+ if (!expectedFace || coveredText.has(fontPath) || expectedFace.family !== face.family || expectedFace.weight !== face.weight || expectedFace.style !== face.style)
2841
+ throw new ScenePreparedPlanRefusal(`plan.fonts.${index}.paths`, "Font paths must cover each authored text face exactly once.");
2842
+ coveredText.add(fontPath);
2843
+ }
2844
+ });
2845
+ if (coveredText.size !== expectedText.size) throw new ScenePreparedPlanRefusal("plan.fonts", "Sealed fonts do not cover every authored text face.");
2846
+ const objects = arrayValue(plan.objects, "plan.objects");
2847
+ if (objects.length !== expected.objects.length)
2848
+ throw new ScenePreparedPlanRefusal("plan.objects", "Prepared objects must cover every authored object exactly once.");
2849
+ objects.forEach((entry, index) => {
2850
+ const path = `plan.objects.${index}`;
2851
+ const object = record(entry, path);
2852
+ exactKeys(object, ["authoredId", "parentId", "kind", "builder", "parts"], path);
2853
+ const authored = expected.objects[index];
2854
+ if (object.authoredId !== authored.object.id || object.parentId !== authored.parentId || object.kind !== authored.object.kind)
2855
+ throw new ScenePreparedPlanRefusal(path, "Prepared object identity disagrees with authored traversal.");
2856
+ const builderless = authored.object.kind === "group" || authored.object.kind === "text" && authored.object.space === "screen";
2857
+ if (object.builder === null) {
2858
+ if (!builderless) throw new ScenePreparedPlanRefusal(`${path}.builder`, "A renderable object requires a builder.");
2859
+ } else {
2860
+ if (builderless) throw new ScenePreparedPlanRefusal(`${path}.builder`, "This authored object must not carry a builder.");
2861
+ descriptor(object.builder, `${path}.builder`);
2862
+ if (object.builder.kind !== object.kind)
2863
+ throw new ScenePreparedPlanRefusal(`${path}.builder.kind`, "Builder kind disagrees with object kind.");
2864
+ if (authored.object.kind === "slab") validateSlabDescriptor(object.builder, authored.object, `${path}.builder`);
2865
+ if (authored.object.kind === "laptop" || authored.object.kind === "phone" || authored.object.kind === "page") validateW5Descriptor(object.builder, authored.object, `${path}.builder`);
2866
+ if (authored.object.kind === "text" && authored.object.space === "world") validateWorldTextDescriptor(object.builder, authored.object, scene.referenceSize.width, output.width, `${path}.builder`);
2867
+ }
2868
+ const parts = arrayValue(object.parts, `${path}.parts`);
2869
+ parts.forEach((partValue, partIndex) => {
2870
+ const partPath = `${path}.parts.${partIndex}`;
2871
+ const part = record(partValue, partPath);
2872
+ exactKeys(part, ["id", "partId", "materialRole", "triangles", "mounted"], partPath);
2873
+ for (const key of ["id", "partId", "materialRole"]) stringValue(part[key], `${partPath}.${key}`);
2874
+ finiteNumber(part.triangles, `${partPath}.triangles`, {
2875
+ integer: true,
2876
+ nonnegative: true
2877
+ });
2878
+ booleanValue(part.mounted, `${partPath}.mounted`);
2879
+ });
2880
+ const builderParts = object.builder && object.builder.parts;
2881
+ if (canonicalScenePreparedJsonV1(
2882
+ parts.map((part) => {
2883
+ const { id: _id, ...rest } = part;
2884
+ return rest;
2885
+ })
2886
+ ) !== canonicalScenePreparedJsonV1(builderParts ?? []))
2887
+ throw new ScenePreparedPlanRefusal(`${path}.parts`, "Prepared parts disagree with builder parts.");
2888
+ parts.forEach((part) => {
2889
+ const row = part;
2890
+ if (row.id !== `${object.authoredId}:${row.partId}`) throw new ScenePreparedPlanRefusal(`${path}.parts`, "Prepared part id is not canonical.");
2891
+ });
2892
+ });
2893
+ const surfaces = arrayValue(plan.surfaces, "plan.surfaces");
2894
+ if (surfaces.length !== expected.surfaces.length)
2895
+ throw new ScenePreparedPlanRefusal("plan.surfaces", "Sealed surfaces must cover every authored binding exactly once.");
2896
+ surfaces.forEach((entry, index) => {
2897
+ const path = `plan.surfaces.${index}`;
2898
+ const surface = record(entry, path);
2899
+ const allowed = ["path", "authoredObjectId", "slot", "binding", "descriptor", "bakeScale", "bakeScaleReason", "resolutionCheck", "realized"];
2900
+ const unknown = Object.keys(surface).find((key) => !allowed.includes(key));
2901
+ if (unknown) throw new ScenePreparedPlanRefusal(`${path}.${unknown}`, "Unknown sealed surface field.");
2902
+ const authored = expected.surfaces[index];
2903
+ if (surface.path !== authored.path || surface.authoredObjectId !== authored.object.id || surface.slot !== authored.slot)
2904
+ throw new ScenePreparedPlanRefusal(path, "Sealed surface identity disagrees with authored traversal.");
2905
+ const parsedBinding = SceneSurfaceBindingV2.safeParse(surface.binding);
2906
+ if (!parsedBinding.success)
2907
+ throw new ScenePreparedPlanRefusal(
2908
+ `${path}.binding${parsedBinding.error.issues[0]?.path.map((part) => `.${String(part)}`).join("") ?? ""}`,
2909
+ "Invalid sealed surface binding."
2910
+ );
2911
+ if (canonicalScenePreparedJsonV1(parsedBinding.data) !== canonicalScenePreparedJsonV1(authored.binding))
2912
+ throw new ScenePreparedPlanRefusal(`${path}.binding`, "Sealed binding differs from authored scene.");
2913
+ slotDescriptor(surface.descriptor, `${path}.descriptor`);
2914
+ const owner = objects.find((object) => object.authoredId === surface.authoredObjectId);
2915
+ const builderSlots = owner.builder.surfaceSlots;
2916
+ const builderSlot = builderSlots.find((slot) => slot.slot === surface.slot);
2917
+ if (!builderSlot || canonicalScenePreparedJsonV1(builderSlot) !== canonicalScenePreparedJsonV1(surface.descriptor))
2918
+ throw new ScenePreparedPlanRefusal(`${path}.descriptor`, "Surface descriptor differs from its builder slot.");
2919
+ const binding = parsedBinding.data;
2920
+ const realized = record(surface.realized, `${path}.realized`);
2921
+ if (realized.source !== binding.source) throw new ScenePreparedPlanRefusal(`${path}.realized.source`, "Realized source disagrees with binding.");
2922
+ if (binding.source === "color") {
2923
+ exactKeys(realized, ["source", "color"], `${path}.realized`);
2924
+ if (realized.color !== binding.color || surface.bakeScale !== void 0 || surface.bakeScaleReason !== void 0 || surface.resolutionCheck !== void 0)
2925
+ throw new ScenePreparedPlanRefusal(`${path}.realized.color`, "Color realization is inconsistent.");
2926
+ } else if (binding.source === "image") {
2927
+ exactKeys(realized, ["source", "image"], `${path}.realized`);
2928
+ imageFacts(realized.image, `${path}.realized.image`);
2929
+ if (surface.bakeScale !== void 0 || surface.bakeScaleReason !== void 0 || surface.resolutionCheck !== void 0)
2930
+ throw new ScenePreparedPlanRefusal(path, "Image realization carries component-only scale facts.");
2931
+ } else if (binding.source === "video") {
2932
+ exactKeys(realized, ["source", "video", "schedule"], `${path}.realized`);
2933
+ const video = videoFacts(realized.video, `${path}.realized.video`);
2934
+ let expected2;
2935
+ try {
2936
+ expected2 = prepareSceneVideoScheduleV1({ presentationIndex: video.presentationIndex, shotRate: output.fps, contentDuration: output.durationInFrames, startFrame: binding.startFrame, endFrame: binding.endFrame });
2937
+ } catch (error) {
2938
+ throw new ScenePreparedPlanRefusal(`${path}.realized.schedule`, error instanceof Error ? error.message : String(error));
2939
+ }
2940
+ if (canonicalScenePreparedJsonV1(realized.schedule) !== canonicalScenePreparedJsonV1(expected2)) throw new ScenePreparedPlanRefusal(`${path}.realized.schedule`, "Video schedule disagrees with verified presentation timestamps, trims, or output rate.");
2941
+ if (surface.bakeScale !== void 0 || surface.bakeScaleReason !== void 0 || surface.resolutionCheck !== void 0) throw new ScenePreparedPlanRefusal(path, "Video realization carries component-only scale facts.");
2942
+ } else {
2943
+ exactKeys(realized, ["source", "captureIdentitySha256", "captureIdentityFacts", "image"], `${path}.realized`);
2944
+ sha256Value(realized.captureIdentitySha256, `${path}.realized.captureIdentitySha256`);
2945
+ captureIdentity(realized.captureIdentityFacts, `${path}.realized.captureIdentityFacts`);
2946
+ imageFacts(realized.image, `${path}.realized.image`, true);
2947
+ if (surface.bakeScale !== 2 && surface.bakeScale !== 3) throw new ScenePreparedPlanRefusal(`${path}.bakeScale`, "Component surface needs a bake scale.");
2948
+ enumValue(surface.bakeScaleReason, ["declared", "default-sufficient", "raised-by-resolution-check"], `${path}.bakeScaleReason`);
2949
+ const selection = record(surface.resolutionCheck, `${path}.resolutionCheck`);
2950
+ exactKeys(selection, ["selected", "reason", "results"], `${path}.resolutionCheck`);
2951
+ if (selection.selected !== surface.bakeScale || selection.reason !== surface.bakeScaleReason)
2952
+ throw new ScenePreparedPlanRefusal(`${path}.resolutionCheck`, "Resolution selection disagrees with sealed surface.");
2953
+ const results = record(selection.results, `${path}.resolutionCheck.results`);
2954
+ exactKeys(results, ["2", "3"], `${path}.resolutionCheck.results`);
2955
+ resolutionResult(results["2"], `${path}.resolutionCheck.results.2`, 2);
2956
+ resolutionResult(results["3"], `${path}.resolutionCheck.results.3`, 3);
2957
+ const result2 = results["2"];
2958
+ const result3 = results["3"];
2959
+ for (const [scale, result] of [
2960
+ [2, result2],
2961
+ [3, result3]
2962
+ ]) {
2963
+ const raster = result.raster;
2964
+ if (raster.width !== binding.layout.width * scale || raster.height !== binding.layout.height * scale || result.worstAt.frame >= output.durationInFrames)
2965
+ throw new ScenePreparedPlanRefusal(`${path}.resolutionCheck.results.${scale}`, "Resolution evidence disagrees with layout or output duration.");
2966
+ }
2967
+ if (surface.bakeScaleReason === "declared" && binding.bakeScale !== surface.bakeScale || surface.bakeScaleReason === "default-sufficient" && (binding.bakeScale !== void 0 || surface.bakeScale !== 2 || result2.meetsBound !== true) || surface.bakeScaleReason === "raised-by-resolution-check" && (binding.bakeScale !== void 0 || surface.bakeScale !== 3 || result2.meetsBound !== false || result3.meetsBound !== true))
2968
+ throw new ScenePreparedPlanRefusal(`${path}.bakeScaleReason`, "Bake-scale reason disagrees with authored selection evidence.");
2969
+ const identity = realized.captureIdentityFacts;
2970
+ if (identity.componentId !== binding.component.componentId || identity.sourceVersionId !== binding.component.sourceVersionId || identity.captureFrame !== binding.captureFrame || identity.layoutWidth !== binding.layout.width || identity.layoutHeight !== binding.layout.height || identity.bakeScale !== surface.bakeScale || identity.captureFrame >= identity.durationInFrames || canonicalScenePreparedJsonV1(identity.fps) !== canonicalScenePreparedJsonV1(output.fps) || canonicalScenePreparedJsonV1(identity.params) !== canonicalScenePreparedJsonV1(binding.params ?? {}) || realized.image.width !== identity.rasterWidth || realized.image.height !== identity.rasterHeight)
2971
+ throw new ScenePreparedPlanRefusal(`${path}.realized.captureIdentityFacts`, "Capture identity disagrees with binding, output or PNG.");
2972
+ }
2973
+ });
2974
+ const separation = record(plan.separation, "plan.separation");
2975
+ exactKeys(separation, ["pairs"], "plan.separation");
2976
+ const pairs = arrayValue(separation.pairs, "plan.separation.pairs");
2977
+ pairs.forEach((entry, index) => {
2978
+ const path = `plan.separation.pairs.${index}`;
2979
+ const pair = record(entry, path);
2980
+ exactKeys(pair, ["constraintIndex", "frame", "objects", "separatingAxisGap", "requiredGap"], path);
2981
+ finiteNumber(pair.constraintIndex, `${path}.constraintIndex`, {
2982
+ integer: true,
2983
+ nonnegative: true
2984
+ });
2985
+ finiteNumber(pair.frame, `${path}.frame`, {
2986
+ integer: true,
2987
+ nonnegative: true
2988
+ });
2989
+ if (pair.frame >= output.durationInFrames) throw new ScenePreparedPlanRefusal(`${path}.frame`, "Separation frame exceeds output.");
2990
+ const ids = arrayValue(pair.objects, `${path}.objects`);
2991
+ if (ids.length !== 2) throw new ScenePreparedPlanRefusal(`${path}.objects`, "Separation pair needs two objects.");
2992
+ ids.forEach((id, i) => stringValue(id, `${path}.objects.${i}`));
2993
+ if (ids.some((id) => !expected.objects.some((candidate) => candidate.object.id === id)))
2994
+ throw new ScenePreparedPlanRefusal(`${path}.objects`, "Separation pair names an unknown object.");
2995
+ finiteNumber(pair.separatingAxisGap, `${path}.separatingAxisGap`, {
2996
+ nonnegative: true
2997
+ });
2998
+ finiteNumber(pair.requiredGap, `${path}.requiredGap`, {
2999
+ nonnegative: true
3000
+ });
3001
+ if (pair.separatingAxisGap < pair.requiredGap)
3002
+ throw new ScenePreparedPlanRefusal(`${path}.separatingAxisGap`, "Sealed separation result violates its required gap.");
3003
+ });
3004
+ const expectedPairs = [];
3005
+ scene.constraints.forEach((constraint, constraintIndex) => {
3006
+ for (let frame = 0; frame < output.durationInFrames; frame += 1) {
3007
+ for (let first = 0; first < constraint.objects.length; first += 1)
3008
+ for (let second = first + 1; second < constraint.objects.length; second += 1)
3009
+ expectedPairs.push({
3010
+ constraintIndex,
3011
+ frame,
3012
+ objects: [constraint.objects[first], constraint.objects[second]],
3013
+ requiredGap: constraint.gap
3014
+ });
3015
+ }
3016
+ });
3017
+ if (pairs.length !== expectedPairs.length)
3018
+ throw new ScenePreparedPlanRefusal("plan.separation.pairs", "Separation evidence must cover every constraint pair and frame.");
3019
+ pairs.forEach((entry, index) => {
3020
+ const pair = entry;
3021
+ const expectedPair = expectedPairs[index];
3022
+ if (pair.constraintIndex !== expectedPair.constraintIndex || pair.frame !== expectedPair.frame || pair.requiredGap !== expectedPair.requiredGap || canonicalScenePreparedJsonV1(pair.objects) !== canonicalScenePreparedJsonV1(expectedPair.objects))
3023
+ throw new ScenePreparedPlanRefusal(`plan.separation.pairs.${index}`, "Separation evidence order or authored constraint facts disagree.");
3024
+ });
3025
+ const ledger = record(plan.ledger, "plan.ledger");
3026
+ exactKeys(
3027
+ ledger,
3028
+ [
3029
+ "owner",
3030
+ "invariant",
3031
+ "allocations",
3032
+ "byOwner",
3033
+ "surfaceTextureBytes",
3034
+ "contextGlBytes",
3035
+ "preparationPeakGlBytes",
3036
+ "cpuBytes",
3037
+ "peakDeclaredLiveBytes",
3038
+ "budgetTriangles",
3039
+ "instantiatedTriangles",
3040
+ "exclusions"
3041
+ ],
3042
+ "plan.ledger"
3043
+ );
3044
+ stringValue(ledger.owner, "plan.ledger.owner");
3045
+ if (ledger.invariant !== "one-plan-one-webgl-context") throw new ScenePreparedPlanRefusal("plan.ledger.invariant", "Invalid ledger invariant.");
3046
+ allocationArray(ledger.allocations, "plan.ledger.allocations");
3047
+ const canonicalOwner = `scene-plan:${objects.map((value) => value.authoredId).join(",")}`;
3048
+ if (ledger.owner !== canonicalOwner) throw new ScenePreparedPlanRefusal("plan.ledger.owner", "Ledger owner does not match prepared objects.");
3049
+ const allocationRows = ledger.allocations;
3050
+ const byOwner = {};
3051
+ for (const allocation2 of allocationRows) {
3052
+ const row = byOwner[allocation2.owner] ??= {
3053
+ contextGlBytes: 0,
3054
+ preparationGlBytes: 0,
3055
+ cpuBytes: 0
3056
+ };
3057
+ if (allocation2.domain === "cpu") row.cpuBytes += allocation2.bytes;
3058
+ else if (allocation2.lifetime === "context") row.contextGlBytes += allocation2.bytes;
3059
+ else row.preparationGlBytes += allocation2.bytes;
3060
+ }
3061
+ const suppliedOwners = record(ledger.byOwner, "plan.ledger.byOwner");
3062
+ if (canonicalScenePreparedJsonV1(suppliedOwners) !== canonicalScenePreparedJsonV1(byOwner))
3063
+ throw new ScenePreparedPlanRefusal("plan.ledger.byOwner", "Owner totals do not recompute from allocation rows.");
3064
+ const exclusions = record(ledger.exclusions, "plan.ledger.exclusions");
3065
+ exactKeys(
3066
+ exclusions,
3067
+ [
3068
+ "crossContextAndHostAggregation",
3069
+ "videoDecode",
3070
+ "browserProcess",
3071
+ "sourceBundles",
3072
+ "captureSession",
3073
+ "driverAllocations",
3074
+ "compositorSurfaces",
3075
+ "decoderCaches"
3076
+ ],
3077
+ "plan.ledger.exclusions"
3078
+ );
3079
+ const requiredExclusions = {
3080
+ crossContextAndHostAggregation: "W7",
3081
+ videoDecode: surfaces.some((surface) => surface.binding && surface.binding.source === "video") ? "binding-owned" : "W6",
3082
+ browserProcess: "host-owned",
3083
+ sourceBundles: "host-owned",
3084
+ captureSession: "provider-owned",
3085
+ driverAllocations: "excluded-unmeasured",
3086
+ compositorSurfaces: "excluded-unmeasured",
3087
+ decoderCaches: "excluded-unmeasured"
3088
+ };
3089
+ if (canonicalScenePreparedJsonV1(exclusions) !== canonicalScenePreparedJsonV1(requiredExclusions))
3090
+ throw new ScenePreparedPlanRefusal("plan.ledger.exclusions", "Ledger exclusions are not canonical.");
3091
+ const contextGl = allocationRows.filter((row) => row.domain === "gl" && row.lifetime === "context").reduce((sum, row) => sum + row.bytes, 0);
3092
+ const preparationGl = allocationRows.filter((row) => row.domain === "gl" && row.lifetime === "preparation").reduce((sum, row) => sum + row.bytes, 0);
3093
+ const cpu = allocationRows.filter((row) => row.domain === "cpu").reduce((sum, row) => sum + row.bytes, 0);
3094
+ const surfaceBytes = allocationRows.filter((row) => row.domain === "gl" && row.resource === "texture" && row.name === "surface-texture").reduce((sum, row) => sum + row.bytes, 0);
3095
+ const budgetTriangles = objects.reduce(
3096
+ (sum, value) => sum + (value.builder?.budgetTriangles ?? 0),
3097
+ 0
3098
+ );
3099
+ const instantiatedTriangles = objects.flatMap((value) => value.parts).reduce((sum, part) => sum + (part.mounted ? part.triangles : 0), 0);
3100
+ const totals = {
3101
+ surfaceTextureBytes: surfaceBytes,
3102
+ contextGlBytes: contextGl,
3103
+ preparationPeakGlBytes: contextGl + preparationGl,
3104
+ cpuBytes: cpu,
3105
+ peakDeclaredLiveBytes: contextGl + preparationGl + cpu,
3106
+ budgetTriangles,
3107
+ instantiatedTriangles
3108
+ };
3109
+ for (const [key, expectedValue] of Object.entries(totals)) {
3110
+ finiteNumber(ledger[key], `plan.ledger.${key}`, {
3111
+ integer: true,
3112
+ nonnegative: true
3113
+ });
3114
+ if (ledger[key] !== expectedValue) throw new ScenePreparedPlanRefusal(`plan.ledger.${key}`, `${key} does not recompute from sealed facts.`);
3115
+ }
3116
+ if (surfaceBytes > SCENE_SURFACE_TEXTURE_BUDGET_BYTES || contextGl + preparationGl > SCENE_CONTEXT_GL_BUDGET_BYTES || budgetTriangles > SCENE_MAX_TRIANGLES)
3117
+ throw new ScenePreparedPlanRefusal("plan.ledger", "Sealed plan exceeds the portable scene budget.");
3118
+ const expectedBase = [
3119
+ ...objects.flatMap((value) => {
3120
+ const object = value;
3121
+ const builder = object.builder;
3122
+ return (builder?.allocations ?? []).map((allocation2) => ({
3123
+ ...allocation2,
3124
+ owner: `${object.authoredId}:${allocation2.owner}`
3125
+ }));
3126
+ }),
3127
+ ...plan.rigAllocations.map((allocation2) => ({ ...allocation2, owner: `rig:${allocation2.owner}` }))
3128
+ ];
3129
+ const actualBase = allocationRows.filter(
3130
+ (row) => row.name !== "surface-texture" && row.name !== "owned-video-canvas" && !(row.owner.startsWith("capture:") && ["encoded-image", "decoded-rgba", "encoded-video"].includes(row.name))
3131
+ );
3132
+ if (canonicalScenePreparedJsonV1(actualBase) !== canonicalScenePreparedJsonV1(expectedBase))
3133
+ throw new ScenePreparedPlanRefusal("plan.ledger.allocations", "Base allocation rows disagree with object and rig facts.");
3134
+ const expectedSurfaceRows = surfaces.flatMap((value) => {
3135
+ const surface = value;
3136
+ const realized = surface.realized;
3137
+ if (realized.source === "color") return [];
3138
+ const image = realized.source === "video" ? realized.video : realized.image;
3139
+ return [
3140
+ {
3141
+ owner: surface.path,
3142
+ name: "surface-texture",
3143
+ domain: "gl",
3144
+ resource: "texture",
3145
+ format: "rgba8",
3146
+ bytes: fullMipBytes(image.width, image.height),
3147
+ lifetime: "context"
3148
+ }
3149
+ ];
3150
+ });
3151
+ const actualSurfaceRows = allocationRows.filter((row) => row.name === "surface-texture");
3152
+ if (canonicalScenePreparedJsonV1(actualSurfaceRows) !== canonicalScenePreparedJsonV1(expectedSurfaceRows))
3153
+ throw new ScenePreparedPlanRefusal("plan.ledger.surfaceTextureBytes", "Surface texture rows disagree with realized images.");
3154
+ const seenHandles = /* @__PURE__ */ new Set();
3155
+ const expectedCaptureRows = surfaces.flatMap((value) => {
3156
+ const realized = value.realized;
3157
+ if (realized.source === "color") return [];
3158
+ const image = realized.source === "video" ? realized.video : realized.image;
3159
+ const handle = image.handle;
3160
+ const firstHandle = !seenHandles.has(handle);
3161
+ seenHandles.add(handle);
3162
+ if (realized.source === "video") return [
3163
+ ...firstHandle ? [{ owner: `capture:${handle}`, name: "encoded-video", domain: "cpu", resource: "encoded-video", format: image.mimeType, bytes: image.byteLength, lifetime: "context" }] : [],
3164
+ { owner: value.path, name: "owned-video-canvas", domain: "cpu", resource: "decoded-video", format: "rgba8", bytes: image.width * image.height * 4, lifetime: "context" }
3165
+ ];
3166
+ if (!firstHandle) return [];
3167
+ return [
3168
+ {
3169
+ owner: `capture:${handle}`,
3170
+ name: "encoded-image",
3171
+ domain: "cpu",
3172
+ resource: "encoded-image",
3173
+ format: image.mimeType,
3174
+ bytes: image.byteLength,
3175
+ lifetime: "context"
3176
+ },
3177
+ {
3178
+ owner: `capture:${handle}`,
3179
+ name: "decoded-rgba",
3180
+ domain: "cpu",
3181
+ resource: "decoded-image",
3182
+ format: "rgba8",
3183
+ bytes: image.width * image.height * 4,
3184
+ lifetime: "preparation"
3185
+ }
3186
+ ];
3187
+ });
3188
+ const actualCaptureRows = allocationRows.filter((row) => row.owner.startsWith("capture:") && ["encoded-image", "decoded-rgba", "encoded-video"].includes(row.name) || row.name === "owned-video-canvas");
3189
+ if (canonicalScenePreparedJsonV1(actualCaptureRows) !== canonicalScenePreparedJsonV1(expectedCaptureRows))
3190
+ throw new ScenePreparedPlanRefusal("plan.ledger.allocations", "Capture allocation rows disagree with realized images.");
3191
+ canonicalScenePreparedJsonV1(plan);
3192
+ }
3193
+ function canonicalScenePreparedJsonV1(value, path = "plan") {
3194
+ if (value === null || typeof value === "boolean" || typeof value === "string") return JSON.stringify(value);
3195
+ if (typeof value === "number") {
3196
+ if (!Number.isFinite(value)) throw new ScenePreparedPlanRefusal(path, `${path} must be finite.`);
3197
+ return JSON.stringify(value);
3198
+ }
3199
+ if (Array.isArray(value)) return `[${value.map((entry, index) => canonicalScenePreparedJsonV1(entry, `${path}.${index}`)).join(",")}]`;
3200
+ const object = record(value, path);
3201
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalScenePreparedJsonV1(object[key], `${path}.${key}`)}`).join(",")}}`;
3202
+ }
3203
+ function parseSealedSceneShotPlanV1(value) {
3204
+ const plan = record(value, "plan");
3205
+ exactKeys(
3206
+ plan,
3207
+ [
3208
+ "version",
3209
+ "status",
3210
+ "sealSha256",
3211
+ "scene",
3212
+ "output",
3213
+ "cameraIdentity",
3214
+ "motion",
3215
+ "objects",
3216
+ "surfaces",
3217
+ "separation",
3218
+ "rigIdentity",
3219
+ "rigAllocations",
3220
+ "fonts",
3221
+ "ledger"
3222
+ ],
3223
+ "plan"
3224
+ );
3225
+ if (plan.version !== 1) throw new ScenePreparedPlanRefusal("plan.version", "Prepared plan version must be 1.");
3226
+ if (plan.status !== "sealed") throw new ScenePreparedPlanRefusal("plan.status", "Prepared plan status must be sealed.");
3227
+ sha256Value(plan.sealSha256, "plan.sealSha256");
3228
+ const output = record(plan.output, "plan.output");
3229
+ exactKeys(output, ["width", "height", "fps", "durationInFrames"], "plan.output");
3230
+ const fps = record(output.fps, "plan.output.fps");
3231
+ exactKeys(fps, ["num", "den"], "plan.output.fps");
3232
+ for (const [path, number] of [
3233
+ ["plan.output.width", output.width],
3234
+ ["plan.output.height", output.height],
3235
+ ["plan.output.durationInFrames", output.durationInFrames],
3236
+ ["plan.output.fps.num", fps.num],
3237
+ ["plan.output.fps.den", fps.den]
3238
+ ]) {
3239
+ if (!Number.isSafeInteger(number) || number <= 0) throw new ScenePreparedPlanRefusal(path, `${path} must be a positive safe integer.`);
3240
+ }
3241
+ const gcd2 = (a, b) => b === 0 ? a : gcd2(b, a % b);
3242
+ if (gcd2(fps.num, fps.den) !== 1)
3243
+ throw new ScenePreparedPlanRefusal("plan.output.fps", "Sealed output FPS must be reduced to canonical terms.");
3244
+ for (const key of ["objects", "surfaces", "rigAllocations", "fonts"])
3245
+ if (!Array.isArray(plan[key])) throw new ScenePreparedPlanRefusal(`plan.${key}`, `plan.${key} must be an array.`);
3246
+ let scene;
3247
+ try {
3248
+ scene = parseSceneShotSpecV2(plan.scene);
3249
+ } catch (error) {
3250
+ const issue = error && typeof error === "object" && "issues" in error && Array.isArray(error.issues) ? error.issues[0]?.path ?? [] : [];
3251
+ throw new ScenePreparedPlanRefusal(`plan.scene${issue.map((part) => `.${String(part)}`).join("")}`, "Invalid authored scene in sealed plan.");
3252
+ }
3253
+ if (scene.referenceSize.width * output.height !== scene.referenceSize.height * output.width)
3254
+ throw new ScenePreparedPlanRefusal("plan.scene.referenceSize", "Scene reference size and sealed output have different aspect ratios.");
3255
+ validateNestedPlan(plan, scene);
3256
+ const clone = structuredClone(plan);
3257
+ clone.scene = scene;
3258
+ canonicalScenePreparedJsonV1(clone);
3259
+ return clone;
3260
+ }
3261
+ async function verifySealedSceneShotPlanV1(value, sha256) {
3262
+ const plan = parseSealedSceneShotPlanV1(value);
3263
+ const { sealSha256, ...facts } = plan;
3264
+ const actual = await sha256(canonicalScenePreparedJsonV1(facts));
3265
+ if (actual !== sealSha256) throw new ScenePreparedPlanRefusal("plan.sealSha256", "Prepared plan seal does not match its canonical facts.");
3266
+ for (const [index, surface] of plan.surfaces.entries()) {
3267
+ if (surface.realized.source !== "component") continue;
3268
+ const captureSha256 = await sha256(canonicalCaptureIdentityV1(surface.realized.captureIdentityFacts));
3269
+ if (captureSha256 !== surface.realized.captureIdentitySha256)
3270
+ throw new ScenePreparedPlanRefusal(
3271
+ `plan.surfaces.${index}.realized.captureIdentitySha256`,
3272
+ "Component capture identity digest does not match its canonical facts."
3273
+ );
3274
+ }
3275
+ const freeze = (entry) => {
3276
+ if (entry && typeof entry === "object" && !Object.isFrozen(entry)) {
3277
+ Object.freeze(entry);
3278
+ Object.values(entry).forEach(freeze);
3279
+ }
3280
+ };
3281
+ freeze(plan);
3282
+ return plan;
3283
+ }
3284
+
3285
+ export {
3286
+ SCENE_SHOT_SPEC_VERSION,
3287
+ SCENE_MAX_LAYERS,
3288
+ SCENE_MAX_GROUP_DEPTH,
3289
+ SCENE_MAX_KEYFRAMES_PER_CHANNEL,
3290
+ SCENE_MAX_KEYFRAMES_PER_SHOT,
3291
+ SCENE_MAX_SURFACES,
3292
+ SCENE_FOV_MIN,
3293
+ SCENE_FOV_MAX,
3294
+ SCENE_LAYER_ID_PATTERN,
3295
+ SCENE_ASSET_SLOT_PATTERN,
3296
+ SCENE_BRAND_COLOR_TOKENS,
3297
+ SCENE_BRAND_FONT_TOKENS,
3298
+ SceneHexColor,
3299
+ SceneBrandColorRef,
3300
+ SceneColor,
3301
+ SceneBrandFontRef,
3302
+ SCENE_FONT_FAMILY_PATTERN,
3303
+ SceneAdmittedFontFamily,
3304
+ SceneFontFamily,
3305
+ SceneLayerId,
3306
+ SceneAssetSlot,
3307
+ SceneReferenceSizeV2,
3308
+ SceneCameraInputV2,
3309
+ SceneCameraV2,
3310
+ SceneTransformV2,
3311
+ SceneBackdropV2,
3312
+ SceneSurfaceBindingV2,
3313
+ SceneMaterialOverrideV2,
3314
+ SceneObjectV2,
3315
+ SceneEasingV2,
3316
+ SceneKeyframeV2,
3317
+ SceneChannelTargetV2,
3318
+ SceneChannelV2,
3319
+ SCENE_CAMERA_PROPERTIES,
3320
+ SCENE_WORLD_OBJECT_PROPERTIES,
3321
+ SCENE_LAPTOP_PROPERTIES,
3322
+ SCENE_SCREEN_TEXT_PROPERTIES,
3323
+ SCENE_ENVIRONMENT_PROPERTIES,
3324
+ SCENE_FOCUS_PROPERTIES,
3325
+ SCENE_CHANNEL_PROPERTIES,
3326
+ SceneFocusV2,
3327
+ ScenePostV2,
3328
+ SceneRigV2,
3329
+ SceneConstraintV2,
3330
+ SCENE_PRESET_SLOTS,
3331
+ SCENE_PRESET_MATERIAL_ROLES,
3332
+ SceneShotSpecV2,
3333
+ parseSceneShotSpecV2,
3334
+ sceneShotImageSlots,
3335
+ sceneShotVideoSlots,
3336
+ sceneShotSurfaceCount,
3337
+ sceneShotJsonSchema,
3338
+ sceneShotJsonSchemaText,
3339
+ contentFrame,
3340
+ normalizedTime,
3341
+ cubicBezierEase,
3342
+ evaluateChannel,
3343
+ sceneTransformMatrix,
3344
+ evaluateScene,
3345
+ isSceneShotSource,
3346
+ readSceneSourceRef,
3347
+ isTimelineClip,
3348
+ isSceneShotClip,
3349
+ offFrameLattice,
3350
+ SCENE_TRANSITION_KINDS,
3351
+ sceneTransitionDurationIssues,
3352
+ sceneSpecIssues,
3353
+ sceneClipShapeIssues,
3354
+ sceneClipOverlapIssue,
3355
+ sceneDedicatedTrackIssue,
3356
+ collectSceneClipIssues,
3357
+ SceneConstraintRefusal,
3358
+ sceneRotationAxesV2,
3359
+ sceneOrientedBoundsV2,
3360
+ sceneSeparatingAxisGapV2,
3361
+ evaluateSceneSeparationV2,
3362
+ SCENE_SHOT_EXAMPLES,
3363
+ sceneShotExample,
3364
+ assertSceneShotExamplesValid,
3365
+ STUDIO_SHADOW_MAP_BUDGET,
3366
+ SCENE_TESSELLATION_VERSION,
3367
+ SCENE_SLAB_BODY_TRIANGLES,
3368
+ SCENE_SLAB_CONTENT_FACE_TRIANGLES,
3369
+ SCENE_SLAB_CONTENT_OFFSET,
3370
+ SCENE_PRESET_TRIANGLES,
3371
+ sceneFullMipLevels,
3372
+ sceneMipTexels,
3373
+ sceneTextureBytes,
3374
+ sceneTriangleCount,
3375
+ sceneDeviceDimensionsV1,
3376
+ sceneLaptopLayoutV1,
3377
+ SCENE_DEVICE_SCREEN_OFFSET_V1,
3378
+ sceneLaptopScreenQuadV1,
3379
+ PHOTOGRAPHIC_STUDIO_RIG_ID,
3380
+ PHOTOGRAPHIC_STUDIO_HDRI_PATH,
3381
+ PHOTOGRAPHIC_STUDIO_HDRI_SHA256,
3382
+ PHOTOGRAPHIC_STUDIO_HDRI_BYTES,
3383
+ PHOTOGRAPHIC_STUDIO_PMREM_VERSION,
3384
+ PHOTOGRAPHIC_STUDIO_LIGHTING_VERSION,
3385
+ PHOTOGRAPHIC_STUDIO_EDGE_INTENSITY,
3386
+ PHOTOGRAPHIC_STUDIO_EDGE_LIGHTS,
3387
+ PHOTOGRAPHIC_STUDIO_GPU_BYTES,
3388
+ PHOTOGRAPHIC_STUDIO_RIG_FACTS,
3389
+ photographicStudioPreparationFactsV1,
3390
+ sceneFocusPreparationAllocationsV1,
3391
+ photographicSceneRigFactsV1,
3392
+ SceneVideoAdmissionRefusal,
3393
+ canonicalSceneVideoRateV1,
3394
+ verifySceneVideoPresentationIndexV1,
3395
+ prepareSceneVideoScheduleV1,
3396
+ sceneVideoSourceIndexV1,
3397
+ sceneVideoMidpointAddressV1,
3398
+ RESOLUTION_BOUND,
3399
+ ALLOWED_BAKE_SCALES,
3400
+ DEFAULT_BAKE_SCALE,
3401
+ sceneCameraPlanesV2,
3402
+ SceneResolutionRefusal,
3403
+ projectToOutputPixels,
3404
+ projectedSlotEdges,
3405
+ checkSurfaceResolution,
3406
+ selectSceneBakeScale,
3407
+ ScenePreparedPlanRefusal,
3408
+ sceneW5PresetAllocationsV1,
3409
+ sceneW5WorldTextAllocationsV1,
3410
+ sceneW5GeneratedRigAllocationsV1,
3411
+ SCENE_CAPTURE_IDENTITY_FIELDS_V1,
3412
+ canonicalScenePreparedJsonV1,
3413
+ parseSealedSceneShotPlanV1,
3414
+ verifySealedSceneShotPlanV1
3415
+ };