videowright 0.1.0

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 (306) hide show
  1. package/README.md +91 -0
  2. package/dist/cli/argv.d.ts +28 -0
  3. package/dist/cli/argv.d.ts.map +1 -0
  4. package/dist/cli/argv.js +115 -0
  5. package/dist/cli/argv.js.map +1 -0
  6. package/dist/cli/bin.d.ts +7 -0
  7. package/dist/cli/bin.d.ts.map +1 -0
  8. package/dist/cli/bin.js +10 -0
  9. package/dist/cli/bin.js.map +1 -0
  10. package/dist/cli/dev.d.ts +19 -0
  11. package/dist/cli/dev.d.ts.map +1 -0
  12. package/dist/cli/dev.js +104 -0
  13. package/dist/cli/dev.js.map +1 -0
  14. package/dist/cli/discover.d.ts +29 -0
  15. package/dist/cli/discover.d.ts.map +1 -0
  16. package/dist/cli/discover.js +104 -0
  17. package/dist/cli/discover.js.map +1 -0
  18. package/dist/cli/discover_project.d.ts +29 -0
  19. package/dist/cli/discover_project.d.ts.map +1 -0
  20. package/dist/cli/discover_project.js +108 -0
  21. package/dist/cli/discover_project.js.map +1 -0
  22. package/dist/cli/errors.d.ts +10 -0
  23. package/dist/cli/errors.d.ts.map +1 -0
  24. package/dist/cli/errors.js +13 -0
  25. package/dist/cli/errors.js.map +1 -0
  26. package/dist/cli/ffmpeg.d.ts +57 -0
  27. package/dist/cli/ffmpeg.d.ts.map +1 -0
  28. package/dist/cli/ffmpeg.js +122 -0
  29. package/dist/cli/ffmpeg.js.map +1 -0
  30. package/dist/cli/index.d.ts +7 -0
  31. package/dist/cli/index.d.ts.map +1 -0
  32. package/dist/cli/index.js +152 -0
  33. package/dist/cli/index.js.map +1 -0
  34. package/dist/cli/playwright_check.d.ts +44 -0
  35. package/dist/cli/playwright_check.d.ts.map +1 -0
  36. package/dist/cli/playwright_check.js +20 -0
  37. package/dist/cli/playwright_check.js.map +1 -0
  38. package/dist/cli/prompt.d.ts +13 -0
  39. package/dist/cli/prompt.d.ts.map +1 -0
  40. package/dist/cli/prompt.js +47 -0
  41. package/dist/cli/prompt.js.map +1 -0
  42. package/dist/cli/render.d.ts +60 -0
  43. package/dist/cli/render.d.ts.map +1 -0
  44. package/dist/cli/render.js +471 -0
  45. package/dist/cli/render.js.map +1 -0
  46. package/dist/cli/script_cmd.d.ts +26 -0
  47. package/dist/cli/script_cmd.d.ts.map +1 -0
  48. package/dist/cli/script_cmd.js +88 -0
  49. package/dist/cli/script_cmd.js.map +1 -0
  50. package/dist/cli/time_shim.d.ts +44 -0
  51. package/dist/cli/time_shim.d.ts.map +1 -0
  52. package/dist/cli/time_shim.js +390 -0
  53. package/dist/cli/time_shim.js.map +1 -0
  54. package/dist/cli/ts_loader.d.ts +28 -0
  55. package/dist/cli/ts_loader.d.ts.map +1 -0
  56. package/dist/cli/ts_loader.js +95 -0
  57. package/dist/cli/ts_loader.js.map +1 -0
  58. package/dist/cli/vite_helpers.d.ts +62 -0
  59. package/dist/cli/vite_helpers.d.ts.map +1 -0
  60. package/dist/cli/vite_helpers.js +273 -0
  61. package/dist/cli/vite_helpers.js.map +1 -0
  62. package/dist/index.d.ts +11 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +14 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/player/hash_router.d.ts +23 -0
  67. package/dist/player/hash_router.d.ts.map +1 -0
  68. package/dist/player/hash_router.js +49 -0
  69. package/dist/player/hash_router.js.map +1 -0
  70. package/dist/player/hud.d.ts +33 -0
  71. package/dist/player/hud.d.ts.map +1 -0
  72. package/dist/player/hud.js +357 -0
  73. package/dist/player/hud.js.map +1 -0
  74. package/dist/player/index.d.ts +123 -0
  75. package/dist/player/index.d.ts.map +1 -0
  76. package/dist/player/index.js +848 -0
  77. package/dist/player/index.js.map +1 -0
  78. package/dist/player/input.d.ts +14 -0
  79. package/dist/player/input.d.ts.map +1 -0
  80. package/dist/player/input.js +90 -0
  81. package/dist/player/input.js.map +1 -0
  82. package/dist/player/slot.d.ts +22 -0
  83. package/dist/player/slot.d.ts.map +1 -0
  84. package/dist/player/slot.js +43 -0
  85. package/dist/player/slot.js.map +1 -0
  86. package/dist/player/transitions/cut.d.ts +7 -0
  87. package/dist/player/transitions/cut.d.ts.map +1 -0
  88. package/dist/player/transitions/cut.js +9 -0
  89. package/dist/player/transitions/cut.js.map +1 -0
  90. package/dist/player/transitions/fade.d.ts +7 -0
  91. package/dist/player/transitions/fade.d.ts.map +1 -0
  92. package/dist/player/transitions/fade.js +18 -0
  93. package/dist/player/transitions/fade.js.map +1 -0
  94. package/dist/player/transitions/index.d.ts +4 -0
  95. package/dist/player/transitions/index.d.ts.map +1 -0
  96. package/dist/player/transitions/index.js +4 -0
  97. package/dist/player/transitions/index.js.map +1 -0
  98. package/dist/player/transitions/slide.d.ts +6 -0
  99. package/dist/player/transitions/slide.d.ts.map +1 -0
  100. package/dist/player/transitions/slide.js +35 -0
  101. package/dist/player/transitions/slide.js.map +1 -0
  102. package/dist/script/index.d.ts +2 -0
  103. package/dist/script/index.d.ts.map +1 -0
  104. package/dist/script/index.js +2 -0
  105. package/dist/script/index.js.map +1 -0
  106. package/dist/script/script.d.ts +10 -0
  107. package/dist/script/script.d.ts.map +1 -0
  108. package/dist/script/script.js +41 -0
  109. package/dist/script/script.js.map +1 -0
  110. package/dist/segment/SegmentRunner.d.ts +52 -0
  111. package/dist/segment/SegmentRunner.d.ts.map +1 -0
  112. package/dist/segment/SegmentRunner.js +187 -0
  113. package/dist/segment/SegmentRunner.js.map +1 -0
  114. package/dist/segment/defineConfig.d.ts +6 -0
  115. package/dist/segment/defineConfig.d.ts.map +1 -0
  116. package/dist/segment/defineConfig.js +7 -0
  117. package/dist/segment/defineConfig.js.map +1 -0
  118. package/dist/segment/defineSegment.d.ts +7 -0
  119. package/dist/segment/defineSegment.d.ts.map +1 -0
  120. package/dist/segment/defineSegment.js +25 -0
  121. package/dist/segment/defineSegment.js.map +1 -0
  122. package/dist/segment/index.d.ts +5 -0
  123. package/dist/segment/index.d.ts.map +1 -0
  124. package/dist/segment/index.js +4 -0
  125. package/dist/segment/index.js.map +1 -0
  126. package/dist/timeline/index.d.ts +73 -0
  127. package/dist/timeline/index.d.ts.map +1 -0
  128. package/dist/timeline/index.js +142 -0
  129. package/dist/timeline/index.js.map +1 -0
  130. package/dist/timeline/loadAudioTrack.d.ts +18 -0
  131. package/dist/timeline/loadAudioTrack.d.ts.map +1 -0
  132. package/dist/timeline/loadAudioTrack.js +44 -0
  133. package/dist/timeline/loadAudioTrack.js.map +1 -0
  134. package/dist/timeline/loadVoiceover.d.ts +18 -0
  135. package/dist/timeline/loadVoiceover.d.ts.map +1 -0
  136. package/dist/timeline/loadVoiceover.js +38 -0
  137. package/dist/timeline/loadVoiceover.js.map +1 -0
  138. package/dist/timeline/resolveTiming.d.ts +28 -0
  139. package/dist/timeline/resolveTiming.d.ts.map +1 -0
  140. package/dist/timeline/resolveTiming.js +63 -0
  141. package/dist/timeline/resolveTiming.js.map +1 -0
  142. package/dist/timeline/validateTiming.d.ts +29 -0
  143. package/dist/timeline/validateTiming.d.ts.map +1 -0
  144. package/dist/timeline/validateTiming.js +62 -0
  145. package/dist/timeline/validateTiming.js.map +1 -0
  146. package/dist/types.d.ts +216 -0
  147. package/dist/types.d.ts.map +1 -0
  148. package/dist/types.js +6 -0
  149. package/dist/types.js.map +1 -0
  150. package/package.json +47 -0
  151. package/skill/SKILL.md +64 -0
  152. package/skill/assets/hello_world/PLAN.md +31 -0
  153. package/skill/assets/hello_world/README.md +27 -0
  154. package/skill/assets/hello_world/audio/audio_plan.md +14 -0
  155. package/skill/assets/hello_world/segments/hello_intro.ts +69 -0
  156. package/skill/assets/hello_world/segments/hello_outro.ts +71 -0
  157. package/skill/assets/hello_world/timeline.ts +15 -0
  158. package/skill/assets/hello_world/voiceover_script/script.md +10 -0
  159. package/skill/assets/install/package.json +10 -0
  160. package/skill/assets/install/tsconfig.json +23 -0
  161. package/skill/assets/styles/editorial-mono/STYLE.md +124 -0
  162. package/skill/assets/styles/editorial-mono/brand.md +85 -0
  163. package/skill/assets/styles/editorial-mono/reference/animations.jsx +752 -0
  164. package/skill/assets/styles/editorial-mono/reference/scenes.html +563 -0
  165. package/skill/assets/styles/editorial-mono/sample/bullet.ts +101 -0
  166. package/skill/assets/styles/editorial-mono/sample/content.ts +104 -0
  167. package/skill/assets/styles/editorial-mono/sample/cta.ts +113 -0
  168. package/skill/assets/styles/editorial-mono/sample/feature.ts +111 -0
  169. package/skill/assets/styles/editorial-mono/sample/grid.ts +97 -0
  170. package/skill/assets/styles/editorial-mono/sample/kinetic.ts +96 -0
  171. package/skill/assets/styles/editorial-mono/sample/section.ts +101 -0
  172. package/skill/assets/styles/editorial-mono/sample/stat.ts +128 -0
  173. package/skill/assets/styles/editorial-mono/sample/title.ts +97 -0
  174. package/skill/assets/styles/editorial-mono/sample/ui-showcase.ts +159 -0
  175. package/skill/assets/styles/editorial-mono/tokens.css +44 -0
  176. package/skill/assets/styles/iso-diagram/STYLE.md +109 -0
  177. package/skill/assets/styles/iso-diagram/brand.md +32 -0
  178. package/skill/assets/styles/iso-diagram/reference/animations.jsx +673 -0
  179. package/skill/assets/styles/iso-diagram/reference/scenes.html +427 -0
  180. package/skill/assets/styles/iso-diagram/sample/bullet.ts +144 -0
  181. package/skill/assets/styles/iso-diagram/sample/content.ts +192 -0
  182. package/skill/assets/styles/iso-diagram/sample/cta.ts +162 -0
  183. package/skill/assets/styles/iso-diagram/sample/feature.ts +205 -0
  184. package/skill/assets/styles/iso-diagram/sample/grid.ts +181 -0
  185. package/skill/assets/styles/iso-diagram/sample/kinetic.ts +102 -0
  186. package/skill/assets/styles/iso-diagram/sample/section.ts +149 -0
  187. package/skill/assets/styles/iso-diagram/sample/stat.ts +164 -0
  188. package/skill/assets/styles/iso-diagram/sample/title.ts +173 -0
  189. package/skill/assets/styles/iso-diagram/sample/ui-showcase.ts +162 -0
  190. package/skill/assets/styles/iso-diagram/tokens.css +40 -0
  191. package/skill/assets/styles/motion-engineering/STYLE.md +106 -0
  192. package/skill/assets/styles/motion-engineering/brand.md +29 -0
  193. package/skill/assets/styles/motion-engineering/reference/animations.jsx +673 -0
  194. package/skill/assets/styles/motion-engineering/reference/scenes.html +513 -0
  195. package/skill/assets/styles/motion-engineering/sample/bullet.ts +176 -0
  196. package/skill/assets/styles/motion-engineering/sample/content.ts +228 -0
  197. package/skill/assets/styles/motion-engineering/sample/cta.ts +209 -0
  198. package/skill/assets/styles/motion-engineering/sample/feature.ts +299 -0
  199. package/skill/assets/styles/motion-engineering/sample/grid.ts +190 -0
  200. package/skill/assets/styles/motion-engineering/sample/kinetic.ts +159 -0
  201. package/skill/assets/styles/motion-engineering/sample/section.ts +196 -0
  202. package/skill/assets/styles/motion-engineering/sample/stat.ts +230 -0
  203. package/skill/assets/styles/motion-engineering/sample/title.ts +219 -0
  204. package/skill/assets/styles/motion-engineering/sample/ui-showcase.ts +267 -0
  205. package/skill/assets/styles/motion-engineering/tokens.css +40 -0
  206. package/skill/assets/styles/neon-terminal/STYLE.md +105 -0
  207. package/skill/assets/styles/neon-terminal/brand.md +27 -0
  208. package/skill/assets/styles/neon-terminal/reference/animations.jsx +673 -0
  209. package/skill/assets/styles/neon-terminal/reference/scenes.html +387 -0
  210. package/skill/assets/styles/neon-terminal/sample/bullet.ts +113 -0
  211. package/skill/assets/styles/neon-terminal/sample/content.ts +117 -0
  212. package/skill/assets/styles/neon-terminal/sample/cta.ts +131 -0
  213. package/skill/assets/styles/neon-terminal/sample/feature.ts +112 -0
  214. package/skill/assets/styles/neon-terminal/sample/grid.ts +128 -0
  215. package/skill/assets/styles/neon-terminal/sample/kinetic.ts +105 -0
  216. package/skill/assets/styles/neon-terminal/sample/section.ts +96 -0
  217. package/skill/assets/styles/neon-terminal/sample/stat.ts +123 -0
  218. package/skill/assets/styles/neon-terminal/sample/title.ts +122 -0
  219. package/skill/assets/styles/neon-terminal/sample/ui-showcase.ts +127 -0
  220. package/skill/assets/styles/neon-terminal/tokens.css +39 -0
  221. package/skill/assets/styles/risograph/STYLE.md +110 -0
  222. package/skill/assets/styles/risograph/brand.md +26 -0
  223. package/skill/assets/styles/risograph/reference/animations.jsx +673 -0
  224. package/skill/assets/styles/risograph/reference/scenes.html +403 -0
  225. package/skill/assets/styles/risograph/sample/bullet.ts +124 -0
  226. package/skill/assets/styles/risograph/sample/content.ts +135 -0
  227. package/skill/assets/styles/risograph/sample/cta.ts +149 -0
  228. package/skill/assets/styles/risograph/sample/feature.ts +152 -0
  229. package/skill/assets/styles/risograph/sample/grid.ts +123 -0
  230. package/skill/assets/styles/risograph/sample/kinetic.ts +125 -0
  231. package/skill/assets/styles/risograph/sample/section.ts +130 -0
  232. package/skill/assets/styles/risograph/sample/stat.ts +145 -0
  233. package/skill/assets/styles/risograph/sample/title.ts +132 -0
  234. package/skill/assets/styles/risograph/sample/ui-showcase.ts +147 -0
  235. package/skill/assets/styles/risograph/tokens.css +39 -0
  236. package/skill/assets/styles/swiss-console/STYLE.md +107 -0
  237. package/skill/assets/styles/swiss-console/brand.md +37 -0
  238. package/skill/assets/styles/swiss-console/reference/animations.jsx +673 -0
  239. package/skill/assets/styles/swiss-console/reference/scenes.html +420 -0
  240. package/skill/assets/styles/swiss-console/sample/bullet.ts +122 -0
  241. package/skill/assets/styles/swiss-console/sample/content.ts +137 -0
  242. package/skill/assets/styles/swiss-console/sample/cta.ts +109 -0
  243. package/skill/assets/styles/swiss-console/sample/feature.ts +163 -0
  244. package/skill/assets/styles/swiss-console/sample/grid.ts +145 -0
  245. package/skill/assets/styles/swiss-console/sample/kinetic.ts +117 -0
  246. package/skill/assets/styles/swiss-console/sample/section.ts +127 -0
  247. package/skill/assets/styles/swiss-console/sample/stat.ts +148 -0
  248. package/skill/assets/styles/swiss-console/sample/title.ts +148 -0
  249. package/skill/assets/styles/swiss-console/sample/ui-showcase.ts +198 -0
  250. package/skill/assets/styles/swiss-console/tokens.css +39 -0
  251. package/skill/install/INSTALL.md +400 -0
  252. package/skill/references/audio/audio_plan.md +199 -0
  253. package/skill/references/audio/build.md +208 -0
  254. package/skill/references/audio/cue_template.md +219 -0
  255. package/skill/references/audio/ffmpeg_cookbook.md +267 -0
  256. package/skill/references/audio/music/music.md +171 -0
  257. package/skill/references/audio/music/providers/elevenlabs.md +170 -0
  258. package/skill/references/audio/music/providers/manual.md +140 -0
  259. package/skill/references/audio/music/providers/openverse.md +265 -0
  260. package/skill/references/audio/sfx/providers/elevenlabs.md +152 -0
  261. package/skill/references/audio/sfx/providers/manual.md +117 -0
  262. package/skill/references/audio/sfx/providers/openverse.md +243 -0
  263. package/skill/references/audio/sfx/sfx.md +149 -0
  264. package/skill/references/audio/styles.md +102 -0
  265. package/skill/references/audio/sync.md +237 -0
  266. package/skill/references/audio/voiceover/animation_sync.md +142 -0
  267. package/skill/references/audio/voiceover/provider_script.md +153 -0
  268. package/skill/references/audio/voiceover/providers/elevenlabs.md +288 -0
  269. package/skill/references/audio/voiceover/providers/manual.md +100 -0
  270. package/skill/references/audio/voiceover/script_writing.md +100 -0
  271. package/skill/references/audio/voiceover/style_intake.md +56 -0
  272. package/skill/references/audio/voiceover/sync_algorithm.md +167 -0
  273. package/skill/references/audio/voiceover.md +296 -0
  274. package/skill/references/audio.md +135 -0
  275. package/skill/references/authoring_segment.md +446 -0
  276. package/skill/references/create_or_edit_video.md +232 -0
  277. package/skill/references/dev_server.md +157 -0
  278. package/skill/references/export.md +145 -0
  279. package/skill/references/new_video.md +117 -0
  280. package/skill/references/project_structure.md +144 -0
  281. package/skill/references/setup.md +109 -0
  282. package/skill/references/setup_new_style.md +158 -0
  283. package/skill/references/styles.md +154 -0
  284. package/skill/references/testing.md +115 -0
  285. package/skill/references/types.md +240 -0
  286. package/src/cli/entry/components/copy_button.ts +42 -0
  287. package/src/cli/entry/components/download_modal.ts +204 -0
  288. package/src/cli/entry/components/empty_state.ts +55 -0
  289. package/src/cli/entry/components/hide_hud_tab.ts +37 -0
  290. package/src/cli/entry/components/icons.ts +31 -0
  291. package/src/cli/entry/components/top_bar.ts +69 -0
  292. package/src/cli/entry/components/video_card.ts +57 -0
  293. package/src/cli/entry/dev_frame.ts +189 -0
  294. package/src/cli/entry/entry_index.ts +16 -0
  295. package/src/cli/entry/entry_video.ts +24 -0
  296. package/src/cli/entry/index.html +12 -0
  297. package/src/cli/entry/parse_slug.ts +14 -0
  298. package/src/cli/entry/render.html +17 -0
  299. package/src/cli/entry/render_entry.ts +121 -0
  300. package/src/cli/entry/styles/base.css +45 -0
  301. package/src/cli/entry/styles/components.css +605 -0
  302. package/src/cli/entry/styles/tokens.css +44 -0
  303. package/src/cli/entry/video.html +22 -0
  304. package/src/cli/entry/views/homepage.ts +66 -0
  305. package/src/cli/entry/views/video_view.ts +286 -0
  306. package/src/cli/entry/virtual.d.ts +8 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Load a voiceover module from disk.
3
+ *
4
+ * Resolves the voiceover.ts file via the shared ts-loader, then
5
+ * resolves audio_file and provider_timing_file to absolute paths.
6
+ */
7
+ import { existsSync } from "node:fs";
8
+ import { dirname, resolve } from "node:path";
9
+ import { UserError } from "../cli/errors.js";
10
+ import { loadModule } from "../cli/ts_loader.js";
11
+ export async function loadVoiceover(args) {
12
+ const { videoFolder, slug } = args;
13
+ const voiceoverPath = resolve(videoFolder, "audio", "originals", "voiceovers", slug, "voiceover.ts");
14
+ if (!existsSync(voiceoverPath)) {
15
+ throw new UserError(`Voiceover not found: ${voiceoverPath}`, `Check that audio/originals/voiceovers/${slug}/voiceover.ts exists in your video folder.`);
16
+ }
17
+ const mod = await loadModule(voiceoverPath);
18
+ const voiceover = mod.default;
19
+ if (!voiceover || typeof voiceover !== "object" || !voiceover.audio_file) {
20
+ throw new UserError(`Invalid voiceover module at ${voiceoverPath}`, "The voiceover.ts file must default-export a Voiceover object with at least an audio_file field.");
21
+ }
22
+ const voiceoverFolder = dirname(voiceoverPath);
23
+ const audioFilePath = resolve(voiceoverFolder, voiceover.audio_file);
24
+ if (!existsSync(audioFilePath)) {
25
+ throw new UserError(`Voiceover audio file not found: ${audioFilePath}`, `The audio_file "${voiceover.audio_file}" referenced in ${voiceoverPath} does not exist.`);
26
+ }
27
+ // Rewrite audio_file to an absolute path so callers can resolve it
28
+ // without knowing the source folder.
29
+ const resolvedVoiceover = {
30
+ ...voiceover,
31
+ audio_file: audioFilePath,
32
+ provider_timing_file: voiceover.provider_timing_file
33
+ ? resolve(voiceoverFolder, voiceover.provider_timing_file)
34
+ : undefined,
35
+ };
36
+ return { voiceover: resolvedVoiceover, voiceoverFolder, audioFilePath };
37
+ }
38
+ //# sourceMappingURL=loadVoiceover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadVoiceover.js","sourceRoot":"","sources":["../../src/timeline/loadVoiceover.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAcjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAuB;IAC1D,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAEnC,MAAM,aAAa,GAAG,OAAO,CAC5B,WAAW,EACX,OAAO,EACP,WAAW,EACX,YAAY,EACZ,IAAI,EACJ,cAAc,CACd,CAAC;IACF,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAClB,wBAAwB,aAAa,EAAE,EACvC,yCAAyC,IAAI,4CAA4C,CACzF,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAoB,CAAC;IAE3C,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC1E,MAAM,IAAI,SAAS,CAClB,+BAA+B,aAAa,EAAE,EAC9C,iGAAiG,CACjG,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAErE,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAClB,mCAAmC,aAAa,EAAE,EAClD,mBAAmB,SAAS,CAAC,UAAU,mBAAmB,aAAa,kBAAkB,CACzF,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,qCAAqC;IACrC,MAAM,iBAAiB,GAAc;QACpC,GAAG,SAAS;QACZ,UAAU,EAAE,aAAa;QACzB,oBAAoB,EAAE,SAAS,CAAC,oBAAoB;YACnD,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,SAAS,CAAC,oBAAoB,CAAC;YAC1D,CAAC,CAAC,SAAS;KACZ,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Resolve the active timing for a playback or render session.
3
+ *
4
+ * Implements the four-level precedence described in the architecture doc:
5
+ * 1. CLI audio track (explicit --audio-track <id>)
6
+ * 2. default_audio_track from timeline.ts
7
+ * 3. default_timing from timeline.ts
8
+ * 4. Per-segment SegmentSpec.advances
9
+ *
10
+ * --audio-track none suppresses levels 1 and 2 but preserves level 3.
11
+ */
12
+ import type { AudioTrack, SegmentSpec, Timing } from "../types.js";
13
+ /** Minimal segment shape needed for timing resolution (id + advances). */
14
+ export type TimingSegment = Pick<SegmentSpec, "id" | "advances">;
15
+ export type ResolvedTiming = {
16
+ source: "audio_track" | "default_timing" | "segments";
17
+ audioTrack?: AudioTrack;
18
+ perSegment: Record<string, number[]>;
19
+ };
20
+ export interface ResolveTimingArgs {
21
+ segments: TimingSegment[];
22
+ defaultTiming?: Timing;
23
+ defaultAudioTrack?: AudioTrack;
24
+ cliAudioTrackId?: string | "none";
25
+ cliAudioTrackModule?: AudioTrack;
26
+ }
27
+ export declare function resolveTiming(args: ResolveTimingArgs): ResolvedTiming;
28
+ //# sourceMappingURL=resolveTiming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTiming.d.ts","sourceRoot":"","sources":["../../src/timeline/resolveTiming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEnE,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,UAAU,CAAC,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,aAAa,GAAG,gBAAgB,GAAG,UAAU,CAAC;IACtD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,mBAAmB,CAAC,EAAE,UAAU,CAAC;CACjC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CA2CrE"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Resolve the active timing for a playback or render session.
3
+ *
4
+ * Implements the four-level precedence described in the architecture doc:
5
+ * 1. CLI audio track (explicit --audio-track <id>)
6
+ * 2. default_audio_track from timeline.ts
7
+ * 3. default_timing from timeline.ts
8
+ * 4. Per-segment SegmentSpec.advances
9
+ *
10
+ * --audio-track none suppresses levels 1 and 2 but preserves level 3.
11
+ */
12
+ export function resolveTiming(args) {
13
+ const { segments, defaultTiming, defaultAudioTrack, cliAudioTrackId, cliAudioTrackModule } = args;
14
+ // Build base from segment advances
15
+ const base = {};
16
+ for (const seg of segments) {
17
+ base[seg.id] = seg.advances;
18
+ }
19
+ // Determine the active timing source
20
+ const isNone = cliAudioTrackId === "none";
21
+ // Level 1: explicit CLI audio track
22
+ if (!isNone && cliAudioTrackModule) {
23
+ return {
24
+ source: "audio_track",
25
+ audioTrack: cliAudioTrackModule,
26
+ perSegment: overlayTiming(base, cliAudioTrackModule.timing),
27
+ };
28
+ }
29
+ // Level 2: default audio track from timeline
30
+ if (!isNone && defaultAudioTrack) {
31
+ return {
32
+ source: "audio_track",
33
+ audioTrack: defaultAudioTrack,
34
+ perSegment: overlayTiming(base, defaultAudioTrack.timing),
35
+ };
36
+ }
37
+ // Level 3: default timing
38
+ if (defaultTiming) {
39
+ return {
40
+ source: "default_timing",
41
+ perSegment: overlayTiming(base, defaultTiming),
42
+ };
43
+ }
44
+ // Level 4: segment advances only
45
+ return {
46
+ source: "segments",
47
+ perSegment: base,
48
+ };
49
+ }
50
+ /**
51
+ * Overlay a Timing's perSegment entries on top of a base record.
52
+ * Segments present in the Timing win; absent segments keep their base value.
53
+ */
54
+ function overlayTiming(base, timing) {
55
+ const result = { ...base };
56
+ for (const [segId, advances] of Object.entries(timing.perSegment)) {
57
+ if (advances) {
58
+ result[segId] = advances;
59
+ }
60
+ }
61
+ return result;
62
+ }
63
+ //# sourceMappingURL=resolveTiming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTiming.js","sourceRoot":"","sources":["../../src/timeline/resolveTiming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH,MAAM,UAAU,aAAa,CAAC,IAAuB;IACpD,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAAC;IAElG,mCAAmC;IACnC,MAAM,IAAI,GAA6B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,qCAAqC;IACrC,MAAM,MAAM,GAAG,eAAe,KAAK,MAAM,CAAC;IAE1C,oCAAoC;IACpC,IAAI,CAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACpC,OAAO;YACN,MAAM,EAAE,aAAa;YACrB,UAAU,EAAE,mBAAmB;YAC/B,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC;SAC3D,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC;QAClC,OAAO;YACN,MAAM,EAAE,aAAa;YACrB,UAAU,EAAE,iBAAiB;YAC7B,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC;SACzD,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,IAAI,aAAa,EAAE,CAAC;QACnB,OAAO;YACN,MAAM,EAAE,gBAAgB;YACxB,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC;SAC9C,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,OAAO;QACN,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,IAAI;KAChB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,IAA8B,EAAE,MAAc;IACpE,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACnE,IAAI,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;QAC1B,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Validation for Timing and Voiceover objects.
3
+ *
4
+ * Called at CLI startup after loading timeline / voiceover modules.
5
+ * Errors block execution; warnings are printed but do not block.
6
+ */
7
+ import type { AudioTrack, Timing } from "../types.js";
8
+ export type ValidationResult = {
9
+ ok: true;
10
+ warnings: string[];
11
+ } | {
12
+ ok: false;
13
+ errors: string[];
14
+ warnings: string[];
15
+ };
16
+ /**
17
+ * Validate a Timing object against known segment ids.
18
+ *
19
+ * - Warns on segment ids in `perSegment` that are not in `segmentIds`.
20
+ * - Errors on non-array values, empty arrays, non-positive numbers, or non-finite numbers.
21
+ */
22
+ export declare function validateTiming(timing: Timing, segmentIds: string[]): ValidationResult;
23
+ /**
24
+ * Validate an AudioTrack object's file references.
25
+ *
26
+ * - Errors if `audio_file` does not exist on disk.
27
+ */
28
+ export declare function validateAudioTrack(audioTrack: AudioTrack, videoFolder: string): ValidationResult;
29
+ //# sourceMappingURL=validateTiming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateTiming.d.ts","sourceRoot":"","sources":["../../src/timeline/validateTiming.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEvD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAsCrF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAahG"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Validation for Timing and Voiceover objects.
3
+ *
4
+ * Called at CLI startup after loading timeline / voiceover modules.
5
+ * Errors block execution; warnings are printed but do not block.
6
+ */
7
+ import { existsSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ /**
10
+ * Validate a Timing object against known segment ids.
11
+ *
12
+ * - Warns on segment ids in `perSegment` that are not in `segmentIds`.
13
+ * - Errors on non-array values, empty arrays, non-positive numbers, or non-finite numbers.
14
+ */
15
+ export function validateTiming(timing, segmentIds) {
16
+ const errors = [];
17
+ const warnings = [];
18
+ const knownIds = new Set(segmentIds);
19
+ for (const [segId, advances] of Object.entries(timing.perSegment)) {
20
+ if (!knownIds.has(segId)) {
21
+ warnings.push(`Timing references unknown segment "${segId}" -- possible typo or renamed segment.`);
22
+ }
23
+ if (!advances)
24
+ continue;
25
+ if (!Array.isArray(advances)) {
26
+ errors.push(`Timing for segment "${segId}": expected an array of numbers.`);
27
+ continue;
28
+ }
29
+ if (advances.length === 0) {
30
+ errors.push(`Timing for segment "${segId}": advances array must not be empty.`);
31
+ continue;
32
+ }
33
+ for (let i = 0; i < advances.length; i++) {
34
+ const v = advances[i];
35
+ if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
36
+ errors.push(`Timing for segment "${segId}": advances[${i}] must be a positive finite number (got ${v}).`);
37
+ }
38
+ }
39
+ }
40
+ if (errors.length > 0) {
41
+ return { ok: false, errors, warnings };
42
+ }
43
+ return { ok: true, warnings };
44
+ }
45
+ /**
46
+ * Validate an AudioTrack object's file references.
47
+ *
48
+ * - Errors if `audio_file` does not exist on disk.
49
+ */
50
+ export function validateAudioTrack(audioTrack, videoFolder) {
51
+ const errors = [];
52
+ const warnings = [];
53
+ const audioPath = resolve(videoFolder, audioTrack.audio_file);
54
+ if (!existsSync(audioPath)) {
55
+ errors.push(`Audio track file not found: ${audioPath}`);
56
+ }
57
+ if (errors.length > 0) {
58
+ return { ok: false, errors, warnings };
59
+ }
60
+ return { ok: true, warnings };
61
+ }
62
+ //# sourceMappingURL=validateTiming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateTiming.js","sourceRoot":"","sources":["../../src/timeline/validateTiming.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,UAAoB;IAClE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAErC,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CACZ,sCAAsC,KAAK,wCAAwC,CACnF,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,kCAAkC,CAAC,CAAC;YAC5E,SAAS;QACV,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,sCAAsC,CAAC,CAAC;YAChF,SAAS;QACV,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5D,MAAM,CAAC,IAAI,CACV,uBAAuB,KAAK,eAAe,CAAC,2CAA2C,CAAC,IAAI,CAC5F,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAsB,EAAE,WAAmB;IAC7E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Public types for the Videowright library.
3
+ */
4
+ export declare const SEGMENT_BRAND: unique symbol;
5
+ /** Runtime context passed to segment lifecycle methods. */
6
+ export interface PlayerContext {
7
+ /** Pauses play() until the next user advance (interactive) or scheduled beat (render). */
8
+ waitForNext(): Promise<void>;
9
+ /** Pauses play() for the given duration in milliseconds. */
10
+ hold(ms: number): Promise<void>;
11
+ /** Aborted when the segment is unmounted. */
12
+ signal: AbortSignal;
13
+ /** Current player mode. */
14
+ mode: "interactive" | "render";
15
+ /** Milliseconds since this segment instance was mounted. */
16
+ clock(): number;
17
+ }
18
+ /** User-facing spec passed to defineSegment. */
19
+ export interface SegmentSpec {
20
+ /** Unique id matching the segment's folder name under segments/. */
21
+ id: string;
22
+ /**
23
+ * Segment-relative seconds at which to fire each 'next' advance during render.
24
+ * Must be monotonically increasing. All values must be positive numbers.
25
+ *
26
+ * Length = number of triggerNext() calls required to traverse this segment, INCLUDING
27
+ * the final press that transitions to the next segment.
28
+ *
29
+ * Examples:
30
+ * - `play() { await ctx.hold(3000); }` -> 1 press total -> `advances: [3.0]`
31
+ * - `play() { await ctx.waitForNext(); }` -> 2 presses -> `advances: [t1, t2]`
32
+ * - `play() { await ctx.waitForNext(); await ctx.waitForNext(); }` -> 3 presses -> `advances: [t1, t2, t3]`
33
+ *
34
+ * Ignored in dev (interactive) mode -- interactive presses drive timing.
35
+ * REQUIRED on every segment.
36
+ */
37
+ advances: number[];
38
+ /** Voiceover text for this segment (used by script() helper and HUD). */
39
+ voiceover?: string;
40
+ /** Freeform notes (not rendered). */
41
+ notes?: string;
42
+ /** Called when the player gives the segment its host element. Optional. */
43
+ mount?(el: HTMLElement, ctx: PlayerContext): void | Promise<void>;
44
+ /** Main animation/content logic. Required. */
45
+ play(ctx: PlayerContext): Promise<void>;
46
+ /** Called when the segment is removed. Optional. */
47
+ unmount?(): void;
48
+ /** Override default next behavior. Return true to consume the press. */
49
+ next?(): boolean;
50
+ /** Override default prev behavior. Return true to consume the press. */
51
+ prev?(): boolean;
52
+ }
53
+ /** A validated segment object returned by defineSegment. */
54
+ export interface Segment extends SegmentSpec {
55
+ readonly [SEGMENT_BRAND]: true;
56
+ }
57
+ /** A single entry in a timeline's segments array. */
58
+ export interface TimelineEntry {
59
+ /** Segment id to resolve (maps to segments/<id>/index.ts). */
60
+ id: string;
61
+ /** Transition to use when entering this segment. String name or configured object. */
62
+ transition?: string | {
63
+ type: string;
64
+ [k: string]: unknown;
65
+ };
66
+ }
67
+ /** Timeline-level metadata. */
68
+ export interface TimelineMeta {
69
+ /** Video title (required). */
70
+ title: string;
71
+ /** Style slug for this video. Falls back to config defaultStyle. */
72
+ style?: string;
73
+ /** Aspect ratio string, e.g. '16:9'. Falls back to config default. */
74
+ aspectRatio?: string;
75
+ /** Resolution as [width, height]. Falls back to config default. */
76
+ resolution?: [number, number];
77
+ /** Frames per second. Falls back to config default. */
78
+ fps?: number;
79
+ }
80
+ /** TimelineMeta after applyMetaDefaults has filled in resolution, fps, and aspectRatio. */
81
+ export type ResolvedTimelineMeta = TimelineMeta & Required<Pick<TimelineMeta, "resolution" | "fps" | "aspectRatio">>;
82
+ /** A complete timeline definition (default export of a timeline.ts file). */
83
+ export interface Timeline {
84
+ meta: TimelineMeta;
85
+ segments: TimelineEntry[];
86
+ /** Standalone timing overrides (used when no audio track is active). */
87
+ default_timing?: Timing;
88
+ /** Default audio track for this video. */
89
+ default_audio_track?: AudioTrack;
90
+ }
91
+ /**
92
+ * Per-segment advance schedule overrides. Used by voiceovers and as a
93
+ * standalone timing layer (`default_timing` on Timeline).
94
+ *
95
+ * Keys are segment ids; values are advance times in seconds (same units as
96
+ * SegmentSpec.advances). A Timing only needs to specify segments it wants
97
+ * to override -- unspecified segments fall back to their own `advances`.
98
+ */
99
+ export type Timing = {
100
+ perSegment: Partial<Record<string, number[]>>;
101
+ };
102
+ /**
103
+ * A single voiceover for a video. Stored at
104
+ * `videos/<video>/audio/originals/voiceovers/<slug>/voiceover.ts`.
105
+ */
106
+ export type Voiceover = {
107
+ /**
108
+ * Audio file path, relative to the voiceover.ts file's directory (e.g.,
109
+ * `"./audio.mp3"`). After loading via `loadVoiceover()`, rewritten to an
110
+ * absolute path.
111
+ *
112
+ * Note: this differs from `AudioTrack.audio_file` which is relative to
113
+ * the video folder. The difference exists because voiceover modules are
114
+ * loaded by `loadVoiceover()` which knows the voiceover.ts location,
115
+ * while audio tracks are imported into timeline.ts and resolved from the
116
+ * video folder by render.ts.
117
+ */
118
+ audio_file: string;
119
+ /** Provider that produced the audio. */
120
+ provider: "elevenlabs" | "manual";
121
+ /**
122
+ * Provider timing JSON path. Same resolution rules as `audio_file`:
123
+ * relative to voiceover.ts directory on disk, absolute after
124
+ * `loadVoiceover()`.
125
+ */
126
+ provider_timing_file?: string;
127
+ /** Per-segment advance timing synced to this audio. */
128
+ timing: Timing;
129
+ /** Freeform notes about this voiceover. */
130
+ notes?: string;
131
+ /**
132
+ * ElevenLabs voice ID for this voiceover. Used by the API flow to select
133
+ * the voice for TTS generation. When omitted, defaults to Asher
134
+ * (`tMvyQtpCVQ0DkixuYm6J`). Ignored when provider is "manual".
135
+ */
136
+ eleven_labs_voice_id?: string;
137
+ };
138
+ /**
139
+ * A rendered audio track for a video. Stored at
140
+ * `videos/<video>/audio/tracks/<id>/track.ts`.
141
+ *
142
+ * Combines voice-over, SFX, and music into a single rendered audio file.
143
+ * The timeline references the active track via `default_audio_track`.
144
+ */
145
+ export type AudioTrack = {
146
+ /**
147
+ * Path to the rendered audio file, relative to the video folder (the
148
+ * directory containing timeline.ts). Both loadAudioTrack() and the
149
+ * default_audio_track import path resolve this from the video folder.
150
+ * loadAudioTrack() rewrites it to an absolute path on return.
151
+ */
152
+ audio_file: string;
153
+ /** Length of the rendered audio in seconds. */
154
+ length_s: number;
155
+ /**
156
+ * Per-segment advance timing synced to this audio track. Computed by the
157
+ * sync-to-audio skill from the plan_snapshot + per-cue VO timing.json files.
158
+ * Same shape and semantics as Voiceover.timing.
159
+ */
160
+ timing: Timing;
161
+ /** Path to the audio plan that produced this track (relative to track.ts). */
162
+ audio_plan_path?: string;
163
+ /** Path to the point-in-time plan snapshot for this track (relative to track.ts). */
164
+ plan_snapshot_path?: string;
165
+ /** ISO timestamp when this track was rendered. */
166
+ created_at?: string;
167
+ /** Freeform notes about this track. */
168
+ notes?: string;
169
+ };
170
+ /** Context passed to transition functions. */
171
+ export interface TransitionContext {
172
+ direction: "forward" | "backward";
173
+ duration?: number;
174
+ }
175
+ /** A transition function that animates between two slot elements. */
176
+ export type Transition = (outgoing: HTMLElement, incoming: HTMLElement, ctx: TransitionContext) => Promise<void>;
177
+ /** Summary of a single video for the homepage and routing. */
178
+ export interface VideoSummary {
179
+ /** Directory name under videos/ -- also the URL slug. */
180
+ slug: string;
181
+ /** Absolute path to videos/<slug>/timeline.ts. */
182
+ timelinePath: string;
183
+ /** meta.title from the timeline. Falls back to slug if load failed. */
184
+ title: string;
185
+ /** Resolved style: meta.style ?? config.defaultStyle ?? "unknown". */
186
+ style: string;
187
+ /** mtime of timeline.ts (epoch ms). Used for sort order. */
188
+ mtimeMs: number;
189
+ /** Error message if loading the timeline threw. */
190
+ loadError?: string;
191
+ }
192
+ /** Project-level info exposed to the browser via a virtual module. */
193
+ export interface ProjectInfo {
194
+ /** Basename of cwd. Shown in the top bar. */
195
+ projectName: string;
196
+ /** Videos sorted by mtime descending. */
197
+ videos: VideoSummary[];
198
+ }
199
+ /** Repo-wide configuration from videowright.config.ts. */
200
+ export interface Config {
201
+ /** Setup marker; bumped when layout changes. */
202
+ projectStructure: "v1";
203
+ /** Slug of the default style in styles/<slug>/. Required after setup. */
204
+ defaultStyle?: string;
205
+ /** Default values for timeline meta fields. */
206
+ defaults?: {
207
+ resolution?: [number, number];
208
+ fps?: number;
209
+ aspectRatio?: string;
210
+ };
211
+ /** Custom transition loaders keyed by name. */
212
+ transitions?: Record<string, () => Promise<{
213
+ default: Transition;
214
+ }>>;
215
+ }
216
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Public types for the Videowright library.
3
+ */
4
+ // ---- Segment brand ----
5
+ export const SEGMENT_BRAND = Symbol.for("videowright.segment");
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,0BAA0B;AAE1B,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "videowright",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Build animated explainer videos with your coding agent",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/scosman/videowright.git"
10
+ },
11
+ "homepage": "https://github.com/scosman/videowright",
12
+ "bugs": {
13
+ "url": "https://github.com/scosman/videowright/issues"
14
+ },
15
+ "keywords": ["video", "animation", "explainer", "coding agent", "agentic coding"],
16
+ "bin": {
17
+ "videowright": "dist/cli/bin.js"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./cli": {
25
+ "types": "./dist/cli/index.d.ts",
26
+ "import": "./dist/cli/index.js"
27
+ }
28
+ },
29
+ "files": ["dist", "skill", "src/cli/entry"],
30
+ "scripts": {
31
+ "build": "tsc -b",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:e2e": "npm run build && npx playwright test"
35
+ },
36
+ "dependencies": {
37
+ "tsx": "^4.19.0",
38
+ "vite": "^6.3.5"
39
+ },
40
+ "devDependencies": {
41
+ "@playwright/test": "^1.59.1",
42
+ "@types/node": "^25.6.2",
43
+ "jsdom": "^29.1.1",
44
+ "vitest": "^3.1.3",
45
+ "yaml": "^2.8.4"
46
+ }
47
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: videowright
3
+ description: Author HTML/CSS/JS animated explainer videos using Videowright. Trigger on requests to create a video, edit a segment, add or change a style, generate a VO script, run the dev server, or export.
4
+ ---
5
+
6
+ # Videowright
7
+
8
+ Videowright lets you author high-quality animated explainer videos in HTML/CSS/JS. Videos are composed from segments — self-contained TypeScript modules that own a DOM element and animate it. A `Timeline` orders segments with transitions, the dev server plays them interactively, and the render pipeline exports to MP4. The core authoring primitives are `defineSegment`, `defineConfig`, and the `Timeline` type.
9
+
10
+ ## When to trigger
11
+
12
+ - "Make me a video about X"
13
+ - "Add a segment to \<video\>"
14
+ - "Edit the intro of \<video\>"
15
+ - "Add a style" / "Change the default style"
16
+ - "Match the look of \<past video\>"
17
+ - "Generate a script for \<video\>"
18
+ - "Add a voiceover" / "Generate a voiceover" / "Record a voiceover"
19
+ - "Add sound effects" / "Add music" / "Add audio"
20
+ - "Run the dev server" / "Preview the video"
21
+ - "Export the video" / "Render the video"
22
+ - "Write tests for \<video\>"
23
+ - Any time `videowright.config.ts` is referenced or the user mentions Videowright
24
+
25
+ ## Setup gate
26
+
27
+ Before doing any work, check `videowright.config.ts` at the repo root:
28
+
29
+ - If the file **does not exist**, OR `defaultStyle` is missing or empty string: load [references/setup.md](references/setup.md) and follow it before continuing.
30
+ - Otherwise: skip `setup.md`. Proceed with intent dispatch below.
31
+
32
+ ## Intent dispatch
33
+
34
+ Read the user's request and route to the matching reference file:
35
+
36
+ | Intent | Reference |
37
+ |---|---|
38
+ | First-time setup of a Videowright project | [references/setup.md](references/setup.md) |
39
+ | New video | [references/new_video.md](references/new_video.md) |
40
+ | Edit a video (add/remove/reorder segments, restyle, rewrite VO) | [references/create_or_edit_video.md](references/create_or_edit_video.md) |
41
+ | Add a new style | [references/setup_new_style.md](references/setup_new_style.md) |
42
+ | Change the default style | [references/styles.md](references/styles.md) |
43
+ | Match a past video's style | [references/styles.md](references/styles.md) |
44
+ | Generate or regenerate a VO script | [references/audio.md](references/audio.md) |
45
+ | Add a voiceover (AI-generated or manual) | [references/audio.md](references/audio.md) |
46
+ | Add sound effects or music | [references/audio.md](references/audio.md) |
47
+ | Run or review the dev server | [references/dev_server.md](references/dev_server.md) |
48
+ | Export the video | [references/export.md](references/export.md) |
49
+ | Write tests | [references/testing.md](references/testing.md) |
50
+
51
+ If the user's request does not map to a clear intent, ask one focused question listing the options above. Do not guess.
52
+
53
+ ## Core principles
54
+
55
+ - **Use `defineSegment` always.** It is the canonical authoring path. It handles beat tracking, abort signals, and typing.
56
+ - **Fill the frame.** Text and visual elements should be large, legible, and use the full video canvas — body text 36px+ at 1080p, headings 64px+, content containers spanning 80-90%+ of the video width. Avoid excessive margins, padding, or whitespace that shrinks content into a small portion of the frame. If text is too small to read comfortably at normal playback size, make it bigger or remove it. Small decorative labels are acceptable when intentional, but body text, headings, stats, and key visuals must dominate the frame. This is a video, not a web page — there is no scrolling, so every pixel of every frame matters.
57
+ - **`waitForNext()` for audio-aligned beats; `hold(ms)` for pacing within a beat.** Use `ctx.waitForNext()` at every point where the video should sync to an audio track advance — this keeps timing decoupled from any single audio recording, so swapping audio tracks only requires new timing data, not re-authoring segments. Use `ctx.hold(ms)` for pauses that are internal to a beat (entrance animations, dwell time before the next interactive point). Do not bake audio-specific durations into `hold()` calls — that couples the segment to one recording and forces re-authoring to swap narration.
58
+ - **Render-safety CR on every segment.** After writing or modifying a segment, review it against the render-safety checklist in [references/create_or_edit_video.md](references/create_or_edit_video.md) (Step 2b). This catches patterns that work in dev but may produce suboptimal results in render (e.g., hold-driven mutation loops where WAAPI would give smoother interpolation).
59
+ - **No `duration` field on segments.** There is no duration property — the segment decides when it is done via its `play()` function.
60
+ - **Any web tech is welcome inside segments.** Three.js, Lottie, animated SVG, shadcn/React, GSAP, echarts — all encouraged. The segment owns its DOM; attach a shadow root if isolation is wanted.
61
+ - **PLAN.md is the working memory.** Read it before iterating on a video; append after meaningful changes. Never delete log entries.
62
+ - **Reuse, don't copy.** Top-level `segments/`, `components/`, `transitions/` are shared across all videos. Any video can use any segment. Don't duplicate.
63
+ - **Audio-first authoring is the default for new videos with VO.** Write the script, then scaffold segments to match. This produces coherent videos.
64
+ - **One-shot when the input is rich.** Never ask a question whose answer is already in the user's input. When the user provides a complete brief, draft the plan and confirm — don't interrogate.
@@ -0,0 +1,31 @@
1
+ # Plan: Hello Videowright
2
+
3
+ ## Purpose
4
+ - Audience: First-time Videowright users
5
+ - Takeaway: See a working video end-to-end and understand the segment + timeline model
6
+ - Constraints / hard guidelines: Keep it short (3 segments). Showcase style tokens and voiceover fields.
7
+
8
+ ## Style
9
+ - Active style: editorial-mono
10
+ - Notes: Uses Editorial Mono as a clean, content-first default. Swap to any other built-in pack or create your own.
11
+
12
+ ## Audio intent
13
+ - Mode: silent
14
+ - Notes: The template ships silent so first render is video-only. The `voiceover_script/script.md` and per-segment `voiceover` fields are pre-populated to demonstrate the script workflow; add audio by sourcing a VO and building a track under `audio/tracks/v1/`.
15
+
16
+ ## Segment outline
17
+ 1. hello-intro -- Title card with animated entrance
18
+ 2. editorial-mono-sample-kinetic -- Style showcase demonstrating kinetic typography
19
+ 3. hello-outro -- Closing card with call to action
20
+
21
+ ## Script (if applicable)
22
+ see voiceover_script/script.md
23
+
24
+ ---
25
+
26
+ ## Log
27
+
28
+ ### YYYY-MM-DD -- Initial scaffold
29
+ - Created via Videowright setup flow
30
+ - 3 segments: intro, style sample, outro
31
+ - Editorial Mono style applied via timeline.ts top-of-file import
@@ -0,0 +1,27 @@
1
+ # Hello Videowright
2
+
3
+ A starter video created during Videowright setup.
4
+
5
+ ## Segments
6
+
7
+ - **hello-intro** -- Title card with animated entrance
8
+ - **editorial-mono-sample-kinetic** -- Kinetic typography showcase (from the Editorial Mono style pack)
9
+ - **hello-outro** -- Closing card with call to action
10
+
11
+ ## Running
12
+
13
+ ```bash
14
+ npx videowright dev
15
+ ```
16
+
17
+ The dev server auto-discovers the most recent timeline. To target this video explicitly:
18
+
19
+ ```bash
20
+ npx videowright dev videos/hello_videowright/timeline.ts
21
+ ```
22
+
23
+ ## Editing
24
+
25
+ - Describe what you want to change and the agent will update segments, timelines, and styles for you
26
+ - The dev server reloads automatically on file changes
27
+ - See `PLAN.md` for the video's design and history