@hypit/hypit 0.1.3 → 0.1.5

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 (486) hide show
  1. package/README.md +5 -3
  2. package/dist/public/caption.d.ts +206 -183
  3. package/dist/public/composition.d.ts +25 -3
  4. package/dist/public/endpoint-kit.d.ts +24 -2
  5. package/dist/public/generation.d.ts +5 -1
  6. package/dist/public/hyperframes.d.ts +19 -1
  7. package/dist/public/narrative.d.ts +17 -3
  8. package/dist/public/performance.d.ts +1590 -0
  9. package/dist/public/program-space.d.ts +179 -32
  10. package/dist/public/runtime-kit.d.ts +21 -8
  11. package/dist/public/sound.d.ts +822 -0
  12. package/dist/public/speech.d.ts +41 -0
  13. package/dist/public/studio-adapter.d.ts +107 -16
  14. package/dist/public/temporal-markup.d.ts +9 -44
  15. package/dist/public/temporal.d.ts +37 -28
  16. package/dist/public/{semantic-track.d.ts → timeline.d.ts} +62 -49
  17. package/examples/README.md +18 -10
  18. package/examples/minimal-author-package/packages/example-component/preview/preview.svml +19 -12
  19. package/examples/minimal-author-package/packages/example-component/src/component.ts +5 -5
  20. package/examples/minimal-author-package/packages/example-component/src/fragment.ts +3 -5
  21. package/examples/minimal-author-package/packages/example-component/src/manifest.ts +8 -10
  22. package/examples/minimal-author-package/packages/example-component/src/surface.ts +2 -2
  23. package/examples/provider-package/README.md +78 -0
  24. package/examples/provider-package/hypit.runtime.json +12 -0
  25. package/examples/provider-package/packages/provider-images/package.json +11 -0
  26. package/examples/provider-package/packages/provider-images/src/activation.ts +24 -0
  27. package/examples/provider-package/packages/provider-images/src/provider.ts +122 -0
  28. package/examples/provider-package/packages/provider-images/tsconfig.json +9 -0
  29. package/examples/semantic-composition/README.md +6 -0
  30. package/examples/semantic-composition/chat.svml +6 -5
  31. package/examples/semantic-composition/packages/chat-scene/README.md +3 -3
  32. package/examples/semantic-composition/packages/chat-scene/src/activation.ts +18 -21
  33. package/examples/semantic-composition/packages/chat-scene/src/render.ts +4 -4
  34. package/examples/semantic-composition/packages/performance-styles/README.md +42 -0
  35. package/examples/semantic-composition/packages/performance-styles/package.json +25 -0
  36. package/examples/semantic-composition/packages/performance-styles/src/activation.ts +57 -0
  37. package/examples/semantic-composition/packages/performance-styles/src/render.ts +54 -0
  38. package/examples/semantic-composition/packages/performance-styles/src/studio.ts +28 -0
  39. package/examples/semantic-composition/packages/performance-styles/tsconfig.json +18 -0
  40. package/examples/semantic-composition/packages/responsive-explainer/README.md +16 -9
  41. package/examples/semantic-composition/packages/responsive-explainer/src/activation.ts +12 -12
  42. package/examples/semantic-composition/packages/responsive-explainer/src/render.ts +5 -5
  43. package/examples/semantic-composition/packages/sound-styles/README.md +36 -0
  44. package/examples/semantic-composition/packages/sound-styles/package.json +25 -0
  45. package/examples/semantic-composition/packages/sound-styles/src/activation.ts +53 -0
  46. package/examples/semantic-composition/packages/sound-styles/src/render.ts +14 -0
  47. package/examples/semantic-composition/packages/sound-styles/tsconfig.json +18 -0
  48. package/package.json +35 -15
  49. package/packages/audio-track/README.md +20 -13
  50. package/packages/audio-track/package.json +1 -1
  51. package/packages/audio-track/src/component.ts +4 -4
  52. package/packages/audio-track/src/fragment.ts +6 -5
  53. package/packages/audio-track/src/index.ts +1 -1
  54. package/packages/audio-track/src/manifest.ts +17 -16
  55. package/packages/audio-track/src/program.ts +21 -25
  56. package/packages/audio-track/src/surface.ts +19 -18
  57. package/packages/audio-track-studio/README.md +23 -0
  58. package/packages/audio-track-studio/src/index.ts +33 -19
  59. package/packages/author-kit/README.md +1 -1
  60. package/packages/background-removal/README.md +4 -7
  61. package/packages/browser-capture/package.json +1 -1
  62. package/packages/build-result/README.md +69 -0
  63. package/packages/build-result/package.json +2 -1
  64. package/packages/build-result/src/decode.ts +12 -8
  65. package/packages/build-result/src/execution-log.ts +17 -0
  66. package/packages/build-result/src/file-reference.ts +42 -0
  67. package/packages/build-result/src/index.ts +4 -0
  68. package/packages/build-result/src/replace-file-windows.ts +58 -0
  69. package/packages/build-result/src/replace-file.ts +11 -0
  70. package/packages/build-result/src/store.ts +58 -14
  71. package/packages/build-result/src/types.ts +41 -8
  72. package/packages/build-result/src/writer.ts +12 -1
  73. package/packages/build-result-fs/README.md +23 -1
  74. package/packages/build-result-s3/README.md +5 -0
  75. package/packages/build-result-s3/src/repository.ts +23 -5
  76. package/packages/caption/README.md +61 -73
  77. package/packages/caption/package.json +3 -2
  78. package/packages/caption/src/activation.ts +2 -2
  79. package/packages/caption/src/component.ts +20 -5
  80. package/packages/caption/src/display.ts +3 -18
  81. package/packages/caption/src/fragment.ts +3 -7
  82. package/packages/caption/src/index.ts +6 -5
  83. package/packages/caption/src/manifest.ts +29 -46
  84. package/packages/caption/src/style.ts +24 -135
  85. package/packages/caption/src/surface.ts +12 -92
  86. package/packages/caption/src/temporalize.ts +18 -53
  87. package/packages/caption/src/types.ts +8 -35
  88. package/packages/caption/src/visibility.ts +23 -0
  89. package/packages/caption-fine/README.md +31 -11
  90. package/packages/caption-fine/package.json +4 -2
  91. package/packages/caption-fine/src/component.ts +3 -3
  92. package/packages/caption-fine/src/fragment.ts +7 -19
  93. package/packages/caption-fine/src/index.ts +2 -0
  94. package/packages/caption-fine/src/manifest.ts +27 -18
  95. package/packages/caption-fine/src/recipe.ts +7 -0
  96. package/packages/caption-fine/src/render.ts +62 -49
  97. package/packages/caption-fine/src/schedule.ts +39 -35
  98. package/packages/caption-fine/src/style.ts +10 -9
  99. package/packages/caption-fine/src/surface.ts +62 -35
  100. package/packages/caption-fine/src/types.ts +2 -0
  101. package/packages/caption-fine-studio/README.md +24 -2
  102. package/packages/caption-fine-studio/package.json +10 -3
  103. package/packages/caption-fine-studio/src/activation.ts +3 -3
  104. package/packages/caption-fine-studio/src/index.ts +61 -40
  105. package/packages/cli/README.md +81 -3
  106. package/packages/cli/package.json +1 -0
  107. package/packages/cli/src/arguments.ts +39 -15
  108. package/packages/cli/src/build-planning.ts +9 -3
  109. package/packages/cli/src/command.ts +7 -1
  110. package/packages/cli/src/commands/environment.ts +66 -27
  111. package/packages/cli/src/commands/execution.ts +81 -26
  112. package/packages/cli/src/commands/results.ts +7 -11
  113. package/packages/cli/src/index.ts +2 -2
  114. package/packages/cli/src/machine-view.ts +15 -5
  115. package/packages/cli/src/main.ts +38 -42
  116. package/packages/cli/src/observation.ts +7 -1
  117. package/packages/cli/src/output.ts +92 -34
  118. package/packages/cli/src/result-export.ts +22 -13
  119. package/packages/cli/src/run-file.ts +19 -8
  120. package/packages/cli/src/view.ts +64 -18
  121. package/packages/comment-sticker/README.md +3 -3
  122. package/packages/comment-sticker/package.json +1 -1
  123. package/packages/comment-sticker/src/component.ts +4 -4
  124. package/packages/comment-sticker/src/fragment.ts +5 -4
  125. package/packages/comment-sticker/src/manifest.ts +7 -6
  126. package/packages/comment-sticker/src/program.ts +9 -9
  127. package/packages/comment-sticker/src/surface.ts +6 -7
  128. package/packages/comment-sticker-studio/src/index.ts +2 -1
  129. package/packages/compiler-node/src/compiler.ts +1 -0
  130. package/packages/composition/README.md +21 -0
  131. package/packages/composition/src/audio-presentation.ts +44 -0
  132. package/packages/composition/src/index.ts +3 -0
  133. package/packages/composition/src/schema.ts +8 -3
  134. package/packages/composition/src/track.ts +15 -1
  135. package/packages/credential-store-env/README.md +1 -1
  136. package/packages/deck-track/README.md +3 -3
  137. package/packages/deck-track/package.json +1 -1
  138. package/packages/deck-track/src/component.ts +5 -5
  139. package/packages/deck-track/src/fragment.ts +6 -5
  140. package/packages/deck-track/src/lower.ts +12 -12
  141. package/packages/deck-track/src/manifest.ts +8 -7
  142. package/packages/deck-track/src/program.ts +13 -16
  143. package/packages/deck-track/src/surface.ts +9 -10
  144. package/packages/driver-node/src/driver.ts +41 -3
  145. package/packages/elevenlabs-speech/README.md +27 -0
  146. package/packages/{provider-xiaomi-mimo → elevenlabs-speech}/package.json +5 -8
  147. package/packages/elevenlabs-speech/src/activation.ts +26 -0
  148. package/packages/elevenlabs-speech/src/fragment.ts +11 -0
  149. package/packages/elevenlabs-speech/src/index.ts +111 -0
  150. package/packages/elevenlabs-speech/src/surface.ts +167 -0
  151. package/packages/endpoint-kit/README.md +24 -0
  152. package/packages/endpoint-kit/src/index.ts +7 -1
  153. package/packages/estimate/README.md +56 -3
  154. package/packages/estimate/src/program.ts +7 -8
  155. package/packages/film/README.md +12 -7
  156. package/packages/film/package.json +1 -1
  157. package/packages/film/src/component.ts +4 -4
  158. package/packages/film/src/fragment.ts +6 -5
  159. package/packages/film/src/manifest.ts +11 -10
  160. package/packages/film/src/program.ts +5 -5
  161. package/packages/film/src/recipe.ts +2 -1
  162. package/packages/film/src/surface.ts +6 -6
  163. package/packages/film-studio/package.json +1 -1
  164. package/packages/film-studio/src/index.ts +2 -2
  165. package/packages/fishaudio-speech/README.md +33 -0
  166. package/packages/fishaudio-speech/package.json +21 -0
  167. package/packages/fishaudio-speech/src/activation.ts +32 -0
  168. package/packages/fishaudio-speech/src/fragment.ts +11 -0
  169. package/packages/fishaudio-speech/src/index.ts +156 -0
  170. package/packages/fishaudio-speech/src/surface.ts +192 -0
  171. package/packages/fonts-open/README.md +5 -0
  172. package/packages/fonts-open/package.json +5 -1
  173. package/packages/fonts-open/src/studio.ts +21 -0
  174. package/packages/generation/README.md +11 -1
  175. package/packages/generation/src/mapping.ts +28 -30
  176. package/packages/gpt-image/README.md +1 -2
  177. package/packages/grok-imagine/README.md +1 -2
  178. package/packages/hyperframes/README.md +7 -3
  179. package/packages/hyperframes/src/browser-program.ts +11 -2
  180. package/packages/hyperframes/src/document.ts +15 -2
  181. package/packages/interview-emoji-reveal/README.md +4 -4
  182. package/packages/interview-emoji-reveal/package.json +1 -1
  183. package/packages/interview-emoji-reveal/src/component.ts +4 -4
  184. package/packages/interview-emoji-reveal/src/fragment.ts +6 -5
  185. package/packages/interview-emoji-reveal/src/manifest.ts +9 -8
  186. package/packages/interview-emoji-reveal/src/program.ts +14 -14
  187. package/packages/interview-emoji-reveal/src/surface.ts +8 -9
  188. package/packages/media-execution/README.md +7 -3
  189. package/packages/media-execution/src/audio-presentation.ts +23 -0
  190. package/packages/media-execution/src/execute.ts +2 -0
  191. package/packages/media-pipeline/README.md +7 -3
  192. package/packages/media-pipeline/src/audio-plan.ts +4 -1
  193. package/packages/media-pipeline/src/component.ts +16 -2
  194. package/packages/media-pipeline/src/manifest.ts +3 -1
  195. package/packages/media-pipeline/src/types.ts +2 -1
  196. package/packages/media-track/README.md +14 -47
  197. package/packages/media-track/package.json +1 -1
  198. package/packages/media-track/preview/preview.svml +5 -5
  199. package/packages/media-track/src/author.ts +12 -6
  200. package/packages/media-track/src/component.ts +10 -16
  201. package/packages/media-track/src/fragment.ts +8 -7
  202. package/packages/media-track/src/index.ts +3 -1
  203. package/packages/media-track/src/lower.ts +7 -7
  204. package/packages/media-track/src/manifest.ts +16 -38
  205. package/packages/media-track/src/program.ts +62 -67
  206. package/packages/media-track/src/sampling.ts +6 -6
  207. package/packages/media-track/src/sequence.ts +8 -8
  208. package/packages/media-track/src/surface.ts +25 -43
  209. package/packages/media-track-studio/README.md +9 -0
  210. package/packages/media-track-studio/src/index.ts +23 -13
  211. package/packages/mimo-speech/README.md +1 -1
  212. package/packages/minimax-h3/README.md +1 -1
  213. package/packages/nano-banana/README.md +1 -1
  214. package/packages/narrative/README.md +23 -1
  215. package/packages/narrative/src/identity.ts +11 -0
  216. package/packages/narrative/src/index.ts +3 -0
  217. package/packages/narrative/src/schema.ts +28 -27
  218. package/packages/narrative/src/selection.ts +52 -0
  219. package/packages/narrative/src/types.ts +2 -0
  220. package/packages/package-loader-node/README.md +30 -0
  221. package/packages/package-loader-node/package.json +5 -2
  222. package/packages/package-loader-node/src/distribution-resolution.ts +10 -3
  223. package/packages/package-loader-node/src/index.ts +1 -0
  224. package/packages/package-loader-node/src/loader.ts +12 -15
  225. package/packages/package-loader-node/src/location.ts +27 -1
  226. package/packages/package-loader-node/src/module-scope.ts +183 -0
  227. package/packages/package-loader-node/src/types.ts +4 -0
  228. package/packages/performance/README.md +83 -0
  229. package/packages/performance/package.json +30 -0
  230. package/packages/performance/src/activation.ts +11 -0
  231. package/packages/performance/src/component.ts +46 -0
  232. package/packages/performance/src/index.ts +6 -0
  233. package/packages/performance/src/manifest.ts +53 -0
  234. package/packages/{media-track/src/performance.ts → performance/src/media.ts} +9 -10
  235. package/packages/performance/src/program.ts +51 -0
  236. package/packages/performance/src/style.ts +35 -0
  237. package/packages/performance/src/surface.ts +99 -0
  238. package/packages/performance-studio/README.md +25 -0
  239. package/packages/performance-studio/package.json +21 -0
  240. package/packages/performance-studio/src/activation.ts +6 -0
  241. package/packages/performance-studio/src/index.ts +84 -0
  242. package/packages/program-space/README.md +9 -18
  243. package/packages/program-space/src/activation.ts +1 -5
  244. package/packages/program-space/src/index.ts +2 -11
  245. package/packages/program-space/src/surface.ts +1 -20
  246. package/packages/project-context-node/README.md +20 -0
  247. package/packages/project-context-node/package.json +10 -0
  248. package/packages/project-context-node/src/index.ts +3 -0
  249. package/packages/{cli → project-context-node}/src/runtime-selection.ts +7 -2
  250. package/packages/provider-hyperframes-local/README.md +43 -15
  251. package/packages/provider-hyperframes-local/src/activation.ts +4 -3
  252. package/packages/provider-hyperframes-local/src/capture-process.ts +26 -4
  253. package/packages/provider-hyperframes-local/src/capture.ts +96 -26
  254. package/packages/provider-hyperframes-local/src/concurrency.ts +86 -0
  255. package/packages/provider-hyperframes-local/src/opaque-capture.ts +92 -0
  256. package/packages/provider-hyperframes-local/src/options.ts +2 -0
  257. package/packages/provider-hyperframes-local/src/program.ts +6 -3
  258. package/packages/provider-hyperframes-local/src/progress.ts +53 -0
  259. package/packages/provider-hyperframes-local/src/provider.ts +18 -5
  260. package/packages/provider-hyperframes-local/src/render.ts +48 -7
  261. package/packages/provider-hypihub/README.md +48 -17
  262. package/packages/provider-hypihub/package.json +2 -0
  263. package/packages/provider-hypihub/src/mapping.ts +31 -7
  264. package/packages/provider-hypihub/src/oauth.ts +6 -0
  265. package/packages/provider-hypihub/src/provider.ts +25 -13
  266. package/packages/provider-hypihub/src/routes.ts +1 -2
  267. package/packages/provider-hypihub/src/upload.ts +2 -0
  268. package/packages/provider-media-local/README.md +3 -4
  269. package/packages/provider-whisperx-local/README.md +57 -0
  270. package/packages/provider-whisperx-local/src/program.ts +1 -0
  271. package/packages/provider-whisperx-local/src/provider.ts +3 -0
  272. package/packages/ranking/README.md +8 -6
  273. package/packages/ranking/package.json +1 -1
  274. package/packages/ranking/src/component.ts +6 -6
  275. package/packages/ranking/src/fragment.ts +11 -7
  276. package/packages/ranking/src/manifest.ts +21 -14
  277. package/packages/ranking/src/render.ts +33 -35
  278. package/packages/ranking/src/schedule.ts +16 -19
  279. package/packages/ranking/src/surface.ts +12 -13
  280. package/packages/ranking-studio/README.md +29 -0
  281. package/packages/ranking-studio/package.json +8 -3
  282. package/packages/ranking-studio/src/index.ts +71 -12
  283. package/packages/render-hyperframes/README.md +7 -7
  284. package/packages/render-hyperframes/package.json +1 -1
  285. package/packages/render-hyperframes/src/component.ts +11 -1
  286. package/packages/render-hyperframes/src/fragment.ts +7 -4
  287. package/packages/render-hyperframes/src/manifest.ts +4 -4
  288. package/packages/render-hyperframes/src/surface.ts +6 -6
  289. package/packages/runtime/README.md +8 -1
  290. package/packages/runtime/src/catalog.ts +2 -0
  291. package/packages/runtime/src/execution.ts +15 -4
  292. package/packages/runtime/src/index.ts +2 -1
  293. package/packages/runtime/src/log.ts +51 -0
  294. package/packages/runtime/src/types.ts +5 -8
  295. package/packages/runtime-host-node/README.md +30 -0
  296. package/packages/runtime-host-node/package.json +1 -0
  297. package/packages/runtime-host-node/src/index.ts +25 -9
  298. package/packages/runtime-host-node/src/packages.ts +47 -50
  299. package/packages/runtime-kit/README.md +5 -0
  300. package/packages/runtime-kit/src/index.ts +3 -1
  301. package/packages/runtime-local/README.md +111 -4
  302. package/packages/runtime-local/src/config.ts +202 -56
  303. package/packages/runtime-local/src/control.ts +9 -1
  304. package/packages/runtime-local/src/executor.ts +66 -0
  305. package/packages/runtime-local/src/host.ts +37 -17
  306. package/packages/runtime-local/src/log.ts +48 -0
  307. package/packages/runtime-local/src/programs.ts +41 -25
  308. package/packages/runtime-local/src/result-writer.ts +2 -0
  309. package/packages/runtime-local/src/runtime.ts +10 -6
  310. package/packages/runtime-local/src/supervisor.ts +142 -0
  311. package/packages/runtime-local/src/types.ts +9 -2
  312. package/packages/runtime-local/src/worker-process.ts +7 -24
  313. package/packages/runtime-local/src/worker.ts +55 -83
  314. package/packages/screen-overlay/README.md +3 -3
  315. package/packages/screen-overlay/package.json +1 -1
  316. package/packages/screen-overlay/src/component.ts +3 -3
  317. package/packages/screen-overlay/src/fragment.ts +5 -4
  318. package/packages/screen-overlay/src/manifest.ts +7 -6
  319. package/packages/screen-overlay/src/program.ts +9 -9
  320. package/packages/screen-overlay/src/surface.ts +6 -7
  321. package/packages/script/README.md +37 -2
  322. package/packages/script/src/edit.ts +123 -73
  323. package/packages/script/src/lexical.ts +40 -6
  324. package/packages/script/src/manifest.ts +2 -2
  325. package/packages/script/src/narrative.ts +1 -0
  326. package/packages/script/src/parser.ts +36 -8
  327. package/packages/script/src/surface.ts +4 -3
  328. package/packages/script/src/types.ts +5 -1
  329. package/packages/script-studio/package.json +9 -3
  330. package/packages/script-studio/src/index.ts +2 -0
  331. package/packages/script-studio/src/projection.ts +105 -0
  332. package/packages/seedance/README.md +30 -1
  333. package/packages/seedance/src/index.ts +18 -7
  334. package/packages/seedance/src/surface.ts +17 -4
  335. package/packages/seedance-kits/README.md +10 -2
  336. package/packages/seedance-kits/kits/speaker-v1.svs +5 -5
  337. package/packages/seedream/README.md +1 -1
  338. package/packages/sound/README.md +81 -0
  339. package/packages/sound/package.json +25 -0
  340. package/packages/sound/src/activation.ts +11 -0
  341. package/packages/sound/src/component.ts +28 -0
  342. package/packages/sound/src/index.ts +5 -0
  343. package/packages/sound/src/manifest.ts +47 -0
  344. package/packages/sound/src/program.ts +84 -0
  345. package/packages/sound/src/style.ts +34 -0
  346. package/packages/sound/src/surface.ts +90 -0
  347. package/packages/sound-studio/README.md +22 -0
  348. package/packages/sound-studio/package.json +20 -0
  349. package/packages/sound-studio/src/activation.ts +6 -0
  350. package/packages/sound-studio/src/index.ts +74 -0
  351. package/packages/speech/README.md +2 -2
  352. package/packages/speech-alignment/src/align.ts +45 -21
  353. package/packages/speech-alignment/src/local.ts +8 -0
  354. package/packages/speech-alignment/src/locate.ts +1 -1
  355. package/packages/store-sqlite/README.md +7 -0
  356. package/packages/store-sqlite/src/store.ts +84 -83
  357. package/packages/studio/INSPECTOR.md +117 -0
  358. package/packages/studio/README.md +149 -14
  359. package/packages/studio/package.json +8 -7
  360. package/packages/studio/src/build-library.ts +99 -84
  361. package/packages/studio/src/companion-assembly.ts +1 -0
  362. package/packages/studio/src/domain.ts +1 -1
  363. package/packages/studio/src/fallback-companions.ts +3 -3
  364. package/packages/studio/src/feedback-server.ts +51 -0
  365. package/packages/studio/src/feedback-store.ts +66 -0
  366. package/packages/studio/src/feedback.ts +60 -0
  367. package/packages/studio/src/library-media.ts +19 -0
  368. package/packages/studio/src/parameter-values.ts +83 -1
  369. package/packages/studio/src/parameters.ts +132 -43
  370. package/packages/studio/src/preview/render.ts +5 -1
  371. package/packages/studio/src/preview/runtime-shim.ts +49 -3
  372. package/packages/studio/src/programme.ts +14 -30
  373. package/packages/studio/src/server.ts +66 -34
  374. package/packages/studio/src/shared.ts +24 -8
  375. package/packages/studio/src/snapshot.ts +21 -132
  376. package/packages/studio/src/studio-preflight.ts +3 -3
  377. package/packages/studio/src/studio-registry.ts +72 -6
  378. package/packages/studio/src/style.css +342 -190
  379. package/packages/studio/src/temporal-edit.ts +131 -0
  380. package/packages/studio/src/ui/artifact-name.ts +80 -0
  381. package/packages/studio/src/ui/artifact-preview.ts +99 -0
  382. package/packages/studio/src/ui/comments.ts +233 -0
  383. package/packages/studio/src/ui/icons.ts +10 -1
  384. package/packages/studio/src/ui/library.ts +373 -192
  385. package/packages/studio/src/ui/main.ts +291 -110
  386. package/packages/studio/src/ui/material-preview.ts +1 -1
  387. package/packages/studio/src/ui/overlay.ts +31 -26
  388. package/packages/studio/src/ui/scrub-preview.ts +108 -0
  389. package/packages/studio/src/ui/sidebar-panel.ts +31 -0
  390. package/packages/studio/src/ui/stage.ts +188 -9
  391. package/packages/studio/src/ui/timeline.ts +187 -281
  392. package/packages/studio/start.ts +19 -5
  393. package/packages/studio-adapter/README.md +151 -11
  394. package/packages/studio-adapter/src/index.ts +126 -14
  395. package/packages/temporal/README.md +6 -6
  396. package/packages/temporal/package.json +1 -1
  397. package/packages/temporal/src/component.ts +5 -6
  398. package/packages/temporal/src/index.ts +8 -6
  399. package/packages/temporal/src/location.ts +11 -11
  400. package/packages/temporal/src/projection.ts +19 -19
  401. package/packages/temporal/src/rational.ts +4 -4
  402. package/packages/temporal-markup/EDITING.md +183 -0
  403. package/packages/temporal-markup/README.md +31 -11
  404. package/packages/temporal-markup/package.json +1 -1
  405. package/packages/temporal-markup/src/index.ts +20 -25
  406. package/packages/temporal-markup/src/space.ts +10 -41
  407. package/packages/timeline/README.md +31 -0
  408. package/packages/{semantic-track → timeline}/package.json +1 -1
  409. package/packages/timeline/src/activation.ts +8 -0
  410. package/packages/timeline/src/component.ts +24 -0
  411. package/packages/timeline/src/identity.ts +60 -0
  412. package/packages/timeline/src/index.ts +6 -0
  413. package/packages/{semantic-track → timeline}/src/location.ts +20 -20
  414. package/packages/timeline/src/manifest.ts +39 -0
  415. package/packages/{semantic-track → timeline}/src/projection.ts +14 -14
  416. package/packages/timeline/src/types.ts +24 -0
  417. package/packages/timeline-author/README.md +68 -0
  418. package/packages/{speech-track → timeline-author}/package.json +6 -4
  419. package/packages/timeline-author/src/activation.ts +22 -0
  420. package/packages/{speech-track → timeline-author}/src/component.ts +15 -13
  421. package/packages/{speech-track → timeline-author}/src/fragment.ts +14 -23
  422. package/packages/timeline-author/src/index.ts +7 -0
  423. package/packages/timeline-author/src/manifest.ts +78 -0
  424. package/packages/timeline-author/src/program.ts +107 -0
  425. package/packages/timeline-author/src/surface.ts +46 -0
  426. package/packages/timeline-author/src/types.ts +6 -0
  427. package/packages/typography-track/README.md +4 -4
  428. package/packages/typography-track/package.json +1 -1
  429. package/packages/typography-track/src/component.ts +4 -4
  430. package/packages/typography-track/src/fragment.ts +4 -3
  431. package/packages/typography-track/src/manifest.ts +11 -9
  432. package/packages/typography-track/src/program.ts +15 -15
  433. package/packages/typography-track/src/surface.ts +17 -18
  434. package/packages/typography-track-studio/package.json +8 -3
  435. package/packages/typography-track-studio/src/index.ts +5 -1
  436. package/packages/video-cli/README.md +16 -4
  437. package/packages/video-cli/package.json +2 -1
  438. package/packages/video-cli/src/creation.ts +25 -9
  439. package/packages/video-cli/src/index.ts +1 -1
  440. package/packages/video-cli/src/studio-distribution.ts +2 -1
  441. package/packages/volcengine-matting/README.md +3 -3
  442. package/packages/whisperx/README.md +8 -1
  443. package/packages/workspace/src/index.ts +2 -0
  444. package/packages/workspace-fs-node/README.md +4 -1
  445. package/packages/workspace-fs-node/src/workspace.ts +3 -1
  446. package/services/whisperx/README.md +14 -0
  447. package/services/whisperx/pyproject.toml +2 -0
  448. package/services/whisperx/uv.lock +20 -20
  449. package/packages/provider-hyperframes-aws-lambda/README.md +0 -30
  450. package/packages/provider-hyperframes-aws-lambda/package.json +0 -30
  451. package/packages/provider-hyperframes-aws-lambda/src/activation.ts +0 -97
  452. package/packages/provider-hyperframes-aws-lambda/src/client.ts +0 -114
  453. package/packages/provider-hyperframes-aws-lambda/src/index.ts +0 -21
  454. package/packages/provider-hyperframes-aws-lambda/src/provider.ts +0 -522
  455. package/packages/provider-kie/README.md +0 -134
  456. package/packages/provider-kie/package.json +0 -43
  457. package/packages/provider-kie/src/activation.ts +0 -71
  458. package/packages/provider-kie/src/index.ts +0 -10
  459. package/packages/provider-kie/src/mapping.ts +0 -182
  460. package/packages/provider-kie/src/provider.ts +0 -657
  461. package/packages/provider-kie/src/routes.ts +0 -150
  462. package/packages/provider-xiaomi-mimo/README.md +0 -32
  463. package/packages/provider-xiaomi-mimo/src/activation.ts +0 -57
  464. package/packages/provider-xiaomi-mimo/src/index.ts +0 -5
  465. package/packages/provider-xiaomi-mimo/src/provider.ts +0 -214
  466. package/packages/runtime/src/environment.ts +0 -6
  467. package/packages/semantic-track/README.md +0 -21
  468. package/packages/semantic-track/src/activation.ts +0 -8
  469. package/packages/semantic-track/src/component.ts +0 -24
  470. package/packages/semantic-track/src/identity.ts +0 -58
  471. package/packages/semantic-track/src/index.ts +0 -6
  472. package/packages/semantic-track/src/manifest.ts +0 -38
  473. package/packages/semantic-track/src/types.ts +0 -26
  474. package/packages/speech-track/README.md +0 -51
  475. package/packages/speech-track/src/activation.ts +0 -19
  476. package/packages/speech-track/src/index.ts +0 -7
  477. package/packages/speech-track/src/manifest.ts +0 -73
  478. package/packages/speech-track/src/program.ts +0 -64
  479. package/packages/speech-track/src/surface.ts +0 -35
  480. package/packages/speech-track/src/types.ts +0 -6
  481. package/packages/speech-track-studio/README.md +0 -3
  482. package/packages/speech-track-studio/package.json +0 -14
  483. package/packages/speech-track-studio/src/activation.ts +0 -7
  484. package/packages/speech-track-studio/src/index.ts +0 -40
  485. /package/packages/{cli → project-context-node}/src/project-context.ts +0 -0
  486. /package/packages/{speech-track → timeline-author}/preview/Track.png +0 -0
@@ -1,3 +1,4 @@
1
+ import type { ExecutionDiagnostic } from "@hypit/runtime";
1
2
  import { execFile, spawn } from "node:child_process";
2
3
  import { promisify } from "node:util";
3
4
  import { fileURLToPath } from "node:url";
@@ -11,7 +12,17 @@ const cleanupMs = 5_000;
11
12
  /** Chrome starts its own process group, so stopping only the Node child is insufficient. */
12
13
  async function killRenderTree(pid: number): Promise<void> {
13
14
  if (process.platform === "win32") {
14
- await exec("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, timeout: cleanupMs });
15
+ try {
16
+ await exec("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, timeout: cleanupMs });
17
+ } catch (error) {
18
+ // The worker may exit after reporting completion, before taskkill opens it.
19
+ // Ask the OS whether it is gone; localized taskkill output is not an API.
20
+ try { process.kill(pid, 0); }
21
+ catch (probeError) {
22
+ if ((probeError as NodeJS.ErrnoException).code === "ESRCH") return;
23
+ }
24
+ throw error;
25
+ }
15
26
  return;
16
27
  }
17
28
  const { stdout } = await exec("ps", ["-A", "-o", "pid=,ppid="], { timeout: cleanupMs });
@@ -47,6 +58,7 @@ export async function runCaptureProcess(
47
58
  signal: AbortSignal,
48
59
  onProgress: (event: HyperframesRenderProgress) => void,
49
60
  entry = new URL("./capture-worker.ts", import.meta.url),
61
+ onDiagnostic?: (diagnostic: ExecutionDiagnostic) => Promise<void>,
50
62
  ): Promise<void> {
51
63
  signal.throwIfAborted();
52
64
  await new Promise<void>((resolve, reject) => {
@@ -58,6 +70,7 @@ export async function runCaptureProcess(
58
70
  let completed = false;
59
71
  let outputBytes = 0;
60
72
  let stderr = "";
73
+ let diagnostics = Promise.resolve();
61
74
  let grace: ReturnType<typeof setTimeout> | undefined;
62
75
  let killing: Promise<void> | undefined;
63
76
  const kill = () => {
@@ -80,8 +93,16 @@ export async function runCaptureProcess(
80
93
  if (error) stderr = `${stderr}${chunk.toString()}`.slice(-32_000);
81
94
  if (outputBytes > input.config.maxProcessOutputBytes) stop(new Error("HyperFrames process output exceeded the configured limit"));
82
95
  };
83
- child.stdout?.on("data", (chunk: Buffer) => log(chunk, false));
84
- child.stderr?.on("data", (chunk: Buffer) => log(chunk, true));
96
+ for (const [stream, pipe] of [["stdout", child.stdout], ["stderr", child.stderr]] as const) {
97
+ pipe?.setEncoding("utf8");
98
+ pipe?.on("data", (text: string) => {
99
+ log(Buffer.from(text), stream === "stderr");
100
+ if (onDiagnostic !== undefined && text.trim()) {
101
+ diagnostics = diagnostics.then(() => onDiagnostic({ stream, level: "info", message: text.trimEnd() }))
102
+ .catch((error) => stop(error instanceof Error ? error : new Error(String(error))));
103
+ }
104
+ });
105
+ }
85
106
  child.on("message", (value: { type: string; event?: HyperframesRenderProgress; error?: string }) => {
86
107
  if (value.type === "progress" && value.event !== undefined) {
87
108
  try { onProgress(value.event); } catch (error) { stop(error instanceof Error ? error : new Error(String(error))); }
@@ -98,7 +119,8 @@ export async function runCaptureProcess(
98
119
  child.on("close", () => {
99
120
  if (grace !== undefined) clearTimeout(grace);
100
121
  signal.removeEventListener("abort", abort);
101
- void (killing ?? Promise.resolve()).then(() => {
122
+ void (killing ?? Promise.resolve()).then(async () => {
123
+ await diagnostics;
102
124
  if (failure !== undefined) reject(failure);
103
125
  else if (!completed) reject(new Error(`HyperFrames process exited before completion: ${stderr}`));
104
126
  else resolve();
@@ -6,6 +6,9 @@ import type { HyperframesRenderProgress, resolveExecutionOptions } from "./rende
6
6
  import { assert, runProcess } from "./process.js";
7
7
  import { verifyOutput } from "./output.js";
8
8
  import { distributeFrameRange, sourceFrameAt, sourceWindows, videoSlots } from "./sampling.js";
9
+ import { renderWorkerLimit } from "./render.js";
10
+ import { CaptureConcurrency } from "./concurrency.js";
11
+ import { createOpaqueFrameCapture } from "./opaque-capture.js";
9
12
 
10
13
  export type CaptureInput = {
11
14
  readonly document: HyperframesDocument;
@@ -22,8 +25,14 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
22
25
  const { document, range, config, directory: work } = input;
23
26
  const signal = controller.signal;
24
27
  const frameCount = range.endFrameExclusive - range.startFrame;
25
- const tasks = distributeFrameRange(range, config.workers);
26
28
  const fps = { num: document.frameRate.numerator, den: document.frameRate.denominator };
29
+ const limit = renderWorkerLimit(config, frameCount, fps.num / fps.den);
30
+ const concurrency = new CaptureConcurrency(limit, config.workers === "auto");
31
+ // Short contiguous batches retain sequential capture while allowing free browsers
32
+ // to help with expensive passages. This queue exists only inside this render.
33
+ const batchCount = Math.max(limit, Math.ceil(frameCount / Math.max(1, Math.round(fps.num / fps.den))));
34
+ const batches = distributeFrameRange(range, batchCount);
35
+ let nextBatch = 0;
27
36
  const started = performance.now();
28
37
  const elapsedMs = () => Math.round(performance.now() - started);
29
38
  const engine = await import(input.engineModule) as typeof import("@hyperframes/engine");
@@ -65,7 +74,11 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
65
74
  const sources = new Map<string, { frames: Map<number, string>; width: number; height: number }>();
66
75
  let sourceFrames = 0;
67
76
  // One source at a time bounds decoder pressure independently of browser concurrency.
68
- for (const source of sourceWindows(slots, range)) {
77
+ const windows = sourceWindows(slots, range);
78
+ const sourceTotal = windows.reduce((sum, source) => sum + source.windows.reduce((count, window) =>
79
+ count + window.endFrameExclusive - window.startFrame, 0), 0);
80
+ if (sourceTotal > 0) onProgress({ phase: "decoding", completed: 0, total: sourceTotal, elapsedMs: elapsedMs() });
81
+ for (const source of windows) {
69
82
  const path = resolve(work, source.src);
70
83
  assert(path.startsWith(`${work}${sep}`), "HyperFrames source is outside the staged project");
71
84
  const frames = new Map<number, string>();
@@ -86,6 +99,7 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
86
99
  width = extracted.metadata.width;
87
100
  height = extracted.metadata.height;
88
101
  sourceFrames += count;
102
+ onProgress({ phase: "decoding", completed: sourceFrames, total: sourceTotal, elapsedMs: elapsedMs() });
89
103
  }
90
104
  sources.set(source.src, { frames, width, height });
91
105
  }
@@ -109,9 +123,24 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
109
123
  const serverUrl = server.url;
110
124
  const outputFrames = join(work, "frames");
111
125
  await mkdir(outputFrames);
112
- onProgress?.({ phase: "prepared", workers: tasks.length, sourceFrames, elapsedMs: elapsedMs() });
113
- const results = await Promise.allSettled(tasks.map(async (task, worker) => {
126
+ onProgress?.({ phase: "prepared", workers: concurrency.target, sourceFrames, elapsedMs: elapsedMs() });
127
+ const jobs: Promise<void>[] = [];
128
+ let active = 0, open = 0, initializing = 0, capturedFrames = 0;
129
+ let startupMs = 0;
130
+ const launch = (): void => {
131
+ const worker = jobs.length;
132
+ jobs.push(runWorker(worker));
133
+ // Failure stops all jobs promptly; all jobs are still awaited below.
134
+ void jobs[worker]!.catch(() => {});
135
+ };
136
+ const runWorker = async (worker: number): Promise<void> => {
114
137
  let session: Session | undefined;
138
+ let ready = false;
139
+ active++;
140
+ open++;
141
+ initializing++;
142
+ const launched = performance.now();
143
+ let setupMs = 0;
115
144
  try {
116
145
  signal.throwIfAborted();
117
146
  const directory = join(work, `worker-${worker}`);
@@ -120,7 +149,9 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
120
149
  frameSrcResolver: (path) => new URL(relative(work, path).split(sep).map(encodeURIComponent).join("/"), `${serverUrl}/`).href,
121
150
  });
122
151
  session = await engine.createCaptureSession(serverUrl, directory, {
123
- ...document.canvas, fps, format: "png",
152
+ // Engine initialization couples PNG to transparent-export mode. Use
153
+ // its opaque session setup; our capture adapter writes lossless PNG.
154
+ ...document.canvas, fps, format: "jpeg",
124
155
  compositionDurationSeconds: document.frameCount * fps.den / fps.num,
125
156
  skipReadinessVideoIds: slots.map((slot) => slot.id),
126
157
  videoMetadataHints: slots.flatMap((slot) => {
@@ -132,38 +163,77 @@ export async function captureStagedVisual(input: CaptureInput, controller: Abort
132
163
  signal.throwIfAborted();
133
164
  const activeSession = session;
134
165
  const browserPid = session.browser.process()?.pid;
135
- onProgress?.({ phase: "worker-initializing", worker, range: task, browserPid, elapsedMs: elapsedMs() });
166
+ onProgress?.({ phase: "worker-initializing", worker, range, browserPid, elapsedMs: elapsedMs() });
136
167
  await stage(`worker ${worker} initialization`, config.initializationTimeoutMs, () => engine.initializeSession(activeSession));
137
- // HyperFrames' PNG capture clears the page/composition backgrounds for
138
- // standalone transparent exports. Here PNGs are intermediate frames of
139
- // a Film, whose authored Canvas background still belongs in the image.
140
- await activeSession.page.evaluate(() => {
141
- globalThis.document.getElementById("__hf_transparent_bg__")?.remove();
142
- });
143
- onProgress?.({ phase: "worker-start", worker, range: task, browserPid, elapsedMs: elapsedMs() });
144
- for (let frame = task.startFrame; frame < task.endFrameExclusive; frame++) {
145
- signal.throwIfAborted();
146
- const captured = await stage(`worker ${worker} frame ${frame}`, config.frameTimeoutMs, async () => {
147
- const capture = await engine.captureFrameToBuffer(activeSession, frame, frame * fps.den / fps.num);
148
- const programError = await activeSession.page.evaluate(() =>
149
- (window as unknown as { __hypitBrowserProgramError?: string }).__hypitBrowserProgramError);
150
- if (programError !== undefined) throw new Error(`Browser program failed at frame ${frame}: ${programError}`);
151
- return capture;
152
- });
153
- await writeFile(join(outputFrames, `${String(frame - range.startFrame).padStart(9, "0")}.png`), captured.buffer);
168
+ const captureFrame = await createOpaqueFrameCapture(activeSession);
169
+ setupMs = performance.now() - launched;
170
+ startupMs = Math.max(startupMs, setupMs);
171
+ ready = true;
172
+ initializing--;
173
+ if (initializing === 0) concurrency.settled(performance.now());
174
+ onProgress?.({ phase: "worker-start", worker, range, browserPid, elapsedMs: elapsedMs() });
175
+ let completed = 0;
176
+ let lastProgressAt = performance.now();
177
+ const timing = { seekMs: 0, prepareMs: 0, screenshotMs: 0 };
178
+ while (true) {
179
+ // Retire only between complete batches, before claiming more work.
180
+ if (active > concurrency.target) break;
181
+ const batch = batches[nextBatch++];
182
+ if (batch === undefined) break;
183
+ for (let frame = batch.startFrame; frame < batch.endFrameExclusive; frame++) {
184
+ signal.throwIfAborted();
185
+ const captured = await stage(`worker ${worker} frame ${frame}`, config.frameTimeoutMs, () => captureFrame(frame));
186
+ timing.seekMs += captured.seekMs;
187
+ timing.prepareMs += captured.prepareMs;
188
+ timing.screenshotMs += captured.screenshotMs;
189
+ await writeFile(join(outputFrames, `${String(frame - range.startFrame).padStart(9, "0")}.png`), captured.buffer);
190
+ completed++;
191
+ capturedFrames++;
192
+ if (performance.now() - lastProgressAt >= 1_000) {
193
+ onProgress({ phase: "worker-progress", worker, completed, elapsedMs: elapsedMs() });
194
+ lastProgressAt = performance.now();
195
+ }
196
+ }
197
+ const decision = concurrency.complete({ now: performance.now(), frames: batch.endFrameExclusive - batch.startFrame,
198
+ remainingFrames: frameCount - capturedFrames, startupMs, active: initializing === 0 ? active : 0 });
199
+ if (decision !== undefined) {
200
+ console.log(`Capture auto: ${active} -> ${decision.workers} browsers; ${decision.fps.toFixed(1)} frames/s; ${decision.reason}`);
201
+ while (active < decision.workers && open < limit && nextBatch < batches.length) launch();
202
+ }
154
203
  }
155
- onProgress?.({ phase: "worker-complete", worker, range: task, browserPid, elapsedMs: elapsedMs() });
204
+ console.log(`Capture worker ${worker}: ${completed} frames; startup ${Math.round(setupMs)} ms; `
205
+ + `seek ${Math.round(timing.seekMs)} ms; prepare ${Math.round(timing.prepareMs)} ms; PNG ${Math.round(timing.screenshotMs)} ms`);
206
+ onProgress({ phase: "worker-complete", worker, completed, browserPid, elapsedMs: elapsedMs() });
156
207
  } catch (error) {
157
208
  controller.abort(error);
158
209
  throw error;
159
210
  } finally {
211
+ active--;
212
+ if (!ready) initializing--;
160
213
  if (session !== undefined) await close(session);
214
+ open--;
215
+ // A restoring decision can wait for a retiring Chrome to release its
216
+ // slot. Closing processes still count against the reserved ceiling.
217
+ if (!signal.aborted && active < concurrency.target && open < limit && nextBatch < batches.length) launch();
161
218
  }
162
- }));
219
+ };
220
+ const initialWorkers = concurrency.target;
221
+ console.log(`Capture: opaque fast PNG; ${config.workers === "auto" ? "auto" : "fixed"} workers ${initialWorkers}, limit ${limit}`);
222
+ for (let i = 0; i < initialWorkers; i++) launch();
223
+ // New workers can join while earlier jobs run; collect every launched job.
224
+ let awaited = 0;
225
+ let failure: unknown;
226
+ while (awaited < jobs.length) {
227
+ const pending = jobs.slice(awaited);
228
+ awaited = jobs.length;
229
+ const results = await Promise.allSettled(pending);
230
+ for (const result of results) if (result.status === "rejected") failure ??= result.reason;
231
+ }
232
+ if (failure !== undefined) throw failure;
163
233
  signal.throwIfAborted();
164
- for (const result of results) if (result.status === "rejected") throw result.reason;
165
234
  await Promise.all(closing.values());
166
235
  const output = join(work, "visual.mp4");
236
+ onProgress({ phase: "encoding", elapsedMs: elapsedMs() });
167
237
  const crf = { draft: 28, standard: 23, high: 18 }[config.quality];
168
238
  await runProcess({ executable: config.ffmpegPath,
169
239
  argv: ["-v", "error", "-y", "-framerate", `${fps.num}/${fps.den}`, "-i", join(outputFrames, "%09d.png"),
@@ -0,0 +1,86 @@
1
+ import { availableParallelism, totalmem } from "node:os";
2
+
3
+ function hostMemory(): number {
4
+ const constrained = process.constrainedMemory?.() ?? 0;
5
+ return constrained > 0 ? Math.min(totalmem(), constrained) : totalmem();
6
+ }
7
+
8
+ /** Stable reservation ceiling. Actual load tuning happens inside the admitted render. */
9
+ export function autoWorkerLimit(cpu = availableParallelism(), memory = hostMemory()): number {
10
+ // Chrome owns raster surfaces, decoded media and the JS heap in separate
11
+ // processes. Leave half the memory and two CPU slots to the host/other stages.
12
+ const byMemory = Math.floor(memory / 2 / (1.5 * 1024 ** 3));
13
+ return Math.max(1, Math.min(Math.max(1, cpu - 2), byMemory));
14
+ }
15
+
16
+ /**
17
+ * Online throughput comparison of useful capture batches. All samples and
18
+ * decisions die with this attempt. A change never invalidates captured frames.
19
+ */
20
+ export class CaptureConcurrency {
21
+ target: number;
22
+ private since: number | undefined;
23
+ private frames = 0;
24
+ private batches = 0;
25
+ private baseline: { workers: number; rate: number } | undefined;
26
+ private previousRate: number | undefined;
27
+ private growth = true;
28
+
29
+ constructor(readonly limit: number, readonly automatic: boolean) {
30
+ this.target = automatic ? Math.max(1, Math.ceil(limit / 2)) : limit;
31
+ }
32
+
33
+ settled(now: number): void {
34
+ this.since = now;
35
+ this.frames = 0;
36
+ this.batches = 0;
37
+ }
38
+
39
+ complete(input: { now: number; frames: number; remainingFrames: number; startupMs: number; active: number }):
40
+ { workers: number; reason: string; fps: number } | undefined {
41
+ if (!this.automatic) return;
42
+ if (input.active !== this.target) { this.since = undefined; return; }
43
+ if (this.since === undefined) { this.settled(input.now); return; }
44
+ this.frames += input.frames;
45
+ this.batches++;
46
+ const elapsed = input.now - this.since;
47
+ // At least two batches per worker and a browser startup's worth of useful
48
+ // work amortize short fluctuations. No synthetic calibration is rendered.
49
+ if (this.batches < this.target * 2 || elapsed < Math.max(1_000, input.startupMs)) return;
50
+ const rate = this.frames * 1_000 / elapsed;
51
+ let next = this.target;
52
+ let reason = "";
53
+ if (this.baseline !== undefined) {
54
+ const before = this.baseline;
55
+ this.baseline = undefined;
56
+ const added = this.target > before.workers;
57
+ if (rate < before.rate * (added ? 1.05 : 0.95)) {
58
+ next = before.workers;
59
+ reason = added ? "extra browser did not improve throughput" : "fewer browsers reduced throughput";
60
+ this.growth = false;
61
+ }
62
+ } else if (this.previousRate !== undefined && rate < this.previousRate * 0.7 && this.target > 1
63
+ && input.remainingFrames > this.frames * 2) {
64
+ // A slower passage is not proof of contention. Compare fewer browsers
65
+ // on subsequent useful work and restore the old count if it is worse.
66
+ this.baseline = { workers: this.target, rate };
67
+ next--;
68
+ reason = "compare fewer browsers after throughput changed";
69
+ this.growth = false;
70
+ }
71
+ if (next === this.target && this.growth && next < this.limit
72
+ && input.remainingFrames / rate * 1_000 > input.startupMs * (next + 1) * 2) {
73
+ // A new browser must have enough remaining work to repay its startup,
74
+ // even if its ideal contribution is only 1/(N+1) of current throughput.
75
+ this.baseline = { workers: this.target, rate };
76
+ next++;
77
+ reason = "remaining capture can amortize another browser";
78
+ }
79
+ this.previousRate = rate;
80
+ this.settled(input.now);
81
+ if (next === this.target) return;
82
+ this.target = next;
83
+ this.since = undefined;
84
+ return { workers: next, reason, fps: rate };
85
+ }
86
+ }
@@ -0,0 +1,92 @@
1
+ import type { CaptureSession, HfProtocol } from "@hyperframes/engine";
2
+ import { getCdpSession } from "@hyperframes/engine";
3
+
4
+ /** The optional completion hooks supplied by the pinned Hyperframes page runtime. */
5
+ type RenderWindow = Window & {
6
+ __hf: HfProtocol & { colorGrading?: { waitForActiveLuts?: () => Promise<void> } };
7
+ __hfWaitForSeekCompletion?: () => Promise<void>;
8
+ __hf_page_composite_pending?: boolean;
9
+ __hf_page_composite_prepare?: () => Promise<void>;
10
+ __hf_page_composite_resolve?: () => void;
11
+ __hypitBrowserProgramError?: string;
12
+ };
13
+
14
+ /**
15
+ * A screenshot adapter for this Provider's opaque MP4 output. Session lifecycle,
16
+ * the page's seek protocol and the video injector remain engine-owned. Encoding
17
+ * the already-composited frame as a fast PNG does not change source alpha.
18
+ * No engine/session methods are replaced and no installed dependency is patched.
19
+ */
20
+ export async function createOpaqueFrameCapture(session: CaptureSession) {
21
+ const { page, options } = session;
22
+ const cdp = await getCdpSession(page);
23
+ // MP4 has no alpha channel. The authored Canvas paints over this final matte.
24
+ await cdp.send("Emulation.setDefaultBackgroundColorOverride", { color: { r: 0, g: 0, b: 0, a: 1 } });
25
+ // These decoded image handles belong to this page, just like its DOM. Retain
26
+ // only URLs used by inline background images; changing a style can need a load
27
+ // even after initializeSession has finished its initial resource readiness.
28
+ await page.evaluate(() => {
29
+ const decoded = new Map<string, Promise<void>>();
30
+ (window as unknown as { __hypitPrepareImages: () => Promise<void> }).__hypitPrepareImages = async () => {
31
+ const pending: Promise<unknown>[] = [];
32
+ if (document.fonts.status === "loading") pending.push(document.fonts.ready);
33
+ for (const image of Array.from(document.images)) {
34
+ if (!image.complete) pending.push(image.decode());
35
+ }
36
+ for (const element of Array.from(document.querySelectorAll<HTMLElement>('[style*="background"]'))) {
37
+ const background = element.style.backgroundImage;
38
+ for (const match of background.matchAll(/url\(\s*(?:"((?:\\.|[^"])*)"|'((?:\\.|[^'])*)'|([^)]*))\s*\)/gu)) {
39
+ const url = (match[1] ?? match[2] ?? match[3] ?? "").trim();
40
+ if (!url) continue;
41
+ let ready = decoded.get(url);
42
+ if (ready === undefined) {
43
+ const image = new Image();
44
+ image.src = url;
45
+ ready = image.decode();
46
+ decoded.set(url, ready);
47
+ }
48
+ pending.push(ready);
49
+ }
50
+ }
51
+ await Promise.all(pending);
52
+ };
53
+ });
54
+ return async (frame: number) => {
55
+ const time = frame * options.fps.den / options.fps.num;
56
+ const start = performance.now();
57
+ await page.evaluate(async t => {
58
+ const w = window as unknown as RenderWindow;
59
+ await w.__hf.seek(t);
60
+ }, time);
61
+ const sought = performance.now();
62
+ await session.onBeforeCapture?.(page, time);
63
+ const composite = await page.evaluate(async () => {
64
+ const w = window as unknown as RenderWindow & { __hypitPrepareImages: () => Promise<void> };
65
+ await w.__hfWaitForSeekCompletion?.();
66
+ await w.__hf.colorGrading?.waitForActiveLuts?.();
67
+ await w.__hypitPrepareImages();
68
+ if (w.__hypitBrowserProgramError !== undefined) throw new Error(w.__hypitBrowserProgramError);
69
+ if (w.__hf_page_composite_pending) {
70
+ await w.__hf_page_composite_prepare?.();
71
+ return true;
72
+ }
73
+ return false;
74
+ });
75
+ if (composite) {
76
+ // The runtime's shader compositor needs a paint between preparing its DOM
77
+ // layers and resolving them. A 1 px capture flushes Chrome's compositor.
78
+ await cdp.send("Page.captureScreenshot", {
79
+ format: "jpeg", quality: 1, clip: { x: 0, y: 0, width: 1, height: 1, scale: 1 },
80
+ });
81
+ await page.evaluate(() => (window as unknown as RenderWindow).__hf_page_composite_resolve?.());
82
+ }
83
+ const prepared = performance.now();
84
+ const result = await cdp.send("Page.captureScreenshot", {
85
+ format: "png", optimizeForSpeed: true, fromSurface: true,
86
+ captureBeyondViewport: options.captureBeyondViewport ?? false,
87
+ clip: { x: 0, y: 0, width: options.width, height: options.height, scale: options.deviceScaleFactor ?? 1 },
88
+ });
89
+ return { buffer: Buffer.from(result.data, "base64"),
90
+ seekMs: sought - start, prepareMs: prepared - sought, screenshotMs: performance.now() - prepared };
91
+ };
92
+ }
@@ -8,6 +8,8 @@ export type HyperframesExecutionOptions = {
8
8
  readonly ffmpegPath?: string;
9
9
  /** Parallel Chrome workers inside one render. This is separate from Provider request concurrency. */
10
10
  readonly workers?: HyperframesWorkers;
11
+ /** Upper bound for auto's browser reservation; explicit workers remain fixed. */
12
+ readonly maxWorkers?: number;
11
13
  readonly quality?: HyperframesQuality;
12
14
  /** Chrome's rasterizer, default hardware. Use software without a usable GPU. */
13
15
  readonly browserGpu?: HyperframesBrowserGpu;
@@ -22,13 +22,16 @@ export function localHyperframesBrowserProgram(
22
22
  input: {
23
23
  readonly id: string;
24
24
  readonly nodePath: string;
25
- readonly hyperframesCliPath: string;
25
+ readonly hyperframesCliPath: string | (() => string);
26
26
  readonly ffprobePath: string;
27
27
  readonly ffmpegPath?: string;
28
28
  },
29
29
  ): ManagedProgram {
30
+ const cliPath = () => typeof input.hyperframesCliPath === "string" ? input.hyperframesCliPath : input.hyperframesCliPath();
30
31
  const probeBrowser = async (): Promise<ManagedProgramState> => {
31
- const located = await run(input.nodePath, [input.hyperframesCliPath, "browser", "path"]);
32
+ let executable: string;
33
+ try { executable = cliPath(); } catch (error) { return { state: "down", detail: error instanceof Error ? error.message : String(error) }; }
34
+ const located = await run(input.nodePath, [executable, "browser", "path"]);
32
35
  if (!located.ok) return { state: "down", detail: `HyperFrames browser is unavailable: ${located.output}` };
33
36
  const path = located.output.trim();
34
37
  if (path.length === 0 || path.includes("\n") || path.includes("\r")) {
@@ -44,7 +47,7 @@ export function localHyperframesBrowserProgram(
44
47
  id: input.id,
45
48
  installation: {
46
49
  probe: probeBrowser,
47
- commands: [{ command: input.nodePath, args: [input.hyperframesCliPath, "browser", "ensure"] }],
50
+ get commands() { return [{ command: input.nodePath, args: [cliPath(), "browser", "ensure"] }]; },
48
51
  },
49
52
  async probe(): Promise<ManagedProgramState> {
50
53
  const browser = await probeBrowser();
@@ -0,0 +1,53 @@
1
+ import type { OperationProgress } from "@hypit/endpoint-kit";
2
+ import type { HyperframesRenderProgress } from "./render.js";
3
+
4
+ /** Reduce parallel capture events to one readable progress report for this render request. */
5
+ export function renderProgressReporter(
6
+ report: ((progress: OperationProgress) => Promise<void>) | undefined,
7
+ totalFrames: number,
8
+ now: () => number = Date.now,
9
+ ) {
10
+ const workers = new Map<number, number>();
11
+ let rendering = false;
12
+ let lastPhase: string | undefined;
13
+ let lastAt = -Infinity;
14
+ let writes = Promise.resolve();
15
+ const frames = () => ({ phase: "rendering frames", completed: [...workers.values()].reduce((a, b) => a + b, 0),
16
+ total: totalFrames, unit: "frames" });
17
+ return {
18
+ onProgress(event: HyperframesRenderProgress): void {
19
+ if (report === undefined) return;
20
+ let progress: OperationProgress;
21
+ switch (event.phase) {
22
+ case "staging": progress = { phase: "preparing resources" }; break;
23
+ case "decoding": progress = { phase: "decoding source frames", completed: event.completed, total: event.total, unit: "frames" }; break;
24
+ case "prepared":
25
+ case "worker-initializing":
26
+ if (rendering) return;
27
+ progress = { phase: "starting browsers" }; break;
28
+ case "worker-start":
29
+ rendering = true;
30
+ workers.set(event.worker, 0);
31
+ progress = frames(); break;
32
+ case "worker-progress":
33
+ workers.set(event.worker, event.completed);
34
+ progress = frames(); break;
35
+ case "worker-complete":
36
+ workers.set(event.worker, event.completed);
37
+ progress = frames(); break;
38
+ case "encoding": progress = { phase: "encoding video" }; break;
39
+ case "storing": progress = { phase: "storing output" }; break;
40
+ case "complete": return;
41
+ }
42
+ const at = now();
43
+ if (progress.phase === lastPhase && at - lastAt < 1_000
44
+ && (progress.total === undefined || progress.completed !== progress.total)) return;
45
+ lastPhase = progress.phase;
46
+ lastAt = at;
47
+ // Attach rejection handling immediately; flush still propagates storage failure to the caller.
48
+ writes = writes.then(() => report(progress));
49
+ void writes.catch(() => {});
50
+ },
51
+ flush: () => writes,
52
+ };
53
+ }
@@ -3,8 +3,9 @@ import { mediaTypes } from "@hypit/media";
3
3
  import { renderHyperframesCapabilities, verifyHyperframesVisualRequest } from "@hypit/render-hyperframes";
4
4
  import { canonicalize } from "@hypit/protocol";
5
5
  import { resolveNodePackageExecutable } from "@hypit/package-loader-node";
6
- import { renderHyperframesVisual, resolveExecutionOptions } from "./render.js";
6
+ import { renderHyperframesVisual, resolveExecutionOptions, renderWorkerLimit } from "./render.js";
7
7
  import type { HyperframesExecutionOptions } from "./options.js";
8
+ import { renderProgressReporter } from "./progress.js";
8
9
 
9
10
  export type * from "./options.js";
10
11
  export const localHyperframesProviderModuleRef = { name: "@hypit/provider-hyperframes-local", version: "1" } as const;
@@ -31,6 +32,8 @@ export function createLocalHyperframesProvider(config: CreateLocalHyperframesPro
31
32
  if (config.browserCapacity !== undefined && (!Number.isSafeInteger(config.browserCapacity) || config.browserCapacity < 1)) {
32
33
  throw new Error("HyperFrames browserCapacity must be a positive integer");
33
34
  }
35
+ const maxWorkers = execution.workers === "auto" ? Math.min(execution.maxWorkers, config.browserCapacity ?? Infinity) : execution.maxWorkers;
36
+ const reserved = { ...execution, maxWorkers };
34
37
  return defineEndpointPackage({
35
38
  module: localHyperframesProviderModuleRef,
36
39
  facet: "render",
@@ -47,15 +50,25 @@ export function createLocalHyperframesProvider(config: CreateLocalHyperframesPro
47
50
  unitsForRequest: (request: import("@hypit/endpoint-kit").EndpointRequest) => {
48
51
  verifyHyperframesVisualRequest(request.constraints);
49
52
  const { document, range } = request.constraints;
50
- return { [browsers]: Math.min(execution.workers,
51
- range === undefined ? document.frameCount : range.endFrameExclusive - range.startFrame) };
53
+ return { [browsers]: renderWorkerLimit(reserved,
54
+ range === undefined ? document.frameCount : range.endFrameExclusive - range.startFrame,
55
+ document.frameRate.numerator / document.frameRate.denominator) };
52
56
  },
53
57
  }),
54
58
  handler: async (context) => {
55
59
  const request = context.need.constraints;
56
60
  verifyHyperframesVisualRequest(request);
57
- const visual = await renderHyperframesVisual(request, { ...config, workers: execution.workers, resources: context.resources });
58
- return { value: { kind: "inline", value: canonicalize(visual) } };
61
+ const frameCount = request.range === undefined ? request.document.frameCount
62
+ : request.range.endFrameExclusive - request.range.startFrame;
63
+ const progress = renderProgressReporter(context.reportProgress, frameCount);
64
+ try {
65
+ const visual = await renderHyperframesVisual(request, { ...config, workers: execution.workers, maxWorkers,
66
+ resources: context.resources, onProgress: progress.onProgress,
67
+ ...(context.reportDiagnostic === undefined ? {} : { onDiagnostic: context.reportDiagnostic }) });
68
+ return { value: { kind: "inline", value: canonicalize(visual) } };
69
+ } finally {
70
+ await progress.flush();
71
+ }
59
72
  },
60
73
  }],
61
74
  });