@cueframe/kernel 0.1.0 → 0.1.1

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 (355) hide show
  1. package/LICENSE +661 -0
  2. package/dist/_members/composition/ops/compositionOps.d.ts +1 -0
  3. package/dist/_members/composition/projection/behind-readiness.d.ts +127 -0
  4. package/dist/_members/composition/projection/index.d.ts +2 -0
  5. package/dist/_members/composition/projection/reframe-readiness.d.ts +156 -0
  6. package/dist/_members/composition/validators/composition.d.ts +42 -25
  7. package/dist/_members/composition/validators/render.d.ts +32 -0
  8. package/dist/_members/core/render/compositionRender.d.ts +17 -1
  9. package/dist/_members/core/render/types.d.ts +31 -0
  10. package/dist/authoring/build-tools.js +6 -6
  11. package/dist/authoring/context.js +4 -4
  12. package/dist/authoring/tools.js +5 -5
  13. package/dist/authoring.js +11 -9
  14. package/dist/{chunk-ZB3C3HM7.js → chunk-7FO3ZJ43.js} +3 -0
  15. package/dist/{chunk-ODL7GQJG.js → chunk-AP6ZIVAV.js} +2 -2
  16. package/dist/{chunk-IBYQG46O.js → chunk-AQBREB5O.js} +1 -1
  17. package/dist/{chunk-YIOY2XEO.js → chunk-DK2ODBNT.js} +15 -5
  18. package/dist/{chunk-JRQVPFLF.js → chunk-HY3RIEZW.js} +1 -1
  19. package/dist/{chunk-GEA7E2FL.js → chunk-JJ6XD7NG.js} +151 -1
  20. package/dist/{chunk-COYMZ76S.js → chunk-MBCXCJOE.js} +1 -1
  21. package/dist/{chunk-37NJ27UU.js → chunk-OCFDUCF4.js} +3 -0
  22. package/dist/{chunk-VOO5FQSS.js → chunk-PDYO3DOJ.js} +5 -5
  23. package/dist/{chunk-MXFAADCQ.js → chunk-PODIYN2K.js} +9 -2
  24. package/dist/{chunk-NFEGPWGS.js → chunk-TOJEXFXL.js} +1 -1
  25. package/dist/{chunk-LM4WDUQJ.js → chunk-UHGSZZDI.js} +1 -1
  26. package/dist/{chunk-XWKZB5U5.js → chunk-ZWMV6DD2.js} +24 -1
  27. package/dist/compose/renderer.js +3 -3
  28. package/dist/compose.js +6 -6
  29. package/dist/composition/graphics-plan.js +1 -1
  30. package/dist/composition/ops.js +3 -3
  31. package/dist/composition/primitive-catalog.js +1 -1
  32. package/dist/composition/projection.js +19 -3
  33. package/dist/composition/validators.js +6 -4
  34. package/dist/composition-ops.js +8 -6
  35. package/dist/composition.js +24 -6
  36. package/dist/{context-OKX3HHFS.js → context-YWUZDG5X.js} +4 -4
  37. package/dist/core/render.js +3 -3
  38. package/dist/core/select.js +2 -2
  39. package/dist/core.js +3 -3
  40. package/dist/errors.js +1 -1
  41. package/dist/mcp/adapter.js +1 -0
  42. package/dist/media-model/testing.js +1071 -718
  43. package/dist/nle-export/server.js +328 -130
  44. package/dist/nle-export.js +7 -6
  45. package/package.json +13 -14
  46. package/dist/_members/account-model/index.js +0 -4
  47. package/dist/_members/account-model/testing/conversationStoreContract.js +0 -168
  48. package/dist/_members/account-model/testing/index.js +0 -7
  49. package/dist/_members/account-model/testing/restorePointStoreContract.js +0 -69
  50. package/dist/_members/account-model/types/index.js +0 -1
  51. package/dist/_members/account-model/validators/contextFile.js +0 -18
  52. package/dist/_members/account-model/validators/conversation.js +0 -95
  53. package/dist/_members/account-model/validators/dashboardInsight.js +0 -16
  54. package/dist/_members/account-model/validators/index.js +0 -7
  55. package/dist/_members/account-model/validators/sessionToken.js +0 -49
  56. package/dist/_members/account-model/validators/uploadToken.js +0 -16
  57. package/dist/_members/account-model/validators/userSettings.js +0 -12
  58. package/dist/_members/account-model/validators/workingComposition.js +0 -20
  59. package/dist/_members/brand-derivation/constants.js +0 -20
  60. package/dist/_members/brand-derivation/contrastSafe.js +0 -114
  61. package/dist/_members/brand-derivation/index.js +0 -1
  62. package/dist/_members/cloud-import/chunk-XKO6ZSWI.js +0 -162
  63. package/dist/_members/cloud-import/chunk-XKO6ZSWI.js.map +0 -1
  64. package/dist/_members/cloud-import/index.js +0 -241
  65. package/dist/_members/cloud-import/index.js.map +0 -1
  66. package/dist/_members/cloud-import/multipartUpload.js +0 -7
  67. package/dist/_members/cloud-import/multipartUpload.js.map +0 -1
  68. package/dist/_members/composition/audio/audioMix.js +0 -61
  69. package/dist/_members/composition/audio/index.js +0 -5
  70. package/dist/_members/composition/audio/mixStaging.js +0 -195
  71. package/dist/_members/composition/audio/musicPack.generated.js +0 -136
  72. package/dist/_members/composition/audio/musicPack.js +0 -36
  73. package/dist/_members/composition/audio/pack.generated.js +0 -603
  74. package/dist/_members/composition/audio/pack.js +0 -48
  75. package/dist/_members/composition/audio/palette.js +0 -134
  76. package/dist/_members/composition/autozoom/index.js +0 -83
  77. package/dist/_members/composition/brand-kit.js +0 -45
  78. package/dist/_members/composition/brief/contract.js +0 -209
  79. package/dist/_members/composition/color-grade.js +0 -80
  80. package/dist/_members/composition/depth.js +0 -8
  81. package/dist/_members/composition/fixtures/index.js +0 -143
  82. package/dist/_members/composition/fixtures/vaayaErrored.d.ts +0 -25
  83. package/dist/_members/composition/fixtures/vaayaErrored.js +0 -203
  84. package/dist/_members/composition/fonts.js +0 -162
  85. package/dist/_members/composition/graphics-plan/compile.js +0 -221
  86. package/dist/_members/composition/graphics-plan/index.js +0 -10
  87. package/dist/_members/composition/graphics-plan/instantiateMotif.js +0 -44
  88. package/dist/_members/composition/graphics-plan/motion.js +0 -66
  89. package/dist/_members/composition/graphics-plan/schemas.js +0 -261
  90. package/dist/_members/composition/graphics-plan/solve.js +0 -145
  91. package/dist/_members/composition/index.js +0 -5
  92. package/dist/_members/composition/invariants/clipPlacement.js +0 -69
  93. package/dist/_members/composition/invariants/composition.js +0 -28
  94. package/dist/_members/composition/invariants/errors.js +0 -10
  95. package/dist/_members/composition/invariants/etag.js +0 -38
  96. package/dist/_members/composition/invariants/ids.js +0 -33
  97. package/dist/_members/composition/invariants/index.js +0 -9
  98. package/dist/_members/composition/invariants/keyframes.js +0 -45
  99. package/dist/_members/composition/invariants/markers.js +0 -29
  100. package/dist/_members/composition/invariants/time.js +0 -20
  101. package/dist/_members/composition/invariants/tracks.js +0 -304
  102. package/dist/_members/composition/ops/compositionOps.js +0 -1056
  103. package/dist/_members/composition/ops/headlessStore.js +0 -94
  104. package/dist/_members/composition/ops/index.js +0 -4
  105. package/dist/_members/composition/ops/selectors.js +0 -118
  106. package/dist/_members/composition/ops/timeline-word.js +0 -1
  107. package/dist/_members/composition/ops/transcript.js +0 -77
  108. package/dist/_members/composition/ops/types.js +0 -16
  109. package/dist/_members/composition/primitive-catalog/catalogQuery.js +0 -150
  110. package/dist/_members/composition/primitive-catalog/disabled.js +0 -32
  111. package/dist/_members/composition/primitive-catalog/generated/meta.js +0 -15161
  112. package/dist/_members/composition/primitive-catalog/index.js +0 -46
  113. package/dist/_members/composition/primitive-catalog/paramValidation.js +0 -231
  114. package/dist/_members/composition/primitive-catalog/projection.js +0 -164
  115. package/dist/_members/composition/primitive-catalog/types.js +0 -14
  116. package/dist/_members/composition/projection/behind-placement.js +0 -62
  117. package/dist/_members/composition/projection/brandKit.js +0 -482
  118. package/dist/_members/composition/projection/cardTokens.js +0 -209
  119. package/dist/_members/composition/projection/composition-floor.js +0 -582
  120. package/dist/_members/composition/projection/cropProjection.js +0 -257
  121. package/dist/_members/composition/projection/index.js +0 -7
  122. package/dist/_members/composition/projection/occlusion-budget.js +0 -104
  123. package/dist/_members/composition/projection/safe-zones.js +0 -99
  124. package/dist/_members/composition/reframe/aspectLoss.js +0 -115
  125. package/dist/_members/composition/reframe/coverage.js +0 -62
  126. package/dist/_members/composition/reframe/derive.js +0 -73
  127. package/dist/_members/composition/reframe/index.js +0 -109
  128. package/dist/_members/composition/reframe/kenBurns.js +0 -64
  129. package/dist/_members/composition/reframe/shotScale.js +0 -176
  130. package/dist/_members/composition/reframe/trajectory.js +0 -124
  131. package/dist/_members/composition/timeline/compound.js +0 -134
  132. package/dist/_members/composition/timeline/index.js +0 -9
  133. package/dist/_members/composition/timeline/intent.js +0 -228
  134. package/dist/_members/composition/timeline/keyframes.js +0 -121
  135. package/dist/_members/composition/timeline/link.js +0 -59
  136. package/dist/_members/composition/timeline/occupancy.js +0 -36
  137. package/dist/_members/composition/timeline/placement.js +0 -74
  138. package/dist/_members/composition/timeline/plan.js +0 -31
  139. package/dist/_members/composition/timeline/resolve.js +0 -170
  140. package/dist/_members/composition/timeline/trims.js +0 -278
  141. package/dist/_members/composition/types/index.js +0 -1
  142. package/dist/_members/composition/validators/brandKit.js +0 -295
  143. package/dist/_members/composition/validators/composePlan.js +0 -113
  144. package/dist/_members/composition/validators/composition.js +0 -703
  145. package/dist/_members/composition/validators/font.js +0 -34
  146. package/dist/_members/composition/validators/graphicKinds.js +0 -30
  147. package/dist/_members/composition/validators/index.js +0 -9
  148. package/dist/_members/composition/validators/project.js +0 -29
  149. package/dist/_members/composition/validators/registry.js +0 -41
  150. package/dist/_members/composition/validators/render.js +0 -139
  151. package/dist/_members/composition/validators/session.js +0 -37
  152. package/dist/_members/composition/validators/webhook.js +0 -72
  153. package/dist/_members/composition-ops/alphaLayers.js +0 -141
  154. package/dist/_members/composition-ops/index.js +0 -637
  155. package/dist/_members/composition-ops/materializeGrid.js +0 -99
  156. package/dist/_members/composition-ops/opRecorder.js +0 -196
  157. package/dist/_members/composition-ops/recordingStore.js +0 -14
  158. package/dist/_members/composition-ops/replaceRegion.js +0 -102
  159. package/dist/_members/composition-ops/testFixtures.js +0 -19
  160. package/dist/_members/core/index.js +0 -5
  161. package/dist/_members/core/render/bookends.js +0 -53
  162. package/dist/_members/core/render/captions.js +0 -127
  163. package/dist/_members/core/render/compositionRender.js +0 -1249
  164. package/dist/_members/core/render/deriveTrajectory.js +0 -889
  165. package/dist/_members/core/render/emphasis.js +0 -16
  166. package/dist/_members/core/render/eval/cropBox.js +0 -139
  167. package/dist/_members/core/render/eval/inclusion.js +0 -96
  168. package/dist/_members/core/render/eval/index.js +0 -146
  169. package/dist/_members/core/render/eval/metrics.js +0 -94
  170. package/dist/_members/core/render/exportSettings.js +0 -65
  171. package/dist/_members/core/render/index.js +0 -20
  172. package/dist/_members/core/render/mapFaceToPerson.js +0 -73
  173. package/dist/_members/core/render/types.js +0 -8
  174. package/dist/_members/core/render/vcam/cameraPath.js +0 -248
  175. package/dist/_members/core/render/vcam/constants.js +0 -115
  176. package/dist/_members/core/render/vcam/errorCodes.js +0 -39
  177. package/dist/_members/core/render/vcam/framing.js +0 -136
  178. package/dist/_members/core/render/vcam/requiredRegion.js +0 -137
  179. package/dist/_members/core/render/vcam/shots.js +0 -53
  180. package/dist/_members/core/render/vcam/subjectSet.js +0 -388
  181. package/dist/_members/core/select/errorCodes.js +0 -38
  182. package/dist/_members/core/select/index.js +0 -26
  183. package/dist/_members/core/select/model.js +0 -18
  184. package/dist/_members/core/select/select.js +0 -244
  185. package/dist/_members/core/select/stabilize.js +0 -242
  186. package/dist/_members/core/types.js +0 -7
  187. package/dist/_members/errors/index.js +0 -1
  188. package/dist/_members/errors/registry.js +0 -560
  189. package/dist/_members/foundation/codes/index.js +0 -31
  190. package/dist/_members/foundation/index.js +0 -2
  191. package/dist/_members/foundation/primitives/index.js +0 -5
  192. package/dist/_members/mcp/adapter.js +0 -771
  193. package/dist/_members/mcp/burnedNames.js +0 -73
  194. package/dist/_members/mcp/core.js +0 -390
  195. package/dist/_members/mcp/curation.js +0 -792
  196. package/dist/_members/mcp/executor.js +0 -142
  197. package/dist/_members/mcp/format.js +0 -9
  198. package/dist/_members/mcp/generated/catalogPreviews.js +0 -95
  199. package/dist/_members/mcp/generated/harness.js +0 -81
  200. package/dist/_members/mcp/generated/operations.js +0 -131
  201. package/dist/_members/mcp/index.js +0 -50
  202. package/dist/_members/mcp/internalLoopTools.js +0 -109
  203. package/dist/_members/mcp/prompts.js +0 -45
  204. package/dist/_members/mcp/resources.js +0 -166
  205. package/dist/_members/mcp/server.js +0 -340
  206. package/dist/_members/mcp/wait.js +0 -378
  207. package/dist/_members/mcp/x402Meta.js +0 -66
  208. package/dist/_members/media-model/api.js +0 -175
  209. package/dist/_members/media-model/index.js +0 -1
  210. package/dist/_members/media-model/media-kind.js +0 -116
  211. package/dist/_members/media-model/testing/index.js +0 -1
  212. package/dist/_members/media-model/testing/mediaStoreContract.js +0 -82
  213. package/dist/_members/media-model/transcriptSearch.js +0 -70
  214. package/dist/_members/media-model/transcriptSearchCases.json +0 -79
  215. package/dist/_members/nle-export/compact-timeline.js +0 -73
  216. package/dist/_members/nle-export/compact-timeline.js.map +0 -1
  217. package/dist/_members/nle-export/composition-to-input.js +0 -147
  218. package/dist/_members/nle-export/composition-to-input.js.map +0 -1
  219. package/dist/_members/nle-export/fcpxml.js +0 -159
  220. package/dist/_members/nle-export/fcpxml.js.map +0 -1
  221. package/dist/_members/nle-export/index.js +0 -12
  222. package/dist/_members/nle-export/index.js.map +0 -1
  223. package/dist/_members/nle-export/premiere.js +0 -161
  224. package/dist/_members/nle-export/premiere.js.map +0 -1
  225. package/dist/_members/nle-export/server/index.js +0 -2
  226. package/dist/_members/nle-export/server/index.js.map +0 -1
  227. package/dist/_members/nle-export/server/pack-zip.js +0 -38
  228. package/dist/_members/nle-export/server/pack-zip.js.map +0 -1
  229. package/dist/_members/nle-export/srt.js +0 -50
  230. package/dist/_members/nle-export/srt.js.map +0 -1
  231. package/dist/_members/nle-export/types.js +0 -9
  232. package/dist/_members/nle-export/types.js.map +0 -1
  233. package/dist/_members/nle-export/xml/index.js +0 -11
  234. package/dist/_members/nle-export/xml/index.js.map +0 -1
  235. package/dist/_members/nle-export/xml-utils.js +0 -48
  236. package/dist/_members/nle-export/xml-utils.js.map +0 -1
  237. package/dist/_members/perception/activeSpeaker.js +0 -103
  238. package/dist/_members/perception/audioQuality.js +0 -76
  239. package/dist/_members/perception/energyBeat.js +0 -99
  240. package/dist/_members/perception/identityMatch.js +0 -91
  241. package/dist/_members/perception/index.js +0 -48
  242. package/dist/_members/perception/visualQuality.js +0 -95
  243. package/dist/_members/plane/conformance.js +0 -90
  244. package/dist/_members/plane/contract.js +0 -49
  245. package/dist/_members/plane/index.js +0 -16
  246. package/dist/_members/primitives/backgrounds/index.js +0 -32
  247. package/dist/_members/primitives/backgrounds/index.js.map +0 -1
  248. package/dist/_members/primitives/captions/index.js +0 -398
  249. package/dist/_members/primitives/captions/index.js.map +0 -1
  250. package/dist/_members/primitives/chunk-2LVE4UIT.js +0 -139
  251. package/dist/_members/primitives/chunk-2LVE4UIT.js.map +0 -1
  252. package/dist/_members/primitives/chunk-3IQUH6KK.js +0 -20
  253. package/dist/_members/primitives/chunk-3IQUH6KK.js.map +0 -1
  254. package/dist/_members/primitives/chunk-3OMURV6Q.js +0 -112
  255. package/dist/_members/primitives/chunk-3OMURV6Q.js.map +0 -1
  256. package/dist/_members/primitives/chunk-4IS6TMEG.js +0 -21
  257. package/dist/_members/primitives/chunk-4IS6TMEG.js.map +0 -1
  258. package/dist/_members/primitives/chunk-5MWGAEGQ.js +0 -1644
  259. package/dist/_members/primitives/chunk-5MWGAEGQ.js.map +0 -1
  260. package/dist/_members/primitives/chunk-7RRPFPQK.js +0 -15
  261. package/dist/_members/primitives/chunk-7RRPFPQK.js.map +0 -1
  262. package/dist/_members/primitives/chunk-DH5TRZK2.js +0 -125
  263. package/dist/_members/primitives/chunk-DH5TRZK2.js.map +0 -1
  264. package/dist/_members/primitives/chunk-E3KNUSFG.js +0 -2337
  265. package/dist/_members/primitives/chunk-E3KNUSFG.js.map +0 -1
  266. package/dist/_members/primitives/chunk-G2B3G4HK.js +0 -47
  267. package/dist/_members/primitives/chunk-G2B3G4HK.js.map +0 -1
  268. package/dist/_members/primitives/chunk-HFPFGZXT.js +0 -1494
  269. package/dist/_members/primitives/chunk-HFPFGZXT.js.map +0 -1
  270. package/dist/_members/primitives/chunk-HIWVFZVH.js +0 -19
  271. package/dist/_members/primitives/chunk-HIWVFZVH.js.map +0 -1
  272. package/dist/_members/primitives/chunk-IDAL6G3Y.js +0 -134
  273. package/dist/_members/primitives/chunk-IDAL6G3Y.js.map +0 -1
  274. package/dist/_members/primitives/chunk-KNC6JP5F.js +0 -34
  275. package/dist/_members/primitives/chunk-KNC6JP5F.js.map +0 -1
  276. package/dist/_members/primitives/chunk-KYRDVZST.js +0 -459
  277. package/dist/_members/primitives/chunk-KYRDVZST.js.map +0 -1
  278. package/dist/_members/primitives/chunk-LWLHS2NS.js +0 -3410
  279. package/dist/_members/primitives/chunk-LWLHS2NS.js.map +0 -1
  280. package/dist/_members/primitives/chunk-QAQ3P7DR.js +0 -8855
  281. package/dist/_members/primitives/chunk-QAQ3P7DR.js.map +0 -1
  282. package/dist/_members/primitives/chunk-ROV2ODWC.js +0 -81
  283. package/dist/_members/primitives/chunk-ROV2ODWC.js.map +0 -1
  284. package/dist/_members/primitives/chunk-TCRC2FH3.js +0 -183
  285. package/dist/_members/primitives/chunk-TCRC2FH3.js.map +0 -1
  286. package/dist/_members/primitives/chunk-US4YEDAA.js +0 -96
  287. package/dist/_members/primitives/chunk-US4YEDAA.js.map +0 -1
  288. package/dist/_members/primitives/chunk-UVKRO5ER.js +0 -13
  289. package/dist/_members/primitives/chunk-UVKRO5ER.js.map +0 -1
  290. package/dist/_members/primitives/chunk-W4EIIF2I.js +0 -10
  291. package/dist/_members/primitives/chunk-W4EIIF2I.js.map +0 -1
  292. package/dist/_members/primitives/chunk-WPGKIPCO.js +0 -3563
  293. package/dist/_members/primitives/chunk-WPGKIPCO.js.map +0 -1
  294. package/dist/_members/primitives/chunk-WZFLEGMD.js +0 -1435
  295. package/dist/_members/primitives/chunk-WZFLEGMD.js.map +0 -1
  296. package/dist/_members/primitives/chunk-XHU3WBEI.js +0 -33
  297. package/dist/_members/primitives/chunk-XHU3WBEI.js.map +0 -1
  298. package/dist/_members/primitives/chunk-YN7WTZZ7.js +0 -46
  299. package/dist/_members/primitives/chunk-YN7WTZZ7.js.map +0 -1
  300. package/dist/_members/primitives/effects/index.js +0 -30
  301. package/dist/_members/primitives/effects/index.js.map +0 -1
  302. package/dist/_members/primitives/hero-text/index.js +0 -14
  303. package/dist/_members/primitives/hero-text/index.js.map +0 -1
  304. package/dist/_members/primitives/index.js +0 -64
  305. package/dist/_members/primitives/index.js.map +0 -1
  306. package/dist/_members/primitives/layouts/index.js +0 -24
  307. package/dist/_members/primitives/layouts/index.js.map +0 -1
  308. package/dist/_members/primitives/lib/easings.js +0 -26
  309. package/dist/_members/primitives/lib/easings.js.map +0 -1
  310. package/dist/_members/primitives/lib/index.js +0 -74
  311. package/dist/_members/primitives/lib/index.js.map +0 -1
  312. package/dist/_members/primitives/lib/palette.js +0 -72
  313. package/dist/_members/primitives/lib/palette.js.map +0 -1
  314. package/dist/_members/primitives/lib/preview.js +0 -8
  315. package/dist/_members/primitives/lib/preview.js.map +0 -1
  316. package/dist/_members/primitives/lib/schema.js +0 -12
  317. package/dist/_members/primitives/lib/schema.js.map +0 -1
  318. package/dist/_members/primitives/lib/seeded-random.js +0 -14
  319. package/dist/_members/primitives/lib/seeded-random.js.map +0 -1
  320. package/dist/_members/primitives/lib/text/contrast.js +0 -12
  321. package/dist/_members/primitives/lib/text/contrast.js.map +0 -1
  322. package/dist/_members/primitives/lib/text/fill.js +0 -8
  323. package/dist/_members/primitives/lib/text/fill.js.map +0 -1
  324. package/dist/_members/primitives/lib/text/fitText.js +0 -10
  325. package/dist/_members/primitives/lib/text/fitText.js.map +0 -1
  326. package/dist/_members/primitives/lib/text/marker.js +0 -8
  327. package/dist/_members/primitives/lib/text/marker.js.map +0 -1
  328. package/dist/_members/primitives/lib/text/motion.js +0 -16
  329. package/dist/_members/primitives/lib/text/motion.js.map +0 -1
  330. package/dist/_members/primitives/lib/text/recolor.js +0 -8
  331. package/dist/_members/primitives/lib/text/recolor.js.map +0 -1
  332. package/dist/_members/primitives/lib/text/spec.js +0 -1
  333. package/dist/_members/primitives/lib/text/spec.js.map +0 -1
  334. package/dist/_members/primitives/lib/text/stage.js +0 -8
  335. package/dist/_members/primitives/lib/text/stage.js.map +0 -1
  336. package/dist/_members/primitives/lib/text/typeface.js +0 -8
  337. package/dist/_members/primitives/lib/text/typeface.js.map +0 -1
  338. package/dist/_members/primitives/lib/tokens.js +0 -24
  339. package/dist/_members/primitives/lib/tokens.js.map +0 -1
  340. package/dist/_members/primitives/lib/types.js +0 -1
  341. package/dist/_members/primitives/lib/types.js.map +0 -1
  342. package/dist/_members/primitives/params-required.json +0 -153
  343. package/dist/_members/primitives/scenes/index.js +0 -25
  344. package/dist/_members/primitives/scenes/index.js.map +0 -1
  345. package/dist/_members/primitives/subject-foreground/index.js +0 -30
  346. package/dist/_members/primitives/subject-foreground/index.js.map +0 -1
  347. package/dist/_members/primitives/text/index.js +0 -57
  348. package/dist/_members/primitives/text/index.js.map +0 -1
  349. package/dist/_members/primitives/transitions/index.js +0 -67
  350. package/dist/_members/primitives/transitions/index.js.map +0 -1
  351. package/dist/_members/primitives/ui-blocks/index.js +0 -76
  352. package/dist/_members/primitives/ui-blocks/index.js.map +0 -1
  353. package/dist/_members/primitives/version-map.json +0 -105
  354. package/dist/_members/primitives/workspace.js +0 -60
  355. package/dist/_members/primitives/workspace.js.map +0 -1
@@ -1,248 +0,0 @@
1
- /**
2
- * vcam/cameraPath.ts — the SINGLE virtual-camera stability stage (Phase 3).
3
- *
4
- * Replaces both `emaSmooth` (for the centers) and the render-side
5
- * `smoothTrajectory` Gaussian with ONE pass per shot. Per the spec
6
- * (2026-06-21-virtual-camera-reframe-design.md, "Virtual-camera stability — one
7
- * stage, no LP"):
8
- *
9
- * 1. Deadzone INCLUSION gate (output-crop-normalized): the camera HOLDS while
10
- * the padded RequiredRegion stays inside a safe region of the current crop
11
- * window; it moves ONLY when the region would exit. This is the inclusion
12
- * INVARIANT — the crop always contains the RequiredRegion ⇒ the subject
13
- * never walks out of frame. This is what turns Phase 2's "static hold" into
14
- * "hold, then glide to re-contain" — a moving subject is TRACKED.
15
- * 2. One Hamming-window weighted mean over each shot run's gated centers
16
- * (acausal, single pass, no lag). Forward-fill at the start; the window
17
- * AUTO-SHRINKS near a shot boundary so it never blends across a cut.
18
- * 3. Null/occluded regions are forward-filled (the last in-shot region) before
19
- * the gate, minus the deleted jump heuristic — gaps longer than the window
20
- * hold; re-emergence outside the pad glides over the window.
21
- *
22
- * No second smoothing: render samples this base path verbatim. The only reset is
23
- * a detector `shotBoundary` — a center jump is NEVER inferred as a cut (the
24
- * deleted `isJump`/`JUMP_THRESHOLD` false-cut teleport).
25
- *
26
- * Coordinate systems: RequiredRegions + centers are SOURCE-NORMALIZED. The
27
- * deadzone is OUTPUT-CROP-NORMALIZED (a fraction of the crop window), so the
28
- * held feel is zoom-invariant — it is converted to source units here using the
29
- * crop-window size. Pure, deterministic, no I/O.
30
- */
31
- import { segmentShots } from './shots';
32
- import { DEADZONE, SMOOTH_WINDOW_SEC, REACQUIRE_GLIDE_PER_SEC } from './constants';
33
- /** Clamp a window center so the crop window stays fully inside the source (the
34
- * same coverage invariant the renderer enforces). A window wider than the axis
35
- * centers. */
36
- function clampCenter(c, sizeFrac) {
37
- if (sizeFrac >= 1)
38
- return 0.5;
39
- const half = sizeFrac / 2;
40
- return Math.max(half, Math.min(1 - half, c));
41
- }
42
- /**
43
- * Minimal center that contains a 1-D interval `[lo, hi]` inside a window of size
44
- * `win` whose interior is shrunk by the deadzone margin `dz` on each side (the
45
- * "safe region"). Returns the current `held` center UNCHANGED when the interval
46
- * already fits in the held window's safe region; otherwise the nearest center
47
- * that just re-contains it (push the window the smallest distance). The deadzone
48
- * is the hold band: motion inside it demands zero camera motion.
49
- */
50
- function gate1D(held, lo, hi, win, dz) {
51
- const safe = Math.max(0, win - 2 * dz); // safe-region width (window minus margins)
52
- const ext = hi - lo;
53
- const mid = (lo + hi) / 2;
54
- // Region wider than the safe band: can't be fully contained at this zoom (an
55
- // OVER-WIDE group — surfaced upstream as `overframed` in a later phase). HOLD
56
- // on the midpoint within the SAME deadzone so the camera doesn't wobble as
57
- // members enter/exit the union box; only re-centre when the midpoint itself
58
- // drifts beyond the deadzone (a real group shift). Without this hold an over-
59
- // wide held group jitters every frame on the union-box breathing.
60
- if (ext >= safe) {
61
- if (Math.abs(mid - held) <= dz)
62
- return held;
63
- return mid > held ? mid - dz : mid + dz;
64
- }
65
- const safeLo = held - safe / 2;
66
- const safeHi = held + safe / 2;
67
- if (lo >= safeLo && hi <= safeHi)
68
- return held; // already contained → HOLD
69
- // Push minimally: if the region exits left, align its left to the safe edge;
70
- // if it exits right, align its right. (Both can't exit — ext < safe.)
71
- if (lo < safeLo)
72
- return lo + safe / 2;
73
- return hi - safe / 2;
74
- }
75
- /**
76
- * Inclusion clamp with a per-frame GLIDE cap: re-contain the FULL region
77
- * `[lo,hi]` inside a window of size `win` centered near `c`, but move no more
78
- * than `maxStep` (source units) from the PREVIOUS output center `prev` in one
79
- * frame. This preserves the inclusion invariant during CONTINUOUS tracking (the
80
- * needed correction is sub-`maxStep`, so it's applied in full) while letting a
81
- * far RE-ACQUISITION (subject re-emerges across the frame after a gap) GLIDE
82
- * over several frames instead of teleporting — the spec's "re-emergence glides
83
- * over the window" (the parabolic re-acquire ease is the deferred L1 slot; this
84
- * is the linear floor). A region wider than the window can't be contained →
85
- * ease toward its midpoint.
86
- */
87
- function includeGlide1D(c, prev, lo, hi, win, maxStep) {
88
- const ext = hi - lo;
89
- let target;
90
- if (ext >= win) {
91
- // Over-wide: can't be contained at this zoom. Keep the smoothed (held) center
92
- // — the gate already held it on the union midpoint within the deadzone, so
93
- // re-snapping to the raw midpoint here would re-introduce the wobble.
94
- target = c;
95
- }
96
- else {
97
- const winLo = c - win / 2;
98
- const winHi = c + win / 2;
99
- if (lo >= winLo && hi <= winHi)
100
- target = c; // already contains the region
101
- else if (lo < winLo)
102
- target = lo + win / 2;
103
- else
104
- target = hi - win / 2;
105
- }
106
- // Glide cap: never jump more than maxStep from the previous output center.
107
- const d = target - prev;
108
- if (d > maxStep)
109
- return prev + maxStep;
110
- if (d < -maxStep)
111
- return prev - maxStep;
112
- return target;
113
- }
114
- /**
115
- * The deadzone INCLUSION gate over one shot's regions. Walks frames in order,
116
- * holding the camera while the padded region stays inside the safe region of the
117
- * current crop window, stepping minimally to re-contain it when it exits. The
118
- * gate is seeded from the first region (so the shot opens framed on its subject,
119
- * not at a stale center). Returns the per-frame GATED center (source-normalized),
120
- * the input to the Hamming smoother.
121
- */
122
- function deadzoneGate(regions, crop, dzX, dzY) {
123
- const out = [];
124
- // Seed the held center on the first region, coverage-clamped.
125
- let hx = clampCenter(regions[0].cx, crop.w);
126
- let hy = clampCenter(regions[0].cy, crop.h);
127
- for (const r of regions) {
128
- const loX = r.cx - r.w / 2;
129
- const hiX = r.cx + r.w / 2;
130
- const loY = r.cy - r.h / 2;
131
- const hiY = r.cy + r.h / 2;
132
- hx = clampCenter(gate1D(hx, loX, hiX, crop.w, dzX), crop.w);
133
- hy = clampCenter(gate1D(hy, loY, hiY, crop.h, dzY), crop.h);
134
- out.push([hx, hy]);
135
- }
136
- return out;
137
- }
138
- /**
139
- * Acausal Hamming-weighted moving average over a shot run's gated centers. The
140
- * half-window auto-shrinks at the shot ENDS (it never reads past `[0, n)`), so a
141
- * shot shorter than the window smooths over its own length and never blends
142
- * across the cut. Single pass, symmetric weights → no lag.
143
- */
144
- function hammingSmooth(centers, halfWindow) {
145
- const n = centers.length;
146
- if (n <= 1 || halfWindow < 1)
147
- return centers.map((c) => [c[0], c[1]]);
148
- // Hamming weight at offset d (|d| ≤ halfWindow): 0.54 + 0.46·cos(π·d/halfWindow).
149
- const weight = (d) => 0.54 + 0.46 * Math.cos((Math.PI * d) / halfWindow);
150
- const out = new Array(n);
151
- for (let i = 0; i < n; i++) {
152
- const lo = Math.max(0, i - halfWindow);
153
- const hi = Math.min(n - 1, i + halfWindow);
154
- let sx = 0;
155
- let sy = 0;
156
- let sw = 0;
157
- for (let j = lo; j <= hi; j++) {
158
- const w = weight(j - i);
159
- sx += centers[j][0] * w;
160
- sy += centers[j][1] * w;
161
- sw += w;
162
- }
163
- out[i] = sw > 0 ? [sx / sw, sy / sw] : [centers[i][0], centers[i][1]];
164
- }
165
- return out;
166
- }
167
- /**
168
- * Forward-fill null regions within a shot slice. A gap (subject occluded) holds
169
- * the LAST in-shot region; a leading gap (no region yet) borrows the first known
170
- * region in the shot. A shot with no region at all (never resolved) → null,
171
- * handled by the caller (center-hold). NO jump back-fill (the deleted heuristic).
172
- */
173
- function fillShot(regions) {
174
- const first = regions.find((r) => r !== null) ?? null;
175
- if (first === null)
176
- return null;
177
- const out = new Array(regions.length);
178
- let last = first;
179
- for (let i = 0; i < regions.length; i++) {
180
- if (regions[i] !== null)
181
- last = regions[i];
182
- out[i] = last;
183
- }
184
- return out;
185
- }
186
- /**
187
- * The single stability stage: per-frame RequiredRegions → smoothed, inclusion-
188
- * satisfying per-frame crop centers (source-normalized). `cropFor(frame)` gives
189
- * the crop window for that frame (it can vary per frame when zoom is eased).
190
- *
191
- * `interiorCuts` are segment-local frame indices where a new shot begins (the
192
- * SAME set the derive already computes). Each shot is gated + smoothed
193
- * independently (deadzone, window clamp, forward-fill all reset at the cut).
194
- * Frames whose shot never resolved a region hold `centerHold` (source center).
195
- */
196
- export function cameraPath(regions, cropFor, fps, interiorCuts, centerHold = [0.5, 0.5]) {
197
- const n = regions.length;
198
- if (n === 0)
199
- return [];
200
- const halfWindow = Math.max(1, Math.round(SMOOTH_WINDOW_SEC * fps));
201
- const out = new Array(n);
202
- const shots = segmentShots(interiorCuts, n);
203
- for (const shot of shots) {
204
- const slice = regions.slice(shot.start, shot.end);
205
- // `regions` are already the outlier-robust RequiredRegions
206
- // (`buildRequiredRegions`): single-frame detector/ReID spikes rejected, so
207
- // the gate frames the temporally-coherent subject, not a flicker.
208
- const filled = fillShot(slice);
209
- if (filled === null) {
210
- // No region resolved anywhere in this shot → hold source center (the
211
- // documented faceless/occluded outcome).
212
- for (let i = shot.start; i < shot.end; i++)
213
- out[i] = [centerHold[0], centerHold[1]];
214
- continue;
215
- }
216
- // Use the shot's first crop window for the deadzone margins (zoom is per-shot
217
- // constant for the non-eased path; eased zoom is rare on all-faces and the
218
- // gate margin is robust to small per-frame zoom drift).
219
- const crop0 = cropFor(shot.start);
220
- const dzX = DEADZONE * crop0.w;
221
- const dzY = DEADZONE * crop0.h;
222
- const gated = deadzoneGate(filled, crop0, dzX, dzY);
223
- // Auto-shrink the window to the shot length so it never blends across a cut.
224
- const hw = Math.min(halfWindow, Math.max(1, gated.length - 1));
225
- const smoothed = hammingSmooth(gated, hw);
226
- // Post-smooth: re-establish the INCLUSION INVARIANT with a per-frame GLIDE
227
- // cap. Continuous tracking needs a sub-cap correction → applied in full (the
228
- // crop contains the RequiredRegion ⇒ the subject never walks out of frame).
229
- // A far RE-ACQUISITION after a gap (subject re-emerges across the frame)
230
- // exceeds the cap → it GLIDES over several frames instead of teleporting (the
231
- // spec's "re-emergence glides over the window"). Then the coverage clamp
232
- // keeps the window inside the source.
233
- const maxStepX = (REACQUIRE_GLIDE_PER_SEC * crop0.w) / fps;
234
- const maxStepY = (REACQUIRE_GLIDE_PER_SEC * crop0.h) / fps;
235
- let prevX = clampCenter(smoothed[0][0], crop0.w);
236
- let prevY = clampCenter(smoothed[0][1], crop0.h);
237
- for (let i = 0; i < smoothed.length; i++) {
238
- const crop = cropFor(shot.start + i);
239
- const r = filled[i];
240
- const incX = includeGlide1D(smoothed[i][0], prevX, r.cx - r.w / 2, r.cx + r.w / 2, crop.w, maxStepX);
241
- const incY = includeGlide1D(smoothed[i][1], prevY, r.cy - r.h / 2, r.cy + r.h / 2, crop.h, maxStepY);
242
- prevX = clampCenter(incX, crop.w);
243
- prevY = clampCenter(incY, crop.h);
244
- out[shot.start + i] = [prevX, prevY];
245
- }
246
- }
247
- return out;
248
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * vcam/constants.ts — all virtual-camera selection params, in one place.
3
- *
4
- * Phase 2 (subject-of-interest selection). The deadzone / Hamming-window /
5
- * zoom-fit params land alongside their modules in Phase 3/5; only the
6
- * Phase-2 selection params live here today.
7
- *
8
- * Sizes are FRAME-HEIGHT fractions (a face box's height ÷ source height), the
9
- * same units the detector emits. Persistence is a fraction of the shot's frames.
10
- */
11
- /** Locked-set gate — drop a track iff
12
- * `(persistence < P_MIN AND size_med < SIZE_DOMINANT) OR (size_med < SIZE_MIN)`.
13
- * A transient small/brief face is dropped (can't re-weight anything); a
14
- * DOMINANT late entrant (low persistence, large size) is KEPT — the flat
15
- * `persistence < 0.5` gate would have silently dropped the intended subject. */
16
- export const P_MIN = 0.5;
17
- /** Minimum face-height fraction to survive the gate at all. */
18
- export const SIZE_MIN = 0.06;
19
- /** Face-height fraction at/above which a brief track is kept regardless of
20
- * persistence (a dominant late entrant). */
21
- export const SIZE_DOMINANT = 0.18;
22
- /** Max members framed for `all-faces` (top-K by dominance). */
23
- export const MAX_SUBJECTS = 3;
24
- /** Dominance weights — `0.4·size + 0.2·centrality + 0.2·persistence + 0.2·speaking`.
25
- * `speaking`/`centrality` feed dominance, NEVER the gate. */
26
- export const DOMINANCE_WEIGHTS = {
27
- size: 0.4,
28
- centrality: 0.2,
29
- persistence: 0.2,
30
- speaking: 0.2,
31
- };
32
- /** Per-frame set-MEMBERSHIP hysteresis (in seconds; converted to frames with
33
- * the source fps). The locked set is the candidate pool; whether a member is
34
- * IN the framed region this frame is decided with add/drop hysteresis so a
35
- * member flickering at the gate boundary doesn't toggle the framing. The
36
- * deadzone (Phase 3) stabilizes PAN; hysteresis stabilizes set MEMBERSHIP. */
37
- export const ENTER_SEC = 0.3;
38
- export const LEAVE_SEC = 0.5;
39
- // ── Phase 3: virtual-camera stability (requiredRegion + cameraPath) ──────────
40
- /**
41
- * Headroom pad added around a RequiredRegion's subject/group extent (a fraction
42
- * of the SOURCE dimension), BEFORE the crop must contain it. This is the
43
- * "frame the head, not just the box" margin — the region the crop is obliged to
44
- * keep in view is the subject box grown by this much on every side. Source-
45
- * normalized (the region geometry is source-normalized; the deadzone below is
46
- * the output-crop-normalized hold band). ~6% of the source on each side gives a
47
- * comfortable margin around a face/group without over-zooming.
48
- */
49
- export const REGION_PAD = 0.06;
50
- /**
51
- * Deadzone inclusion gate (OUTPUT-CROP-NORMALIZED — a fraction of the crop
52
- * WINDOW, the same units as the render's old `DEADZONE`). The camera HOLDS while
53
- * the padded RequiredRegion stays inside the crop window shrunk by this margin
54
- * on each side (the "safe region"); it only moves when the region would exit
55
- * that safe region. Calibrated ≥ the render's old `DEADZONE=0.018` hold effect
56
- * on a static head, so the relocated deadzone is no looser than before. 0.10 of
57
- * the crop width is the spec's `REGION_PAD ≈ 0.10 of crop width` value — the
58
- * band inside which subject motion demands ZERO camera motion (the locked feel).
59
- */
60
- export const DEADZONE = 0.1;
61
- /**
62
- * Hamming smoothing window half-length, in SECONDS (converted to frames with the
63
- * source fps; `round(0.5·fps)` ≈ 15 frames at 30fps). The single acausal
64
- * Hamming-weighted mean over each shot run's deadzone-gated centers — replaces
65
- * the old forward+backward EMA AND the render Gaussian. Clamped to the shot run
66
- * (forward-fill at the start, window auto-shrinks near a shot boundary) so it
67
- * never blends across a cut. A shorter window = snappier tracking; longer =
68
- * more damped glide. 0.5s matches the spec.
69
- */
70
- export const SMOOTH_WINDOW_SEC = 0.5;
71
- /**
72
- * Outlier-reject median-filter radius on the RequiredRegion series, in SECONDS
73
- * (converted to frames with the source fps). A single-frame detector/ReID spike
74
- * — the box teleporting across the frame for one frame, then snapping back —
75
- * would yank the deadzone gate (the chase, re-skinned). A short median rejects
76
- * it while tracking a clean multi-frame walk exactly (an order statistic, not a
77
- * low-pass — lag-free on a monotone pan). ~0.12s (≈4 frames at 30fps, ≈7 at
78
- * 60fps) outvotes a short multi-frame ReID swap without smearing a real walk.
79
- */
80
- export const REGION_MEDIAN_SEC = 0.12;
81
- /**
82
- * Maximum per-frame camera-center move during the inclusion clamp, as a fraction
83
- * of the CROP WINDOW per second (converted to source-units-per-frame with the
84
- * crop size + fps). Sized GENEROUSLY: a real walking subject (≈3–5 crop-widths/s)
85
- * tracks unthrottled, so the inclusion invariant holds frame-to-frame and the
86
- * subject stays in frame (inclusionRate ≈ 1.0). The cap exists only to convert a
87
- * single-frame TELEPORT — a detector ReID swap or a far re-emergence after a gap
88
- * that would otherwise snap the camera a full frame-width in one frame — into a
89
- * bounded ≤`cap/fps` step (the spec's "re-emergence glides over the window"; the
90
- * parabolic re-acquire ease is the deferred L1 slot). At the cap the worst step
91
- * is bounded well below the raw-centroid chase, while continuous tracking is
92
- * unaffected. 8 crop-widths/sec ≈ 0.13 crop-widths/frame at 60fps.
93
- */
94
- export const REACQUIRE_GLIDE_PER_SEC = 8;
95
- // ── Phase 5: zoom-to-fit framing (framing.ts) ────────────────────────────────
96
- /**
97
- * The bake's crop-window zoom clamp, mirrored here so `fitZoom` produces a zoom
98
- * inside the SAME envelope the renderer (`computeCropTransform` `bakedZoom`) and
99
- * `cropWindowSizeFor` enforce. ZOOM_MAX = 1.0 is the widest honest frame (the
100
- * largest source-fitting output-aspect rect — can't widen past the source);
101
- * ZOOM_MIN = 0.4 is the tightest crop the bake allows. A fitting group is framed
102
- * at the contained zoom clamped into [ZOOM_MIN, ZOOM_MAX]; an over-wide group
103
- * pads-and-holds at ZOOM_MAX and is flagged `overframed`.
104
- */
105
- export const ZOOM_MIN = 0.4;
106
- export const ZOOM_MAX = 1.0;
107
- /**
108
- * Min-shot-length floor for ZOOM recalibration, in SECONDS (converted to frames
109
- * with the source fps; round(0.4*fps) is ~12 frames at 30fps). A shot SHORTER
110
- * than this inherits the PREVIOUS shot's zoom rather than recalibrating, so a
111
- * sub-second montage (3-15-frame sub-shots, each independently zoom-fit) can't
112
- * STROBE the zoom channel (the named regression risk this design introduces).
113
- * Pan still resets at every cut - only the zoom is held; the floor is zoom-only.
114
- */
115
- export const MIN_ZOOM_SHOT_FRAMES = 0.4;
@@ -1,39 +0,0 @@
1
- /**
2
- * vcam/errorCodes.ts — the virtual-camera error vocabulary, as VALUES.
3
- *
4
- * Every code the vcam layer can throw (now or in a later phase) is listed here
5
- * as a literal value, and the registry-membership CI test
6
- * (`__tests__/vcamErrorRegistry.test.ts`) imports THIS list and asserts each one
7
- * exists in `@cueframe/errors`'s `ERROR_CODES`. Importing the codes as values
8
- * (not grepping string literals) means a template/variable code can't slip past
9
- * the gate — an unregistered code would make the CFERR decode fall back to
10
- * `internal_error` and 500 (the `clip_source_track_mismatch` landmine).
11
- *
12
- * Phase 1 registers all of these in `packages/errors/src/registry.ts` even
13
- * though some are thrown only in later phases (`subject_uncontainable`,
14
- * `trajectory_internal_gap`, `clip_trajectory_merge_conflict`) — registered now
15
- * so they can never 500 when the phase that throws them lands.
16
- *
17
- * `subject_not_tracked` is the EXISTING segment-level resolution gate (kept,
18
- * registered already); it's listed so the membership test covers the full vcam
19
- * throw surface in one place.
20
- *
21
- * NOT codes (documented emitted outcomes / legal inputs, never throws):
22
- * - `overframed` — an emitted boolean OUTCOME on an `all-faces` segment whose
23
- * group is wider than the aspect (the widest honest frame), NOT an error.
24
- * - a legacy track with no `shotBoundaries` → ONE shot (no
25
- * `shot_boundaries_missing` throw); an empty locked set → center-hold.
26
- */
27
- /** The error codes the vcam (virtual-camera) layer is allowed to throw. Each
28
- * MUST be a row in `@cueframe/errors` `ERROR_CODES` (asserted in CI). */
29
- export const VCAM_ERROR_CODES = [
30
- 'subject_not_tracked',
31
- // D10 (P5 4b): generated-footage sibling of `subject_not_tracked`, thrown by the
32
- // same `deriveTrajectory` resolution gate when `isGenerated` is true. Listed so
33
- // the membership test covers it (it would otherwise CFERR-decode to
34
- // internal_error → 500, the clip_source_track_mismatch landmine).
35
- 'subject_detection_failed_on_generated',
36
- 'subject_uncontainable',
37
- 'trajectory_internal_gap',
38
- 'clip_trajectory_merge_conflict',
39
- ];
@@ -1,136 +0,0 @@
1
- /**
2
- * vcam/framing.ts — zoom-to-fit for ALL modes (Phase 5).
3
- *
4
- * Generalizes today's face-only `calibrateShotScale` (which sizes ZOOM to a
5
- * single detected face's median height for an editorial `shotScale`) into a
6
- * mode-agnostic `fitZoom` that sizes the crop to CONTAIN a locked subject set's
7
- * union extent — the automatic fit the spec mandates (brain spec
8
- * 2026-06-21-virtual-camera-reframe-design.md, "Framing & zoom-to-fit (ALL
9
- * modes)" + RESOLVED #2 "automatic-fit only for the ship").
10
- *
11
- * The headline consumer is `all-faces`: today every all-faces segment ships at
12
- * zoom 1.0 (the widest honest frame) because `calibrateShotScale` has no
13
- * `shotScale` target for it and returns the fallback. A group that FITS 9:16 is
14
- * then left with wasted margin instead of being framed tight. `fitZoom` sizes the
15
- * crop to the group's union so a fitting group is zoom-fit (zoom < 1.0), while an
16
- * over-wide group (members at opposite edges of a 16:9 frame) is the documented
17
- * `overframed` outcome: pad-and-hold at zoom 1.0, emit the flag, NEVER silently
18
- * clip a wing and NEVER auto-drop a member (the editorial drop/cut decision
19
- * belongs to the driving agent — RESOLVED #1).
20
- *
21
- * Hold scope is explicit (resolves the per-frame-vs-per-shot contradiction):
22
- * zoom re-fits PER SHOT (a punch-in at every detector cut), but WITHIN a shot the
23
- * fit is sized ONCE over the shot-wide union extent and HELD — no per-frame
24
- * breathing. A pre-pass here computes the shot-max union BEFORE the per-shot zoom
25
- * split slices the series.
26
- *
27
- * Sub-second montage cuts are a named zoom-strobe regression risk (3–15-frame
28
- * sub-shots each independently calibrated → strobe). Mitigation: a min-shot-
29
- * length floor — a shot shorter than `MIN_ZOOM_SHOT_FRAMES` INHERITS the previous
30
- * shot's zoom (pan still resets at the cut; pan is cameraPath's job). So a 1-frame
31
- * shot can't spike the zoom channel.
32
- *
33
- * Pure, deterministic, no I/O. Boxes are source-normalized `[x1,y1,x2,y2]` ∈
34
- * [0,1]; `pad` is a fraction of the source dimension (the SAME `REGION_PAD` the
35
- * RequiredRegion uses, so the crop is sized to contain exactly the padded region
36
- * the camera is obliged to keep in frame).
37
- */
38
- import { REGION_PAD, MIN_ZOOM_SHOT_FRAMES, ZOOM_MIN, ZOOM_MAX } from './constants';
39
- import { segmentShots } from './shots';
40
- import { cropWindowSizeFor } from '../deriveTrajectory';
41
- /**
42
- * The shot-wide padded union extent of a per-frame box series, over `[start,
43
- * end)`. The union of every present box in the shot, grown by `pad` on each side
44
- * (clamped to [0,1]). Returns null when no box is present anywhere in the shot
45
- * (the camera center-holds; there is nothing to fit). Source-normalized.
46
- */
47
- export function shotUnionExtent(boxes, start, end, pad = REGION_PAD) {
48
- let x1 = Infinity;
49
- let y1 = Infinity;
50
- let x2 = -Infinity;
51
- let y2 = -Infinity;
52
- let any = false;
53
- for (let i = start; i < end; i++) {
54
- const b = i >= 0 && i < boxes.length ? boxes[i] : null;
55
- if (!b)
56
- continue;
57
- any = true;
58
- if (b[0] < x1)
59
- x1 = b[0];
60
- if (b[1] < y1)
61
- y1 = b[1];
62
- if (b[2] > x2)
63
- x2 = b[2];
64
- if (b[3] > y2)
65
- y2 = b[3];
66
- }
67
- if (!any)
68
- return null;
69
- const px1 = Math.max(0, x1 - pad);
70
- const py1 = Math.max(0, y1 - pad);
71
- const px2 = Math.min(1, x2 + pad);
72
- const py2 = Math.min(1, y2 + pad);
73
- return { w: Math.max(0, px2 - px1), h: Math.max(0, py2 - py1) };
74
- }
75
- /**
76
- * The automatic zoom-to-fit for ONE shot's union extent. Sizes the crop so the
77
- * largest output-aspect rect that fits the source, scaled by this zoom, CONTAINS
78
- * the padded union on BOTH axes. The crop window scales linearly with zoom
79
- * (`cropWindowSizeFor`: `cropW *= zoom`), so the smallest zoom that contains a
80
- * source-normalized union of size `(uw, uh)` is `max(uw/Wb, uh/Hb)` where
81
- * `(Wb, Hb)` is the zoom-1.0 window.
82
- *
83
- * Fits (≤ 1.0) → that zoom, clamped to the floor. Doesn't fit (> 1.0 needed, the
84
- * group is wider than the output aspect) → `overframed`, pad-and-hold at 1.0. A
85
- * null/empty extent → no subject to fit → zoom 1.0, not overframed (center-hold).
86
- */
87
- export function fitZoom(extent, srcW, srcH, aspect) {
88
- if (extent === null || extent.w <= 0 || extent.h <= 0) {
89
- return { zoom: ZOOM_MAX, overframed: false };
90
- }
91
- const base = cropWindowSizeFor(srcW, srcH, ZOOM_MAX, aspect); // zoom-1.0 window
92
- if (base.w <= 0 || base.h <= 0)
93
- return { zoom: ZOOM_MAX, overframed: false };
94
- const needed = Math.max(extent.w / base.w, extent.h / base.h);
95
- if (needed > ZOOM_MAX) {
96
- // Wider than the output aspect at zoom 1.0 — the documented `overframed`
97
- // outcome. Pad-and-hold at the widest honest frame.
98
- return { zoom: ZOOM_MAX, overframed: true };
99
- }
100
- // Fits: take the contained zoom, clamped to the zoom floor (so an extremely
101
- // small group can't over-zoom past ZOOM_MIN — the bake's tightest crop).
102
- return { zoom: Math.max(ZOOM_MIN, needed), overframed: false };
103
- }
104
- /**
105
- * Per-shot automatic zoom-to-fit over a segment's per-frame union-box series.
106
- * Computes the shot-wide union extent per shot, then `fitZoom`, applying the
107
- * min-shot-length floor: a shot shorter than `MIN_ZOOM_SHOT_FRAMES` INHERITS the
108
- * previous shot's fit (zoom + overframed) so a sub-second montage cut can't strobe
109
- * the zoom channel; pan still resets at the cut (cameraPath owns pan). The first
110
- * shot has no predecessor → it fits on its own extent regardless of length (a
111
- * single short clip must still frame its subject).
112
- *
113
- * Returns one `ShotFit` per shot, in shot order — the same shot order
114
- * `segmentShots(interiorCuts, segLen)` produces. The caller (deriveTrajectory)
115
- * applies the per-shot zoom to the matching sub-shot it already slices.
116
- */
117
- export function fitZoomPerShot(boxes, interiorCuts, srcW, srcH, aspect, fps, segLen, pad = REGION_PAD) {
118
- const shots = segmentShots(interiorCuts, segLen);
119
- const minLen = Math.max(1, Math.round(MIN_ZOOM_SHOT_FRAMES * fps));
120
- const out = [];
121
- let prev = null;
122
- for (const shot of shots) {
123
- const shotLen = shot.end - shot.start;
124
- if (prev !== null && shotLen < minLen) {
125
- // Sub-MIN_ZOOM_SHOT_FRAMES → inherit the previous shot's zoom (no recalibrate
126
- // → no strobe). Pan still resets at the cut in cameraPath.
127
- out.push({ zoom: prev.zoom, overframed: prev.overframed });
128
- continue;
129
- }
130
- const extent = shotUnionExtent(boxes, shot.start, shot.end, pad);
131
- const fit = fitZoom(extent, srcW, srcH, aspect);
132
- out.push(fit);
133
- prev = fit;
134
- }
135
- return out;
136
- }
@@ -1,137 +0,0 @@
1
- /**
2
- * vcam/requiredRegion.ts — every focus mode's per-frame target, lowered into a
3
- * `RequiredRegion`: the SOURCE-NORMALIZED box the crop window MUST contain.
4
- *
5
- * Phase 3 of the virtual-camera redesign (brain spec
6
- * 2026-06-21-virtual-camera-reframe-design.md, "Every focus mode lowers to a
7
- * RequiredRegion" + "Virtual-camera stability"). A RequiredRegion is the padded
8
- * subject/group extent (subject box + headroom pad). `cameraPath` then enforces
9
- * the INCLUSION INVARIANT: the crop always contains the RequiredRegion ⇒ the
10
- * subject can never walk out of frame.
11
- *
12
- * This is a REPRESENTATION, not a behavior change for the modes that already
13
- * work: `face` lowers its single per-frame subject box; `point` a zero-size rect
14
- * at the authored location; `frame-center` the center rect. Only `all-faces`
15
- * gains tracking — its region is the per-frame framed-group union box from
16
- * `subjectSet` (hysteresis-filtered), so the camera follows a moving group.
17
- *
18
- * Pure, deterministic, no I/O. All boxes are source-normalized `[x1,y1,x2,y2]`
19
- * ∈ [0,1]; `REGION_PAD` is a fraction of the source dimension (the geometry is
20
- * source-normalized — the deadzone in `cameraPath` is the output-crop-normalized
21
- * hold band, a separate stage).
22
- */
23
- import { REGION_PAD, REGION_MEDIAN_SEC } from './constants';
24
- import { segmentShots } from './shots';
25
- /** Pad a source-normalized box on every side by `pad` (clamped to [0,1]) and
26
- * express it as a `RequiredRegion` (center + padded size). A zero-area box (a
27
- * `point` target) yields a zero-size padded region at the point. */
28
- export function padBox(box, pad = REGION_PAD) {
29
- const [x1, y1, x2, y2] = box;
30
- const px1 = Math.max(0, Math.min(1, x1 - pad));
31
- const py1 = Math.max(0, Math.min(1, y1 - pad));
32
- const px2 = Math.max(0, Math.min(1, x2 + pad));
33
- const py2 = Math.max(0, Math.min(1, y2 + pad));
34
- return {
35
- cx: (px1 + px2) / 2,
36
- cy: (py1 + py2) / 2,
37
- w: Math.max(0, px2 - px1),
38
- h: Math.max(0, py2 - py1),
39
- };
40
- }
41
- /** A zero-size RequiredRegion at a single point (the `point` mode target, or any
42
- * centroid-only target with no extent). Padded so a point still carries a small
43
- * margin the crop must contain. */
44
- export function pointRegion(c, pad = REGION_PAD) {
45
- return padBox([c[0], c[1], c[0], c[1]], pad);
46
- }
47
- /** The frame-center RequiredRegion — a zero-size region at source center. The
48
- * crop centering on it is a no-op (the window is already centerable), so this
49
- * preserves `frame-center`'s `[0.5,0.5]` behavior exactly. */
50
- export function centerRegion() {
51
- return { cx: 0.5, cy: 0.5, w: 0, h: 0 };
52
- }
53
- /**
54
- * Lower a per-frame series of source-normalized union boxes (e.g. `all-faces`'s
55
- * per-frame framed-group box from `subjectSet.perFrameBoxes`) into per-frame
56
- * RequiredRegions. A null box (no framed member present — occlusion / gap) →
57
- * null region; `cameraPath` forward-fills the last in-shot region across the
58
- * gap (so the camera holds, not snaps to center, during a brief occlusion).
59
- */
60
- export function regionsFromBoxes(boxes, pad = REGION_PAD) {
61
- return boxes.map((b) => (b === null ? null : padBox(b, pad)));
62
- }
63
- /**
64
- * Lower a per-frame series of centroid-only targets (the `face` /
65
- * `active-speaker` per-frame center) into zero-size RequiredRegions. These modes
66
- * carry a single subject point, not a measured box, in the crop-center path —
67
- * lowering to a padded point keeps their framing (the camera points at the
68
- * subject) while giving `cameraPath` a region to include. A null center (subject
69
- * absent this frame) → null region (forward-filled by `cameraPath`).
70
- */
71
- export function regionsFromCenters(centers, pad = REGION_PAD) {
72
- return centers.map((c) => (c === null ? null : pointRegion(c, pad)));
73
- }
74
- /** Median of a numeric window (lower-median for even length). */
75
- function med(xs) {
76
- const s = [...xs].sort((a, b) => a - b);
77
- return s[Math.floor(s.length / 2)];
78
- }
79
- /**
80
- * Outlier-robust median filter on a region series within a SHOT (radius `r`
81
- * frames). Single-frame detector/ReID spikes — a crowd ReID flicker that
82
- * teleports the box across the frame for one frame, then snaps back — would
83
- * otherwise yank the camera (the chase, re-skinned). A short median on
84
- * cx/cy/w/h rejects them while tracking a clean multi-frame walk (an order
85
- * statistic, lag-free on a monotone pan). The window is clamped to the shot
86
- * (never reads past a cut). Null entries are skipped in the window (a present
87
- * spike isn't outvoted by absent frames). Identity for r < 1.
88
- */
89
- export function medianFilterRegions(regions, r, shotStart, shotEnd) {
90
- const out = [...regions];
91
- if (r < 1)
92
- return out;
93
- for (let i = shotStart; i < shotEnd; i++) {
94
- if (regions[i] === null)
95
- continue;
96
- const lo = Math.max(shotStart, i - r);
97
- const hi = Math.min(shotEnd - 1, i + r);
98
- const cx = [];
99
- const cy = [];
100
- const w = [];
101
- const h = [];
102
- for (let j = lo; j <= hi; j++) {
103
- const rj = regions[j];
104
- if (rj === null)
105
- continue;
106
- cx.push(rj.cx);
107
- cy.push(rj.cy);
108
- w.push(rj.w);
109
- h.push(rj.h);
110
- }
111
- if (cx.length === 0)
112
- continue;
113
- out[i] = { cx: med(cx), cy: med(cy), w: med(w), h: med(h) };
114
- }
115
- return out;
116
- }
117
- /**
118
- * Build the canonical per-frame RequiredRegions for an `all-faces` segment from
119
- * the per-frame framed-group union boxes (`subjectSet.perFrameBoxes`): pad each
120
- * box, then outlier-reject single-frame spikes per shot. This is THE region the
121
- * camera is obliged to contain AND the region the eval measures inclusion
122
- * against — one source of truth, so the gate is honest (the camera is judged on
123
- * the same temporally-coherent region it frames). `interiorCuts` are the segment-
124
- * local shot boundaries (median windows never cross a cut). `fps` sizes the
125
- * median radius. Null boxes (occlusion) stay null (forward-filled by cameraPath).
126
- */
127
- export function buildRequiredRegions(boxes, fps, interiorCuts, pad = REGION_PAD) {
128
- const padded = regionsFromBoxes(boxes, pad);
129
- const r = Math.round(REGION_MEDIAN_SEC * fps);
130
- if (r < 1)
131
- return padded;
132
- let out = padded;
133
- for (const shot of segmentShots(interiorCuts, padded.length)) {
134
- out = medianFilterRegions(out, r, shot.start, shot.end);
135
- }
136
- return out;
137
- }