@hypit/hypit 0.1.4 → 0.1.6

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 (462) hide show
  1. package/README.md +4 -2
  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 +77 -15
  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 +8 -1
  30. package/examples/semantic-composition/chat.svml +6 -5
  31. package/examples/semantic-composition/hypit.runtime.json +1 -1
  32. package/examples/semantic-composition/packages/chat-scene/README.md +3 -3
  33. package/examples/semantic-composition/packages/chat-scene/src/activation.ts +18 -21
  34. package/examples/semantic-composition/packages/chat-scene/src/render.ts +4 -4
  35. package/examples/semantic-composition/packages/performance-styles/README.md +42 -0
  36. package/examples/semantic-composition/packages/performance-styles/package.json +25 -0
  37. package/examples/semantic-composition/packages/performance-styles/src/activation.ts +57 -0
  38. package/examples/semantic-composition/packages/performance-styles/src/render.ts +54 -0
  39. package/examples/semantic-composition/packages/performance-styles/src/studio.ts +28 -0
  40. package/examples/semantic-composition/packages/performance-styles/tsconfig.json +18 -0
  41. package/examples/semantic-composition/packages/responsive-explainer/README.md +16 -9
  42. package/examples/semantic-composition/packages/responsive-explainer/src/activation.ts +12 -12
  43. package/examples/semantic-composition/packages/responsive-explainer/src/render.ts +5 -5
  44. package/examples/semantic-composition/packages/sound-styles/README.md +36 -0
  45. package/examples/semantic-composition/packages/sound-styles/package.json +25 -0
  46. package/examples/semantic-composition/packages/sound-styles/src/activation.ts +53 -0
  47. package/examples/semantic-composition/packages/sound-styles/src/render.ts +14 -0
  48. package/examples/semantic-composition/packages/sound-styles/tsconfig.json +18 -0
  49. package/package.json +35 -15
  50. package/packages/audio-track/README.md +20 -13
  51. package/packages/audio-track/package.json +1 -1
  52. package/packages/audio-track/src/component.ts +4 -4
  53. package/packages/audio-track/src/fragment.ts +6 -5
  54. package/packages/audio-track/src/index.ts +1 -1
  55. package/packages/audio-track/src/manifest.ts +17 -16
  56. package/packages/audio-track/src/program.ts +21 -25
  57. package/packages/audio-track/src/surface.ts +19 -18
  58. package/packages/audio-track-studio/README.md +23 -0
  59. package/packages/audio-track-studio/src/index.ts +28 -17
  60. package/packages/author-kit/README.md +1 -1
  61. package/packages/background-removal/README.md +4 -7
  62. package/packages/browser-capture/package.json +1 -1
  63. package/packages/build-result/README.md +11 -0
  64. package/packages/build-result/src/decode.ts +2 -0
  65. package/packages/build-result/src/execution-log.ts +17 -0
  66. package/packages/build-result/src/index.ts +1 -0
  67. package/packages/build-result/src/store.ts +14 -1
  68. package/packages/build-result/src/types.ts +3 -0
  69. package/packages/build-result-s3/src/repository.ts +4 -0
  70. package/packages/caption/README.md +61 -73
  71. package/packages/caption/package.json +3 -2
  72. package/packages/caption/src/activation.ts +2 -2
  73. package/packages/caption/src/component.ts +20 -5
  74. package/packages/caption/src/display.ts +3 -18
  75. package/packages/caption/src/fragment.ts +3 -7
  76. package/packages/caption/src/index.ts +6 -5
  77. package/packages/caption/src/manifest.ts +29 -46
  78. package/packages/caption/src/style.ts +24 -135
  79. package/packages/caption/src/surface.ts +12 -92
  80. package/packages/caption/src/temporalize.ts +18 -53
  81. package/packages/caption/src/types.ts +8 -35
  82. package/packages/caption/src/visibility.ts +23 -0
  83. package/packages/caption-fine/README.md +21 -9
  84. package/packages/caption-fine/package.json +4 -2
  85. package/packages/caption-fine/src/component.ts +3 -3
  86. package/packages/caption-fine/src/fragment.ts +7 -19
  87. package/packages/caption-fine/src/index.ts +2 -0
  88. package/packages/caption-fine/src/manifest.ts +26 -17
  89. package/packages/caption-fine/src/recipe.ts +7 -0
  90. package/packages/caption-fine/src/render.ts +14 -13
  91. package/packages/caption-fine/src/schedule.ts +39 -35
  92. package/packages/caption-fine/src/style.ts +10 -9
  93. package/packages/caption-fine/src/surface.ts +62 -35
  94. package/packages/caption-fine/src/types.ts +2 -0
  95. package/packages/caption-fine-studio/README.md +24 -2
  96. package/packages/caption-fine-studio/package.json +3 -1
  97. package/packages/caption-fine-studio/src/activation.ts +3 -3
  98. package/packages/caption-fine-studio/src/index.ts +54 -42
  99. package/packages/cli/README.md +57 -4
  100. package/packages/cli/package.json +1 -0
  101. package/packages/cli/src/arguments.ts +25 -6
  102. package/packages/cli/src/build-planning.ts +3 -2
  103. package/packages/cli/src/command.ts +6 -0
  104. package/packages/cli/src/commands/environment.ts +28 -18
  105. package/packages/cli/src/commands/execution.ts +81 -26
  106. package/packages/cli/src/commands/results.ts +7 -11
  107. package/packages/cli/src/index.ts +2 -2
  108. package/packages/cli/src/machine-view.ts +3 -2
  109. package/packages/cli/src/main.ts +34 -42
  110. package/packages/cli/src/observation.ts +7 -1
  111. package/packages/cli/src/output.ts +65 -23
  112. package/packages/cli/src/view.ts +66 -19
  113. package/packages/comment-sticker/README.md +3 -3
  114. package/packages/comment-sticker/package.json +1 -1
  115. package/packages/comment-sticker/src/component.ts +4 -4
  116. package/packages/comment-sticker/src/fragment.ts +5 -4
  117. package/packages/comment-sticker/src/manifest.ts +7 -6
  118. package/packages/comment-sticker/src/program.ts +9 -9
  119. package/packages/comment-sticker/src/surface.ts +6 -7
  120. package/packages/composition/README.md +21 -0
  121. package/packages/composition/src/audio-presentation.ts +44 -0
  122. package/packages/composition/src/index.ts +3 -0
  123. package/packages/composition/src/schema.ts +8 -3
  124. package/packages/composition/src/track.ts +15 -1
  125. package/packages/credential-store-env/README.md +1 -1
  126. package/packages/deck-track/README.md +3 -3
  127. package/packages/deck-track/package.json +1 -1
  128. package/packages/deck-track/src/component.ts +5 -5
  129. package/packages/deck-track/src/fragment.ts +6 -5
  130. package/packages/deck-track/src/lower.ts +12 -12
  131. package/packages/deck-track/src/manifest.ts +8 -7
  132. package/packages/deck-track/src/program.ts +13 -16
  133. package/packages/deck-track/src/surface.ts +9 -10
  134. package/packages/driver-node/src/driver.ts +41 -3
  135. package/packages/elevenlabs-speech/README.md +27 -0
  136. package/packages/{provider-xiaomi-mimo → elevenlabs-speech}/package.json +5 -8
  137. package/packages/elevenlabs-speech/src/activation.ts +26 -0
  138. package/packages/elevenlabs-speech/src/fragment.ts +11 -0
  139. package/packages/elevenlabs-speech/src/index.ts +111 -0
  140. package/packages/elevenlabs-speech/src/surface.ts +167 -0
  141. package/packages/endpoint-kit/README.md +24 -0
  142. package/packages/endpoint-kit/src/index.ts +7 -1
  143. package/packages/film/README.md +12 -7
  144. package/packages/film/package.json +1 -1
  145. package/packages/film/src/component.ts +4 -4
  146. package/packages/film/src/fragment.ts +6 -5
  147. package/packages/film/src/manifest.ts +11 -10
  148. package/packages/film/src/program.ts +5 -5
  149. package/packages/film/src/recipe.ts +2 -1
  150. package/packages/film/src/surface.ts +10 -7
  151. package/packages/film-studio/package.json +1 -1
  152. package/packages/film-studio/src/index.ts +2 -2
  153. package/packages/fishaudio-speech/README.md +33 -0
  154. package/packages/fishaudio-speech/package.json +21 -0
  155. package/packages/fishaudio-speech/src/activation.ts +32 -0
  156. package/packages/fishaudio-speech/src/fragment.ts +11 -0
  157. package/packages/fishaudio-speech/src/index.ts +156 -0
  158. package/packages/fishaudio-speech/src/surface.ts +192 -0
  159. package/packages/generation/README.md +11 -1
  160. package/packages/generation/src/mapping.ts +28 -30
  161. package/packages/gpt-image/README.md +1 -2
  162. package/packages/grok-imagine/README.md +1 -2
  163. package/packages/hyperframes/README.md +7 -3
  164. package/packages/hyperframes/src/browser-program.ts +11 -2
  165. package/packages/hyperframes/src/document.ts +15 -2
  166. package/packages/interview-emoji-reveal/README.md +4 -4
  167. package/packages/interview-emoji-reveal/package.json +1 -1
  168. package/packages/interview-emoji-reveal/src/component.ts +4 -4
  169. package/packages/interview-emoji-reveal/src/fragment.ts +6 -5
  170. package/packages/interview-emoji-reveal/src/manifest.ts +9 -8
  171. package/packages/interview-emoji-reveal/src/program.ts +14 -14
  172. package/packages/interview-emoji-reveal/src/surface.ts +8 -9
  173. package/packages/media-execution/README.md +7 -3
  174. package/packages/media-execution/src/audio-presentation.ts +23 -0
  175. package/packages/media-execution/src/execute.ts +2 -0
  176. package/packages/media-pipeline/README.md +7 -3
  177. package/packages/media-pipeline/src/audio-plan.ts +4 -1
  178. package/packages/media-pipeline/src/component.ts +16 -2
  179. package/packages/media-pipeline/src/manifest.ts +3 -1
  180. package/packages/media-pipeline/src/types.ts +2 -1
  181. package/packages/media-track/README.md +14 -47
  182. package/packages/media-track/package.json +1 -1
  183. package/packages/media-track/preview/preview.svml +5 -5
  184. package/packages/media-track/src/author.ts +12 -6
  185. package/packages/media-track/src/component.ts +10 -16
  186. package/packages/media-track/src/fragment.ts +8 -7
  187. package/packages/media-track/src/index.ts +3 -1
  188. package/packages/media-track/src/lower.ts +7 -7
  189. package/packages/media-track/src/manifest.ts +16 -38
  190. package/packages/media-track/src/program.ts +62 -67
  191. package/packages/media-track/src/sampling.ts +6 -6
  192. package/packages/media-track/src/sequence.ts +8 -8
  193. package/packages/media-track/src/surface.ts +25 -43
  194. package/packages/media-track-studio/README.md +9 -0
  195. package/packages/media-track-studio/src/index.ts +16 -11
  196. package/packages/mimo-speech/README.md +1 -1
  197. package/packages/minimax-h3/README.md +1 -1
  198. package/packages/nano-banana/README.md +1 -1
  199. package/packages/narrative/README.md +23 -1
  200. package/packages/narrative/src/identity.ts +11 -0
  201. package/packages/narrative/src/index.ts +3 -0
  202. package/packages/narrative/src/schema.ts +28 -27
  203. package/packages/narrative/src/selection.ts +52 -0
  204. package/packages/narrative/src/types.ts +2 -0
  205. package/packages/package-loader-node/README.md +30 -0
  206. package/packages/package-loader-node/package.json +5 -2
  207. package/packages/package-loader-node/src/distribution-resolution.ts +10 -3
  208. package/packages/package-loader-node/src/index.ts +1 -0
  209. package/packages/package-loader-node/src/loader.ts +12 -15
  210. package/packages/package-loader-node/src/location.ts +27 -1
  211. package/packages/package-loader-node/src/module-scope.ts +183 -0
  212. package/packages/package-loader-node/src/types.ts +4 -0
  213. package/packages/performance/README.md +83 -0
  214. package/packages/performance/package.json +30 -0
  215. package/packages/performance/src/activation.ts +11 -0
  216. package/packages/performance/src/component.ts +46 -0
  217. package/packages/performance/src/index.ts +6 -0
  218. package/packages/performance/src/manifest.ts +53 -0
  219. package/packages/{media-track/src/performance.ts → performance/src/media.ts} +9 -10
  220. package/packages/performance/src/program.ts +51 -0
  221. package/packages/performance/src/style.ts +35 -0
  222. package/packages/performance/src/surface.ts +99 -0
  223. package/packages/performance-studio/README.md +25 -0
  224. package/packages/performance-studio/package.json +21 -0
  225. package/packages/performance-studio/src/activation.ts +6 -0
  226. package/packages/performance-studio/src/index.ts +84 -0
  227. package/packages/program-space/README.md +9 -18
  228. package/packages/program-space/src/activation.ts +1 -5
  229. package/packages/program-space/src/index.ts +2 -11
  230. package/packages/program-space/src/surface.ts +1 -20
  231. package/packages/project-context-node/README.md +20 -0
  232. package/packages/project-context-node/package.json +10 -0
  233. package/packages/project-context-node/src/index.ts +3 -0
  234. package/packages/{cli → project-context-node}/src/runtime-selection.ts +7 -2
  235. package/packages/provider-hyperframes-local/README.md +43 -15
  236. package/packages/provider-hyperframes-local/src/activation.ts +4 -3
  237. package/packages/provider-hyperframes-local/src/capture-process.ts +26 -4
  238. package/packages/provider-hyperframes-local/src/capture.ts +96 -26
  239. package/packages/provider-hyperframes-local/src/concurrency.ts +86 -0
  240. package/packages/provider-hyperframes-local/src/opaque-capture.ts +92 -0
  241. package/packages/provider-hyperframes-local/src/options.ts +2 -0
  242. package/packages/provider-hyperframes-local/src/program.ts +6 -3
  243. package/packages/provider-hyperframes-local/src/progress.ts +53 -0
  244. package/packages/provider-hyperframes-local/src/provider.ts +18 -5
  245. package/packages/provider-hyperframes-local/src/render.ts +48 -7
  246. package/packages/provider-hypihub/README.md +48 -17
  247. package/packages/provider-hypihub/package.json +2 -0
  248. package/packages/provider-hypihub/src/mapping.ts +31 -7
  249. package/packages/provider-hypihub/src/oauth.ts +6 -0
  250. package/packages/provider-hypihub/src/provider.ts +25 -13
  251. package/packages/provider-hypihub/src/routes.ts +1 -2
  252. package/packages/provider-hypihub/src/upload.ts +2 -0
  253. package/packages/provider-media-local/README.md +3 -4
  254. package/packages/provider-whisperx-local/src/program.ts +1 -0
  255. package/packages/provider-whisperx-local/src/provider.ts +3 -0
  256. package/packages/ranking/README.md +8 -6
  257. package/packages/ranking/package.json +1 -1
  258. package/packages/ranking/src/component.ts +6 -6
  259. package/packages/ranking/src/fragment.ts +11 -7
  260. package/packages/ranking/src/manifest.ts +21 -14
  261. package/packages/ranking/src/render.ts +22 -26
  262. package/packages/ranking/src/schedule.ts +16 -19
  263. package/packages/ranking/src/surface.ts +12 -13
  264. package/packages/ranking-studio/README.md +18 -0
  265. package/packages/ranking-studio/src/index.ts +61 -9
  266. package/packages/render-hyperframes/README.md +7 -7
  267. package/packages/render-hyperframes/package.json +1 -1
  268. package/packages/render-hyperframes/src/component.ts +11 -1
  269. package/packages/render-hyperframes/src/fragment.ts +7 -4
  270. package/packages/render-hyperframes/src/manifest.ts +4 -4
  271. package/packages/render-hyperframes/src/surface.ts +6 -6
  272. package/packages/runtime/README.md +8 -1
  273. package/packages/runtime/src/execution.ts +15 -4
  274. package/packages/runtime/src/index.ts +2 -1
  275. package/packages/runtime/src/log.ts +51 -0
  276. package/packages/runtime/src/types.ts +5 -8
  277. package/packages/runtime-host-node/README.md +30 -0
  278. package/packages/runtime-host-node/package.json +1 -0
  279. package/packages/runtime-host-node/src/index.ts +24 -9
  280. package/packages/runtime-host-node/src/packages.ts +47 -50
  281. package/packages/runtime-kit/README.md +5 -0
  282. package/packages/runtime-kit/src/index.ts +3 -1
  283. package/packages/runtime-local/README.md +109 -4
  284. package/packages/runtime-local/src/config.ts +202 -56
  285. package/packages/runtime-local/src/control.ts +9 -1
  286. package/packages/runtime-local/src/executor.ts +66 -0
  287. package/packages/runtime-local/src/host.ts +37 -17
  288. package/packages/runtime-local/src/log.ts +48 -0
  289. package/packages/runtime-local/src/programs.ts +2 -1
  290. package/packages/runtime-local/src/result-writer.ts +2 -0
  291. package/packages/runtime-local/src/runtime.ts +8 -6
  292. package/packages/runtime-local/src/supervisor.ts +142 -0
  293. package/packages/runtime-local/src/types.ts +9 -2
  294. package/packages/runtime-local/src/worker-process.ts +7 -24
  295. package/packages/runtime-local/src/worker.ts +55 -83
  296. package/packages/screen-overlay/README.md +3 -3
  297. package/packages/screen-overlay/package.json +1 -1
  298. package/packages/screen-overlay/src/component.ts +3 -3
  299. package/packages/screen-overlay/src/fragment.ts +5 -4
  300. package/packages/screen-overlay/src/manifest.ts +7 -6
  301. package/packages/screen-overlay/src/program.ts +9 -9
  302. package/packages/screen-overlay/src/surface.ts +6 -7
  303. package/packages/script/README.md +30 -5
  304. package/packages/script/src/manifest.ts +4 -3
  305. package/packages/script/src/narrative.ts +1 -0
  306. package/packages/script/src/parser.ts +45 -6
  307. package/packages/script/src/surface.ts +4 -3
  308. package/packages/script/src/types.ts +1 -1
  309. package/packages/script-studio/package.json +9 -3
  310. package/packages/script-studio/src/index.ts +2 -0
  311. package/packages/script-studio/src/projection.ts +105 -0
  312. package/packages/seedance/README.md +30 -1
  313. package/packages/seedance/src/index.ts +18 -7
  314. package/packages/seedance/src/surface.ts +17 -4
  315. package/packages/seedance-kits/README.md +7 -1
  316. package/packages/seedream/README.md +1 -1
  317. package/packages/sound/README.md +81 -0
  318. package/packages/sound/package.json +25 -0
  319. package/packages/sound/src/activation.ts +11 -0
  320. package/packages/sound/src/component.ts +28 -0
  321. package/packages/sound/src/index.ts +5 -0
  322. package/packages/sound/src/manifest.ts +47 -0
  323. package/packages/sound/src/program.ts +84 -0
  324. package/packages/sound/src/style.ts +34 -0
  325. package/packages/sound/src/surface.ts +90 -0
  326. package/packages/sound-studio/README.md +22 -0
  327. package/packages/sound-studio/package.json +20 -0
  328. package/packages/sound-studio/src/activation.ts +6 -0
  329. package/packages/sound-studio/src/index.ts +74 -0
  330. package/packages/speech/README.md +2 -2
  331. package/packages/speech-alignment/src/local.ts +8 -0
  332. package/packages/speech-alignment/src/locate.ts +1 -1
  333. package/packages/store-sqlite/README.md +7 -0
  334. package/packages/store-sqlite/src/store.ts +84 -83
  335. package/packages/studio/INSPECTOR.md +15 -2
  336. package/packages/studio/LOCALIZATION.md +98 -0
  337. package/packages/studio/README.md +101 -14
  338. package/packages/studio/locales/en.json +197 -0
  339. package/packages/studio/locales/zh-CN.json +191 -0
  340. package/packages/studio/package.json +8 -7
  341. package/packages/studio/src/companion-assembly.ts +1 -0
  342. package/packages/studio/src/domain.ts +1 -1
  343. package/packages/studio/src/fallback-companions.ts +3 -3
  344. package/packages/studio/src/feedback-server.ts +51 -0
  345. package/packages/studio/src/feedback-store.ts +66 -0
  346. package/packages/studio/src/feedback.ts +60 -0
  347. package/packages/studio/src/localization-node.ts +46 -0
  348. package/packages/studio/src/localization.ts +85 -0
  349. package/packages/studio/src/parameter-values.ts +43 -1
  350. package/packages/studio/src/parameters.ts +114 -24
  351. package/packages/studio/src/preview/render.ts +5 -1
  352. package/packages/studio/src/preview/runtime-shim.ts +49 -3
  353. package/packages/studio/src/programme.ts +14 -30
  354. package/packages/studio/src/server.ts +10 -6
  355. package/packages/studio/src/shared.ts +4 -2
  356. package/packages/studio/src/snapshot.ts +21 -132
  357. package/packages/studio/src/studio-preflight.ts +3 -3
  358. package/packages/studio/src/studio-registry.ts +50 -6
  359. package/packages/studio/src/style.css +223 -50
  360. package/packages/studio/src/ui/artifact-name.ts +8 -6
  361. package/packages/studio/src/ui/artifact-preview.ts +5 -4
  362. package/packages/studio/src/ui/code.ts +17 -16
  363. package/packages/studio/src/ui/comments.ts +234 -0
  364. package/packages/studio/src/ui/dropdown.ts +68 -0
  365. package/packages/studio/src/ui/i18n.ts +109 -0
  366. package/packages/studio/src/ui/icons.ts +6 -0
  367. package/packages/studio/src/ui/library.ts +76 -65
  368. package/packages/studio/src/ui/main.ts +223 -211
  369. package/packages/studio/src/ui/overlay.ts +12 -8
  370. package/packages/studio/src/ui/scrub-preview.ts +110 -0
  371. package/packages/studio/src/ui/stage.ts +93 -29
  372. package/packages/studio/src/ui/timeline.ts +190 -118
  373. package/packages/studio/start.ts +41 -22
  374. package/packages/studio-adapter/README.md +130 -11
  375. package/packages/studio-adapter/src/index.ts +100 -13
  376. package/packages/temporal/README.md +6 -6
  377. package/packages/temporal/package.json +1 -1
  378. package/packages/temporal/src/component.ts +5 -6
  379. package/packages/temporal/src/index.ts +8 -6
  380. package/packages/temporal/src/location.ts +11 -11
  381. package/packages/temporal/src/projection.ts +19 -19
  382. package/packages/temporal/src/rational.ts +4 -4
  383. package/packages/temporal-markup/EDITING.md +2 -2
  384. package/packages/temporal-markup/README.md +27 -11
  385. package/packages/temporal-markup/package.json +1 -1
  386. package/packages/temporal-markup/src/index.ts +20 -25
  387. package/packages/temporal-markup/src/space.ts +10 -41
  388. package/packages/timeline/README.md +31 -0
  389. package/packages/{semantic-track → timeline}/package.json +1 -1
  390. package/packages/timeline/src/activation.ts +8 -0
  391. package/packages/timeline/src/component.ts +24 -0
  392. package/packages/timeline/src/identity.ts +60 -0
  393. package/packages/timeline/src/index.ts +6 -0
  394. package/packages/{semantic-track → timeline}/src/location.ts +20 -20
  395. package/packages/timeline/src/manifest.ts +39 -0
  396. package/packages/{semantic-track → timeline}/src/projection.ts +14 -14
  397. package/packages/timeline/src/types.ts +24 -0
  398. package/packages/timeline-author/README.md +68 -0
  399. package/packages/{speech-track → timeline-author}/package.json +6 -4
  400. package/packages/timeline-author/src/activation.ts +22 -0
  401. package/packages/{speech-track → timeline-author}/src/component.ts +15 -13
  402. package/packages/{speech-track → timeline-author}/src/fragment.ts +14 -23
  403. package/packages/timeline-author/src/index.ts +7 -0
  404. package/packages/timeline-author/src/manifest.ts +78 -0
  405. package/packages/timeline-author/src/program.ts +107 -0
  406. package/packages/timeline-author/src/surface.ts +46 -0
  407. package/packages/timeline-author/src/types.ts +6 -0
  408. package/packages/typography-track/README.md +4 -4
  409. package/packages/typography-track/package.json +1 -1
  410. package/packages/typography-track/src/component.ts +4 -4
  411. package/packages/typography-track/src/fragment.ts +4 -3
  412. package/packages/typography-track/src/manifest.ts +11 -9
  413. package/packages/typography-track/src/program.ts +15 -15
  414. package/packages/typography-track/src/surface.ts +17 -18
  415. package/packages/video-cli/README.md +4 -1
  416. package/packages/video-cli/package.json +2 -1
  417. package/packages/video-cli/src/creation.ts +14 -4
  418. package/packages/video-cli/src/index.ts +1 -1
  419. package/packages/video-cli/src/studio-distribution.ts +2 -1
  420. package/packages/volcengine-matting/README.md +3 -3
  421. package/packages/whisperx/README.md +1 -1
  422. package/services/whisperx/README.md +14 -0
  423. package/services/whisperx/pyproject.toml +2 -0
  424. package/services/whisperx/uv.lock +20 -20
  425. package/packages/provider-hyperframes-aws-lambda/README.md +0 -30
  426. package/packages/provider-hyperframes-aws-lambda/package.json +0 -30
  427. package/packages/provider-hyperframes-aws-lambda/src/activation.ts +0 -97
  428. package/packages/provider-hyperframes-aws-lambda/src/client.ts +0 -114
  429. package/packages/provider-hyperframes-aws-lambda/src/index.ts +0 -21
  430. package/packages/provider-hyperframes-aws-lambda/src/provider.ts +0 -522
  431. package/packages/provider-kie/README.md +0 -134
  432. package/packages/provider-kie/package.json +0 -43
  433. package/packages/provider-kie/src/activation.ts +0 -71
  434. package/packages/provider-kie/src/index.ts +0 -10
  435. package/packages/provider-kie/src/mapping.ts +0 -182
  436. package/packages/provider-kie/src/provider.ts +0 -657
  437. package/packages/provider-kie/src/routes.ts +0 -150
  438. package/packages/provider-xiaomi-mimo/README.md +0 -32
  439. package/packages/provider-xiaomi-mimo/src/activation.ts +0 -57
  440. package/packages/provider-xiaomi-mimo/src/index.ts +0 -5
  441. package/packages/provider-xiaomi-mimo/src/provider.ts +0 -214
  442. package/packages/runtime/src/environment.ts +0 -6
  443. package/packages/semantic-track/README.md +0 -21
  444. package/packages/semantic-track/src/activation.ts +0 -8
  445. package/packages/semantic-track/src/component.ts +0 -24
  446. package/packages/semantic-track/src/identity.ts +0 -58
  447. package/packages/semantic-track/src/index.ts +0 -6
  448. package/packages/semantic-track/src/manifest.ts +0 -38
  449. package/packages/semantic-track/src/types.ts +0 -26
  450. package/packages/speech-track/README.md +0 -51
  451. package/packages/speech-track/src/activation.ts +0 -19
  452. package/packages/speech-track/src/index.ts +0 -7
  453. package/packages/speech-track/src/manifest.ts +0 -73
  454. package/packages/speech-track/src/program.ts +0 -64
  455. package/packages/speech-track/src/surface.ts +0 -35
  456. package/packages/speech-track/src/types.ts +0 -6
  457. package/packages/speech-track-studio/README.md +0 -3
  458. package/packages/speech-track-studio/package.json +0 -14
  459. package/packages/speech-track-studio/src/activation.ts +0 -7
  460. package/packages/speech-track-studio/src/index.ts +0 -40
  461. /package/packages/{cli → project-context-node}/src/project-context.ts +0 -0
  462. /package/packages/{speech-track → timeline-author}/preview/Track.png +0 -0
@@ -9,24 +9,32 @@ content identity or hidden output metadata.
9
9
 
10
10
  Normalized transparent videos displayed by Media or project components use the ordinary video path.
11
11
  The engine decodes them to PNG frames with alpha, then Chrome blends them with lower layers and
12
- the authored Canvas background. PNG is the intermediate capture format; it does not ask to erase
13
- the Film's background. The returned MP4 contains the completed composition.
12
+ the authored Canvas background. Final frames use fast PNG encoding after Chrome has composited
13
+ the image onto an opaque canvas (black beneath the authored background). This trades intermediate
14
+ compression effort for speed without JPEG frame compression. Source alpha, CSS filters, canvas and
15
+ WebGL remain part of the browser composition. The returned MP4 contains the completed composition.
14
16
 
15
17
  Runtime configuration separates work size from shared capacity:
16
18
 
17
19
  - `defaultConcurrency` limits whole render requests admitted by the Endpoint's Runtime capacity resource.
18
20
  - Optional `browserCapacity` limits Chrome slots shared by all render Needs in the same `pool`.
19
- Each Need reserves its actual worker count atomically with the whole-request slot. A request larger
20
- than this budget reports a configuration error; it does not silently reduce the requested workers.
21
+ Each Need reserves its fixed count or automatic ceiling atomically with the whole-request slot.
22
+ An explicit count larger than this budget reports a configuration error. Auto fits its ceiling
23
+ to the shared budget before admission.
21
24
  Both reservations last for the whole Need, including preparation and final encoding. Closing one
22
25
  Chrome early does not release part of that reservation. Without `browserCapacity`, admission uses
23
26
  the whole-request limit alone.
24
27
  - `workers` controls independent Chrome processes inside one admitted render. Explicit counts are
25
- honored, capped only by selected frame count. `auto` uses half the available CPU parallelism,
26
- bounded to 1–4 workers.
28
+ honored, capped only by selected frame count. `auto` starts below its ceiling, then compares useful
29
+ capture batches before adding another browser. Insufficient benefit or falling throughput retires
30
+ surplus browsers between batches. Completed frames are retained; there is no calibration render.
31
+ - Optional `maxWorkers` sets the automatic ceiling. Without it, the Provider leaves two CPU slots
32
+ and half the machine/container memory for other work, budgeting about 1.5 GiB per Chrome process.
33
+ Auto also caps the reservation to the number of roughly one-second batches in the selected range.
27
34
 
28
35
  The Runtime Profile configures these controls through the Provider because they are deployment policy.
29
- `auto` is resolved when the Provider is activated, so admission and execution use the same count. It never
36
+ The automatic ceiling is fixed at activation, and range sizing is identical in admission and execution.
37
+ Actual concurrency adapts inside that reservation; it is not a machine-wide history or performance database. It never
30
38
  renders program audio; `@hypit/provider-media-local` separately prepares `TimelineAudio` and muxes
31
39
  the final media.
32
40
 
@@ -48,11 +56,16 @@ For a Runtime Profile, place `pool` on the Endpoint entry and these Provider set
48
56
  This configuration admits one 4-worker request at a time under its 6-browser budget even though
49
57
  the whole-request limit is 2. Two 4-worker requests would need 8 browser slots. Choose budgets from
50
58
  the actual machine and shared workloads; the example is not an automatic tuning recommendation.
51
- Reload the Runtime Worker after changing its Profile and restart Studio if it uses that Profile.
59
+ New Builds use the edited Provider configuration; active Builds retain their selected configuration.
60
+ Restart Studio if its existing session needs to load the edited Profile.
52
61
 
53
62
  `browserGpu` picks Chrome's rasterizer and defaults to `hardware`. Set `software` without a usable
54
63
  GPU, or `auto` to let the engine decide. Capture uses screenshots and independent browser processes;
55
- the CLI's automatic worker and drawElement policies do not override the count.
64
+ the CLI's automatic worker and drawElement policies do not override the count. The opaque screenshot
65
+ adapter uses the engine's public session, video injector and page seek protocol plus Chrome CDP.
66
+ It waits for seek completion, dynamic images/fonts and the page compositor before capturing PNGs.
67
+ The pinned engine couples its PNG session setup to transparent export, so this adapter initializes
68
+ an opaque session and chooses PNG separately at capture. It does not patch engine methods or files.
56
69
 
57
70
  The Runtime Adapter also declares one managed browser program. `programs up` invokes the pinned
58
71
  HyperFrames CLI's `browser ensure`; its probe resolves and starts that browser and checks
@@ -71,8 +84,12 @@ const visual = await renderHyperframesVisual(
71
84
 
72
85
  Omit `range` for the complete document. Ranges are zero-based and half-open on the original
73
86
  programme clock. At 30 fps, `[240, 360)` returns 120 frames covering seconds 8–12.
74
- `onProgress` reports preparation, worker ranges, browser PIDs, completion and elapsed time to direct
75
- executor callers. The Runtime Provider does not currently forward these events as Build progress.
87
+ `onProgress` reports resource staging, source decoding, the requested range, per-worker frame counts, browser PIDs,
88
+ encoding, storage and elapsed time to direct executor callers. The Provider reduces parallel worker
89
+ events to a single phase and aggregate count through `context.reportProgress`. CLI status and follow
90
+ display that activity without `--verbose`. Counter writes are coalesced to about once per second;
91
+ phase changes and completed counts are reported promptly. Local activity belongs to the running
92
+ Command, not a remote Operation or a repeatedly rewritten Result manifest.
76
93
  `processTimeoutMs` defaults to 30 minutes and starts one deadline before resource preparation. It
77
94
  covers resource reads, Surface validation, rendering and output storage. `signal` can end the same
78
95
  execution earlier. ResourceStore I/O and Surface probes receive the cancellation signal; a custom
@@ -95,10 +112,13 @@ cleanup. A completed worker closes its Chrome immediately.
95
112
  One call stages the HTML and every declared asset once. Typed Surface validation includes a complete
96
113
  decoded-frame count, even for a short render interval. The renderer then finds source-frame windows
97
114
  needed by that interval, merges overlapping windows, and extracts them one source/window at a time.
98
- Decoded PNGs are shared by all workers in this call. Each worker initializes its own page and captures
99
- its assigned frames at their original absolute times. Output numbering starts at zero; final H.264
100
- encoding runs once after all workers finish. More workers parallelize capture, while staging,
101
- validation, source extraction and final encoding still contribute their own cost.
115
+ Decoded PNGs are shared by all workers in this call. Each worker initializes its own page, then takes
116
+ short contiguous frame batches from the render's in-memory queue. A free worker can continue with
117
+ another batch instead of waiting for a worker assigned a more expensive passage. Batches span at
118
+ most about one second and preserve the original absolute frame times. Output numbering starts at
119
+ zero; final H.264 encoding runs once after all workers finish. This division is internal to one
120
+ render call; it does not create or resume Builds. Extra workers help only while aggregate throughput
121
+ improves. Staging, validation, source extraction and final encoding still contribute their own cost.
102
122
 
103
123
  Each call has its own temporary directory, local server port and Chrome processes. Separate renders
104
124
  do not share staged files or decoded PNGs. Exact compiler sampling markers retain loops, holds and
@@ -127,3 +147,11 @@ Direct `renderHyperframesVisual()` calls do not enter Runtime's shared admission
127
147
 
128
148
  The current package executes trusted official code in a local process. It is not a sandbox for
129
149
  untrusted documents or community renderer implementations.
150
+
151
+ Provider calls forward renderer diagnostics through `context.reportDiagnostic`. The initial record
152
+ states the frame count, worker policy/ceiling, capture path, quality, GPU mode and selected encoder.
153
+ Phase timings, concurrency changes and per-worker seek/preparation/PNG totals are recorded once
154
+ per phase, decision or finished worker. Disposable capture-process
155
+ stdout/stderr are streamed as diagnostics and drained before the call returns; existing output limits
156
+ still apply. Phase changes cover preparation, decoding, browser startup, capture, encoding and storage.
157
+ `hypit logs <build-id>` retains those records after temporary rendering files have been removed.
@@ -21,7 +21,7 @@ const localHyperframesRuntimeAdapter = createRuntimeEndpointAdapterFacet({
21
21
  if (context.pool === undefined) throw new Error("local HyperFrames Provider Pool is required");
22
22
  const config = runtimeConfigObject(context.config, "local HyperFrames");
23
23
  runtimeConfigExact(config, [
24
- "nodePath", "hyperframesCliPath", "ffprobePath", "ffmpegPath", "workers", "quality", "browserGpu",
24
+ "nodePath", "hyperframesCliPath", "ffprobePath", "ffmpegPath", "workers", "maxWorkers", "quality", "browserGpu",
25
25
  "defaultConcurrency", "browserCapacity", "initializationTimeoutMs", "frameTimeoutMs", "processTimeoutMs", "maxProcessOutputBytes", "maxRenderedBytes",
26
26
  ], "local HyperFrames");
27
27
  runtimeConfigString(config.nodePath, "HyperFrames nodePath");
@@ -43,11 +43,12 @@ const localHyperframesRuntimeAdapter = createRuntimeEndpointAdapterFacet({
43
43
  const configuredCli = runtimeConfigString(config.hyperframesCliPath, "HyperFrames hyperframesCliPath");
44
44
  const configuredFfprobe = runtimeConfigString(config.ffprobePath, "HyperFrames ffprobePath");
45
45
  const nodePath = resolveRuntimeExecutable(context.dataRoot, configuredNode ?? process.execPath);
46
- const hyperframesCliPath = resolveRuntimeExecutable(context.dataRoot, configuredCli ?? defaultHyperframesCliPath());
46
+ const hyperframesCliPath = () => resolveRuntimeExecutable(context.dataRoot, configuredCli ?? defaultHyperframesCliPath());
47
47
  const configuredFfmpeg = runtimeConfigString(config.ffmpegPath, "HyperFrames ffmpegPath");
48
48
  const ffmpegPath = resolveRuntimeExecutable(context.dataRoot, configuredFfmpeg ?? "ffmpeg");
49
49
  const ffprobePath = resolveRuntimeExecutable(context.dataRoot, configuredFfprobe ?? "ffprobe");
50
50
  const defaultConcurrency = runtimeConfigPositiveInteger(config.defaultConcurrency, "HyperFrames defaultConcurrency");
51
+ const maxWorkers = runtimeConfigPositiveInteger(config.maxWorkers, "HyperFrames maxWorkers");
51
52
  const browserCapacity = runtimeConfigPositiveInteger(config.browserCapacity, "HyperFrames browserCapacity");
52
53
  const initializationTimeoutMs = runtimeConfigPositiveInteger(config.initializationTimeoutMs, "HyperFrames initializationTimeoutMs");
53
54
  const frameTimeoutMs = runtimeConfigPositiveInteger(config.frameTimeoutMs, "HyperFrames frameTimeoutMs");
@@ -59,10 +60,10 @@ const localHyperframesRuntimeAdapter = createRuntimeEndpointAdapterFacet({
59
60
  instance: context.instance,
60
61
  pool: context.pool,
61
62
  nodePath,
62
- hyperframesCliPath,
63
63
  ffprobePath,
64
64
  ffmpegPath,
65
65
  ...(workers === undefined ? {} : { workers: workers as HyperframesWorkers }),
66
+ ...(maxWorkers === undefined ? {} : { maxWorkers }),
66
67
  ...(quality === undefined ? {} : { quality: quality as HyperframesQuality }),
67
68
  ...(browserGpu === undefined ? {} : { browserGpu: browserGpu as HyperframesBrowserGpu }),
68
69
  ...(defaultConcurrency === undefined ? {} : { defaultConcurrency }),
@@ -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();