@hypit/hypit 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/README.md +5 -3
  2. package/dist/public/caption.d.ts +206 -183
  3. package/dist/public/composition.d.ts +25 -3
  4. package/dist/public/endpoint-kit.d.ts +24 -2
  5. package/dist/public/generation.d.ts +5 -1
  6. package/dist/public/hyperframes.d.ts +19 -1
  7. package/dist/public/narrative.d.ts +17 -3
  8. package/dist/public/performance.d.ts +1590 -0
  9. package/dist/public/program-space.d.ts +179 -32
  10. package/dist/public/runtime-kit.d.ts +21 -8
  11. package/dist/public/sound.d.ts +822 -0
  12. package/dist/public/speech.d.ts +41 -0
  13. package/dist/public/studio-adapter.d.ts +107 -16
  14. package/dist/public/temporal-markup.d.ts +9 -44
  15. package/dist/public/temporal.d.ts +37 -28
  16. package/dist/public/{semantic-track.d.ts → timeline.d.ts} +62 -49
  17. package/examples/README.md +18 -10
  18. package/examples/minimal-author-package/packages/example-component/preview/preview.svml +19 -12
  19. package/examples/minimal-author-package/packages/example-component/src/component.ts +5 -5
  20. package/examples/minimal-author-package/packages/example-component/src/fragment.ts +3 -5
  21. package/examples/minimal-author-package/packages/example-component/src/manifest.ts +8 -10
  22. package/examples/minimal-author-package/packages/example-component/src/surface.ts +2 -2
  23. package/examples/provider-package/README.md +78 -0
  24. package/examples/provider-package/hypit.runtime.json +12 -0
  25. package/examples/provider-package/packages/provider-images/package.json +11 -0
  26. package/examples/provider-package/packages/provider-images/src/activation.ts +24 -0
  27. package/examples/provider-package/packages/provider-images/src/provider.ts +122 -0
  28. package/examples/provider-package/packages/provider-images/tsconfig.json +9 -0
  29. package/examples/semantic-composition/README.md +6 -0
  30. package/examples/semantic-composition/chat.svml +6 -5
  31. package/examples/semantic-composition/packages/chat-scene/README.md +3 -3
  32. package/examples/semantic-composition/packages/chat-scene/src/activation.ts +18 -21
  33. package/examples/semantic-composition/packages/chat-scene/src/render.ts +4 -4
  34. package/examples/semantic-composition/packages/performance-styles/README.md +42 -0
  35. package/examples/semantic-composition/packages/performance-styles/package.json +25 -0
  36. package/examples/semantic-composition/packages/performance-styles/src/activation.ts +57 -0
  37. package/examples/semantic-composition/packages/performance-styles/src/render.ts +54 -0
  38. package/examples/semantic-composition/packages/performance-styles/src/studio.ts +28 -0
  39. package/examples/semantic-composition/packages/performance-styles/tsconfig.json +18 -0
  40. package/examples/semantic-composition/packages/responsive-explainer/README.md +16 -9
  41. package/examples/semantic-composition/packages/responsive-explainer/src/activation.ts +12 -12
  42. package/examples/semantic-composition/packages/responsive-explainer/src/render.ts +5 -5
  43. package/examples/semantic-composition/packages/sound-styles/README.md +36 -0
  44. package/examples/semantic-composition/packages/sound-styles/package.json +25 -0
  45. package/examples/semantic-composition/packages/sound-styles/src/activation.ts +53 -0
  46. package/examples/semantic-composition/packages/sound-styles/src/render.ts +14 -0
  47. package/examples/semantic-composition/packages/sound-styles/tsconfig.json +18 -0
  48. package/package.json +35 -15
  49. package/packages/audio-track/README.md +20 -13
  50. package/packages/audio-track/package.json +1 -1
  51. package/packages/audio-track/src/component.ts +4 -4
  52. package/packages/audio-track/src/fragment.ts +6 -5
  53. package/packages/audio-track/src/index.ts +1 -1
  54. package/packages/audio-track/src/manifest.ts +17 -16
  55. package/packages/audio-track/src/program.ts +21 -25
  56. package/packages/audio-track/src/surface.ts +19 -18
  57. package/packages/audio-track-studio/README.md +23 -0
  58. package/packages/audio-track-studio/src/index.ts +33 -19
  59. package/packages/author-kit/README.md +1 -1
  60. package/packages/background-removal/README.md +4 -7
  61. package/packages/browser-capture/package.json +1 -1
  62. package/packages/build-result/README.md +69 -0
  63. package/packages/build-result/package.json +2 -1
  64. package/packages/build-result/src/decode.ts +12 -8
  65. package/packages/build-result/src/execution-log.ts +17 -0
  66. package/packages/build-result/src/file-reference.ts +42 -0
  67. package/packages/build-result/src/index.ts +4 -0
  68. package/packages/build-result/src/replace-file-windows.ts +58 -0
  69. package/packages/build-result/src/replace-file.ts +11 -0
  70. package/packages/build-result/src/store.ts +58 -14
  71. package/packages/build-result/src/types.ts +41 -8
  72. package/packages/build-result/src/writer.ts +12 -1
  73. package/packages/build-result-fs/README.md +23 -1
  74. package/packages/build-result-s3/README.md +5 -0
  75. package/packages/build-result-s3/src/repository.ts +23 -5
  76. package/packages/caption/README.md +61 -73
  77. package/packages/caption/package.json +3 -2
  78. package/packages/caption/src/activation.ts +2 -2
  79. package/packages/caption/src/component.ts +20 -5
  80. package/packages/caption/src/display.ts +3 -18
  81. package/packages/caption/src/fragment.ts +3 -7
  82. package/packages/caption/src/index.ts +6 -5
  83. package/packages/caption/src/manifest.ts +29 -46
  84. package/packages/caption/src/style.ts +24 -135
  85. package/packages/caption/src/surface.ts +12 -92
  86. package/packages/caption/src/temporalize.ts +18 -53
  87. package/packages/caption/src/types.ts +8 -35
  88. package/packages/caption/src/visibility.ts +23 -0
  89. package/packages/caption-fine/README.md +31 -11
  90. package/packages/caption-fine/package.json +4 -2
  91. package/packages/caption-fine/src/component.ts +3 -3
  92. package/packages/caption-fine/src/fragment.ts +7 -19
  93. package/packages/caption-fine/src/index.ts +2 -0
  94. package/packages/caption-fine/src/manifest.ts +27 -18
  95. package/packages/caption-fine/src/recipe.ts +7 -0
  96. package/packages/caption-fine/src/render.ts +62 -49
  97. package/packages/caption-fine/src/schedule.ts +39 -35
  98. package/packages/caption-fine/src/style.ts +10 -9
  99. package/packages/caption-fine/src/surface.ts +62 -35
  100. package/packages/caption-fine/src/types.ts +2 -0
  101. package/packages/caption-fine-studio/README.md +24 -2
  102. package/packages/caption-fine-studio/package.json +10 -3
  103. package/packages/caption-fine-studio/src/activation.ts +3 -3
  104. package/packages/caption-fine-studio/src/index.ts +61 -40
  105. package/packages/cli/README.md +81 -3
  106. package/packages/cli/package.json +1 -0
  107. package/packages/cli/src/arguments.ts +39 -15
  108. package/packages/cli/src/build-planning.ts +9 -3
  109. package/packages/cli/src/command.ts +7 -1
  110. package/packages/cli/src/commands/environment.ts +66 -27
  111. package/packages/cli/src/commands/execution.ts +81 -26
  112. package/packages/cli/src/commands/results.ts +7 -11
  113. package/packages/cli/src/index.ts +2 -2
  114. package/packages/cli/src/machine-view.ts +15 -5
  115. package/packages/cli/src/main.ts +38 -42
  116. package/packages/cli/src/observation.ts +7 -1
  117. package/packages/cli/src/output.ts +92 -34
  118. package/packages/cli/src/result-export.ts +22 -13
  119. package/packages/cli/src/run-file.ts +19 -8
  120. package/packages/cli/src/view.ts +64 -18
  121. package/packages/comment-sticker/README.md +3 -3
  122. package/packages/comment-sticker/package.json +1 -1
  123. package/packages/comment-sticker/src/component.ts +4 -4
  124. package/packages/comment-sticker/src/fragment.ts +5 -4
  125. package/packages/comment-sticker/src/manifest.ts +7 -6
  126. package/packages/comment-sticker/src/program.ts +9 -9
  127. package/packages/comment-sticker/src/surface.ts +6 -7
  128. package/packages/comment-sticker-studio/src/index.ts +2 -1
  129. package/packages/compiler-node/src/compiler.ts +1 -0
  130. package/packages/composition/README.md +21 -0
  131. package/packages/composition/src/audio-presentation.ts +44 -0
  132. package/packages/composition/src/index.ts +3 -0
  133. package/packages/composition/src/schema.ts +8 -3
  134. package/packages/composition/src/track.ts +15 -1
  135. package/packages/credential-store-env/README.md +1 -1
  136. package/packages/deck-track/README.md +3 -3
  137. package/packages/deck-track/package.json +1 -1
  138. package/packages/deck-track/src/component.ts +5 -5
  139. package/packages/deck-track/src/fragment.ts +6 -5
  140. package/packages/deck-track/src/lower.ts +12 -12
  141. package/packages/deck-track/src/manifest.ts +8 -7
  142. package/packages/deck-track/src/program.ts +13 -16
  143. package/packages/deck-track/src/surface.ts +9 -10
  144. package/packages/driver-node/src/driver.ts +41 -3
  145. package/packages/elevenlabs-speech/README.md +27 -0
  146. package/packages/{provider-xiaomi-mimo → elevenlabs-speech}/package.json +5 -8
  147. package/packages/elevenlabs-speech/src/activation.ts +26 -0
  148. package/packages/elevenlabs-speech/src/fragment.ts +11 -0
  149. package/packages/elevenlabs-speech/src/index.ts +111 -0
  150. package/packages/elevenlabs-speech/src/surface.ts +167 -0
  151. package/packages/endpoint-kit/README.md +24 -0
  152. package/packages/endpoint-kit/src/index.ts +7 -1
  153. package/packages/estimate/README.md +56 -3
  154. package/packages/estimate/src/program.ts +7 -8
  155. package/packages/film/README.md +12 -7
  156. package/packages/film/package.json +1 -1
  157. package/packages/film/src/component.ts +4 -4
  158. package/packages/film/src/fragment.ts +6 -5
  159. package/packages/film/src/manifest.ts +11 -10
  160. package/packages/film/src/program.ts +5 -5
  161. package/packages/film/src/recipe.ts +2 -1
  162. package/packages/film/src/surface.ts +6 -6
  163. package/packages/film-studio/package.json +1 -1
  164. package/packages/film-studio/src/index.ts +2 -2
  165. package/packages/fishaudio-speech/README.md +33 -0
  166. package/packages/fishaudio-speech/package.json +21 -0
  167. package/packages/fishaudio-speech/src/activation.ts +32 -0
  168. package/packages/fishaudio-speech/src/fragment.ts +11 -0
  169. package/packages/fishaudio-speech/src/index.ts +156 -0
  170. package/packages/fishaudio-speech/src/surface.ts +192 -0
  171. package/packages/fonts-open/README.md +5 -0
  172. package/packages/fonts-open/package.json +5 -1
  173. package/packages/fonts-open/src/studio.ts +21 -0
  174. package/packages/generation/README.md +11 -1
  175. package/packages/generation/src/mapping.ts +28 -30
  176. package/packages/gpt-image/README.md +1 -2
  177. package/packages/grok-imagine/README.md +1 -2
  178. package/packages/hyperframes/README.md +7 -3
  179. package/packages/hyperframes/src/browser-program.ts +11 -2
  180. package/packages/hyperframes/src/document.ts +15 -2
  181. package/packages/interview-emoji-reveal/README.md +4 -4
  182. package/packages/interview-emoji-reveal/package.json +1 -1
  183. package/packages/interview-emoji-reveal/src/component.ts +4 -4
  184. package/packages/interview-emoji-reveal/src/fragment.ts +6 -5
  185. package/packages/interview-emoji-reveal/src/manifest.ts +9 -8
  186. package/packages/interview-emoji-reveal/src/program.ts +14 -14
  187. package/packages/interview-emoji-reveal/src/surface.ts +8 -9
  188. package/packages/media-execution/README.md +7 -3
  189. package/packages/media-execution/src/audio-presentation.ts +23 -0
  190. package/packages/media-execution/src/execute.ts +2 -0
  191. package/packages/media-pipeline/README.md +7 -3
  192. package/packages/media-pipeline/src/audio-plan.ts +4 -1
  193. package/packages/media-pipeline/src/component.ts +16 -2
  194. package/packages/media-pipeline/src/manifest.ts +3 -1
  195. package/packages/media-pipeline/src/types.ts +2 -1
  196. package/packages/media-track/README.md +14 -47
  197. package/packages/media-track/package.json +1 -1
  198. package/packages/media-track/preview/preview.svml +5 -5
  199. package/packages/media-track/src/author.ts +12 -6
  200. package/packages/media-track/src/component.ts +10 -16
  201. package/packages/media-track/src/fragment.ts +8 -7
  202. package/packages/media-track/src/index.ts +3 -1
  203. package/packages/media-track/src/lower.ts +7 -7
  204. package/packages/media-track/src/manifest.ts +16 -38
  205. package/packages/media-track/src/program.ts +62 -67
  206. package/packages/media-track/src/sampling.ts +6 -6
  207. package/packages/media-track/src/sequence.ts +8 -8
  208. package/packages/media-track/src/surface.ts +25 -43
  209. package/packages/media-track-studio/README.md +9 -0
  210. package/packages/media-track-studio/src/index.ts +23 -13
  211. package/packages/mimo-speech/README.md +1 -1
  212. package/packages/minimax-h3/README.md +1 -1
  213. package/packages/nano-banana/README.md +1 -1
  214. package/packages/narrative/README.md +23 -1
  215. package/packages/narrative/src/identity.ts +11 -0
  216. package/packages/narrative/src/index.ts +3 -0
  217. package/packages/narrative/src/schema.ts +28 -27
  218. package/packages/narrative/src/selection.ts +52 -0
  219. package/packages/narrative/src/types.ts +2 -0
  220. package/packages/package-loader-node/README.md +30 -0
  221. package/packages/package-loader-node/package.json +5 -2
  222. package/packages/package-loader-node/src/distribution-resolution.ts +10 -3
  223. package/packages/package-loader-node/src/index.ts +1 -0
  224. package/packages/package-loader-node/src/loader.ts +12 -15
  225. package/packages/package-loader-node/src/location.ts +27 -1
  226. package/packages/package-loader-node/src/module-scope.ts +183 -0
  227. package/packages/package-loader-node/src/types.ts +4 -0
  228. package/packages/performance/README.md +83 -0
  229. package/packages/performance/package.json +30 -0
  230. package/packages/performance/src/activation.ts +11 -0
  231. package/packages/performance/src/component.ts +46 -0
  232. package/packages/performance/src/index.ts +6 -0
  233. package/packages/performance/src/manifest.ts +53 -0
  234. package/packages/{media-track/src/performance.ts → performance/src/media.ts} +9 -10
  235. package/packages/performance/src/program.ts +51 -0
  236. package/packages/performance/src/style.ts +35 -0
  237. package/packages/performance/src/surface.ts +99 -0
  238. package/packages/performance-studio/README.md +25 -0
  239. package/packages/performance-studio/package.json +21 -0
  240. package/packages/performance-studio/src/activation.ts +6 -0
  241. package/packages/performance-studio/src/index.ts +84 -0
  242. package/packages/program-space/README.md +9 -18
  243. package/packages/program-space/src/activation.ts +1 -5
  244. package/packages/program-space/src/index.ts +2 -11
  245. package/packages/program-space/src/surface.ts +1 -20
  246. package/packages/project-context-node/README.md +20 -0
  247. package/packages/project-context-node/package.json +10 -0
  248. package/packages/project-context-node/src/index.ts +3 -0
  249. package/packages/{cli → project-context-node}/src/runtime-selection.ts +7 -2
  250. package/packages/provider-hyperframes-local/README.md +43 -15
  251. package/packages/provider-hyperframes-local/src/activation.ts +4 -3
  252. package/packages/provider-hyperframes-local/src/capture-process.ts +26 -4
  253. package/packages/provider-hyperframes-local/src/capture.ts +96 -26
  254. package/packages/provider-hyperframes-local/src/concurrency.ts +86 -0
  255. package/packages/provider-hyperframes-local/src/opaque-capture.ts +92 -0
  256. package/packages/provider-hyperframes-local/src/options.ts +2 -0
  257. package/packages/provider-hyperframes-local/src/program.ts +6 -3
  258. package/packages/provider-hyperframes-local/src/progress.ts +53 -0
  259. package/packages/provider-hyperframes-local/src/provider.ts +18 -5
  260. package/packages/provider-hyperframes-local/src/render.ts +48 -7
  261. package/packages/provider-hypihub/README.md +48 -17
  262. package/packages/provider-hypihub/package.json +2 -0
  263. package/packages/provider-hypihub/src/mapping.ts +31 -7
  264. package/packages/provider-hypihub/src/oauth.ts +6 -0
  265. package/packages/provider-hypihub/src/provider.ts +25 -13
  266. package/packages/provider-hypihub/src/routes.ts +1 -2
  267. package/packages/provider-hypihub/src/upload.ts +2 -0
  268. package/packages/provider-media-local/README.md +3 -4
  269. package/packages/provider-whisperx-local/README.md +57 -0
  270. package/packages/provider-whisperx-local/src/program.ts +1 -0
  271. package/packages/provider-whisperx-local/src/provider.ts +3 -0
  272. package/packages/ranking/README.md +8 -6
  273. package/packages/ranking/package.json +1 -1
  274. package/packages/ranking/src/component.ts +6 -6
  275. package/packages/ranking/src/fragment.ts +11 -7
  276. package/packages/ranking/src/manifest.ts +21 -14
  277. package/packages/ranking/src/render.ts +33 -35
  278. package/packages/ranking/src/schedule.ts +16 -19
  279. package/packages/ranking/src/surface.ts +12 -13
  280. package/packages/ranking-studio/README.md +29 -0
  281. package/packages/ranking-studio/package.json +8 -3
  282. package/packages/ranking-studio/src/index.ts +71 -12
  283. package/packages/render-hyperframes/README.md +7 -7
  284. package/packages/render-hyperframes/package.json +1 -1
  285. package/packages/render-hyperframes/src/component.ts +11 -1
  286. package/packages/render-hyperframes/src/fragment.ts +7 -4
  287. package/packages/render-hyperframes/src/manifest.ts +4 -4
  288. package/packages/render-hyperframes/src/surface.ts +6 -6
  289. package/packages/runtime/README.md +8 -1
  290. package/packages/runtime/src/catalog.ts +2 -0
  291. package/packages/runtime/src/execution.ts +15 -4
  292. package/packages/runtime/src/index.ts +2 -1
  293. package/packages/runtime/src/log.ts +51 -0
  294. package/packages/runtime/src/types.ts +5 -8
  295. package/packages/runtime-host-node/README.md +30 -0
  296. package/packages/runtime-host-node/package.json +1 -0
  297. package/packages/runtime-host-node/src/index.ts +25 -9
  298. package/packages/runtime-host-node/src/packages.ts +47 -50
  299. package/packages/runtime-kit/README.md +5 -0
  300. package/packages/runtime-kit/src/index.ts +3 -1
  301. package/packages/runtime-local/README.md +111 -4
  302. package/packages/runtime-local/src/config.ts +202 -56
  303. package/packages/runtime-local/src/control.ts +9 -1
  304. package/packages/runtime-local/src/executor.ts +66 -0
  305. package/packages/runtime-local/src/host.ts +37 -17
  306. package/packages/runtime-local/src/log.ts +48 -0
  307. package/packages/runtime-local/src/programs.ts +41 -25
  308. package/packages/runtime-local/src/result-writer.ts +2 -0
  309. package/packages/runtime-local/src/runtime.ts +10 -6
  310. package/packages/runtime-local/src/supervisor.ts +142 -0
  311. package/packages/runtime-local/src/types.ts +9 -2
  312. package/packages/runtime-local/src/worker-process.ts +7 -24
  313. package/packages/runtime-local/src/worker.ts +55 -83
  314. package/packages/screen-overlay/README.md +3 -3
  315. package/packages/screen-overlay/package.json +1 -1
  316. package/packages/screen-overlay/src/component.ts +3 -3
  317. package/packages/screen-overlay/src/fragment.ts +5 -4
  318. package/packages/screen-overlay/src/manifest.ts +7 -6
  319. package/packages/screen-overlay/src/program.ts +9 -9
  320. package/packages/screen-overlay/src/surface.ts +6 -7
  321. package/packages/script/README.md +37 -2
  322. package/packages/script/src/edit.ts +123 -73
  323. package/packages/script/src/lexical.ts +40 -6
  324. package/packages/script/src/manifest.ts +2 -2
  325. package/packages/script/src/narrative.ts +1 -0
  326. package/packages/script/src/parser.ts +36 -8
  327. package/packages/script/src/surface.ts +4 -3
  328. package/packages/script/src/types.ts +5 -1
  329. package/packages/script-studio/package.json +9 -3
  330. package/packages/script-studio/src/index.ts +2 -0
  331. package/packages/script-studio/src/projection.ts +105 -0
  332. package/packages/seedance/README.md +30 -1
  333. package/packages/seedance/src/index.ts +18 -7
  334. package/packages/seedance/src/surface.ts +17 -4
  335. package/packages/seedance-kits/README.md +10 -2
  336. package/packages/seedance-kits/kits/speaker-v1.svs +5 -5
  337. package/packages/seedream/README.md +1 -1
  338. package/packages/sound/README.md +81 -0
  339. package/packages/sound/package.json +25 -0
  340. package/packages/sound/src/activation.ts +11 -0
  341. package/packages/sound/src/component.ts +28 -0
  342. package/packages/sound/src/index.ts +5 -0
  343. package/packages/sound/src/manifest.ts +47 -0
  344. package/packages/sound/src/program.ts +84 -0
  345. package/packages/sound/src/style.ts +34 -0
  346. package/packages/sound/src/surface.ts +90 -0
  347. package/packages/sound-studio/README.md +22 -0
  348. package/packages/sound-studio/package.json +20 -0
  349. package/packages/sound-studio/src/activation.ts +6 -0
  350. package/packages/sound-studio/src/index.ts +74 -0
  351. package/packages/speech/README.md +2 -2
  352. package/packages/speech-alignment/src/align.ts +45 -21
  353. package/packages/speech-alignment/src/local.ts +8 -0
  354. package/packages/speech-alignment/src/locate.ts +1 -1
  355. package/packages/store-sqlite/README.md +7 -0
  356. package/packages/store-sqlite/src/store.ts +84 -83
  357. package/packages/studio/INSPECTOR.md +117 -0
  358. package/packages/studio/README.md +149 -14
  359. package/packages/studio/package.json +8 -7
  360. package/packages/studio/src/build-library.ts +99 -84
  361. package/packages/studio/src/companion-assembly.ts +1 -0
  362. package/packages/studio/src/domain.ts +1 -1
  363. package/packages/studio/src/fallback-companions.ts +3 -3
  364. package/packages/studio/src/feedback-server.ts +51 -0
  365. package/packages/studio/src/feedback-store.ts +66 -0
  366. package/packages/studio/src/feedback.ts +60 -0
  367. package/packages/studio/src/library-media.ts +19 -0
  368. package/packages/studio/src/parameter-values.ts +83 -1
  369. package/packages/studio/src/parameters.ts +132 -43
  370. package/packages/studio/src/preview/render.ts +5 -1
  371. package/packages/studio/src/preview/runtime-shim.ts +49 -3
  372. package/packages/studio/src/programme.ts +14 -30
  373. package/packages/studio/src/server.ts +66 -34
  374. package/packages/studio/src/shared.ts +24 -8
  375. package/packages/studio/src/snapshot.ts +21 -132
  376. package/packages/studio/src/studio-preflight.ts +3 -3
  377. package/packages/studio/src/studio-registry.ts +72 -6
  378. package/packages/studio/src/style.css +342 -190
  379. package/packages/studio/src/temporal-edit.ts +131 -0
  380. package/packages/studio/src/ui/artifact-name.ts +80 -0
  381. package/packages/studio/src/ui/artifact-preview.ts +99 -0
  382. package/packages/studio/src/ui/comments.ts +233 -0
  383. package/packages/studio/src/ui/icons.ts +10 -1
  384. package/packages/studio/src/ui/library.ts +373 -192
  385. package/packages/studio/src/ui/main.ts +291 -110
  386. package/packages/studio/src/ui/material-preview.ts +1 -1
  387. package/packages/studio/src/ui/overlay.ts +31 -26
  388. package/packages/studio/src/ui/scrub-preview.ts +108 -0
  389. package/packages/studio/src/ui/sidebar-panel.ts +31 -0
  390. package/packages/studio/src/ui/stage.ts +188 -9
  391. package/packages/studio/src/ui/timeline.ts +187 -281
  392. package/packages/studio/start.ts +19 -5
  393. package/packages/studio-adapter/README.md +151 -11
  394. package/packages/studio-adapter/src/index.ts +126 -14
  395. package/packages/temporal/README.md +6 -6
  396. package/packages/temporal/package.json +1 -1
  397. package/packages/temporal/src/component.ts +5 -6
  398. package/packages/temporal/src/index.ts +8 -6
  399. package/packages/temporal/src/location.ts +11 -11
  400. package/packages/temporal/src/projection.ts +19 -19
  401. package/packages/temporal/src/rational.ts +4 -4
  402. package/packages/temporal-markup/EDITING.md +183 -0
  403. package/packages/temporal-markup/README.md +31 -11
  404. package/packages/temporal-markup/package.json +1 -1
  405. package/packages/temporal-markup/src/index.ts +20 -25
  406. package/packages/temporal-markup/src/space.ts +10 -41
  407. package/packages/timeline/README.md +31 -0
  408. package/packages/{semantic-track → timeline}/package.json +1 -1
  409. package/packages/timeline/src/activation.ts +8 -0
  410. package/packages/timeline/src/component.ts +24 -0
  411. package/packages/timeline/src/identity.ts +60 -0
  412. package/packages/timeline/src/index.ts +6 -0
  413. package/packages/{semantic-track → timeline}/src/location.ts +20 -20
  414. package/packages/timeline/src/manifest.ts +39 -0
  415. package/packages/{semantic-track → timeline}/src/projection.ts +14 -14
  416. package/packages/timeline/src/types.ts +24 -0
  417. package/packages/timeline-author/README.md +68 -0
  418. package/packages/{speech-track → timeline-author}/package.json +6 -4
  419. package/packages/timeline-author/src/activation.ts +22 -0
  420. package/packages/{speech-track → timeline-author}/src/component.ts +15 -13
  421. package/packages/{speech-track → timeline-author}/src/fragment.ts +14 -23
  422. package/packages/timeline-author/src/index.ts +7 -0
  423. package/packages/timeline-author/src/manifest.ts +78 -0
  424. package/packages/timeline-author/src/program.ts +107 -0
  425. package/packages/timeline-author/src/surface.ts +46 -0
  426. package/packages/timeline-author/src/types.ts +6 -0
  427. package/packages/typography-track/README.md +4 -4
  428. package/packages/typography-track/package.json +1 -1
  429. package/packages/typography-track/src/component.ts +4 -4
  430. package/packages/typography-track/src/fragment.ts +4 -3
  431. package/packages/typography-track/src/manifest.ts +11 -9
  432. package/packages/typography-track/src/program.ts +15 -15
  433. package/packages/typography-track/src/surface.ts +17 -18
  434. package/packages/typography-track-studio/package.json +8 -3
  435. package/packages/typography-track-studio/src/index.ts +5 -1
  436. package/packages/video-cli/README.md +16 -4
  437. package/packages/video-cli/package.json +2 -1
  438. package/packages/video-cli/src/creation.ts +25 -9
  439. package/packages/video-cli/src/index.ts +1 -1
  440. package/packages/video-cli/src/studio-distribution.ts +2 -1
  441. package/packages/volcengine-matting/README.md +3 -3
  442. package/packages/whisperx/README.md +8 -1
  443. package/packages/workspace/src/index.ts +2 -0
  444. package/packages/workspace-fs-node/README.md +4 -1
  445. package/packages/workspace-fs-node/src/workspace.ts +3 -1
  446. package/services/whisperx/README.md +14 -0
  447. package/services/whisperx/pyproject.toml +2 -0
  448. package/services/whisperx/uv.lock +20 -20
  449. package/packages/provider-hyperframes-aws-lambda/README.md +0 -30
  450. package/packages/provider-hyperframes-aws-lambda/package.json +0 -30
  451. package/packages/provider-hyperframes-aws-lambda/src/activation.ts +0 -97
  452. package/packages/provider-hyperframes-aws-lambda/src/client.ts +0 -114
  453. package/packages/provider-hyperframes-aws-lambda/src/index.ts +0 -21
  454. package/packages/provider-hyperframes-aws-lambda/src/provider.ts +0 -522
  455. package/packages/provider-kie/README.md +0 -134
  456. package/packages/provider-kie/package.json +0 -43
  457. package/packages/provider-kie/src/activation.ts +0 -71
  458. package/packages/provider-kie/src/index.ts +0 -10
  459. package/packages/provider-kie/src/mapping.ts +0 -182
  460. package/packages/provider-kie/src/provider.ts +0 -657
  461. package/packages/provider-kie/src/routes.ts +0 -150
  462. package/packages/provider-xiaomi-mimo/README.md +0 -32
  463. package/packages/provider-xiaomi-mimo/src/activation.ts +0 -57
  464. package/packages/provider-xiaomi-mimo/src/index.ts +0 -5
  465. package/packages/provider-xiaomi-mimo/src/provider.ts +0 -214
  466. package/packages/runtime/src/environment.ts +0 -6
  467. package/packages/semantic-track/README.md +0 -21
  468. package/packages/semantic-track/src/activation.ts +0 -8
  469. package/packages/semantic-track/src/component.ts +0 -24
  470. package/packages/semantic-track/src/identity.ts +0 -58
  471. package/packages/semantic-track/src/index.ts +0 -6
  472. package/packages/semantic-track/src/manifest.ts +0 -38
  473. package/packages/semantic-track/src/types.ts +0 -26
  474. package/packages/speech-track/README.md +0 -51
  475. package/packages/speech-track/src/activation.ts +0 -19
  476. package/packages/speech-track/src/index.ts +0 -7
  477. package/packages/speech-track/src/manifest.ts +0 -73
  478. package/packages/speech-track/src/program.ts +0 -64
  479. package/packages/speech-track/src/surface.ts +0 -35
  480. package/packages/speech-track/src/types.ts +0 -6
  481. package/packages/speech-track-studio/README.md +0 -3
  482. package/packages/speech-track-studio/package.json +0 -14
  483. package/packages/speech-track-studio/src/activation.ts +0 -7
  484. package/packages/speech-track-studio/src/index.ts +0 -40
  485. /package/packages/{cli → project-context-node}/src/project-context.ts +0 -0
  486. /package/packages/{speech-track → timeline-author}/preview/Track.png +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { Clip, StudioFailure, StudioInspectorDomain, StudioSnapshot } from "../shared.js";
2
2
  import type { CanonicalValue, ValueSchema } from "@hypit/protocol";
3
- import { validateParameterValue } from "../parameter-values.js";
3
+ import { parameterAuthorValue, parameterControlForSchema, parameterNumber, parameterOption, parameterRecordSchema, parameterRecordVariants, validateParameterValue } from "../parameter-values.js";
4
4
  import { createCodePane } from "./code.js";
5
5
  import { icon } from "./icons.js";
6
6
  import { createLibraryPane } from "./library.js";
@@ -9,8 +9,11 @@ import type { Highlight } from "./code.js";
9
9
  import { intentAtOffset, spanAtOffset } from "./markers.js";
10
10
  import { clipAtOffset, createStore } from "./selection.js";
11
11
  import { createStage } from "./stage.js";
12
+ import { semanticGestureSpan } from "../temporal-edit.js";
13
+ import type { SemanticTarget } from "../temporal-edit.js";
12
14
  import { applyStudioMutation } from "./writeback.js";
13
15
  import { createTimeline } from "./timeline.js";
16
+ import { createComments } from "./comments.js";
14
17
  import "../style.css";
15
18
 
16
19
  const app = document.querySelector<HTMLElement>("#app")!;
@@ -37,6 +40,10 @@ app.innerHTML = `
37
40
  <div class="topbar-right">
38
41
  <div class="meta" data-meta></div>
39
42
  <div class="status" data-status></div>
43
+ <div class="view-tabs" role="tablist" aria-label="Studio view">
44
+ <button type="button" role="tab" data-view="studio" aria-selected="true">${icon("studio")}Studio</button>
45
+ <button type="button" role="tab" data-view="comments" aria-selected="false">${icon("comments")}Comments</button>
46
+ </div>
40
47
  </div>
41
48
  </header>
42
49
  <main class="studio-shell">
@@ -60,9 +67,10 @@ app.innerHTML = `
60
67
 
61
68
  const store = createStore();
62
69
  const code = createCodePane();
63
- const library = createLibraryPane(code);
70
+ const stage = createStage(store, (id) => library.selectArtifact(id));
71
+ const library = createLibraryPane(code, (artifact) => stage.openArtifact(artifact), (artifact) => stage.renameArtifact(artifact));
64
72
  const timeline = createTimeline(store);
65
- const stage = createStage(store);
73
+ const comments = createComments(store, stage);
66
74
  app.querySelector<HTMLElement>("[data-library]")!.append(library.element);
67
75
  app.querySelector<HTMLElement>("[data-timeline]")!.append(timeline.element);
68
76
  app.querySelector<HTMLElement>("[data-stage]")!.append(stage.element);
@@ -72,8 +80,9 @@ app.querySelector<HTMLElement>("[data-stage]")!.append(stage.element);
72
80
  const shell = app.querySelector<HTMLElement>(".studio-shell")!;
73
81
  const upperShell = app.querySelector<HTMLElement>(".upper-shell")!;
74
82
  const workspacePanel = app.querySelector<HTMLElement>(".workspace-panel")!;
83
+ const sidebarMinimum = 320;
75
84
  const sourceHandle = createHandle({
76
- axis: "column", initial: Math.round(window.innerWidth * 0.34), minimum: 280,
85
+ axis: "column", initial: 416, minimum: sidebarMinimum,
77
86
  maximum: () => Math.max(360, upperShell.clientWidth - 720),
78
87
  apply: (size) => { upperShell.style.setProperty("--source-width", `${size}px`); },
79
88
  remember: "hypit-studio.v3.source-width",
@@ -81,13 +90,21 @@ const sourceHandle = createHandle({
81
90
  sourceHandle.classList.add("source-handle");
82
91
  upperShell.insertBefore(sourceHandle, app.querySelector<HTMLElement>("[data-stage]")!);
83
92
  const workspaceHandle = createHandle({
84
- axis: "column", initial: Math.round(window.innerWidth * 0.22), minimum: 270, invert: true,
93
+ axis: "column", initial: Math.round(window.innerWidth * 0.22), minimum: sidebarMinimum, invert: true,
85
94
  maximum: () => Math.max(320, upperShell.clientWidth - 680),
86
95
  apply: (size) => { upperShell.style.setProperty("--workspace-width", `${size}px`); },
87
96
  remember: "hypit-studio.v3.workspace-width",
88
97
  });
89
98
  workspaceHandle.classList.add("workspace-handle");
90
99
  upperShell.insertBefore(workspaceHandle, workspacePanel);
100
+ const commentsHandle = createHandle({
101
+ axis: "column", initial: 360, minimum: 280, invert: true,
102
+ maximum: () => Math.max(280, upperShell.clientWidth - 360),
103
+ apply: (size) => { upperShell.style.setProperty("--comments-width", `${size}px`); },
104
+ remember: "hypit-studio.comments-width",
105
+ });
106
+ commentsHandle.classList.add("comments-handle");
107
+ upperShell.append(commentsHandle, comments.element);
91
108
  shell.insertBefore(createHandle({
92
109
  axis: "row", initial: Math.round(window.innerHeight * 0.45), minimum: 220, invert: true,
93
110
  maximum: () => Math.max(260, shell.clientHeight - 260),
@@ -101,6 +118,29 @@ const project = app.querySelector<HTMLElement>("[data-project]")!;
101
118
  const status = app.querySelector<HTMLElement>("[data-status]")!;
102
119
  const failureView = app.querySelector<HTMLElement>("[data-failure]")!;
103
120
 
121
+ const changeView = (): void => {
122
+ const current = window.location.hash === "#comments" ? "comments" : "studio";
123
+ shell.dataset.view = current;
124
+ app.querySelectorAll<HTMLButtonElement>(".view-tabs [data-view]").forEach((button) => {
125
+ const selected = button.dataset.view === current;
126
+ button.setAttribute("aria-selected", String(selected)); button.tabIndex = selected ? 0 : -1;
127
+ });
128
+ comments.activate(current === "comments");
129
+ };
130
+ app.querySelectorAll<HTMLButtonElement>(".view-tabs [data-view]").forEach((button) => {
131
+ button.addEventListener("click", () => { window.location.hash = button.dataset.view!; });
132
+ button.addEventListener("keydown", (event) => {
133
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
134
+ event.preventDefault();
135
+ event.stopPropagation();
136
+ const next = button.dataset.view === "studio" ? "comments" : "studio";
137
+ window.location.hash = next;
138
+ app.querySelector<HTMLButtonElement>(`.view-tabs [data-view="${next}"]`)!.focus();
139
+ });
140
+ });
141
+ window.addEventListener("hashchange", changeView);
142
+ changeView();
143
+
104
144
  timeline.element.addEventListener("studio:write", (event) => {
105
145
  const state = (event as CustomEvent<{ readonly state?: string }>).detail.state;
106
146
  status.className = state === "error" ? "status error" : state === "saved" ? "status saved" : "status saving";
@@ -151,7 +191,7 @@ const domainPresentation: Readonly<Record<StudioInspectorDomain, { readonly labe
151
191
  how: { label: "How", icon: "how" },
152
192
  when: { label: "When", icon: "when" },
153
193
  };
154
- const domainOrder: readonly StudioInspectorDomain[] = ["where", "how", "when"];
194
+ const domainOrder: readonly StudioInspectorDomain[] = ["where", "when", "how"];
155
195
  const inspectorDomainByEntity = new Map<string, StudioInspectorDomain>();
156
196
  const inspectorPageByEntity = new Map<string, string>();
157
197
 
@@ -191,9 +231,22 @@ function textValue(value: CanonicalValue): string {
191
231
  return typeof value === "string" ? value : value === null ? "" : String(value);
192
232
  }
193
233
 
234
+ function restoreParameterControls(entityId: string): void {
235
+ const current = store.current();
236
+ if (current?.selection.kind === "clip" && current.selection.clipId === entityId) {
237
+ renderInspector(current.snapshot, entityId);
238
+ }
239
+ }
240
+
194
241
  function commitControl(entityId: string, parameter: Clip["inspector"][number], replacement: CanonicalValue): void {
195
- if (sameValue(replacement, parameter.value)) return;
196
- void writeParameter(entityId, parameter, replacement);
242
+ try {
243
+ if (sameValue(parameterAuthorValue(parameter, replacement), parameter.value)) return;
244
+ void writeParameter(entityId, parameter, replacement);
245
+ } catch (error) {
246
+ restoreParameterControls(entityId);
247
+ status.textContent = "Invalid value"; status.className = "status error";
248
+ status.title = error instanceof Error ? error.message : String(error);
249
+ }
197
250
  }
198
251
 
199
252
  let openColorPicker: {
@@ -214,8 +267,8 @@ document.addEventListener("keydown", (event) => {
214
267
  }, true);
215
268
 
216
269
  function selectControl(
217
- entityId: string,
218
270
  parameter: Clip["inspector"][number],
271
+ change: (value: CanonicalValue) => void,
219
272
  ): HTMLElement {
220
273
  const control = document.createElement("div");
221
274
  control.className = "parameter-select parameter-control";
@@ -227,8 +280,11 @@ function selectControl(
227
280
  trigger.setAttribute("aria-expanded", "false");
228
281
  const selected = document.createElement("span");
229
282
  selected.className = "parameter-select-value";
230
- const current = textValue(parameter.value);
231
- selected.textContent = current;
283
+ const choices = (parameter.options ?? []).map(parameterOption);
284
+ if (choices.some(option => option.description || option.preview)) control.classList.add("rich-options");
285
+ const current = choices.find(option => option.value === parameter.value)
286
+ ?? choices.find(option => String(option.value) === String(parameter.value));
287
+ selected.textContent = current?.label ?? textValue(parameter.value);
232
288
  const chevron = document.createElement("span");
233
289
  chevron.className = "parameter-select-chevron";
234
290
  chevron.innerHTML = icon("chevron");
@@ -242,22 +298,32 @@ function selectControl(
242
298
  menu.hidden = true;
243
299
  trigger.setAttribute("aria-controls", menu.id);
244
300
 
245
- const options = (parameter.options ?? []).map((option) => {
301
+ const options = choices.map((option) => {
246
302
  const item = document.createElement("button");
247
303
  item.type = "button";
248
304
  item.className = `parameter-select-option${option === current ? " active" : ""}`;
249
- item.textContent = option;
305
+ const label = document.createElement("span"); label.textContent = option.label;
306
+ item.append(label);
307
+ if (option.description) { const description = document.createElement("small"); description.textContent = option.description; item.append(description); }
308
+ if (option.preview?.kind === "color") {
309
+ const swatch = document.createElement("span"); swatch.className = "parameter-option-swatch";
310
+ swatch.style.backgroundColor = option.preview.color; item.prepend(swatch);
311
+ } else if (option.preview?.kind === "font") {
312
+ const sample = document.createElement("span"); sample.className = "parameter-option-font";
313
+ sample.style.fontFamily = option.preview.family; sample.textContent = option.preview.sample ?? option.label;
314
+ sample.setAttribute("aria-hidden", "true"); item.append(sample);
315
+ }
250
316
  item.setAttribute("role", "option");
251
317
  item.setAttribute("aria-selected", String(option === current));
252
318
  item.addEventListener("click", () => {
253
- selected.textContent = option;
319
+ selected.textContent = option.label;
254
320
  for (const sibling of options) {
255
321
  sibling.classList.toggle("active", sibling === item);
256
322
  sibling.setAttribute("aria-selected", String(sibling === item));
257
323
  }
258
324
  close(false);
259
325
  trigger.focus();
260
- commitControl(entityId, parameter, option);
326
+ change(option.value);
261
327
  });
262
328
  return item;
263
329
  });
@@ -358,7 +424,7 @@ function colorValueControl(
358
424
  value.value = initial;
359
425
  value.setAttribute("aria-label", label);
360
426
  value.spellcheck = false;
361
- const alpha = exact && initial.length === 9 ? initial.slice(7) : "";
427
+ let alpha = exact && initial.length === 9 ? initial.slice(7) : "";
362
428
  const replacement = (): string => `${picker.value.toUpperCase()}${alpha}`;
363
429
  const close = (): void => {
364
430
  if (openColorPicker?.root === colorControl) openColorPicker = undefined;
@@ -389,7 +455,12 @@ function colorValueControl(
389
455
  });
390
456
  value.addEventListener(draft ? "input" : "change", () => {
391
457
  const next = value.value.trim();
392
- swatch.style.background = /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/iu.test(next) ? next : "transparent";
458
+ const valid = /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/iu.test(next);
459
+ swatch.style.background = valid ? next : "transparent";
460
+ if (valid) {
461
+ picker.value = next.slice(0, 7);
462
+ alpha = next.length === 9 ? next.slice(7) : "";
463
+ }
393
464
  change(next);
394
465
  });
395
466
  colorControl.append(swatch, picker);
@@ -403,7 +474,8 @@ function fieldLabel(name: string): string {
403
474
 
404
475
  function blankValue(schema: ValueSchema): CanonicalValue {
405
476
  if (schema.kind === "string") return "";
406
- if (schema.kind === "number" || schema.kind === "null" || schema.kind === "literal" || schema.kind === "oneOf") return null;
477
+ if (schema.kind === "literal") return schema.value;
478
+ if (schema.kind === "number" || schema.kind === "null" || schema.kind === "oneOf") return null;
407
479
  if (schema.kind === "boolean") return false;
408
480
  if (schema.kind === "array") return [];
409
481
  if (schema.kind === "object") return Object.fromEntries(Object.entries(schema.fields)
@@ -418,31 +490,12 @@ function scalarDraftControl(
418
490
  held: CanonicalValue | undefined,
419
491
  change: (value: CanonicalValue) => void,
420
492
  ): HTMLElement {
421
- if (schema.kind === "string" && schema.format === "color") {
422
- return colorValueControl(label, typeof held === "string" ? held : "", change, true);
423
- }
424
- const input = document.createElement("input");
425
- input.className = "parameter-value parameter-structured-value";
426
- input.setAttribute("aria-label", label);
427
- input.spellcheck = false;
428
- if (schema.kind === "boolean") {
429
- input.type = "checkbox";
430
- input.checked = held === true;
431
- input.addEventListener("change", () => change(input.checked));
432
- return input;
433
- }
434
- input.type = "text";
435
- if (schema.kind === "number") input.inputMode = "decimal";
436
- input.value = held === undefined || held === null ? "" : textValue(held);
437
- input.addEventListener("input", () => {
438
- if (schema.kind === "number") {
439
- const number = Number(input.value);
440
- change(input.value.trim().length === 0 || !Number.isFinite(number) ? null : number);
441
- } else {
442
- change(input.value);
443
- }
444
- });
445
- return input;
493
+ return parameterControl("", {
494
+ id: label, label, domain: "how", section: { id: "record", label: "" },
495
+ control: parameterControlForSchema(schema) ?? "text",
496
+ ...(schema.kind === "string" && schema.enum !== undefined ? { options: schema.enum } : {}),
497
+ schema, value: held ?? null,
498
+ }, change);
446
499
  }
447
500
 
448
501
  function recordDraftControl(
@@ -456,30 +509,34 @@ function recordDraftControl(
456
509
  const fields = document.createElement("div");
457
510
  fields.className = "parameter-record-fields";
458
511
  for (const [name, field] of Object.entries(schema.fields)) {
459
- const row = document.createElement("label");
460
- row.className = "parameter-record-field";
461
- const copy = document.createElement("span");
462
- copy.textContent = fieldLabel(name);
463
- row.append(copy, scalarDraftControl(fieldLabel(name), field.schema, record[name], (next) => {
512
+ if (field.schema.kind === "literal") continue;
513
+ fields.append(scalarDraftControl(fieldLabel(name), field.schema, record[name], (next) => {
464
514
  record = { ...record, [name]: next };
465
515
  change(record);
466
516
  }));
467
- fields.append(row);
468
517
  }
469
518
  return fields;
470
519
  }
471
520
 
472
- function structuredControl(entityId: string, parameter: Clip["inspector"][number]): HTMLElement {
473
- const schema = parameter.schema;
521
+ function structuredControl(entityId: string, parameter: Clip["inspector"][number], change: (value: CanonicalValue) => void): HTMLElement {
522
+ const declaredSchema = parameter.schema;
474
523
  const shell = document.createElement("div");
475
524
  shell.className = "parameter-structured";
476
- if (schema === undefined || (schema.kind !== "array" && schema.kind !== "object")) return shell;
525
+ if (declaredSchema === undefined || (declaredSchema.kind !== "array" && declaredSchema.kind !== "object" && !parameterRecordVariants(declaredSchema).length)) return shell;
477
526
  let draft = structuredClone(parameter.value);
478
- let refreshDecisions = (): void => {};
527
+ const notice = document.createElement("div");
528
+ notice.className = "parameter-structured-notice";
529
+ notice.setAttribute("role", "status");
530
+ const save = (): void => {
531
+ const changed = !sameValue(draft, parameter.value);
532
+ notice.hidden = !changed || valid();
533
+ notice.textContent = "Complete the required fields with valid values to save.";
534
+ if (changed && valid()) change(draft);
535
+ };
479
536
 
480
537
  const valid = (): boolean => {
481
538
  try {
482
- validateParameterValue(draft, schema, parameter.label);
539
+ validateParameterValue(draft, declaredSchema, parameter.label);
483
540
  return true;
484
541
  } catch {
485
542
  return false;
@@ -488,6 +545,21 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
488
545
 
489
546
  const render = (): void => {
490
547
  shell.replaceChildren();
548
+ const variants = parameterRecordVariants(declaredSchema);
549
+ const schema = declaredSchema.kind === "array" ? declaredSchema : parameterRecordSchema(declaredSchema, draft);
550
+ if (schema === undefined) return;
551
+ if (variants.length) {
552
+ const active = variants.find(variant => variant.schema === schema)!;
553
+ shell.append(parameterControl(entityId, {
554
+ ...parameter, control: "select", value: variants.indexOf(active),
555
+ options: variants.map((variant, index) => ({ value: index, label: textValue(variant.value) })),
556
+ }, (next) => {
557
+ if (next === variants.indexOf(active)) return;
558
+ draft = blankValue(variants[Number(next)]!.schema);
559
+ render();
560
+ save();
561
+ }));
562
+ }
491
563
  const summary = document.createElement("div");
492
564
  summary.className = "parameter-structured-summary";
493
565
  const count = document.createElement("span");
@@ -495,7 +567,7 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
495
567
  ? `${draft.length} ${draft.length === 1 ? "item" : "items"}`
496
568
  : "Structured value";
497
569
  summary.append(count);
498
- shell.append(summary);
570
+ if (schema.kind === "array") shell.append(summary);
499
571
 
500
572
  if (schema.kind === "array") {
501
573
  const list = Array.isArray(draft) ? draft : [];
@@ -508,12 +580,12 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
508
580
  ? recordDraftControl(schema.items, item, (next) => {
509
581
  const current = Array.isArray(draft) ? draft : [];
510
582
  draft = current.map((candidate, heldIndex) => heldIndex === index ? next : candidate);
511
- refreshDecisions();
583
+ save();
512
584
  })
513
585
  : scalarDraftControl(`${parameter.label} ${index + 1}`, schema.items, item, (next) => {
514
586
  const current = Array.isArray(draft) ? draft : [];
515
587
  draft = current.map((candidate, heldIndex) => heldIndex === index ? next : candidate);
516
- refreshDecisions();
588
+ save();
517
589
  });
518
590
  const actions = document.createElement("span");
519
591
  actions.className = "parameter-list-actions";
@@ -523,6 +595,7 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
523
595
  reordered.splice(index + offset, 0, moving!);
524
596
  draft = reordered;
525
597
  render();
598
+ save();
526
599
  };
527
600
  const up = document.createElement("button");
528
601
  up.type = "button";
@@ -545,6 +618,7 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
545
618
  const current = Array.isArray(draft) ? draft : [];
546
619
  draft = current.filter((_, heldIndex) => heldIndex !== index);
547
620
  render();
621
+ save();
548
622
  });
549
623
  actions.append(up, down, remove);
550
624
  row.append(editor, actions);
@@ -554,7 +628,7 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
554
628
  } else {
555
629
  shell.append(recordDraftControl(schema, draft, (next) => {
556
630
  draft = next;
557
- refreshDecisions();
631
+ save();
558
632
  }));
559
633
  }
560
634
 
@@ -571,80 +645,124 @@ function structuredControl(entityId: string, parameter: Clip["inspector"][number
571
645
  const current = Array.isArray(draft) ? draft : [];
572
646
  draft = [...current, blankValue(schema.items)];
573
647
  render();
648
+ save();
574
649
  });
575
650
  footer.append(add);
576
651
  }
577
- const decisions = document.createElement("span");
578
- decisions.className = "parameter-structured-decisions";
579
- const reset = document.createElement("button");
580
- reset.type = "button";
581
- reset.textContent = "Reset";
582
- reset.disabled = sameValue(draft, parameter.value);
583
- reset.addEventListener("click", () => {
584
- draft = structuredClone(parameter.value);
585
- render();
586
- });
587
- const apply = document.createElement("button");
588
- apply.type = "button";
589
- apply.className = "primary";
590
- apply.textContent = "Apply";
591
- apply.addEventListener("click", () => commitControl(entityId, parameter, draft));
592
- refreshDecisions = () => {
593
- reset.disabled = sameValue(draft, parameter.value);
594
- apply.disabled = reset.disabled || !valid();
595
- };
596
- refreshDecisions();
597
- decisions.append(reset, apply);
598
- footer.append(decisions);
599
- shell.append(footer);
652
+ if (schema.kind === "array") shell.append(footer);
653
+ notice.hidden = sameValue(draft, parameter.value) || valid();
654
+ shell.append(notice);
600
655
  };
601
656
  render();
602
657
  return shell;
603
658
  }
604
659
 
605
- function parameterControl(entityId: string, parameter: Clip["inspector"][number]): HTMLElement {
660
+ function parameterControl(
661
+ entityId: string,
662
+ parameter: Clip["inspector"][number],
663
+ change?: (value: CanonicalValue) => void,
664
+ ): HTMLElement {
665
+ const editable = parameter.edit !== undefined || change !== undefined;
666
+ const commit = change ?? ((value: CanonicalValue) => commitControl(entityId, parameter, value));
667
+ if (editable && parameter.control === "record" && parameter.schema !== undefined && parameterRecordVariants(parameter.schema).length) {
668
+ return structuredControl(entityId, parameter, commit);
669
+ }
606
670
  const row = document.createElement("div");
607
- row.className = `parameter-row parameter-editable control-${parameter.control}`;
671
+ row.className = `parameter-row ${!editable ? "parameter-readonly" : "parameter-editable"} control-${parameter.control}`;
608
672
  const name = document.createElement("span");
609
673
  name.className = "parameter-label";
610
674
  name.textContent = parameter.label;
611
675
  name.title = parameter.summary ?? parameter.label;
612
676
  const right = document.createElement("span");
613
677
  right.className = "parameter-right parameter-control";
678
+ if (!editable) {
679
+ const value = document.createElement("span");
680
+ value.className = "parameter-display";
681
+ let displayValue = textValue(parameter.value);
682
+ let unitLabel = parameter.unit;
683
+ if (parameter.control === "number") {
684
+ try {
685
+ const numeric = parameterNumber(change && parameter.value === null ? 0 : parameter.value, parameter.number);
686
+ displayValue = String(numeric.value);
687
+ unitLabel = numeric.suffix || parameter.unit;
688
+ } catch { /* Keep the actual value visible when it is not a scalar. */ }
689
+ } else if (parameter.control === "select") {
690
+ displayValue = parameter.options?.map(parameterOption).find(option => option.value === parameter.value)?.label ?? displayValue;
691
+ }
692
+ value.textContent = displayValue;
693
+ right.append(value);
694
+ if (unitLabel !== undefined) {
695
+ const unit = document.createElement("small"); unit.textContent = unitLabel; right.append(unit);
696
+ }
697
+ row.append(name, right);
698
+ return row;
699
+ }
700
+ let unitLabel = parameter.unit;
614
701
  if (parameter.control === "select") {
615
- right.append(selectControl(entityId, parameter));
702
+ right.append(selectControl(parameter, commit));
616
703
  } else if (parameter.control === "color") {
617
704
  right.append(colorValueControl(parameter.label, textValue(parameter.value), (next) => {
618
- commitControl(entityId, parameter, next);
705
+ commit(next);
619
706
  }));
707
+ if (parameter.swatches?.length) {
708
+ const palette = document.createElement("span"); palette.className = "parameter-swatches";
709
+ for (const color of parameter.swatches) {
710
+ const swatch = document.createElement("button"); swatch.type = "button";
711
+ swatch.style.backgroundColor = color; swatch.title = color; swatch.setAttribute("aria-label", `Use ${color}`);
712
+ swatch.addEventListener("click", () => commit(color)); palette.append(swatch);
713
+ }
714
+ right.append(palette);
715
+ }
620
716
  } else if (parameter.control === "list" || parameter.control === "record") {
621
- right.append(structuredControl(entityId, parameter));
717
+ right.append(structuredControl(entityId, parameter, commit));
622
718
  } else {
719
+ if (parameter.multiline && parameter.control === "text") {
720
+ const value = document.createElement("textarea"); value.className = "parameter-value parameter-multiline";
721
+ value.value = textValue(parameter.value); value.setAttribute("aria-label", parameter.label);
722
+ value.addEventListener("change", () => commit(value.value)); right.append(value);
723
+ row.append(name, right); return row;
724
+ }
623
725
  const value = document.createElement("input");
624
726
  value.type = parameter.control === "boolean" ? "checkbox" : "text";
625
- if (parameter.control === "number") value.inputMode = "decimal";
626
727
  value.className = "parameter-value";
627
728
  value.setAttribute("aria-label", parameter.label);
628
729
  value.spellcheck = false;
629
730
  if (value.type === "checkbox") value.checked = parameter.value === true || parameter.value === "true";
630
- else value.value = textValue(parameter.value);
731
+ else if (parameter.control === "number") {
732
+ try {
733
+ const numeric = parameterNumber(change && parameter.value === null ? 0 : parameter.value, parameter.number);
734
+ value.type = "number"; value.inputMode = "decimal"; value.value = change && parameter.value === null ? "" : String(numeric.value);
735
+ const schema = parameter.schema?.kind === "number" ? parameter.schema : undefined;
736
+ const scale = parameter.number?.scale ?? 1;
737
+ const minimum = parameter.number?.minimum ?? (schema?.minimum === undefined ? undefined : schema.minimum * scale);
738
+ const maximum = parameter.number?.maximum ?? (schema?.maximum === undefined ? undefined : schema.maximum * scale);
739
+ value.step = String(parameter.number?.step ?? (schema?.integer ? scale : "any"));
740
+ if (minimum !== undefined) value.min = String(minimum);
741
+ if (maximum !== undefined) value.max = String(maximum);
742
+ value.required = true;
743
+ unitLabel = numeric.suffix || parameter.unit;
744
+ } catch (error) {
745
+ value.value = textValue(parameter.value); value.readOnly = true;
746
+ value.title = error instanceof Error ? error.message : String(error);
747
+ }
748
+ } else value.value = textValue(parameter.value);
631
749
  value.dataset.parameterId = parameter.id;
632
- value.title = parameter.summary ?? parameter.label;
750
+ if (!value.title) value.title = parameter.summary ?? parameter.label;
633
751
  value.addEventListener("change", () => {
634
752
  if (value.type === "checkbox") {
635
- commitControl(entityId, parameter, value.checked);
753
+ commit(value.checked);
636
754
  } else if (parameter.control === "number") {
637
- const number = Number(value.value);
638
- if (Number.isFinite(number)) commitControl(entityId, parameter, number);
755
+ if (change) commit(Number.isFinite(value.valueAsNumber) ? value.valueAsNumber : null);
756
+ else if (value.reportValidity()) commit(value.valueAsNumber);
639
757
  } else {
640
- commitControl(entityId, parameter, value.value);
758
+ commit(value.value);
641
759
  }
642
760
  });
643
761
  right.append(value);
644
762
  }
645
- if (parameter.unit !== undefined) {
763
+ if (unitLabel !== undefined) {
646
764
  const unit = document.createElement("small");
647
- unit.textContent = parameter.unit;
765
+ unit.textContent = unitLabel;
648
766
  right.append(unit);
649
767
  }
650
768
  row.append(name, right);
@@ -671,6 +789,7 @@ async function writeParameter(entityId: string, parameter: Clip["inspector"][num
671
789
  const state = store.current();
672
790
  if (state === undefined) return;
673
791
  parameterWriteState = "Saving";
792
+ status.title = "";
674
793
  status.textContent = parameterWriteState;
675
794
  status.className = "status saving";
676
795
  try {
@@ -685,9 +804,13 @@ async function writeParameter(entityId: string, parameter: Clip["inspector"][num
685
804
  status.textContent = parameterWriteState;
686
805
  status.className = "status saved";
687
806
  } catch (error) {
807
+ // Validation and stale-revision rejections do not publish a new snapshot.
808
+ // Restore the accepted value just as a rejected compilation does.
809
+ restoreParameterControls(entityId);
688
810
  parameterWriteState = "Failed";
689
811
  status.textContent = error instanceof Error ? "Save failed" : parameterWriteState;
690
812
  status.className = "status error";
813
+ status.title = error instanceof Error ? error.message : String(error);
691
814
  }
692
815
  }
693
816
 
@@ -732,7 +855,7 @@ function renderInspector(snapshot: StudioSnapshot, clipId: string | undefined):
732
855
  defaultWorkspaceHeading();
733
856
  const empty = document.createElement("div");
734
857
  empty.className = "inspector-empty";
735
- empty.textContent = "No adjustable parameters";
858
+ empty.textContent = "No details for this selection";
736
859
  inspector.replaceChildren(empty);
737
860
  return;
738
861
  }
@@ -743,17 +866,22 @@ function renderInspector(snapshot: StudioSnapshot, clipId: string | undefined):
743
866
 
744
867
  const domainFields = clip.inspector.filter((field) => field.domain === activeDomain);
745
868
  const pages = new Map<string, { readonly label: string; readonly fields: typeof domainFields }>();
746
- for (const field of domainFields) {
747
- const id = field.page?.id ?? "default";
869
+ const commonFields = domainFields.filter(field => field.page === undefined);
870
+ for (const field of domainFields.filter(field => field.page !== undefined)) {
871
+ const id = field.page!.id;
748
872
  const page = pages.get(id) ?? { label: field.page?.label ?? "", fields: [] };
749
873
  pages.set(id, { ...page, fields: [...page.fields, field] });
750
874
  }
751
875
  const pageIds = [...pages.keys()];
876
+ if (pageIds.length === 0) {
877
+ inspector.replaceChildren(...parameterGroups(clip.id, commonFields));
878
+ return;
879
+ }
752
880
  const memoryKey = `${clip.id}:${activeDomain}`;
753
881
  const rememberedPage = inspectorPageByEntity.get(memoryKey);
754
882
  const activePage = rememberedPage !== undefined && pages.has(rememberedPage) ? rememberedPage : pageIds[0]!;
755
883
  inspectorPageByEntity.set(memoryKey, activePage);
756
- const page = pages.get(activePage)!;
884
+ const page = pages.get(activePage);
757
885
  const subtabs = document.createElement("div");
758
886
  subtabs.className = "inspector-subtabs";
759
887
  if (pageIds.length > 1) {
@@ -771,7 +899,7 @@ function renderInspector(snapshot: StudioSnapshot, clipId: string | undefined):
771
899
  return button;
772
900
  }));
773
901
  }
774
- inspector.replaceChildren(...(pageIds.length > 1 ? [subtabs] : []), ...parameterGroups(clip.id, page.fields));
902
+ inspector.replaceChildren(...(pageIds.length > 1 ? [subtabs] : []), ...parameterGroups(clip.id, [...commonFields, ...(page?.fields ?? [])]));
775
903
  }
776
904
 
777
905
  function renderSemanticInspector(snapshot: StudioSnapshot, segmentId: string): void {
@@ -780,28 +908,80 @@ function renderSemanticInspector(snapshot: StudioSnapshot, segmentId: string): v
780
908
  defaultWorkspaceHeading();
781
909
  const empty = document.createElement("div");
782
910
  empty.className = "inspector-empty";
783
- empty.textContent = "No adjustable parameters";
911
+ empty.textContent = "No details for this selection";
784
912
  inspector.replaceChildren(empty);
785
913
  }
786
914
 
915
+ /** Exact identity choice for coincident anchors, through the same Companion-declared handles. */
916
+ function semanticAnchorInspector(snapshot: StudioSnapshot, kind: "selection" | "moment", id: string): HTMLElement {
917
+ const semantic = snapshot.semantic;
918
+ const consumers = snapshot.tracks.flatMap((track) => track.clips).flatMap((clip) => clip.editHandles
919
+ .filter((handle) => handle.enabled && handle.semantic?.kind === kind && handle.semantic.id === id)
920
+ .map((handle) => ({ clip, handle })));
921
+ const current = kind === "selection" ? semantic?.selections.find((item) => item.id === id)
922
+ : semantic?.moments.find((item) => item.id === id);
923
+ if (!semantic || !current) return group("Timing", []);
924
+ const endpoints = "anchorId" in current ? [["Moment", current.anchorId]]
925
+ : [["Start", current.startAnchorId], ["End", current.endAnchorId]];
926
+ return group("Semantic anchors", endpoints.map(([label, anchorId]) => {
927
+ const anchor = semantic.anchors.find((item) => item.id === anchorId)!;
928
+ const describe = (item: typeof anchor) => {
929
+ const word = semantic.tokens.find((token) => token.id === item.tokenId)?.text;
930
+ return `${item.kind.replaceAll("-", " ")}${word ? ` · ${word}` : ""}${item.segmentId ? ` · ${item.segmentId}` : ""}`;
931
+ };
932
+ const candidates = semantic.anchors.filter((item) => item.frame === anchor.frame).flatMap((item) => {
933
+ const target: SemanticTarget = "anchorId" in current ? { kind: "moment", anchorId: item.id }
934
+ : { kind: "selection", startAnchorId: label === "Start" ? item.id : current.startAnchorId,
935
+ endAnchorId: label === "End" ? item.id : current.endAnchorId };
936
+ const owner = consumers.find(({ handle }) => semanticGestureSpan(semantic.anchors, handle, target) !== undefined);
937
+ return owner ? [{ item, target, ...owner }] : [];
938
+ });
939
+ if (candidates.length < 2) return property(label!, describe(anchor));
940
+ const row = document.createElement("label");
941
+ row.className = "property";
942
+ const name = document.createElement("span");
943
+ name.textContent = label!;
944
+ const control = document.createElement("select");
945
+ control.className = "parameter-value";
946
+ control.setAttribute("aria-label", `${label} semantic anchor`);
947
+ for (const { item } of candidates) {
948
+ const option = document.createElement("option"); option.value = item.id; option.textContent = describe(item);
949
+ control.append(option);
950
+ }
951
+ control.value = anchorId!;
952
+ control.addEventListener("change", () => {
953
+ const choice = candidates.find(({ item }) => item.id === control.value)!;
954
+ const span = semanticGestureSpan(semantic.anchors, choice.handle, choice.target)!;
955
+ const temporal = choice.handle.temporal!;
956
+ control.disabled = true;
957
+ status.textContent = "Saving"; status.className = "status saving";
958
+ void applyStudioMutation({ type: "timeline.adjust", revision: snapshot.revision,
959
+ entityId: choice.clip.id, gesture: choice.handle.gesture,
960
+ target: temporal.kind === "instant" ? { kind: "instant", frame: span.startFrame, semantic: choice.target }
961
+ : { kind: "window", ...span, semantic: choice.target },
962
+ }).then(() => { status.textContent = "Saved"; status.className = "status saved"; })
963
+ .catch((error: unknown) => {
964
+ control.value = anchorId!; status.textContent = "Save failed"; status.className = "status error";
965
+ status.title = error instanceof Error ? error.message : String(error);
966
+ }).finally(() => { control.disabled = false; });
967
+ });
968
+ row.append(name, control);
969
+ return row;
970
+ }));
971
+ }
972
+
787
973
  function renderSemanticSelectionInspector(snapshot: StudioSnapshot, selectionId: string): void {
788
974
  const selection = snapshot.semantic?.selections.find((item) => item.id === selectionId);
789
975
  if (selection === undefined) { inspector.replaceChildren(); return; }
790
976
  defaultWorkspaceHeading();
791
- const empty = document.createElement("div");
792
- empty.className = "inspector-empty";
793
- empty.textContent = "Adjust on the timeline";
794
- inspector.replaceChildren(empty);
977
+ inspector.replaceChildren(semanticAnchorInspector(snapshot, "selection", selectionId));
795
978
  }
796
979
 
797
980
  function renderSemanticMomentInspector(snapshot: StudioSnapshot, momentId: string): void {
798
981
  const moment = snapshot.semantic?.moments.find((item) => item.id === momentId);
799
982
  if (moment === undefined) { inspector.replaceChildren(); return; }
800
983
  defaultWorkspaceHeading();
801
- const empty = document.createElement("div");
802
- empty.className = "inspector-empty";
803
- empty.textContent = "Adjust on the timeline";
804
- inspector.replaceChildren(empty);
984
+ inspector.replaceChildren(semanticAnchorInspector(snapshot, "moment", momentId));
805
985
  }
806
986
 
807
987
  // The word being spoken at the playhead, which is the point of carrying token
@@ -938,6 +1118,7 @@ window.addEventListener("keydown", (event) => {
938
1118
  event.preventDefault();
939
1119
  return;
940
1120
  }
1121
+ if (shell.dataset.view === "comments" && ["-", "_", "=", "+", "\\", "f", "F"].includes(event.key)) return;
941
1122
  if (event.key === "-" || event.key === "_") {
942
1123
  timeline.zoomOut();
943
1124
  event.preventDefault();
@@ -966,6 +1147,7 @@ window.addEventListener("keydown", (event) => {
966
1147
  function applySnapshot(snapshot: StudioSnapshot): void {
967
1148
  failureView.textContent = "";
968
1149
  status.className = "status";
1150
+ status.title = "";
969
1151
  status.textContent = "";
970
1152
  renderMeta(snapshot);
971
1153
  library.show(snapshot);
@@ -993,7 +1175,6 @@ const response = await fetch("/__studio/session");
993
1175
  const initial = await response.json() as StudioSnapshot | StudioFailure;
994
1176
  if (response.ok && "tracks" in initial) applySnapshot(initial);
995
1177
  else applyFailure(initial as StudioFailure);
996
- void library.refresh();
997
1178
 
998
1179
  type Hot = { on(event: string, listener: (value: unknown) => void): void };
999
1180
  const hot = (import.meta as ImportMeta & { hot?: Hot }).hot;