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,108 @@
1
+ /**
2
+ * Shared project discovery: finds config and timeline, throws UserError on missing.
3
+ * Used by dev, script, and render commands.
4
+ */
5
+ import { statSync } from "node:fs";
6
+ import { basename, dirname } from "node:path";
7
+ import { findAllTimelines, findConfig, findTimeline } from "./discover.js";
8
+ import { UserError } from "./errors.js";
9
+ /**
10
+ * Find config and timeline for a consumer project.
11
+ * Throws UserError with actionable hints on missing config or timeline.
12
+ *
13
+ * @param cwd - Consumer repo root
14
+ * @param positional - Optional explicit timeline path
15
+ * @param commandName - Command name for error messages (e.g. "dev", "render")
16
+ */
17
+ export function discoverProject(cwd, positional, commandName) {
18
+ const configPath = findConfig(cwd);
19
+ if (!configPath) {
20
+ throw new UserError(`No videowright.config.ts found at ${cwd}`, "The Videowright skill can scaffold one. Run setup first.");
21
+ }
22
+ const timelinePath = findTimeline(cwd, positional);
23
+ if (!timelinePath) {
24
+ throw new UserError(`No videos found at ${cwd}/videos/`, `Create one or pass a path: videowright ${commandName} <path-to-timeline>`);
25
+ }
26
+ return { configPath, timelinePath };
27
+ }
28
+ /**
29
+ * Discover the project and load summaries for every video under videos/.
30
+ * Used by `dev` (whole project) and `render` (when prompting / auto-picking).
31
+ *
32
+ * When a Vite server is provided, timelines are loaded via `ssrLoadModule`
33
+ * which handles Vite-specific imports (CSS, virtual modules). Without a
34
+ * server, uses dynamic `import()` which may fail for files with Vite imports.
35
+ */
36
+ export async function discoverAllVideos(root, viteServer) {
37
+ const timelinePaths = findAllTimelines(root);
38
+ // Load config for default style fallback
39
+ let defaultStyle = "unknown";
40
+ const configPath = findConfig(root);
41
+ if (configPath) {
42
+ try {
43
+ let configMod;
44
+ if (viteServer) {
45
+ configMod = (await viteServer.ssrLoadModule(configPath));
46
+ }
47
+ else {
48
+ configMod = (await import(configPath));
49
+ }
50
+ const config = configMod.default;
51
+ if (config?.defaultStyle) {
52
+ defaultStyle = config.defaultStyle;
53
+ }
54
+ }
55
+ catch {
56
+ // Config load failure is not fatal for discovery
57
+ }
58
+ }
59
+ const videos = [];
60
+ for (const timelinePath of timelinePaths) {
61
+ const videoDir = dirname(timelinePath);
62
+ const slug = basename(videoDir);
63
+ let mtimeMs = 0;
64
+ try {
65
+ mtimeMs = statSync(timelinePath).mtimeMs;
66
+ }
67
+ catch {
68
+ // If stat fails, keep 0
69
+ }
70
+ let title = slug;
71
+ let style = defaultStyle;
72
+ let loadError;
73
+ try {
74
+ let timelineMod;
75
+ if (viteServer) {
76
+ timelineMod = (await viteServer.ssrLoadModule(timelinePath));
77
+ }
78
+ else {
79
+ timelineMod = (await import(timelinePath));
80
+ }
81
+ const timeline = timelineMod.default;
82
+ if (timeline?.meta?.title) {
83
+ title = timeline.meta.title;
84
+ }
85
+ if (timeline?.meta?.style) {
86
+ style = timeline.meta.style;
87
+ }
88
+ }
89
+ catch (e) {
90
+ loadError = e instanceof Error ? e.message : String(e);
91
+ }
92
+ videos.push({
93
+ slug,
94
+ timelinePath,
95
+ title,
96
+ style,
97
+ mtimeMs,
98
+ loadError,
99
+ });
100
+ }
101
+ // Sort by mtime descending (most recently modified first)
102
+ videos.sort((a, b) => b.mtimeMs - a.mtimeMs);
103
+ return {
104
+ projectName: basename(root),
105
+ videos,
106
+ };
107
+ }
108
+ //# sourceMappingURL=discover_project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover_project.js","sourceRoot":"","sources":["../../src/cli/discover_project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Typed error classes for the CLI.
3
+ */
4
+ /** A user-facing error with an optional actionable hint. Exit code 1. */
5
+ export declare class UserError extends Error {
6
+ name: string;
7
+ hint?: string;
8
+ constructor(message: string, hint?: string);
9
+ }
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/cli/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yEAAyE;AACzE,qBAAa,SAAU,SAAQ,KAAK;IAC1B,IAAI,SAAe;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEF,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAI1C"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Typed error classes for the CLI.
3
+ */
4
+ /** A user-facing error with an optional actionable hint. Exit code 1. */
5
+ export class UserError extends Error {
6
+ name = "UserError";
7
+ hint;
8
+ constructor(message, hint) {
9
+ super(message);
10
+ this.hint = hint;
11
+ }
12
+ }
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/cli/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yEAAyE;AACzE,MAAM,OAAO,SAAU,SAAQ,KAAK;IAC1B,IAAI,GAAG,WAAW,CAAC;IAC5B,IAAI,CAAU;IAEd,YAAY,OAAe,EAAE,IAAa;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ffmpeg detection and execution helpers.
3
+ * Assumes ffmpeg is installed on the system PATH; does not bundle it.
4
+ */
5
+ import { spawn } from "node:child_process";
6
+ /**
7
+ * Find ffmpeg on the system PATH.
8
+ * Returns the absolute path to the ffmpeg binary.
9
+ * Throws UserError with install instructions if not found.
10
+ */
11
+ export declare function findFfmpeg(): string;
12
+ /**
13
+ * Write a buffer to a writable stream, honoring backpressure.
14
+ * If write() returns false, waits for 'drain' before resolving.
15
+ *
16
+ * When waiting for drain, we attach both a 'drain' and 'error' listener.
17
+ * Whichever fires first removes the other to prevent listener accumulation
18
+ * across repeated backpressure events.
19
+ */
20
+ export declare function writeWithBackpressure(stream: NodeJS.WritableStream, data: Buffer): Promise<void>;
21
+ export interface FfmpegResult {
22
+ code: number;
23
+ stderr: string;
24
+ }
25
+ /**
26
+ * Build the ffmpeg argument array for a render pass.
27
+ *
28
+ * Video duration is always canonical:
29
+ * - If audio is shorter than video, silence is padded to fill the gap.
30
+ * - If audio is longer than video, audio is truncated at the video end.
31
+ * - If no audio is provided, the output is video-only.
32
+ *
33
+ * The ffmpeg pattern used:
34
+ * -map 0:v:0 -map 1:a:0 (explicit stream selection)
35
+ * -af "apad" -shortest (pad audio with silence to infinite length,
36
+ * then -shortest clips at the video end)
37
+ *
38
+ * This works because apad extends the audio to infinite length, making the
39
+ * video stream always the shortest. -shortest then terminates the output
40
+ * when the video (the shorter stream) ends. The net effect: output duration
41
+ * equals video duration regardless of audio length.
42
+ */
43
+ export declare function buildFfmpegArgs(opts: {
44
+ fps: number;
45
+ outputPath: string;
46
+ audioFilePath?: string;
47
+ }): string[];
48
+ /**
49
+ * Spawn ffmpeg with the given arguments.
50
+ * Returns a promise that resolves when the process exits.
51
+ * stderr is captured for diagnostics.
52
+ */
53
+ export declare function spawnFfmpeg(ffmpegPath: string, args: string[]): {
54
+ process: ReturnType<typeof spawn>;
55
+ done: Promise<FfmpegResult>;
56
+ };
57
+ //# sourceMappingURL=ffmpeg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../src/cli/ffmpeg.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAIzD;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAuBnC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhG;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,EAAE,CAkCX;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,GACZ;IACF,OAAO,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;IAClC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5B,CAoBA"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * ffmpeg detection and execution helpers.
3
+ * Assumes ffmpeg is installed on the system PATH; does not bundle it.
4
+ */
5
+ import { execFileSync, spawn } from "node:child_process";
6
+ import { platform } from "node:os";
7
+ import { UserError } from "./errors.js";
8
+ /**
9
+ * Find ffmpeg on the system PATH.
10
+ * Returns the absolute path to the ffmpeg binary.
11
+ * Throws UserError with install instructions if not found.
12
+ */
13
+ export function findFfmpeg() {
14
+ const isWindows = platform() === "win32";
15
+ const whichCmd = isWindows ? "where" : "which";
16
+ try {
17
+ const result = execFileSync(whichCmd, ["ffmpeg"], {
18
+ encoding: "utf-8",
19
+ stdio: ["pipe", "pipe", "pipe"],
20
+ });
21
+ // `which` and `where` may return multiple lines; take the first
22
+ const path = result.trim().split("\n")[0].trim();
23
+ if (path)
24
+ return path;
25
+ }
26
+ catch {
27
+ // not found
28
+ }
29
+ const installHint = isWindows
30
+ ? "Install ffmpeg: https://ffmpeg.org/download.html or via `choco install ffmpeg` / `winget install ffmpeg`"
31
+ : platform() === "darwin"
32
+ ? "Install ffmpeg: `brew install ffmpeg` or download from https://ffmpeg.org/download.html"
33
+ : "Install ffmpeg: `sudo apt install ffmpeg` or download from https://ffmpeg.org/download.html";
34
+ throw new UserError("ffmpeg not found on PATH", installHint);
35
+ }
36
+ /**
37
+ * Write a buffer to a writable stream, honoring backpressure.
38
+ * If write() returns false, waits for 'drain' before resolving.
39
+ *
40
+ * When waiting for drain, we attach both a 'drain' and 'error' listener.
41
+ * Whichever fires first removes the other to prevent listener accumulation
42
+ * across repeated backpressure events.
43
+ */
44
+ export function writeWithBackpressure(stream, data) {
45
+ return new Promise((resolve, reject) => {
46
+ const canContinue = stream.write(data);
47
+ if (canContinue) {
48
+ resolve();
49
+ }
50
+ else {
51
+ const onDrain = () => {
52
+ stream.removeListener("error", onError);
53
+ resolve();
54
+ };
55
+ const onError = (err) => {
56
+ stream.removeListener("drain", onDrain);
57
+ reject(err);
58
+ };
59
+ stream.once("drain", onDrain);
60
+ stream.once("error", onError);
61
+ }
62
+ });
63
+ }
64
+ /**
65
+ * Build the ffmpeg argument array for a render pass.
66
+ *
67
+ * Video duration is always canonical:
68
+ * - If audio is shorter than video, silence is padded to fill the gap.
69
+ * - If audio is longer than video, audio is truncated at the video end.
70
+ * - If no audio is provided, the output is video-only.
71
+ *
72
+ * The ffmpeg pattern used:
73
+ * -map 0:v:0 -map 1:a:0 (explicit stream selection)
74
+ * -af "apad" -shortest (pad audio with silence to infinite length,
75
+ * then -shortest clips at the video end)
76
+ *
77
+ * This works because apad extends the audio to infinite length, making the
78
+ * video stream always the shortest. -shortest then terminates the output
79
+ * when the video (the shorter stream) ends. The net effect: output duration
80
+ * equals video duration regardless of audio length.
81
+ */
82
+ export function buildFfmpegArgs(opts) {
83
+ const { fps, outputPath, audioFilePath } = opts;
84
+ const args = ["-y", "-f", "image2pipe", "-framerate", String(fps), "-i", "pipe:0"];
85
+ if (audioFilePath) {
86
+ args.push("-i", audioFilePath);
87
+ }
88
+ args.push("-c:v", "libx264", "-pix_fmt", "yuv420p", "-preset", "fast", "-crf", "18");
89
+ if (audioFilePath) {
90
+ // Video duration drives output length. apad pads the audio with silence
91
+ // to infinite length, making video the shortest stream. -shortest then
92
+ // clips at the video end, truncating longer audio or filling shorter
93
+ // audio with silence.
94
+ args.push("-c:a", "aac", "-b:a", "192k", "-map", "0:v:0", "-map", "1:a:0", "-af", "apad", "-shortest");
95
+ }
96
+ args.push("-r", String(fps), outputPath);
97
+ return args;
98
+ }
99
+ /**
100
+ * Spawn ffmpeg with the given arguments.
101
+ * Returns a promise that resolves when the process exits.
102
+ * stderr is captured for diagnostics.
103
+ */
104
+ export function spawnFfmpeg(ffmpegPath, args) {
105
+ const proc = spawn(ffmpegPath, args, {
106
+ stdio: ["pipe", "pipe", "pipe"],
107
+ });
108
+ let stderr = "";
109
+ proc.stderr?.on("data", (chunk) => {
110
+ stderr += chunk.toString();
111
+ });
112
+ const done = new Promise((resolve, reject) => {
113
+ proc.on("close", (code) => {
114
+ resolve({ code: code ?? 1, stderr });
115
+ });
116
+ proc.on("error", (err) => {
117
+ reject(err);
118
+ });
119
+ });
120
+ return { process: proc, done };
121
+ }
122
+ //# sourceMappingURL=ffmpeg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/cli/ffmpeg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * videowright CLI main logic.
3
+ * Parses argv, dispatches to dev, script, and render subcommands.
4
+ * The binary entry point is bin.ts (not this file).
5
+ */
6
+ export declare function main(argv?: string[]): Promise<number>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA8DH,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8F3D"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * videowright CLI main logic.
3
+ * Parses argv, dispatches to dev, script, and render subcommands.
4
+ * The binary entry point is bin.ts (not this file).
5
+ */
6
+ import { readFileSync } from "node:fs";
7
+ import { dirname, resolve } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { ArgvError, parseArgv } from "./argv.js";
10
+ import { UserError } from "./errors.js";
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ function readVersion() {
13
+ try {
14
+ // Walk up from dist/cli/ or src/cli/ to find package.json
15
+ let dir = __dirname;
16
+ for (let i = 0; i < 5; i++) {
17
+ const pkgPath = resolve(dir, "package.json");
18
+ try {
19
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
20
+ if (pkg.name === "videowright")
21
+ return pkg.version;
22
+ }
23
+ catch {
24
+ // not found here, keep going up
25
+ }
26
+ dir = dirname(dir);
27
+ }
28
+ return "0.0.0";
29
+ }
30
+ catch {
31
+ return "0.0.0";
32
+ }
33
+ }
34
+ const HELP_TEXT = `Usage: videowright <command> [options]
35
+
36
+ Commands:
37
+ dev Start the dev server (homepage at http://localhost:5173/)
38
+ script [path] Generate voiceover script
39
+ render [video] Deterministic frame-by-frame export via JS time injection + ffmpeg
40
+
41
+ Options:
42
+ --port <n> Dev server port (default: 5173)
43
+ --write Write script to file instead of stdout
44
+ --width <n> Video width in pixels (render only, default: 1920)
45
+ --height <n> Video height in pixels (render only, default: 1080)
46
+ --fps <n> Frames per second (render only, default: 60)
47
+ --output <path> Output file path (render only, default: output.mp4)
48
+ --audio-track <id> Use audio track from audio/tracks/<id>/ (render)
49
+ --audio-track none Disable audio track (ignore default_audio_track)
50
+ --verbose Show extra detail
51
+ --help Show this help
52
+ --version Show version
53
+ `;
54
+ function formatError(message, hint, verbose, stack) {
55
+ let out = `videowright: ${message}`;
56
+ if (hint) {
57
+ out += `\nhint: ${hint}`;
58
+ }
59
+ if (verbose && stack) {
60
+ out += `\n\n${stack}`;
61
+ }
62
+ return out;
63
+ }
64
+ export async function main(argv) {
65
+ let parsed;
66
+ try {
67
+ parsed = parseArgv(argv);
68
+ }
69
+ catch (e) {
70
+ if (e instanceof ArgvError) {
71
+ console.error(formatError(e.message));
72
+ return 1;
73
+ }
74
+ throw e;
75
+ }
76
+ const { command, positional, flags } = parsed;
77
+ if (command === "help") {
78
+ console.log(HELP_TEXT);
79
+ return 0;
80
+ }
81
+ if (command === "version") {
82
+ console.log(readVersion());
83
+ return 0;
84
+ }
85
+ const cwd = process.cwd();
86
+ try {
87
+ if (command === "dev") {
88
+ const { runDev } = await import("./dev.js");
89
+ const result = await runDev({
90
+ cwd,
91
+ port: flags.port,
92
+ verbose: flags.verbose,
93
+ });
94
+ console.log(`\n videowright dev server running at ${result.url}\n`);
95
+ // Keep alive until Ctrl-C
96
+ await new Promise((resolve) => {
97
+ const onSignal = () => {
98
+ process.removeListener("SIGINT", onSignal);
99
+ process.removeListener("SIGTERM", onSignal);
100
+ result.close().then(() => resolve());
101
+ };
102
+ process.on("SIGINT", onSignal);
103
+ process.on("SIGTERM", onSignal);
104
+ });
105
+ return 0;
106
+ }
107
+ if (command === "script") {
108
+ const { runScript } = await import("./script_cmd.js");
109
+ const result = await runScript({
110
+ cwd,
111
+ positional,
112
+ write: flags.write,
113
+ verbose: flags.verbose,
114
+ });
115
+ if (result.writtenTo) {
116
+ console.log(`Script written to ${result.writtenTo}`);
117
+ }
118
+ else {
119
+ process.stdout.write(result.markdown);
120
+ }
121
+ return 0;
122
+ }
123
+ if (command === "render") {
124
+ const { runRender } = await import("./render.js");
125
+ const result = await runRender({
126
+ cwd,
127
+ positional,
128
+ width: flags.width,
129
+ height: flags.height,
130
+ fps: flags.fps,
131
+ output: flags.output,
132
+ verbose: flags.verbose,
133
+ audioTrack: flags.audioTrack,
134
+ });
135
+ console.log(`\n Rendered ${result.frames} frames (${result.duration.toFixed(1)}s) to ${result.outputPath}\n`);
136
+ return 0;
137
+ }
138
+ }
139
+ catch (e) {
140
+ if (e instanceof UserError) {
141
+ console.error(formatError(e.message, e.hint));
142
+ return 1;
143
+ }
144
+ // System error -- always print stack per spec
145
+ const error = e instanceof Error ? e : new Error(String(e));
146
+ console.error(formatError(error.message, undefined, true, error.stack));
147
+ return 2;
148
+ }
149
+ // Should not reach here
150
+ return 0;
151
+ }
152
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Playwright availability check.
3
+ * Playwright is an optional runtime dependency -- only needed for the render command.
4
+ * Dynamic import so consumers who only use dev/script don't pay the cost.
5
+ */
6
+ export interface PlaywrightModule {
7
+ chromium: {
8
+ launch(opts?: Record<string, unknown>): Promise<PlaywrightBrowser>;
9
+ };
10
+ }
11
+ export interface PlaywrightBrowser {
12
+ newContext(opts?: Record<string, unknown>): Promise<PlaywrightBrowserContext>;
13
+ close(): Promise<void>;
14
+ }
15
+ export interface PlaywrightBrowserContext {
16
+ newPage(): Promise<PlaywrightPage>;
17
+ close(): Promise<void>;
18
+ }
19
+ /**
20
+ * Minimal stub of Playwright's Page interface.
21
+ * Intentionally narrow -- only the methods render.ts actually uses.
22
+ * Playwright's real `evaluate` accepts arg-passing overloads; we don't need them.
23
+ */
24
+ export interface PlaywrightPage {
25
+ goto(url: string, opts?: Record<string, unknown>): Promise<unknown>;
26
+ setViewportSize(size: {
27
+ width: number;
28
+ height: number;
29
+ }): Promise<void>;
30
+ screenshot(opts?: Record<string, unknown>): Promise<Buffer>;
31
+ evaluate<T>(fn: (() => T) | string): Promise<T>;
32
+ waitForFunction(fn: (() => unknown) | string, opts?: Record<string, unknown>): Promise<unknown>;
33
+ addInitScript(opts: {
34
+ content: string;
35
+ }): Promise<void>;
36
+ on(event: string, handler: (...args: unknown[]) => void): void;
37
+ close(): Promise<void>;
38
+ }
39
+ /**
40
+ * Dynamically import playwright.
41
+ * Throws UserError with a clear install hint if playwright is not available.
42
+ */
43
+ export declare function ensurePlaywright(): Promise<PlaywrightModule>;
44
+ //# sourceMappingURL=playwright_check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright_check.d.ts","sourceRoot":"","sources":["../../src/cli/playwright_check.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE;QACT,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;KACnE,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACxC,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,eAAe,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChD,eAAe,CAAC,EAAE,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChG,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAUlE"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Playwright availability check.
3
+ * Playwright is an optional runtime dependency -- only needed for the render command.
4
+ * Dynamic import so consumers who only use dev/script don't pay the cost.
5
+ */
6
+ import { UserError } from "./errors.js";
7
+ /**
8
+ * Dynamically import playwright.
9
+ * Throws UserError with a clear install hint if playwright is not available.
10
+ */
11
+ export async function ensurePlaywright() {
12
+ try {
13
+ const pw = await import("playwright");
14
+ return pw;
15
+ }
16
+ catch {
17
+ throw new UserError("Playwright is not installed", "Install it with: npm install playwright && npx playwright install chromium");
18
+ }
19
+ }
20
+ //# sourceMappingURL=playwright_check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright_check.js","sourceRoot":"","sources":["../../src/cli/playwright_check.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAkCxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACrC,IAAI,CAAC;QACJ,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACtC,OAAO,EAAiC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,SAAS,CAClB,6BAA6B,EAC7B,4EAA4E,CAC5E,CAAC;IACH,CAAC;AACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Interactive video selection prompt for `videowright render`.
3
+ * Uses Node's built-in readline -- no new dependencies.
4
+ */
5
+ import type { VideoSummary } from "../types.js";
6
+ /**
7
+ * Display a numbered list of videos and prompt the user to pick one.
8
+ * Retries up to 3 times on invalid input, then throws UserError.
9
+ *
10
+ * @returns The timelinePath of the selected video.
11
+ */
12
+ export declare function promptVideoSelection(videos: VideoSummary[]): Promise<string>;
13
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/cli/prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqClF"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Interactive video selection prompt for `videowright render`.
3
+ * Uses Node's built-in readline -- no new dependencies.
4
+ */
5
+ import { createInterface } from "node:readline";
6
+ import { UserError } from "./errors.js";
7
+ const MAX_RETRIES = 3;
8
+ /**
9
+ * Display a numbered list of videos and prompt the user to pick one.
10
+ * Retries up to 3 times on invalid input, then throws UserError.
11
+ *
12
+ * @returns The timelinePath of the selected video.
13
+ */
14
+ export async function promptVideoSelection(videos) {
15
+ console.log("\nPick a video to render:");
16
+ for (let i = 0; i < videos.length; i++) {
17
+ const v = videos[i];
18
+ console.log(` ${i + 1}. ${v.slug} — "${v.title}" — ${v.style}`);
19
+ }
20
+ console.log();
21
+ // Output to stderr so the prompt text doesn't pollute stdout if piped
22
+ const rl = createInterface({
23
+ input: process.stdin,
24
+ output: process.stderr,
25
+ });
26
+ let attempts = 0;
27
+ try {
28
+ while (attempts < MAX_RETRIES) {
29
+ const answer = await new Promise((resolve) => {
30
+ rl.question(`Enter number [1-${videos.length}]: `, resolve);
31
+ });
32
+ const num = Number.parseInt(answer.trim(), 10);
33
+ if (!Number.isNaN(num) && num >= 1 && num <= videos.length) {
34
+ return videos[num - 1].timelinePath;
35
+ }
36
+ attempts++;
37
+ if (attempts < MAX_RETRIES) {
38
+ console.error(`Invalid selection: "${answer.trim()}". Try again.`);
39
+ }
40
+ }
41
+ }
42
+ finally {
43
+ rl.close();
44
+ }
45
+ throw new UserError("Failed to select a video after 3 attempts.");
46
+ }
47
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/cli/prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAsB;IAChE,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,sEAAsE;IACtE,MAAM,EAAE,GAAG,eAAe,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,CAAC;QACJ,OAAO,QAAQ,GAAG,WAAW,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;gBACpD,EAAE,CAAC,QAAQ,CAAC,mBAAmB,MAAM,CAAC,MAAM,KAAK,EAAE,OAAO,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YAEH,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC5D,OAAO,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;YACrC,CAAC;YAED,QAAQ,EAAE,CAAC;YACX,IAAI,QAAQ,GAAG,WAAW,EAAE,CAAC;gBAC5B,OAAO,CAAC,KAAK,CAAC,uBAAuB,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACpE,CAAC;QACF,CAAC;IACF,CAAC;YAAS,CAAC;QACV,EAAE,CAAC,KAAK,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;AACnE,CAAC"}