@hypit/hypit 0.1.4 → 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 (449) 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 +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 +32 -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 +28 -17
  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 +11 -0
  63. package/packages/build-result/src/decode.ts +2 -0
  64. package/packages/build-result/src/execution-log.ts +17 -0
  65. package/packages/build-result/src/index.ts +1 -0
  66. package/packages/build-result/src/store.ts +14 -1
  67. package/packages/build-result/src/types.ts +3 -0
  68. package/packages/build-result-s3/src/repository.ts +4 -0
  69. package/packages/caption/README.md +61 -73
  70. package/packages/caption/package.json +3 -2
  71. package/packages/caption/src/activation.ts +2 -2
  72. package/packages/caption/src/component.ts +20 -5
  73. package/packages/caption/src/display.ts +3 -18
  74. package/packages/caption/src/fragment.ts +3 -7
  75. package/packages/caption/src/index.ts +6 -5
  76. package/packages/caption/src/manifest.ts +29 -46
  77. package/packages/caption/src/style.ts +24 -135
  78. package/packages/caption/src/surface.ts +12 -92
  79. package/packages/caption/src/temporalize.ts +18 -53
  80. package/packages/caption/src/types.ts +8 -35
  81. package/packages/caption/src/visibility.ts +23 -0
  82. package/packages/caption-fine/README.md +14 -9
  83. package/packages/caption-fine/package.json +4 -2
  84. package/packages/caption-fine/src/component.ts +3 -3
  85. package/packages/caption-fine/src/fragment.ts +7 -19
  86. package/packages/caption-fine/src/index.ts +2 -0
  87. package/packages/caption-fine/src/manifest.ts +26 -17
  88. package/packages/caption-fine/src/recipe.ts +7 -0
  89. package/packages/caption-fine/src/render.ts +14 -13
  90. package/packages/caption-fine/src/schedule.ts +39 -35
  91. package/packages/caption-fine/src/style.ts +10 -9
  92. package/packages/caption-fine/src/surface.ts +62 -35
  93. package/packages/caption-fine/src/types.ts +2 -0
  94. package/packages/caption-fine-studio/README.md +24 -2
  95. package/packages/caption-fine-studio/package.json +3 -1
  96. package/packages/caption-fine-studio/src/activation.ts +3 -3
  97. package/packages/caption-fine-studio/src/index.ts +54 -42
  98. package/packages/cli/README.md +57 -4
  99. package/packages/cli/package.json +1 -0
  100. package/packages/cli/src/arguments.ts +25 -6
  101. package/packages/cli/src/build-planning.ts +3 -2
  102. package/packages/cli/src/command.ts +6 -0
  103. package/packages/cli/src/commands/environment.ts +28 -18
  104. package/packages/cli/src/commands/execution.ts +81 -26
  105. package/packages/cli/src/commands/results.ts +7 -11
  106. package/packages/cli/src/index.ts +2 -2
  107. package/packages/cli/src/machine-view.ts +3 -2
  108. package/packages/cli/src/main.ts +34 -42
  109. package/packages/cli/src/observation.ts +7 -1
  110. package/packages/cli/src/output.ts +65 -23
  111. package/packages/cli/src/view.ts +64 -18
  112. package/packages/comment-sticker/README.md +3 -3
  113. package/packages/comment-sticker/package.json +1 -1
  114. package/packages/comment-sticker/src/component.ts +4 -4
  115. package/packages/comment-sticker/src/fragment.ts +5 -4
  116. package/packages/comment-sticker/src/manifest.ts +7 -6
  117. package/packages/comment-sticker/src/program.ts +9 -9
  118. package/packages/comment-sticker/src/surface.ts +6 -7
  119. package/packages/composition/README.md +21 -0
  120. package/packages/composition/src/audio-presentation.ts +44 -0
  121. package/packages/composition/src/index.ts +3 -0
  122. package/packages/composition/src/schema.ts +8 -3
  123. package/packages/composition/src/track.ts +15 -1
  124. package/packages/credential-store-env/README.md +1 -1
  125. package/packages/deck-track/README.md +3 -3
  126. package/packages/deck-track/package.json +1 -1
  127. package/packages/deck-track/src/component.ts +5 -5
  128. package/packages/deck-track/src/fragment.ts +6 -5
  129. package/packages/deck-track/src/lower.ts +12 -12
  130. package/packages/deck-track/src/manifest.ts +8 -7
  131. package/packages/deck-track/src/program.ts +13 -16
  132. package/packages/deck-track/src/surface.ts +9 -10
  133. package/packages/driver-node/src/driver.ts +41 -3
  134. package/packages/elevenlabs-speech/README.md +27 -0
  135. package/packages/{provider-xiaomi-mimo → elevenlabs-speech}/package.json +5 -8
  136. package/packages/elevenlabs-speech/src/activation.ts +26 -0
  137. package/packages/elevenlabs-speech/src/fragment.ts +11 -0
  138. package/packages/elevenlabs-speech/src/index.ts +111 -0
  139. package/packages/elevenlabs-speech/src/surface.ts +167 -0
  140. package/packages/endpoint-kit/README.md +24 -0
  141. package/packages/endpoint-kit/src/index.ts +7 -1
  142. package/packages/film/README.md +12 -7
  143. package/packages/film/package.json +1 -1
  144. package/packages/film/src/component.ts +4 -4
  145. package/packages/film/src/fragment.ts +6 -5
  146. package/packages/film/src/manifest.ts +11 -10
  147. package/packages/film/src/program.ts +5 -5
  148. package/packages/film/src/recipe.ts +2 -1
  149. package/packages/film/src/surface.ts +6 -6
  150. package/packages/film-studio/package.json +1 -1
  151. package/packages/film-studio/src/index.ts +2 -2
  152. package/packages/fishaudio-speech/README.md +33 -0
  153. package/packages/fishaudio-speech/package.json +21 -0
  154. package/packages/fishaudio-speech/src/activation.ts +32 -0
  155. package/packages/fishaudio-speech/src/fragment.ts +11 -0
  156. package/packages/fishaudio-speech/src/index.ts +156 -0
  157. package/packages/fishaudio-speech/src/surface.ts +192 -0
  158. package/packages/generation/README.md +11 -1
  159. package/packages/generation/src/mapping.ts +28 -30
  160. package/packages/gpt-image/README.md +1 -2
  161. package/packages/grok-imagine/README.md +1 -2
  162. package/packages/hyperframes/README.md +7 -3
  163. package/packages/hyperframes/src/browser-program.ts +11 -2
  164. package/packages/hyperframes/src/document.ts +15 -2
  165. package/packages/interview-emoji-reveal/README.md +4 -4
  166. package/packages/interview-emoji-reveal/package.json +1 -1
  167. package/packages/interview-emoji-reveal/src/component.ts +4 -4
  168. package/packages/interview-emoji-reveal/src/fragment.ts +6 -5
  169. package/packages/interview-emoji-reveal/src/manifest.ts +9 -8
  170. package/packages/interview-emoji-reveal/src/program.ts +14 -14
  171. package/packages/interview-emoji-reveal/src/surface.ts +8 -9
  172. package/packages/media-execution/README.md +7 -3
  173. package/packages/media-execution/src/audio-presentation.ts +23 -0
  174. package/packages/media-execution/src/execute.ts +2 -0
  175. package/packages/media-pipeline/README.md +7 -3
  176. package/packages/media-pipeline/src/audio-plan.ts +4 -1
  177. package/packages/media-pipeline/src/component.ts +16 -2
  178. package/packages/media-pipeline/src/manifest.ts +3 -1
  179. package/packages/media-pipeline/src/types.ts +2 -1
  180. package/packages/media-track/README.md +14 -47
  181. package/packages/media-track/package.json +1 -1
  182. package/packages/media-track/preview/preview.svml +5 -5
  183. package/packages/media-track/src/author.ts +12 -6
  184. package/packages/media-track/src/component.ts +10 -16
  185. package/packages/media-track/src/fragment.ts +8 -7
  186. package/packages/media-track/src/index.ts +3 -1
  187. package/packages/media-track/src/lower.ts +7 -7
  188. package/packages/media-track/src/manifest.ts +16 -38
  189. package/packages/media-track/src/program.ts +62 -67
  190. package/packages/media-track/src/sampling.ts +6 -6
  191. package/packages/media-track/src/sequence.ts +8 -8
  192. package/packages/media-track/src/surface.ts +25 -43
  193. package/packages/media-track-studio/README.md +9 -0
  194. package/packages/media-track-studio/src/index.ts +16 -11
  195. package/packages/mimo-speech/README.md +1 -1
  196. package/packages/minimax-h3/README.md +1 -1
  197. package/packages/nano-banana/README.md +1 -1
  198. package/packages/narrative/README.md +23 -1
  199. package/packages/narrative/src/identity.ts +11 -0
  200. package/packages/narrative/src/index.ts +3 -0
  201. package/packages/narrative/src/schema.ts +28 -27
  202. package/packages/narrative/src/selection.ts +52 -0
  203. package/packages/narrative/src/types.ts +2 -0
  204. package/packages/package-loader-node/README.md +30 -0
  205. package/packages/package-loader-node/package.json +5 -2
  206. package/packages/package-loader-node/src/distribution-resolution.ts +10 -3
  207. package/packages/package-loader-node/src/index.ts +1 -0
  208. package/packages/package-loader-node/src/loader.ts +12 -15
  209. package/packages/package-loader-node/src/location.ts +27 -1
  210. package/packages/package-loader-node/src/module-scope.ts +183 -0
  211. package/packages/package-loader-node/src/types.ts +4 -0
  212. package/packages/performance/README.md +83 -0
  213. package/packages/performance/package.json +30 -0
  214. package/packages/performance/src/activation.ts +11 -0
  215. package/packages/performance/src/component.ts +46 -0
  216. package/packages/performance/src/index.ts +6 -0
  217. package/packages/performance/src/manifest.ts +53 -0
  218. package/packages/{media-track/src/performance.ts → performance/src/media.ts} +9 -10
  219. package/packages/performance/src/program.ts +51 -0
  220. package/packages/performance/src/style.ts +35 -0
  221. package/packages/performance/src/surface.ts +99 -0
  222. package/packages/performance-studio/README.md +25 -0
  223. package/packages/performance-studio/package.json +21 -0
  224. package/packages/performance-studio/src/activation.ts +6 -0
  225. package/packages/performance-studio/src/index.ts +84 -0
  226. package/packages/program-space/README.md +9 -18
  227. package/packages/program-space/src/activation.ts +1 -5
  228. package/packages/program-space/src/index.ts +2 -11
  229. package/packages/program-space/src/surface.ts +1 -20
  230. package/packages/project-context-node/README.md +20 -0
  231. package/packages/project-context-node/package.json +10 -0
  232. package/packages/project-context-node/src/index.ts +3 -0
  233. package/packages/{cli → project-context-node}/src/runtime-selection.ts +7 -2
  234. package/packages/provider-hyperframes-local/README.md +43 -15
  235. package/packages/provider-hyperframes-local/src/activation.ts +4 -3
  236. package/packages/provider-hyperframes-local/src/capture-process.ts +26 -4
  237. package/packages/provider-hyperframes-local/src/capture.ts +96 -26
  238. package/packages/provider-hyperframes-local/src/concurrency.ts +86 -0
  239. package/packages/provider-hyperframes-local/src/opaque-capture.ts +92 -0
  240. package/packages/provider-hyperframes-local/src/options.ts +2 -0
  241. package/packages/provider-hyperframes-local/src/program.ts +6 -3
  242. package/packages/provider-hyperframes-local/src/progress.ts +53 -0
  243. package/packages/provider-hyperframes-local/src/provider.ts +18 -5
  244. package/packages/provider-hyperframes-local/src/render.ts +48 -7
  245. package/packages/provider-hypihub/README.md +48 -17
  246. package/packages/provider-hypihub/package.json +2 -0
  247. package/packages/provider-hypihub/src/mapping.ts +31 -7
  248. package/packages/provider-hypihub/src/oauth.ts +6 -0
  249. package/packages/provider-hypihub/src/provider.ts +25 -13
  250. package/packages/provider-hypihub/src/routes.ts +1 -2
  251. package/packages/provider-hypihub/src/upload.ts +2 -0
  252. package/packages/provider-media-local/README.md +3 -4
  253. package/packages/provider-whisperx-local/src/program.ts +1 -0
  254. package/packages/provider-whisperx-local/src/provider.ts +3 -0
  255. package/packages/ranking/README.md +8 -6
  256. package/packages/ranking/package.json +1 -1
  257. package/packages/ranking/src/component.ts +6 -6
  258. package/packages/ranking/src/fragment.ts +11 -7
  259. package/packages/ranking/src/manifest.ts +21 -14
  260. package/packages/ranking/src/render.ts +22 -26
  261. package/packages/ranking/src/schedule.ts +16 -19
  262. package/packages/ranking/src/surface.ts +12 -13
  263. package/packages/ranking-studio/README.md +18 -0
  264. package/packages/ranking-studio/src/index.ts +61 -9
  265. package/packages/render-hyperframes/README.md +7 -7
  266. package/packages/render-hyperframes/package.json +1 -1
  267. package/packages/render-hyperframes/src/component.ts +11 -1
  268. package/packages/render-hyperframes/src/fragment.ts +7 -4
  269. package/packages/render-hyperframes/src/manifest.ts +4 -4
  270. package/packages/render-hyperframes/src/surface.ts +6 -6
  271. package/packages/runtime/README.md +8 -1
  272. package/packages/runtime/src/execution.ts +15 -4
  273. package/packages/runtime/src/index.ts +2 -1
  274. package/packages/runtime/src/log.ts +51 -0
  275. package/packages/runtime/src/types.ts +5 -8
  276. package/packages/runtime-host-node/README.md +30 -0
  277. package/packages/runtime-host-node/package.json +1 -0
  278. package/packages/runtime-host-node/src/index.ts +24 -9
  279. package/packages/runtime-host-node/src/packages.ts +47 -50
  280. package/packages/runtime-kit/README.md +5 -0
  281. package/packages/runtime-kit/src/index.ts +3 -1
  282. package/packages/runtime-local/README.md +92 -4
  283. package/packages/runtime-local/src/config.ts +202 -56
  284. package/packages/runtime-local/src/control.ts +9 -1
  285. package/packages/runtime-local/src/executor.ts +66 -0
  286. package/packages/runtime-local/src/host.ts +37 -17
  287. package/packages/runtime-local/src/log.ts +48 -0
  288. package/packages/runtime-local/src/programs.ts +2 -1
  289. package/packages/runtime-local/src/result-writer.ts +2 -0
  290. package/packages/runtime-local/src/runtime.ts +8 -6
  291. package/packages/runtime-local/src/supervisor.ts +142 -0
  292. package/packages/runtime-local/src/types.ts +9 -2
  293. package/packages/runtime-local/src/worker-process.ts +7 -24
  294. package/packages/runtime-local/src/worker.ts +55 -83
  295. package/packages/screen-overlay/README.md +3 -3
  296. package/packages/screen-overlay/package.json +1 -1
  297. package/packages/screen-overlay/src/component.ts +3 -3
  298. package/packages/screen-overlay/src/fragment.ts +5 -4
  299. package/packages/screen-overlay/src/manifest.ts +7 -6
  300. package/packages/screen-overlay/src/program.ts +9 -9
  301. package/packages/screen-overlay/src/surface.ts +6 -7
  302. package/packages/script/README.md +15 -2
  303. package/packages/script/src/manifest.ts +2 -2
  304. package/packages/script/src/narrative.ts +1 -0
  305. package/packages/script/src/surface.ts +4 -3
  306. package/packages/script/src/types.ts +1 -1
  307. package/packages/script-studio/package.json +9 -3
  308. package/packages/script-studio/src/index.ts +2 -0
  309. package/packages/script-studio/src/projection.ts +105 -0
  310. package/packages/seedance/README.md +30 -1
  311. package/packages/seedance/src/index.ts +18 -7
  312. package/packages/seedance/src/surface.ts +17 -4
  313. package/packages/seedance-kits/README.md +7 -1
  314. package/packages/seedream/README.md +1 -1
  315. package/packages/sound/README.md +81 -0
  316. package/packages/sound/package.json +25 -0
  317. package/packages/sound/src/activation.ts +11 -0
  318. package/packages/sound/src/component.ts +28 -0
  319. package/packages/sound/src/index.ts +5 -0
  320. package/packages/sound/src/manifest.ts +47 -0
  321. package/packages/sound/src/program.ts +84 -0
  322. package/packages/sound/src/style.ts +34 -0
  323. package/packages/sound/src/surface.ts +90 -0
  324. package/packages/sound-studio/README.md +22 -0
  325. package/packages/sound-studio/package.json +20 -0
  326. package/packages/sound-studio/src/activation.ts +6 -0
  327. package/packages/sound-studio/src/index.ts +74 -0
  328. package/packages/speech/README.md +2 -2
  329. package/packages/speech-alignment/src/local.ts +8 -0
  330. package/packages/speech-alignment/src/locate.ts +1 -1
  331. package/packages/store-sqlite/README.md +7 -0
  332. package/packages/store-sqlite/src/store.ts +84 -83
  333. package/packages/studio/INSPECTOR.md +15 -2
  334. package/packages/studio/README.md +88 -14
  335. package/packages/studio/package.json +8 -7
  336. package/packages/studio/src/companion-assembly.ts +1 -0
  337. package/packages/studio/src/domain.ts +1 -1
  338. package/packages/studio/src/fallback-companions.ts +3 -3
  339. package/packages/studio/src/feedback-server.ts +51 -0
  340. package/packages/studio/src/feedback-store.ts +66 -0
  341. package/packages/studio/src/feedback.ts +60 -0
  342. package/packages/studio/src/parameter-values.ts +43 -1
  343. package/packages/studio/src/parameters.ts +114 -24
  344. package/packages/studio/src/preview/render.ts +5 -1
  345. package/packages/studio/src/preview/runtime-shim.ts +49 -3
  346. package/packages/studio/src/programme.ts +14 -30
  347. package/packages/studio/src/server.ts +10 -6
  348. package/packages/studio/src/shared.ts +4 -2
  349. package/packages/studio/src/snapshot.ts +21 -132
  350. package/packages/studio/src/studio-preflight.ts +3 -3
  351. package/packages/studio/src/studio-registry.ts +50 -6
  352. package/packages/studio/src/style.css +209 -48
  353. package/packages/studio/src/ui/comments.ts +233 -0
  354. package/packages/studio/src/ui/icons.ts +5 -0
  355. package/packages/studio/src/ui/main.ts +145 -86
  356. package/packages/studio/src/ui/overlay.ts +12 -8
  357. package/packages/studio/src/ui/scrub-preview.ts +108 -0
  358. package/packages/studio/src/ui/stage.ts +67 -4
  359. package/packages/studio/src/ui/timeline.ts +177 -108
  360. package/packages/studio/start.ts +4 -2
  361. package/packages/studio-adapter/README.md +130 -11
  362. package/packages/studio-adapter/src/index.ts +100 -13
  363. package/packages/temporal/README.md +6 -6
  364. package/packages/temporal/package.json +1 -1
  365. package/packages/temporal/src/component.ts +5 -6
  366. package/packages/temporal/src/index.ts +8 -6
  367. package/packages/temporal/src/location.ts +11 -11
  368. package/packages/temporal/src/projection.ts +19 -19
  369. package/packages/temporal/src/rational.ts +4 -4
  370. package/packages/temporal-markup/EDITING.md +2 -2
  371. package/packages/temporal-markup/README.md +27 -11
  372. package/packages/temporal-markup/package.json +1 -1
  373. package/packages/temporal-markup/src/index.ts +20 -25
  374. package/packages/temporal-markup/src/space.ts +10 -41
  375. package/packages/timeline/README.md +31 -0
  376. package/packages/{semantic-track → timeline}/package.json +1 -1
  377. package/packages/timeline/src/activation.ts +8 -0
  378. package/packages/timeline/src/component.ts +24 -0
  379. package/packages/timeline/src/identity.ts +60 -0
  380. package/packages/timeline/src/index.ts +6 -0
  381. package/packages/{semantic-track → timeline}/src/location.ts +20 -20
  382. package/packages/timeline/src/manifest.ts +39 -0
  383. package/packages/{semantic-track → timeline}/src/projection.ts +14 -14
  384. package/packages/timeline/src/types.ts +24 -0
  385. package/packages/timeline-author/README.md +68 -0
  386. package/packages/{speech-track → timeline-author}/package.json +6 -4
  387. package/packages/timeline-author/src/activation.ts +22 -0
  388. package/packages/{speech-track → timeline-author}/src/component.ts +15 -13
  389. package/packages/{speech-track → timeline-author}/src/fragment.ts +14 -23
  390. package/packages/timeline-author/src/index.ts +7 -0
  391. package/packages/timeline-author/src/manifest.ts +78 -0
  392. package/packages/timeline-author/src/program.ts +107 -0
  393. package/packages/timeline-author/src/surface.ts +46 -0
  394. package/packages/timeline-author/src/types.ts +6 -0
  395. package/packages/typography-track/README.md +4 -4
  396. package/packages/typography-track/package.json +1 -1
  397. package/packages/typography-track/src/component.ts +4 -4
  398. package/packages/typography-track/src/fragment.ts +4 -3
  399. package/packages/typography-track/src/manifest.ts +11 -9
  400. package/packages/typography-track/src/program.ts +15 -15
  401. package/packages/typography-track/src/surface.ts +17 -18
  402. package/packages/video-cli/README.md +4 -1
  403. package/packages/video-cli/package.json +2 -1
  404. package/packages/video-cli/src/creation.ts +14 -4
  405. package/packages/video-cli/src/index.ts +1 -1
  406. package/packages/video-cli/src/studio-distribution.ts +2 -1
  407. package/packages/volcengine-matting/README.md +3 -3
  408. package/packages/whisperx/README.md +1 -1
  409. package/services/whisperx/README.md +14 -0
  410. package/services/whisperx/pyproject.toml +2 -0
  411. package/services/whisperx/uv.lock +20 -20
  412. package/packages/provider-hyperframes-aws-lambda/README.md +0 -30
  413. package/packages/provider-hyperframes-aws-lambda/package.json +0 -30
  414. package/packages/provider-hyperframes-aws-lambda/src/activation.ts +0 -97
  415. package/packages/provider-hyperframes-aws-lambda/src/client.ts +0 -114
  416. package/packages/provider-hyperframes-aws-lambda/src/index.ts +0 -21
  417. package/packages/provider-hyperframes-aws-lambda/src/provider.ts +0 -522
  418. package/packages/provider-kie/README.md +0 -134
  419. package/packages/provider-kie/package.json +0 -43
  420. package/packages/provider-kie/src/activation.ts +0 -71
  421. package/packages/provider-kie/src/index.ts +0 -10
  422. package/packages/provider-kie/src/mapping.ts +0 -182
  423. package/packages/provider-kie/src/provider.ts +0 -657
  424. package/packages/provider-kie/src/routes.ts +0 -150
  425. package/packages/provider-xiaomi-mimo/README.md +0 -32
  426. package/packages/provider-xiaomi-mimo/src/activation.ts +0 -57
  427. package/packages/provider-xiaomi-mimo/src/index.ts +0 -5
  428. package/packages/provider-xiaomi-mimo/src/provider.ts +0 -214
  429. package/packages/runtime/src/environment.ts +0 -6
  430. package/packages/semantic-track/README.md +0 -21
  431. package/packages/semantic-track/src/activation.ts +0 -8
  432. package/packages/semantic-track/src/component.ts +0 -24
  433. package/packages/semantic-track/src/identity.ts +0 -58
  434. package/packages/semantic-track/src/index.ts +0 -6
  435. package/packages/semantic-track/src/manifest.ts +0 -38
  436. package/packages/semantic-track/src/types.ts +0 -26
  437. package/packages/speech-track/README.md +0 -51
  438. package/packages/speech-track/src/activation.ts +0 -19
  439. package/packages/speech-track/src/index.ts +0 -7
  440. package/packages/speech-track/src/manifest.ts +0 -73
  441. package/packages/speech-track/src/program.ts +0 -64
  442. package/packages/speech-track/src/surface.ts +0 -35
  443. package/packages/speech-track/src/types.ts +0 -6
  444. package/packages/speech-track-studio/README.md +0 -3
  445. package/packages/speech-track-studio/package.json +0 -14
  446. package/packages/speech-track-studio/src/activation.ts +0 -7
  447. package/packages/speech-track-studio/src/index.ts +0 -40
  448. /package/packages/{cli → project-context-node}/src/project-context.ts +0 -0
  449. /package/packages/{speech-track → timeline-author}/preview/Track.png +0 -0
@@ -2,12 +2,18 @@ import { createArtifactPreview } from "./artifact-preview.js";
2
2
  import type { StudioArtifactView, StudioSnapshot } from "../shared.js";
3
3
  import { createOverlay } from "./overlay.js";
4
4
  import { icon, setIcon } from "./icons.js";
5
+ import { createScrubPreview } from "./scrub-preview.js";
5
6
  import type { State, Store } from "./selection.js";
6
7
 
7
8
  export type Stage = {
8
9
  readonly element: HTMLElement;
9
10
  /** Start or stop playback, however the request arrived. */
10
11
  toggle(): void;
12
+ pause(): void;
13
+ /** Change picture interaction while preserving Studio's selection. */
14
+ setReviewMode(enabled: boolean): void;
15
+ /** Return from a Result artifact to the composition player. */
16
+ showComposition(): void;
11
17
  openArtifact(artifact: StudioArtifactView): void;
12
18
  renameArtifact(artifact: StudioArtifactView): void;
13
19
  };
@@ -33,7 +39,9 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
33
39
  <iframe title="Preview" sandbox="allow-scripts allow-same-origin" allow="autoplay"></iframe>
34
40
  </div>
35
41
  </div>
36
- <input class="stage-scrubber" type="range" min="0" max="0" value="0" step="0.01" aria-label="Media time" hidden>
42
+ <div class="stage-progress" hidden>
43
+ <input class="stage-scrubber" type="range" min="0" max="0" value="0" step="0.01" aria-label="Media time" hidden>
44
+ </div>
37
45
  <div class="stage-bar">
38
46
  <span class="stage-time" hidden></span>
39
47
  <div class="stage-transport">
@@ -55,6 +63,7 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
55
63
  const viewport = element.querySelector<HTMLElement>(".stage-viewport")!;
56
64
  const scaler = element.querySelector<HTMLElement>(".stage-scaler")!;
57
65
  const iframe = element.querySelector<HTMLIFrameElement>("iframe")!;
66
+ let reviewMode = false;
58
67
  /**
59
68
  * Measure a clip in the rendered picture.
60
69
  *
@@ -99,13 +108,45 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
99
108
  // Clicking the picture selects whatever is drawn under the pointer, which is
100
109
  // the third way into the same selection.
101
110
  scaler.addEventListener("click", (event) => {
111
+ if (reviewMode) { toggle(); return; }
102
112
  const clip = overlay.hitTest(event.clientX, event.clientY);
103
113
  if (clip !== undefined) store.select(clip.id, "video");
104
114
  else store.clearSelection();
105
115
  });
116
+ // A broad component frame can cover smaller elements. Offer all actual
117
+ // Companion hits without special knowledge of captions or any other family.
118
+ let selectionMenu: HTMLElement | undefined;
119
+ const closeSelectionMenu = (): void => { selectionMenu?.remove(); selectionMenu = undefined; };
120
+ scaler.addEventListener("contextmenu", (event) => {
121
+ if (reviewMode) { event.preventDefault(); return; }
122
+ const hits = overlay.hitsAt(event.clientX, event.clientY);
123
+ if (hits.length === 0) return;
124
+ event.preventDefault(); stop(); closeSelectionMenu();
125
+ const menu = document.createElement("div");
126
+ menu.className = "selection-menu";
127
+ menu.setAttribute("role", "menu"); menu.setAttribute("aria-label", "Elements at this point");
128
+ for (const hit of hits) {
129
+ const option = document.createElement("button"); option.type = "button";
130
+ option.setAttribute("role", "menuitem"); option.textContent = hit.display.title;
131
+ option.title = hit.id;
132
+ option.addEventListener("click", () => { store.select(hit.id, "video"); closeSelectionMenu(); });
133
+ menu.append(option);
134
+ }
135
+ element.append(menu);
136
+ menu.style.left = `${Math.max(0, Math.min(event.clientX, window.innerWidth - menu.offsetWidth - 8))}px`;
137
+ menu.style.top = `${Math.max(0, Math.min(event.clientY, window.innerHeight - menu.offsetHeight - 8))}px`;
138
+ selectionMenu = menu; menu.querySelector("button")?.focus();
139
+ });
140
+ document.addEventListener("pointerdown", (event) => {
141
+ if (event.target instanceof Node && !selectionMenu?.contains(event.target)) closeSelectionMenu();
142
+ });
143
+ document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeSelectionMenu(); });
106
144
  // The room around the picture is empty in the plainest sense.
107
145
  viewport.addEventListener("click", (event) => {
108
- if (event.target === viewport) store.clearSelection();
146
+ if (event.target === viewport) {
147
+ if (reviewMode) toggle();
148
+ else store.clearSelection();
149
+ }
109
150
  });
110
151
  const play = element.querySelector<HTMLButtonElement>("[data-play]")!;
111
152
  const previous = element.querySelector<HTMLButtonElement>("[data-previous]")!;
@@ -213,6 +254,18 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
213
254
  const title = element.querySelector<HTMLElement>(".stage-title strong")!;
214
255
  const back = element.querySelector<HTMLButtonElement>(".stage-return")!;
215
256
  const scrubber = element.querySelector<HTMLInputElement>(".stage-scrubber")!;
257
+ const progress = element.querySelector<HTMLElement>(".stage-progress")!;
258
+ const scrubPreview = createScrubPreview(progress);
259
+ scrubber.addEventListener("pointermove", (event) => {
260
+ if (!reviewMode || state === undefined || scrubber.disabled || artifactPreview.selected !== undefined) return;
261
+ const box = scrubber.getBoundingClientRect();
262
+ // Native range thumbs travel between their centers, not the input edges.
263
+ const fraction = Math.max(0, Math.min(1, (event.clientX - box.left - 6) / Math.max(1, box.width - 12)));
264
+ const frame = Math.round(fraction * Math.max(0, state.snapshot.space.frameCount - 1));
265
+ scrubPreview.show(state.snapshot, frame, event.clientX);
266
+ });
267
+ scrubber.addEventListener("pointerleave", scrubPreview.hide);
268
+ scrubber.addEventListener("blur", scrubPreview.hide);
216
269
  const time = element.querySelector<HTMLElement>(".stage-time")!;
217
270
  const transport = element.querySelector<HTMLElement>(".stage-transport")!;
218
271
  const clock = (seconds: number): string => {
@@ -227,7 +280,7 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
227
280
  const duration = media !== undefined && Number.isFinite(media.duration) ? media.duration : 0;
228
281
  transport.hidden = !timed;
229
282
  mute.hidden = !timed;
230
- scrubber.hidden = !timed;
283
+ progress.hidden = scrubber.hidden = !timed;
231
284
  time.hidden = !timed;
232
285
  play.disabled = duration <= 0 || media?.error != null;
233
286
  previous.disabled = next.disabled = play.disabled;
@@ -244,7 +297,7 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
244
297
  const compositionTime = (): void => {
245
298
  if (state === undefined || artifactPreview.selected !== undefined) return;
246
299
  const rate = fps(state.snapshot);
247
- scrubber.hidden = time.hidden = false;
300
+ progress.hidden = scrubber.hidden = time.hidden = false;
248
301
  scrubber.disabled = !ready;
249
302
  scrubber.step = String(1 / rate);
250
303
  scrubber.max = String(Math.max(0, state.snapshot.space.frameCount - 1) / rate);
@@ -309,6 +362,7 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
309
362
  state = value;
310
363
  if (moved && value.playhead.origin !== "play" && artifactPreview.selected !== undefined) returnToComposition();
311
364
  if (value.snapshot.revision !== mounted) {
365
+ scrubPreview.clear();
312
366
  mounted = value.snapshot.revision;
313
367
  if (artifactPreview.selected === undefined) stop();
314
368
  ready = false;
@@ -339,6 +393,15 @@ export function createStage(store: Store, selectedArtifact: (id: string | undefi
339
393
  });
340
394
 
341
395
  return { element, toggle, openArtifact,
396
+ setReviewMode(enabled) {
397
+ reviewMode = enabled;
398
+ element.classList.toggle("stage-review", enabled);
399
+ overlay.element.style.display = enabled ? "none" : "";
400
+ closeSelectionMenu();
401
+ if (!enabled) scrubPreview.clear();
402
+ },
403
+ pause() { stop(); artifactPreview.media?.pause(); },
404
+ showComposition() { if (artifactPreview.selected !== undefined) returnToComposition(); },
342
405
  renameArtifact(artifact) {
343
406
  const selected = artifactPreview.selected;
344
407
  if (selected?.build === artifact.build && selected.output === artifact.output) title.textContent = artifact.displayName ?? artifact.output;
@@ -147,11 +147,20 @@ export function createTimeline(store: Store): Timeline {
147
147
  let paintFrame = 0;
148
148
  let rebuildFrame = 0;
149
149
  const readableWordLabels = new Set<string>();
150
+ let selectedWord: string | undefined;
151
+ let overlapMenu: HTMLElement | undefined;
152
+ const closeOverlapMenu = () => { overlapMenu?.remove(); overlapMenu = undefined; };
153
+ element.addEventListener("pointerdown", event => {
154
+ if (!overlapMenu?.contains(event.target as Node)) closeOverlapMenu();
155
+ });
156
+ element.addEventListener("keydown", event => { if (event.key === "Escape") closeOverlapMenu(); });
157
+ body.addEventListener("scroll", closeOverlapMenu);
150
158
  let clipNodes: readonly {
151
159
  readonly node: HTMLElement;
152
160
  readonly start: number;
153
161
  readonly end: number;
154
162
  readonly id: string;
163
+ readonly selectionGroup?: string;
155
164
  }[] = [];
156
165
  let semanticNodes: readonly {
157
166
  readonly node: HTMLElement;
@@ -301,7 +310,7 @@ export function createTimeline(store: Store): Timeline {
301
310
  const from = place(preview.startFrame, state.snapshot.space.frameCount, zoom.window());
302
311
  const to = place(preview.endFrameExclusive, state.snapshot.space.frameCount, zoom.window());
303
312
  activeEdit.node.style.left = `${from * 100}%`;
304
- activeEdit.node.style.width = `max(2px, calc(${Math.max(0, to - from) * 100}% - ${itemMetrics.gapPx}px))`;
313
+ activeEdit.node.style.width = `max(2px, ${Math.max(0, to - from) * 100}%)`;
305
314
  }
306
315
  return;
307
316
  }
@@ -479,29 +488,32 @@ export function createTimeline(store: Store): Timeline {
479
488
  .sort((left, right) => (left.binding.lane.order ?? 0) - (right.binding.lane.order ?? 0));
480
489
 
481
490
  const buildSemanticLane = (snapshot: StudioSnapshot): void => {
482
- if (snapshot.semantic === undefined) {
491
+ if (snapshot.semantic === undefined || snapshot.semantic.segments.length === 0) {
483
492
  return;
484
493
  }
485
494
  const presentation = snapshot.semantic.presentation;
486
- // This is one semantic ruler group, not a regular Film lane. Its three
487
- // bands share the same inner inset as ordinary items, so the first pixel
488
- // of the semantic content aligns with the first pixel of the label card.
489
- const laneHeight = presentation.lane.heightPx;
490
- const bandHeight = Math.max(1, (laneHeight - itemMetrics.insetYPx * 2) / 3);
495
+ const bandHeight = Math.max(1, (presentation.lane.heightPx - itemMetrics.insetYPx * 2) / 3);
496
+ const bands = [
497
+ { kind: "segment", height: bandHeight },
498
+ ...(snapshot.semantic.tokens.length > 0 ? [{ kind: "word", height: bandHeight }] : []),
499
+ ...(snapshot.semantic.selections.length + snapshot.semantic.moments.length > 0
500
+ ? [{ kind: "intent", height: bandHeight }] : []),
501
+ ];
502
+ const laneHeight = bands.reduce((height, band) => height + band.height, itemMetrics.insetYPx * 2);
491
503
  const label = createTrackLabel(
492
- presentation.label ?? "Semantic",
504
+ presentation.label ?? "Timeline",
493
505
  presentation.tone,
494
506
  presentation.icon,
495
- `${snapshot.semantic.segments.length} take${snapshot.semantic.segments.length === 1 ? "" : "s"}`,
507
+ `${snapshot.semantic.segments.length} segment${snapshot.semantic.segments.length === 1 ? "" : "s"}`,
496
508
  laneHeight,
497
509
  );
498
510
  label.classList.add("track-label-semantic");
511
+ label.style.height = `calc(var(--timeline-ruler-height) + ${laneHeight}px)`;
499
512
  labels.append(label);
500
513
 
501
514
  const lane = document.createElement("div");
502
515
  lane.className = `lane semantic-lane track-tone-${presentation.tone}`;
503
516
  lane.style.height = `${laneHeight}px`;
504
- lane.style.setProperty("--semantic-band-height", `${bandHeight}px`);
505
517
  const laneWidth = lanes.clientWidth;
506
518
  const nextSemanticNodes: {
507
519
  node: HTMLElement;
@@ -511,12 +523,15 @@ export function createTimeline(store: Store): Timeline {
511
523
  kind: "segment" | "word" | "selection" | "moment";
512
524
  }[] = [];
513
525
 
514
- const bands = ["segment", "word", "intent"] as const;
515
- for (const [index, kind] of bands.entries()) {
526
+ let bandTop = itemMetrics.insetYPx;
527
+ for (const { kind, height } of bands) {
516
528
  const band = document.createElement("div");
517
529
  band.className = `semantic-band semantic-band-${kind}`;
518
- band.style.top = `${itemMetrics.insetYPx + index * bandHeight}px`;
530
+ band.style.top = `${bandTop}px`;
531
+ band.style.setProperty("--semantic-band-height", `${height}px`);
532
+ band.setAttribute("aria-label", kind === "intent" ? "Selections and Moments" : kind === "word" ? "Words" : "Segments");
519
533
  lane.append(band);
534
+ bandTop += height;
520
535
  }
521
536
  const segmentBand = lane.querySelector<HTMLElement>(".semantic-band-segment")!;
522
537
  const wordBand = lane.querySelector<HTMLElement>(".semantic-band-word")!;
@@ -535,7 +550,7 @@ export function createTimeline(store: Store): Timeline {
535
550
  node.dataset.semanticSegment = segment.id;
536
551
  node.style.left = `${from * 100}%`;
537
552
  const segmentWidth = Math.max(0, to - from) * 100;
538
- node.style.width = `max(2px, calc(${segmentWidth}% - ${itemMetrics.gapPx}px))`;
553
+ node.style.width = `max(2px, ${segmentWidth}%)`;
539
554
  node.title = segment.id;
540
555
  const segmentLabel = document.createElement("span");
541
556
  segmentLabel.className = "semantic-segment-label";
@@ -575,12 +590,12 @@ export function createTimeline(store: Store): Timeline {
575
590
  word.setAttribute("aria-label", token.text);
576
591
  word.style.left = `${wordFrom * 100}%`;
577
592
  const wordWidth = Math.max(0, wordTo - wordFrom);
578
- word.style.width = `max(1px, calc(${wordWidth * 100}% - ${itemMetrics.gapPx}px))`;
593
+ word.style.width = `max(1px, ${wordWidth * 100}%)`;
579
594
  word.title = token.text;
580
595
  const text = document.createElement("span");
581
596
  text.className = "semantic-word-label";
582
597
  text.textContent = token.text;
583
- const wordWidthPx = Math.max(1, wordWidth * laneWidth - itemMetrics.gapPx);
598
+ const wordWidthPx = Math.max(1, wordWidth * laneWidth);
584
599
  const textWidthPx = measuredText(token.text);
585
600
  const contentWidthPx = Math.max(0, wordWidthPx - wordLabelPaddingPx * 2);
586
601
  const wasReadable = readableWordLabels.has(token.id);
@@ -596,6 +611,8 @@ export function createTimeline(store: Store): Timeline {
596
611
  word.append(wordContent);
597
612
  word.addEventListener("click", (event) => {
598
613
  event.stopPropagation();
614
+ store.clearSelection();
615
+ selectedWord = token.id;
599
616
  store.seek(token.startFrame, "timeline");
600
617
  });
601
618
  nextSemanticNodes.push({ node: word, id: token.id, start: token.startFrame, end: token.endFrameExclusive, kind: "word" });
@@ -611,7 +628,7 @@ export function createTimeline(store: Store): Timeline {
611
628
  node.tabIndex = 0;
612
629
  node.setAttribute("aria-label", `Selection ${selection.id}`);
613
630
  node.style.left = `${from * 100}%`;
614
- node.style.width = `max(2px, calc(${Math.max(0, to - from) * 100}% - ${itemMetrics.gapPx}px))`;
631
+ node.style.width = `max(2px, ${Math.max(0, to - from) * 100}%)`;
615
632
  node.title = selection.id;
616
633
  const selectionLabel = document.createElement("span");
617
634
  selectionLabel.className = "semantic-selection-label";
@@ -645,6 +662,33 @@ export function createTimeline(store: Store): Timeline {
645
662
  intentBand.append(node);
646
663
  nextSemanticNodes.push({ node, id: moment.id, start: moment.frame, end: moment.frame + 1, kind: "moment" });
647
664
  }
665
+ lane.addEventListener("contextmenu", event => {
666
+ const hits = nextSemanticNodes.filter(({ node }) => {
667
+ const rect = node.getBoundingClientRect();
668
+ return event.clientX >= rect.left && event.clientX < rect.right
669
+ && event.clientY >= rect.top && event.clientY < rect.bottom;
670
+ });
671
+ if (hits.length < 2) return;
672
+ event.preventDefault();
673
+ closeOverlapMenu();
674
+ const menu = document.createElement("div");
675
+ menu.className = "timeline-overlap-menu";
676
+ menu.setAttribute("role", "menu");
677
+ menu.setAttribute("aria-label", "Overlapping timeline objects");
678
+ for (const hit of [...hits].reverse()) {
679
+ const option = document.createElement("button");
680
+ option.type = "button";
681
+ option.setAttribute("role", "menuitem");
682
+ option.textContent = hit.node.getAttribute("aria-label") ?? hit.id;
683
+ option.addEventListener("click", () => { closeOverlapMenu(); hit.node.click(); });
684
+ menu.append(option);
685
+ }
686
+ element.append(menu);
687
+ menu.style.left = `${Math.max(0, Math.min(event.clientX, window.innerWidth - menu.offsetWidth - 8))}px`;
688
+ menu.style.top = `${Math.max(0, Math.min(event.clientY, window.innerHeight - menu.offsetHeight - 8))}px`;
689
+ overlapMenu = menu;
690
+ menu.querySelector("button")?.focus();
691
+ });
648
692
  rows.append(lane);
649
693
  semanticNodes = nextSemanticNodes;
650
694
  };
@@ -652,118 +696,139 @@ export function createTimeline(store: Store): Timeline {
652
696
  const buildTrack = (
653
697
  snapshot: StudioSnapshot,
654
698
  track: StudioSnapshot["tracks"][number],
655
- nextClipNodes: { node: HTMLElement; start: number; end: number; id: string }[],
699
+ nextClipNodes: { node: HTMLElement; start: number; end: number; id: string; selectionGroup?: string }[],
656
700
  attached = false,
657
701
  ): void => {
702
+ const trackBands = track.binding.bands ?? [];
703
+ const banded = trackBands.length > 0;
704
+ const layout = [
705
+ ...trackBands.filter(band => band.placement === "before"),
706
+ { id: undefined, heightPx: track.binding.lane.heightPx, display: "content" as const, tone: track.binding.tone },
707
+ ...trackBands.filter(band => band.placement === "after"),
708
+ ];
709
+ const totalHeight = layout.reduce((sum, band) => sum + band.heightPx, 0)
710
+ + (banded ? 2 * itemMetrics.insetYPx : 0);
658
711
  const tone = track.binding.tone;
659
- const laneHeight = track.binding.lane.heightPx;
660
712
  const displayedItems = track.clips.length;
661
713
  const label = createTrackLabel(
662
714
  displayTrackName(track),
663
715
  tone,
664
716
  track.binding.icon,
665
717
  `${displayedItems} item${displayedItems === 1 ? "" : "s"}`,
666
- laneHeight,
718
+ totalHeight,
667
719
  attached,
668
720
  );
669
721
  label.classList.add(`track-facet-${track.binding.facet}`);
670
722
  labels.append(label);
671
723
 
672
- const lane = document.createElement("div");
673
- lane.className = `lane track-tone-${tone} track-facet-${track.binding.facet}${attached ? " lane-attached" : ""}`;
674
- lane.style.height = `${laneHeight}px`;
675
- lane.style.setProperty("--lane-height", `${laneHeight}px`);
676
- const laneWidth = lanes.clientWidth;
724
+ const trackBody = document.createElement("div");
725
+ trackBody.className = banded ? "timeline-track timeline-track-banded" : "timeline-track";
726
+ trackBody.dataset.track = track.id;
727
+ trackBody.style.height = `${totalHeight}px`;
677
728
  const materialMounts: {
678
729
  readonly target: HTMLElement;
679
730
  readonly preview: Extract<StudioSnapshot["tracks"][number]["clips"][number]["display"]["layers"][number], { readonly kind: "preview" }>["preview"];
680
731
  }[] = [];
681
- // Stable order preserves Companion projection order when stack levels tie.
682
- for (const clip of [...track.clips].sort((a, b) => a.stackOrder - b.stackOrder)) {
683
- const from = place(clip.startFrame, snapshot.space.frameCount, zoom.window());
684
- const to = place(clip.endFrameExclusive, snapshot.space.frameCount, zoom.window());
685
- if (to <= 0 || from >= 1) continue;
686
- const node = document.createElement("button");
687
- node.type = "button";
688
- node.className = `clip clip-tone-${tone} clip-facet-${track.binding.facet} clip-chrome-${clip.presentation.chrome}`;
689
- node.classList.toggle("clip-editable", clip.editHandles.some((handle) => handle.enabled));
690
- node.dataset.clip = clip.id;
691
- node.setAttribute("aria-label", clip.display.title);
692
- node.style.left = `${from * 100}%`;
693
- node.style.width = `max(2px, calc(${Math.max(0, to - from) * 100}% - ${itemMetrics.gapPx}px))`;
694
- const visibleWidthPx = visibleItemWidth(from, to, laneWidth);
695
- node.classList.toggle("clip-preview-wide", visibleWidthPx >= 92);
696
- node.title = `${clip.display.title} · ${clip.startFrame}-${clip.endFrameExclusive}f`;
697
- node.innerHTML = `<span class="clip-head"><span class="clip-name"></span><span class="clip-meta"></span></span><span class="clip-body"><span class="clip-layers" aria-hidden="true"></span><span class="clip-phases"></span></span><span class="clip-selection" aria-hidden="true"></span>`;
698
- const layers = node.querySelector<HTMLElement>(".clip-layers")!;
699
- clip.display.layers.forEach((layer, index) => {
700
- const layerNode = document.createElement("span");
701
- layerNode.className = `clip-layer clip-layer-${layer.kind} clip-layer-role-${layer.role}`;
702
- layerNode.style.zIndex = String(index + 1);
703
- if (layer.kind === "text") {
704
- layerNode.textContent = layer.text;
705
- } else {
706
- layerNode.classList.add(`clip-layer-layout-${layer.layout}`);
707
- materialMounts.push({ target: layerNode, preview: layer.preview });
732
+ for (const band of layout) {
733
+ const tone = band.tone ?? track.binding.tone;
734
+ const laneHeight = band.heightPx;
735
+ const lane = document.createElement("div");
736
+ lane.className = `lane track-tone-${tone} track-facet-${track.binding.facet}${attached ? " lane-attached" : ""}`;
737
+ lane.style.height = `${laneHeight}px`;
738
+ if (banded) lane.classList.add("track-band", `track-band-${band.display}`);
739
+ if (band.id !== undefined) lane.dataset.band = band.id;
740
+ lane.style.setProperty("--lane-height", `${laneHeight}px`);
741
+ const laneWidth = lanes.clientWidth;
742
+ // Stable order preserves Companion projection order when stack levels tie.
743
+ for (const clip of track.clips.filter(clip => clip.band === band.id).sort((a, b) => a.stackOrder - b.stackOrder)) {
744
+ const from = place(clip.startFrame, snapshot.space.frameCount, zoom.window());
745
+ const to = place(clip.endFrameExclusive, snapshot.space.frameCount, zoom.window());
746
+ if (to <= 0 || from >= 1) continue;
747
+ const node = document.createElement("button");
748
+ node.type = "button";
749
+ node.className = `clip clip-tone-${tone} clip-facet-${track.binding.facet} clip-chrome-${clip.presentation.chrome}`;
750
+ node.classList.toggle("clip-editable", clip.editHandles.some((handle) => handle.enabled));
751
+ node.dataset.clip = clip.id;
752
+ node.setAttribute("aria-label", clip.display.title);
753
+ node.style.left = `${from * 100}%`;
754
+ node.style.width = `max(2px, ${Math.max(0, to - from) * 100}%)`;
755
+ const visibleWidthPx = visibleItemWidth(from, to, laneWidth);
756
+ node.classList.toggle("clip-preview-wide", visibleWidthPx >= 92);
757
+ node.title = `${clip.display.title} · ${clip.startFrame}-${clip.endFrameExclusive}f`;
758
+ node.innerHTML = `<span class="clip-head"><span class="clip-name"></span><span class="clip-meta"></span></span><span class="clip-body"><span class="clip-layers" aria-hidden="true"></span><span class="clip-phases"></span></span><span class="clip-boundary" aria-hidden="true"></span><span class="clip-selection" aria-hidden="true"></span>`;
759
+ const layers = node.querySelector<HTMLElement>(".clip-layers")!;
760
+ clip.display.layers.forEach((layer, index) => {
761
+ const layerNode = document.createElement("span");
762
+ layerNode.className = `clip-layer clip-layer-${layer.kind} clip-layer-role-${layer.role}`;
763
+ layerNode.style.zIndex = String(index + 1);
764
+ if (layer.kind === "text") {
765
+ layerNode.textContent = layer.text;
766
+ } else {
767
+ layerNode.classList.add(`clip-layer-layout-${layer.layout}`);
768
+ materialMounts.push({ target: layerNode, preview: layer.preview });
769
+ }
770
+ layers.append(layerNode);
771
+ });
772
+ const phaseLayer = node.querySelector<HTMLElement>(".clip-phases")!;
773
+ for (const phase of clip.temporal?.phases ?? []) {
774
+ if (phase.endFrameExclusive <= clip.startFrame || phase.startFrame >= clip.endFrameExclusive) continue;
775
+ const phaseNode = document.createElement("span");
776
+ phaseNode.className = `clip-phase clip-phase-${phase.role}`;
777
+ phaseNode.style.left = `${(phase.startFrame - clip.startFrame) / Math.max(1, clip.endFrameExclusive - clip.startFrame) * 100}%`;
778
+ phaseNode.style.width = `${(phase.endFrameExclusive - phase.startFrame) / Math.max(1, clip.endFrameExclusive - clip.startFrame) * 100}%`;
779
+ phaseLayer.append(phaseNode);
708
780
  }
709
- layers.append(layerNode);
710
- });
711
- const phaseLayer = node.querySelector<HTMLElement>(".clip-phases")!;
712
- for (const phase of clip.temporal?.phases ?? []) {
713
- if (phase.endFrameExclusive <= clip.startFrame || phase.startFrame >= clip.endFrameExclusive) continue;
714
- const phaseNode = document.createElement("span");
715
- phaseNode.className = `clip-phase clip-phase-${phase.role}`;
716
- phaseNode.style.left = `${(phase.startFrame - clip.startFrame) / Math.max(1, clip.endFrameExclusive - clip.startFrame) * 100}%`;
717
- phaseNode.style.width = `${(phase.endFrameExclusive - phase.startFrame) / Math.max(1, clip.endFrameExclusive - clip.startFrame) * 100}%`;
718
- phaseLayer.append(phaseNode);
719
- }
720
- const metaText = clip.presentation.chrome === "point"
721
- ? `${(clip.startFrame / fps(snapshot)).toFixed(2)}s`
722
- : `${((clip.endFrameExclusive - clip.startFrame) / fps(snapshot)).toFixed(2)}s`;
723
- const headerLabel = node.querySelector<HTMLElement>(".clip-name")!;
724
- node.querySelector(".clip-meta")!.textContent = metaText;
725
- headerLabel.textContent = clip.display.title;
726
- node.addEventListener("pointerdown", (event) => {
727
- const rect = node.getBoundingClientRect();
728
- const edge = Math.min(8, Math.max(4, rect.width / 3));
729
- const nearStart = event.clientX - rect.left <= edge;
730
- const nearEnd = rect.right - event.clientX <= edge;
731
- const handle = clip.editHandles.find((candidate) =>
732
- candidate.enabled
733
- && ((candidate.gesture === "trim-start" && nearStart)
734
- || (candidate.gesture === "trim-end" && nearEnd)
735
- || (candidate.gesture === "move" && !nearStart && !nearEnd)));
736
- if (handle !== undefined
737
- && (handle.gesture === "move" || handle.gesture === "trim-start" || handle.gesture === "trim-end")) {
738
- event.stopPropagation();
739
- const pointerFrame = handle.coordinate === "semantic-anchor"
740
- ? rawFrameAt(event.clientX)
741
- : frameAt(event.clientX);
742
- activeEdit = {
743
- clip,
744
- handle,
745
- startFrame: pointerFrame,
746
- pointerId: event.pointerId,
747
- node,
748
- originalLeft: node.style.left,
749
- originalWidth: node.style.width,
750
- };
751
- node.classList.add("editing");
752
- try { lanes.setPointerCapture(event.pointerId); } catch { /* local pointer */ }
781
+ const metaText = clip.presentation.chrome === "point"
782
+ ? `${(clip.startFrame / fps(snapshot)).toFixed(2)}s`
783
+ : `${((clip.endFrameExclusive - clip.startFrame) / fps(snapshot)).toFixed(2)}s`;
784
+ const headerLabel = node.querySelector<HTMLElement>(".clip-name")!;
785
+ node.querySelector(".clip-meta")!.textContent = metaText;
786
+ headerLabel.textContent = clip.display.title;
787
+ node.addEventListener("pointerdown", (event) => {
788
+ const rect = node.getBoundingClientRect();
789
+ const edge = Math.min(8, Math.max(4, rect.width / 3));
790
+ const nearStart = event.clientX - rect.left <= edge;
791
+ const nearEnd = rect.right - event.clientX <= edge;
792
+ const handle = clip.editHandles.find((candidate) =>
793
+ candidate.enabled
794
+ && ((candidate.gesture === "trim-start" && nearStart)
795
+ || (candidate.gesture === "trim-end" && nearEnd)
796
+ || (candidate.gesture === "move" && !nearStart && !nearEnd)));
797
+ if (handle !== undefined
798
+ && (handle.gesture === "move" || handle.gesture === "trim-start" || handle.gesture === "trim-end")) {
799
+ event.stopPropagation();
800
+ const pointerFrame = handle.coordinate === "semantic-anchor"
801
+ ? rawFrameAt(event.clientX)
802
+ : frameAt(event.clientX);
803
+ activeEdit = {
804
+ clip,
805
+ handle,
806
+ startFrame: pointerFrame,
807
+ pointerId: event.pointerId,
808
+ node,
809
+ originalLeft: node.style.left,
810
+ originalWidth: node.style.width,
811
+ };
812
+ node.classList.add("editing");
813
+ try { lanes.setPointerCapture(event.pointerId); } catch { /* local pointer */ }
814
+ store.select(clip.id, "timeline");
815
+ return;
816
+ }
753
817
  store.select(clip.id, "timeline");
754
- return;
755
- }
756
- store.select(clip.id, "timeline");
757
- });
758
- node.addEventListener("dblclick", () => store.seek(clip.startFrame, "timeline"));
759
- nextClipNodes.push({ node, start: clip.startFrame, end: clip.endFrameExclusive, id: clip.id });
760
- lane.append(node);
818
+ });
819
+ node.addEventListener("dblclick", () => store.seek(clip.startFrame, "timeline"));
820
+ nextClipNodes.push({ node, start: clip.startFrame, end: clip.endFrameExclusive, id: clip.id, ...(clip.selectionGroup === undefined ? {} : { selectionGroup: clip.selectionGroup }) });
821
+ lane.append(node);
822
+ }
823
+ trackBody.append(lane);
761
824
  }
762
- rows.append(lane);
825
+ rows.append(trackBody);
826
+ // Cached storyboards render synchronously and need connected, measurable targets.
763
827
  for (const item of materialMounts) mountMaterialPreview(item.target, item.preview);
764
828
  };
765
829
 
766
830
  const build = (snapshot: StudioSnapshot): void => {
831
+ closeOverlapMenu();
767
832
  const scrollTop = body.scrollTop;
768
833
  labels.replaceChildren();
769
834
  rows.replaceChildren();
@@ -771,9 +836,9 @@ export function createTimeline(store: Store): Timeline {
771
836
  const corner = document.createElement("div");
772
837
  corner.className = "label-corner";
773
838
  corner.textContent = "";
774
- labels.append(corner);
839
+ if (snapshot.semantic === undefined || snapshot.semantic.segments.length === 0) labels.append(corner);
775
840
  buildSemanticLane(snapshot);
776
- const nextClipNodes: { node: HTMLElement; start: number; end: number; id: string }[] = [];
841
+ const nextClipNodes: { node: HTMLElement; start: number; end: number; id: string; selectionGroup?: string }[] = [];
777
842
  for (const track of snapshot.tracks) {
778
843
  const attachedTo = track.binding.lane.attachedTo;
779
844
  if (attachedTo !== undefined) continue;
@@ -804,8 +869,10 @@ export function createTimeline(store: Store): Timeline {
804
869
  playhead.classList.toggle("outside", position < 0 || position > lanes.clientWidth);
805
870
  playheadGrip.classList.toggle("outside", position < 0 || position > lanes.clientWidth);
806
871
  timelineTime.textContent = frameTimecode(snapshot, head.frame);
872
+ const chosen = selection.kind === "clip" ? store.clip(selection.clipId) : undefined;
807
873
  for (const item of clipNodes) {
808
- const selected = selection.kind === "clip" && selection.clipId === item.id;
874
+ const selected = chosen !== undefined && (chosen.id === item.id
875
+ || (chosen.selectionGroup !== undefined && chosen.selectionGroup === item.selectionGroup));
809
876
  item.node.classList.toggle("selected", selected);
810
877
  item.node.setAttribute("aria-pressed", String(selected));
811
878
  item.node.classList.toggle("live", head.frame >= item.start && head.frame < item.end);
@@ -815,7 +882,8 @@ export function createTimeline(store: Store): Timeline {
815
882
  (item.kind === "segment" || item.kind === "selection")
816
883
  && head.frame >= item.start && head.frame < item.end);
817
884
  item.node.classList.toggle("current", item.kind === "word" && head.frame >= item.start && head.frame < item.end);
818
- const selected = (item.kind === "segment"
885
+ const selected = (item.kind === "word" && selectedWord === item.id && selection.kind === "none")
886
+ || (item.kind === "segment"
819
887
  && selection.kind === "semantic-segment" && selection.segmentId === item.id)
820
888
  || (item.kind === "selection"
821
889
  && selection.kind === "semantic-selection" && selection.selectionId === item.id)
@@ -862,6 +930,7 @@ export function createTimeline(store: Store): Timeline {
862
930
  zoom.subscribe(scheduleBuild);
863
931
  store.subscribe((value) => {
864
932
  state = value;
933
+ if (value.selection.kind !== "none") selectedWord = undefined;
865
934
  if (value.snapshot.revision !== built) {
866
935
  built = value.snapshot.revision;
867
936
  build(value.snapshot);
@@ -50,7 +50,7 @@ export async function runStudio(argv: readonly string[], io: Pick<CliIo, "write"
50
50
  const runArgument = values.get("run");
51
51
  if (runArgument === undefined || runArgument.trim().length === 0) invalidArguments("Missing --run");
52
52
  const { createServer } = await import("vite");
53
- const { findRuntimeProfile, resolveProjectRoot } = await import("@hypit/cli");
53
+ const { findRuntimeProfile, resolveProjectRoot } = await import("@hypit/project-context-node");
54
54
  const { resolveDistributionPackageImport } = await import("@hypit/package-loader-node");
55
55
  const { videoCliDistribution, videoStudioCompanionPackages } = await import("@hypit/video-cli");
56
56
  const { openStudioBuildLibrary } = await import("./src/build-library.js");
@@ -58,6 +58,7 @@ export async function runStudio(argv: readonly string[], io: Pick<CliIo, "write"
58
58
  const { loadStudioDomain } = await import("./src/domain.js");
59
59
  const { loadStudioRun } = await import("./src/run.js");
60
60
  const { studioPlugin } = await import("./src/server.js");
61
+ const { studioFeedbackPlugin } = await import("./src/feedback-server.js");
61
62
  const { inspectStudioRun } = await import("./src/studio-preflight.js");
62
63
  const runPath = resolve(invokedFrom, runArgument);
63
64
  const packageRootArgument = values.get("package-root");
@@ -136,7 +137,7 @@ export async function runStudio(argv: readonly string[], io: Pick<CliIo, "write"
136
137
  // available for Source and material previews.
137
138
  fs: { allow: [workspaceRoot, packageRoot, distributionPackageRoot, here] },
138
139
  },
139
- plugins: [distributionImports, studioPlugin({
140
+ plugins: [distributionImports, studioFeedbackPlugin(workspaceRoot, runPath), studioPlugin({
140
141
  source,
141
142
  runPath,
142
143
  workspaceRoot,
@@ -152,4 +153,5 @@ export async function runStudio(argv: readonly string[], io: Pick<CliIo, "write"
152
153
  });
153
154
  await server.listen();
154
155
  server.printUrls();
156
+ for (const url of server.resolvedUrls?.local ?? []) io.write(` Comments ${url}#comments\n`);
155
157
  }