@hraness/slopcamera 3.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 (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,375 @@
1
+ import {
2
+ ProjectEditPlanV1Schema,
3
+ SourceIntervalSchema,
4
+ SpeedRangeSchema,
5
+ type ProjectEditPlanV1,
6
+ type ProjectPlacementV1,
7
+ type SourceInterval,
8
+ type VideoProjectV1,
9
+ } from "../contracts";
10
+ import { canonicalJsonSha256 } from "./canonical-json";
11
+ import { intersectIntervals, subtractIntervals, unionIntervals } from "./intervals";
12
+ import {
13
+ assertProjectCameraMoveBindings,
14
+ hashProjectCameraSync,
15
+ } from "./project-camera";
16
+
17
+ export function hashProjectStructure(project: VideoProjectV1): string {
18
+ return canonicalJsonSha256({
19
+ assets: project.assets,
20
+ placements: project.placements,
21
+ projectId: project.projectId,
22
+ referencePlacementId: project.referencePlacementId,
23
+ timeline: project.timeline,
24
+ });
25
+ }
26
+
27
+ export function hashPlacementSync(placement: ProjectPlacementV1): string {
28
+ return hashProjectCameraSync(placement);
29
+ }
30
+
31
+ /** Enabled placements whose project-clock mapping is not trusted for structural automation. */
32
+ export function unverifiedEnabledPlacementIds(
33
+ project: VideoProjectV1,
34
+ ): readonly ProjectPlacementV1["placementId"][] {
35
+ return project.placements
36
+ .filter(placement => placement.enabled && placement.sync.provenance.kind === "unverified")
37
+ .map(placement => placement.placementId)
38
+ .sort((left, right) => left.localeCompare(right));
39
+ }
40
+
41
+ /**
42
+ * Bind automated global speech edits to every audible placement that was
43
+ * considered by music and speech protection. Presentation gain does not
44
+ * affect protection, while clock maps, enabled stream identities, and their
45
+ * immutable media descriptions do.
46
+ */
47
+ export function hashEnabledAudioProtectionStructure(project: VideoProjectV1): string {
48
+ const assets = new Map(project.assets.map(asset => [asset.assetId, asset]));
49
+ const streams = project.placements.flatMap(placement => {
50
+ if (!placement.enabled) return [];
51
+ const asset = assets.get(placement.assetId);
52
+ if (asset === undefined) return [];
53
+ return placement.audio.flatMap(configured => {
54
+ if (!configured.presentation.enabled) return [];
55
+ const stream = asset.streams.find(candidate => candidate.streamId === configured.streamId);
56
+ if (stream?.kind !== "audio") return [];
57
+ return [{
58
+ assetId: asset.assetId,
59
+ assetRange: placement.assetRange,
60
+ placementId: placement.placementId,
61
+ streamId: stream.streamId,
62
+ streamSha256: canonicalJsonSha256({ assetDurationUs: asset.durationUs, stream }),
63
+ sync: placement.sync,
64
+ }];
65
+ });
66
+ }).sort((left, right) => (
67
+ left.placementId.localeCompare(right.placementId)
68
+ || left.streamId.localeCompare(right.streamId)
69
+ ));
70
+ return canonicalJsonSha256(streams);
71
+ }
72
+
73
+ function normalizeSpeed(
74
+ ranges: ProjectEditPlanV1["speed"],
75
+ keep: readonly SourceInterval[],
76
+ baseSpeed: number,
77
+ ): ProjectEditPlanV1["speed"] {
78
+ const parsed = ranges.map(candidate => SpeedRangeSchema.parse(candidate));
79
+ if (parsed.length === 0 || keep.length === 0) return [];
80
+
81
+ // Later authored ranges win. Coordinate compression plus a disjoint-set
82
+ // "next unassigned segment" index applies that precedence in reverse while
83
+ // visiting every atomic segment at most once. The previous repeated
84
+ // subtract/union implementation was quadratic on already-normalized plans.
85
+ const coordinates = [...new Set([
86
+ ...parsed.flatMap(speed => [
87
+ speed.range.startUs,
88
+ speed.range.endUs,
89
+ ]),
90
+ ...keep.flatMap(interval => [interval.startUs, interval.endUs]),
91
+ ])].sort((left, right) => left - right);
92
+ const coordinateIndexes = new Map(
93
+ coordinates.map((coordinate, index) => [coordinate, index]),
94
+ );
95
+ const segmentRates: (number | undefined)[] = Array.from({
96
+ length: Math.max(0, coordinates.length - 1),
97
+ });
98
+ const nextUnassigned = new Uint32Array(segmentRates.length + 1);
99
+ for (let index = 0; index < nextUnassigned.length; index += 1) {
100
+ nextUnassigned[index] = index;
101
+ }
102
+ const findNextUnassigned = (input: number): number => {
103
+ let root = input;
104
+ while (nextUnassigned[root] !== root) {
105
+ root = nextUnassigned[root]!;
106
+ }
107
+ let index = input;
108
+ while (nextUnassigned[index] !== index) {
109
+ const next = nextUnassigned[index]!;
110
+ nextUnassigned[index] = root;
111
+ index = next;
112
+ }
113
+ return root;
114
+ };
115
+ const coordinateIndex = (coordinate: number): number => {
116
+ const index = coordinateIndexes.get(coordinate);
117
+ if (index === undefined) {
118
+ throw new TypeError("Speed normalization lost a range boundary.");
119
+ }
120
+ return index;
121
+ };
122
+ for (let rangeIndex = parsed.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
123
+ const speed = parsed[rangeIndex]!;
124
+ const endIndex = coordinateIndex(speed.range.endUs);
125
+ let segmentIndex = findNextUnassigned(
126
+ coordinateIndex(speed.range.startUs),
127
+ );
128
+ while (segmentIndex < endIndex) {
129
+ segmentRates[segmentIndex] = speed.rate;
130
+ nextUnassigned[segmentIndex] = findNextUnassigned(segmentIndex + 1);
131
+ segmentIndex = findNextUnassigned(segmentIndex);
132
+ }
133
+ }
134
+
135
+ const merged: { range: SourceInterval; rate: number }[] = [];
136
+ let keepIndex = 0;
137
+ for (
138
+ let segmentIndex = 0;
139
+ segmentIndex < segmentRates.length;
140
+ segmentIndex += 1
141
+ ) {
142
+ const startUs = coordinates[segmentIndex]!;
143
+ const endUs = coordinates[segmentIndex + 1]!;
144
+ while (
145
+ keepIndex < keep.length
146
+ && keep[keepIndex]!.endUs <= startUs
147
+ ) {
148
+ keepIndex += 1;
149
+ }
150
+ const kept = keep[keepIndex];
151
+ const rate = segmentRates[segmentIndex];
152
+ if (
153
+ rate === undefined
154
+ || rate === baseSpeed
155
+ || kept === undefined
156
+ || kept.startUs > startUs
157
+ || kept.endUs < endUs
158
+ ) {
159
+ continue;
160
+ }
161
+ const prior = merged.at(-1);
162
+ if (
163
+ prior !== undefined
164
+ && prior.rate === rate
165
+ && prior.range.endUs === startUs
166
+ ) {
167
+ merged[merged.length - 1] = {
168
+ range: { endUs, startUs: prior.range.startUs },
169
+ rate,
170
+ };
171
+ } else {
172
+ merged.push({ range: { endUs, startUs }, rate });
173
+ }
174
+ }
175
+ return merged;
176
+ }
177
+
178
+ function overlapsKeep(range: SourceInterval, keep: readonly SourceInterval[]): boolean {
179
+ return keep.some(interval => interval.startUs < range.endUs && interval.endUs > range.startUs);
180
+ }
181
+
182
+ export function normalizeProjectEditPlan(plan: ProjectEditPlanV1): ProjectEditPlanV1 {
183
+ const bounds = { startUs: 0, endUs: plan.timelineDurationUs };
184
+ const keep = unionIntervals(plan.keep.map(interval => SourceIntervalSchema.parse(interval)))
185
+ .flatMap(interval => intersectIntervals([interval], [bounds]));
186
+ const speed = normalizeSpeed(plan.speed, keep, plan.baseSpeed);
187
+ const overlays = plan.overlays
188
+ .filter(overlay => overlapsKeep(overlay.range, keep))
189
+ .sort((left, right) => (
190
+ left.range.startUs - right.range.startUs
191
+ || left.zIndex - right.zIndex
192
+ || left.overlayId.localeCompare(right.overlayId)
193
+ ));
194
+ const zooms = plan.zooms
195
+ .filter(zoom => overlapsKeep(zoom.operation.range, keep))
196
+ .sort((left, right) => (
197
+ left.operation.range.startUs - right.operation.range.startUs
198
+ || left.placementId.localeCompare(right.placementId)
199
+ || left.operation.zoomId.localeCompare(right.operation.zoomId)
200
+ ));
201
+ const cameraMoves = plan.cameraMoves
202
+ .filter(move => overlapsKeep(move.projectRange, keep))
203
+ .sort((left, right) => (
204
+ left.projectRange.startUs - right.projectRange.startUs
205
+ || left.placementId.localeCompare(right.placementId)
206
+ || left.streamId.localeCompare(right.streamId)
207
+ || left.cameraMoveId.localeCompare(right.cameraMoveId)
208
+ ));
209
+ const derivations = [...plan.derivations].sort((left, right) => (
210
+ left.projectRange.startUs - right.projectRange.startUs
211
+ || left.projectRange.endUs - right.projectRange.endUs
212
+ || left.decisionId.localeCompare(right.decisionId)
213
+ ));
214
+ return ProjectEditPlanV1Schema.parse({
215
+ ...plan,
216
+ cameraMoves,
217
+ derivations,
218
+ keep,
219
+ overlays,
220
+ speed,
221
+ zooms,
222
+ });
223
+ }
224
+
225
+ export function createDefaultProjectEditPlan(
226
+ project: VideoProjectV1,
227
+ planId: ProjectEditPlanV1["planId"],
228
+ timestamp: string,
229
+ ): ProjectEditPlanV1 {
230
+ return ProjectEditPlanV1Schema.parse({
231
+ baseSpeed: 1,
232
+ cameraMoves: [],
233
+ createdAt: timestamp,
234
+ derivations: [],
235
+ effects: {
236
+ clicks: { enabled: false },
237
+ cursor: { enabled: false },
238
+ keystrokes: { enabled: false },
239
+ metadataPlacementId: project.referencePlacementId,
240
+ typedText: { enabled: false },
241
+ },
242
+ keep: [{ startUs: 0, endUs: project.timeline.durationUs }],
243
+ kind: "slopcamera.project-edit-plan",
244
+ overlays: [],
245
+ planId,
246
+ projectId: project.projectId,
247
+ projectStructureSha256: hashProjectStructure(project),
248
+ schemaVersion: 1,
249
+ speed: [],
250
+ timelineDurationUs: project.timeline.durationUs,
251
+ updatedAt: timestamp,
252
+ zooms: [],
253
+ });
254
+ }
255
+
256
+ function requireCurrentStructure(plan: ProjectEditPlanV1, project: VideoProjectV1): void {
257
+ if (plan.projectId !== project.projectId) throw new Error("Project edit plan belongs to another project.");
258
+ if (plan.projectStructureSha256 !== hashProjectStructure(project)) {
259
+ throw new Error("Project structure changed; rebase the edit plan before mutating it.");
260
+ }
261
+ }
262
+
263
+ export function cutProjectPlan(
264
+ project: VideoProjectV1,
265
+ plan: ProjectEditPlanV1,
266
+ range: SourceInterval,
267
+ updatedAt: string,
268
+ ): ProjectEditPlanV1 {
269
+ requireCurrentStructure(plan, project);
270
+ const cut = SourceIntervalSchema.parse(range);
271
+ return normalizeProjectEditPlan({
272
+ ...plan,
273
+ keep: subtractIntervals(plan.keep, [cut]),
274
+ updatedAt,
275
+ });
276
+ }
277
+
278
+ export function trimProjectPlan(
279
+ project: VideoProjectV1,
280
+ plan: ProjectEditPlanV1,
281
+ range: SourceInterval,
282
+ updatedAt: string,
283
+ ): ProjectEditPlanV1 {
284
+ requireCurrentStructure(plan, project);
285
+ const trim = SourceIntervalSchema.parse(range);
286
+ return normalizeProjectEditPlan({
287
+ ...plan,
288
+ keep: intersectIntervals(plan.keep, [trim]),
289
+ updatedAt,
290
+ });
291
+ }
292
+
293
+ export function setProjectSpeed(
294
+ project: VideoProjectV1,
295
+ plan: ProjectEditPlanV1,
296
+ range: SourceInterval,
297
+ rate: number,
298
+ updatedAt: string,
299
+ ): ProjectEditPlanV1 {
300
+ requireCurrentStructure(plan, project);
301
+ const speed = SpeedRangeSchema.parse({ range, rate });
302
+ return normalizeProjectEditPlan({ ...plan, speed: [...plan.speed, speed], updatedAt });
303
+ }
304
+
305
+ export function rebaseProjectEditPlan(
306
+ previousProject: VideoProjectV1,
307
+ nextProject: VideoProjectV1,
308
+ plan: ProjectEditPlanV1,
309
+ updatedAt: string,
310
+ ): ProjectEditPlanV1 {
311
+ requireCurrentStructure(plan, previousProject);
312
+ if (previousProject.projectId !== nextProject.projectId) {
313
+ throw new Error("Cannot rebase an edit plan across project identities.");
314
+ }
315
+ if (
316
+ plan.derivations.some(derivation => derivation.origin.kind === "asset-analysis")
317
+ && hashEnabledAudioProtectionStructure(previousProject)
318
+ !== hashEnabledAudioProtectionStructure(nextProject)
319
+ ) {
320
+ throw new Error(
321
+ "Analysis-derived global edits are stale after the enabled-audio protection structure changed.",
322
+ );
323
+ }
324
+ for (const derivation of plan.derivations) {
325
+ if (derivation.origin.kind === "asset-analysis") {
326
+ const origin = derivation.origin;
327
+ const placement = nextProject.placements.find(
328
+ candidate => candidate.placementId === origin.placementId,
329
+ );
330
+ if (
331
+ placement === undefined
332
+ || placement.assetId !== origin.assetId
333
+ || hashPlacementSync(placement) !== origin.syncMapSha256
334
+ ) {
335
+ throw new Error(
336
+ `Analysis-derived edit ${derivation.decisionId} is stale after placement synchronization changed.`,
337
+ );
338
+ }
339
+ } else if (
340
+ derivation.origin.kind === "project-analysis"
341
+ && derivation.origin.projectStructureSha256 !== hashProjectStructure(nextProject)
342
+ ) {
343
+ throw new Error(
344
+ `Analysis-derived edit ${derivation.decisionId} is stale after the project structure changed.`,
345
+ );
346
+ }
347
+ }
348
+ for (const move of plan.cameraMoves) {
349
+ assertProjectCameraMoveBindings(nextProject, move);
350
+ }
351
+ const extendsUncutTail = plan.keep.at(-1)?.endUs === previousProject.timeline.durationUs;
352
+ const keep = nextProject.timeline.durationUs > previousProject.timeline.durationUs && extendsUncutTail
353
+ ? [
354
+ ...plan.keep.slice(0, -1),
355
+ {
356
+ startUs: plan.keep.at(-1)!.startUs,
357
+ endUs: nextProject.timeline.durationUs,
358
+ },
359
+ ]
360
+ : plan.keep.flatMap(interval => intersectIntervals([interval], [{
361
+ startUs: 0,
362
+ endUs: nextProject.timeline.durationUs,
363
+ }]));
364
+ return normalizeProjectEditPlan(ProjectEditPlanV1Schema.parse({
365
+ ...plan,
366
+ keep,
367
+ projectStructureSha256: hashProjectStructure(nextProject),
368
+ timelineDurationUs: nextProject.timeline.durationUs,
369
+ updatedAt,
370
+ }));
371
+ }
372
+
373
+ export function hashProjectEditPlan(plan: ProjectEditPlanV1): string {
374
+ return canonicalJsonSha256(normalizeProjectEditPlan(plan));
375
+ }
@@ -0,0 +1,303 @@
1
+ import {
2
+ ProjectEditPlanV1Schema,
3
+ ProjectRenderPlanV1Schema,
4
+ VideoProjectV1Schema,
5
+ type ProjectEditPlanV1,
6
+ type ProjectRenderPlanV1,
7
+ type ProjectPlacementV1,
8
+ type SourceInterval,
9
+ type VideoProjectV1,
10
+ } from "../contracts";
11
+ import { canonicalJsonSha256 } from "./canonical-json";
12
+ import { compileProjectCameraSegments } from "./project-camera";
13
+ import { compileProjectMetadata, type ProjectMetadataContext } from "./project-metadata";
14
+ import { hashProjectEditPlan, hashProjectStructure } from "./project-plan";
15
+ import {
16
+ buildProjectOutputTimeMap,
17
+ interpolateMicroseconds,
18
+ mapPlacementToOutputSlices,
19
+ mapProjectIntervalToOutputSlices,
20
+ } from "./project-time";
21
+
22
+ export interface ProjectRenderSettings {
23
+ readonly background?: string;
24
+ readonly frameRate?: number;
25
+ readonly metadata?: readonly ProjectMetadataContext[];
26
+ readonly pixelHeight?: number;
27
+ readonly pixelWidth?: number;
28
+ }
29
+
30
+ export function hashProjectRenderPlanComposition(
31
+ planInput: ProjectRenderPlanV1,
32
+ ): string {
33
+ const plan = ProjectRenderPlanV1Schema.parse(planInput);
34
+ const { planSha256: _planSha256, ...body } = plan;
35
+ void _planSha256;
36
+ return canonicalJsonSha256(body);
37
+ }
38
+
39
+ export function assertProjectRenderPlanComposition(
40
+ planInput: ProjectRenderPlanV1,
41
+ ): ProjectRenderPlanV1 {
42
+ const plan = ProjectRenderPlanV1Schema.parse(planInput);
43
+ const actual = hashProjectRenderPlanComposition(plan);
44
+ if (actual !== plan.planSha256) {
45
+ throw new TypeError(
46
+ `Project render plan composition hash mismatch: expected ${plan.planSha256}, received ${actual}.`,
47
+ );
48
+ }
49
+ return plan;
50
+ }
51
+
52
+ function intersection(left: SourceInterval, right: SourceInterval): SourceInterval | null {
53
+ const startUs = Math.max(left.startUs, right.startUs);
54
+ const endUs = Math.min(left.endUs, right.endUs);
55
+ return endUs > startUs ? { endUs, startUs } : null;
56
+ }
57
+
58
+ function mapSubrange(
59
+ value: SourceInterval,
60
+ input: SourceInterval,
61
+ output: SourceInterval,
62
+ ): SourceInterval {
63
+ return {
64
+ endUs: interpolateMicroseconds(
65
+ value.endUs,
66
+ input.startUs,
67
+ input.endUs,
68
+ output.startUs,
69
+ output.endUs,
70
+ ),
71
+ startUs: interpolateMicroseconds(
72
+ value.startUs,
73
+ input.startUs,
74
+ input.endUs,
75
+ output.startUs,
76
+ output.endUs,
77
+ ),
78
+ };
79
+ }
80
+
81
+ function placementSlices(
82
+ project: VideoProjectV1,
83
+ plan: ProjectEditPlanV1,
84
+ placement: ProjectPlacementV1,
85
+ ): {
86
+ readonly audio: ProjectRenderPlanV1["audioSlices"];
87
+ readonly missingCoverage: boolean;
88
+ readonly video: ProjectRenderPlanV1["videoSlices"];
89
+ } {
90
+ const asset = project.assets.find(candidate => candidate.assetId === placement.assetId);
91
+ if (asset === undefined) throw new Error(`Placement ${placement.placementId} references an unknown asset.`);
92
+ const outputMap = buildProjectOutputTimeMap(plan);
93
+ const timelineSlices = mapPlacementToOutputSlices(placement, outputMap);
94
+ const video: ProjectRenderPlanV1["videoSlices"][number][] = [];
95
+ const audio: ProjectRenderPlanV1["audioSlices"][number][] = [];
96
+ let missingCoverage = false;
97
+
98
+ for (const timelineSlice of timelineSlices) {
99
+ for (const configured of placement.video) {
100
+ if (!configured.presentation.enabled) continue;
101
+ const stream = asset.streams.find(candidate => candidate.streamId === configured.streamId);
102
+ if (stream?.kind !== "video") throw new Error(`Placement ${placement.placementId} has an invalid video stream.`);
103
+ let coveredUs = 0;
104
+ for (const media of stream.segments) {
105
+ const assetRange = intersection(timelineSlice.asset, media.assetRange);
106
+ if (assetRange === null) continue;
107
+ coveredUs += assetRange.endUs - assetRange.startUs;
108
+ video.push({
109
+ assetId: asset.assetId,
110
+ assetRange,
111
+ bytes: media.bytes,
112
+ codec: media.codec,
113
+ container: media.container,
114
+ fileRange: mapSubrange(assetRange, media.assetRange, media.fileRange),
115
+ kind: "video",
116
+ outputRange: mapSubrange(assetRange, timelineSlice.asset, timelineSlice.output),
117
+ path: media.path,
118
+ placementId: placement.placementId,
119
+ presentation: configured.presentation,
120
+ projectRange: mapSubrange(assetRange, timelineSlice.asset, timelineSlice.project),
121
+ projectSpeed: timelineSlice.speed,
122
+ role: stream.role,
123
+ sha256: media.sha256,
124
+ streamId: stream.streamId,
125
+ streamIndex: media.streamIndex,
126
+ });
127
+ }
128
+ missingCoverage ||= coveredUs < timelineSlice.asset.endUs - timelineSlice.asset.startUs;
129
+ }
130
+ for (const configured of placement.audio) {
131
+ if (!configured.presentation.enabled) continue;
132
+ const stream = asset.streams.find(candidate => candidate.streamId === configured.streamId);
133
+ if (stream?.kind !== "audio") throw new Error(`Placement ${placement.placementId} has an invalid audio stream.`);
134
+ let coveredUs = 0;
135
+ for (const media of stream.segments) {
136
+ const assetRange = intersection(timelineSlice.asset, media.assetRange);
137
+ if (assetRange === null) continue;
138
+ coveredUs += assetRange.endUs - assetRange.startUs;
139
+ audio.push({
140
+ assetId: asset.assetId,
141
+ assetRange,
142
+ bytes: media.bytes,
143
+ codec: media.codec,
144
+ container: media.container,
145
+ fileRange: mapSubrange(assetRange, media.assetRange, media.fileRange),
146
+ kind: "audio",
147
+ outputRange: mapSubrange(assetRange, timelineSlice.asset, timelineSlice.output),
148
+ path: media.path,
149
+ placementId: placement.placementId,
150
+ presentation: configured.presentation,
151
+ projectRange: mapSubrange(assetRange, timelineSlice.asset, timelineSlice.project),
152
+ projectSpeed: timelineSlice.speed,
153
+ role: stream.role,
154
+ sha256: media.sha256,
155
+ streamId: stream.streamId,
156
+ streamIndex: media.streamIndex,
157
+ });
158
+ }
159
+ missingCoverage ||= coveredUs < timelineSlice.asset.endUs - timelineSlice.asset.startUs;
160
+ }
161
+ }
162
+ return { audio, missingCoverage, video };
163
+ }
164
+
165
+ export function compileProjectRenderPlan(
166
+ projectInput: VideoProjectV1,
167
+ planInput: ProjectEditPlanV1,
168
+ settings: ProjectRenderSettings = {},
169
+ ): ProjectRenderPlanV1 {
170
+ const project = VideoProjectV1Schema.parse(projectInput);
171
+ const plan = ProjectEditPlanV1Schema.parse(planInput);
172
+ if (plan.projectId !== project.projectId) throw new Error("Project render plan belongs to another project.");
173
+ const projectStructureSha256 = hashProjectStructure(project);
174
+ if (plan.projectStructureSha256 !== projectStructureSha256) {
175
+ throw new Error("Project structure and edit plan are out of sync.");
176
+ }
177
+ const outputMap = buildProjectOutputTimeMap(plan);
178
+ const outputSettings = {
179
+ frameRate: settings.frameRate ?? 60,
180
+ pixelHeight: settings.pixelHeight ?? 1_080,
181
+ pixelWidth: settings.pixelWidth ?? 1_920,
182
+ };
183
+ if (
184
+ !Number.isFinite(outputSettings.frameRate)
185
+ || outputSettings.frameRate <= 0
186
+ || outputSettings.frameRate > 240
187
+ || !Number.isSafeInteger(outputSettings.pixelHeight)
188
+ || outputSettings.pixelHeight <= 0
189
+ || outputSettings.pixelHeight > 16_384
190
+ || !Number.isSafeInteger(outputSettings.pixelWidth)
191
+ || outputSettings.pixelWidth <= 0
192
+ || outputSettings.pixelWidth > 16_384
193
+ || outputSettings.pixelWidth * outputSettings.pixelHeight > 134_217_728
194
+ ) {
195
+ throw new RangeError("Invalid project render output dimensions or frame rate.");
196
+ }
197
+ for (const placement of project.placements) {
198
+ if (!placement.enabled) continue;
199
+ for (const configured of placement.video) {
200
+ const presentation = configured.presentation;
201
+ if (!presentation.enabled || presentation.layout.kind !== "output-pixels") continue;
202
+ if (
203
+ presentation.layout.x + presentation.layout.width > outputSettings.pixelWidth
204
+ || presentation.layout.y + presentation.layout.height > outputSettings.pixelHeight
205
+ ) {
206
+ throw new RangeError(
207
+ `Video layout for ${placement.placementId}:${configured.streamId} exceeds the project render output.`,
208
+ );
209
+ }
210
+ }
211
+ }
212
+ const videoSlices: ProjectRenderPlanV1["videoSlices"][number][] = [];
213
+ const audioSlices: ProjectRenderPlanV1["audioSlices"][number][] = [];
214
+ const warnings: ProjectRenderPlanV1["warnings"][number][] = [];
215
+ for (const placement of project.placements) {
216
+ if (!placement.enabled) {
217
+ warnings.push({
218
+ code: "disabled-placement",
219
+ message: `Placement ${placement.placementId} is disabled and will not render.`,
220
+ placementId: placement.placementId,
221
+ });
222
+ continue;
223
+ }
224
+ if (placement.sync.provenance.kind === "unverified") {
225
+ warnings.push({
226
+ code: "unverified-sync",
227
+ message: `Placement ${placement.placementId} has not been aligned; its current timing is provisional.`,
228
+ placementId: placement.placementId,
229
+ });
230
+ }
231
+ const resolved = placementSlices(project, plan, placement);
232
+ videoSlices.push(...resolved.video);
233
+ audioSlices.push(...resolved.audio);
234
+ if (resolved.missingCoverage) {
235
+ warnings.push({
236
+ code: "missing-media-coverage",
237
+ message: `Placement ${placement.placementId} has a kept interval without source media.`,
238
+ placementId: placement.placementId,
239
+ });
240
+ }
241
+ }
242
+ const overlays = plan.overlays.flatMap(operation => {
243
+ const slices = mapProjectIntervalToOutputSlices(outputMap, operation.range);
244
+ const visibleDurationUs = slices.reduce((sum, slice) => sum + slice.output.endUs - slice.output.startUs, 0);
245
+ let playbackOffsetUs = 0;
246
+ return slices.map(slice => {
247
+ const resolved = {
248
+ operation,
249
+ outputRange: slice.output,
250
+ playbackOffsetUs,
251
+ projectRange: slice.project,
252
+ visibleDurationUs,
253
+ };
254
+ playbackOffsetUs += slice.output.endUs - slice.output.startUs;
255
+ return resolved;
256
+ });
257
+ });
258
+ videoSlices.sort((left, right) => (
259
+ left.presentation.enabled && right.presentation.enabled
260
+ ? left.presentation.layer - right.presentation.layer
261
+ : 0
262
+ ) || left.outputRange.startUs - right.outputRange.startUs || left.streamId.localeCompare(right.streamId));
263
+ audioSlices.sort((left, right) => (
264
+ left.outputRange.startUs - right.outputRange.startUs || left.streamId.localeCompare(right.streamId)
265
+ ));
266
+ const cameraSegments = compileProjectCameraSegments(
267
+ project,
268
+ plan,
269
+ videoSlices,
270
+ outputSettings,
271
+ );
272
+ const metadata = compileProjectMetadata(
273
+ project,
274
+ plan,
275
+ outputMap,
276
+ settings.metadata ?? [],
277
+ outputSettings,
278
+ cameraSegments,
279
+ );
280
+ const body = {
281
+ audioSlices,
282
+ cameraKeyframes: metadata.cameraKeyframes,
283
+ cameraSegments,
284
+ effects: metadata.effects,
285
+ kind: "slopcamera.project-render-plan" as const,
286
+ output: {
287
+ background: settings.background ?? "#000000ff",
288
+ durationUs: outputMap.durationUs,
289
+ ...outputSettings,
290
+ },
291
+ overlays,
292
+ projectEditPlanSha256: hashProjectEditPlan(plan),
293
+ projectId: project.projectId,
294
+ projectStructureSha256,
295
+ schemaVersion: 1 as const,
296
+ videoSlices,
297
+ warnings,
298
+ };
299
+ return ProjectRenderPlanV1Schema.parse({
300
+ ...body,
301
+ planSha256: canonicalJsonSha256(body),
302
+ });
303
+ }