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,88 @@
1
+ /**
2
+ * `videowright script` — load a timeline and segments in Node, produce the
3
+ * voiceover script as Markdown.
4
+ */
5
+ import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { script } from "../script/script.js";
8
+ import { discoverProject } from "./discover_project.js";
9
+ import { loadModule } from "./ts_loader.js";
10
+ /**
11
+ * Build a SegmentLoaderMap by walking the cwd/segments/ directory in Node.
12
+ * Each segments/<id>/index.ts becomes an entry in the map.
13
+ * Directories without an index.ts are warned about.
14
+ */
15
+ export function buildNodeSegmentLoaderMap(cwd) {
16
+ const segmentsDir = join(cwd, "segments");
17
+ const map = new Map();
18
+ if (!existsSync(segmentsDir))
19
+ return map;
20
+ let entries;
21
+ try {
22
+ entries = readdirSync(segmentsDir);
23
+ }
24
+ catch {
25
+ return map;
26
+ }
27
+ const skipped = [];
28
+ for (const entry of entries) {
29
+ const entryPath = join(segmentsDir, entry);
30
+ // Only consider directories
31
+ try {
32
+ if (!statSync(entryPath).isDirectory())
33
+ continue;
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ const indexPath = join(entryPath, "index.ts");
39
+ if (existsSync(indexPath)) {
40
+ const absPath = resolve(indexPath);
41
+ map.set(entry, async () => {
42
+ const mod = await loadModule(absPath);
43
+ return { default: mod.default };
44
+ });
45
+ }
46
+ else {
47
+ skipped.push(entry);
48
+ }
49
+ }
50
+ if (skipped.length > 0) {
51
+ console.warn(`buildNodeSegmentLoaderMap: skipped directories without index.ts: ${skipped.join(", ")}`);
52
+ }
53
+ return map;
54
+ }
55
+ /**
56
+ * Run the script flow. Returns the markdown and optional write path.
57
+ */
58
+ export async function runScript(opts) {
59
+ const { cwd, positional, write, verbose } = opts;
60
+ const { configPath, timelinePath } = discoverProject(cwd, positional, "script");
61
+ if (verbose) {
62
+ console.log(`config: ${configPath}`);
63
+ console.log(`timeline: ${timelinePath}`);
64
+ }
65
+ // 3. Load config + timeline (config loaded to validate; not used directly here)
66
+ await loadModule(configPath);
67
+ const timelineMod = await loadModule(timelinePath);
68
+ const timeline = timelineMod.default;
69
+ // 4. Build segment loader map from filesystem
70
+ const segmentLoaders = buildNodeSegmentLoaderMap(cwd);
71
+ // 5. Generate script
72
+ const markdown = await script(timeline, segmentLoaders);
73
+ // 6. Write or return
74
+ let writtenTo;
75
+ if (write) {
76
+ // Infer video folder from timeline path: parent dir of timeline.ts
77
+ const videoDir = dirname(timelinePath);
78
+ const voiceoverScriptDir = join(videoDir, "voiceover_script");
79
+ if (!existsSync(voiceoverScriptDir)) {
80
+ mkdirSync(voiceoverScriptDir, { recursive: true });
81
+ }
82
+ const outputPath = join(voiceoverScriptDir, "script.md");
83
+ writeFileSync(outputPath, markdown, "utf-8");
84
+ writtenTo = outputPath;
85
+ }
86
+ return { markdown, writtenTo };
87
+ }
88
+ //# sourceMappingURL=script_cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script_cmd.js","sourceRoot":"","sources":["../../src/cli/script_cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * JS time injection shim for deterministic rendering.
3
+ *
4
+ * This module exports the shim source code as a string. The render driver
5
+ * injects it into the page via `page.addInitScript()` BEFORE navigation,
6
+ * so it runs in every frame context before any user code.
7
+ *
8
+ * ## Two-mode design
9
+ *
10
+ * The shim operates in two modes:
11
+ *
12
+ * 1. **Passthrough mode** (default, during boot): Virtual clock advances at
13
+ * wall-clock rate. setTimeout, setInterval, requestAnimationFrame all
14
+ * delegate to the REAL browser implementations so that Vite HMR, module
15
+ * loading, library init (lottie-web, echarts, three.js), and the render
16
+ * entry boot() function can complete normally. `performance.now()` and
17
+ * `Date.now()` return real elapsed time since shim install. Real timer
18
+ * IDs and metadata are tracked so they can be converted to virtual
19
+ * timers at mode switch (preserving pending callbacks).
20
+ *
21
+ * 2. **Driver-controlled mode** (during render capture): The driver controls
22
+ * advancement via `__VW_ADVANCE_CLOCK__(deltaMs)`. Timers and RAF
23
+ * callbacks only fire when the driver advances time. WAAPI animations
24
+ * are paused and driven by explicit currentTime updates.
25
+ *
26
+ * The shim starts in passthrough mode. When the Node driver is ready to
27
+ * start capture (after `__VW_RENDER_READY__` is set), it calls
28
+ * `window.__VW_ENGAGE_VIRTUAL_TIME__()` to switch to driver-controlled
29
+ * mode. From that point, real timers stop firing and the driver advances
30
+ * virtual time explicitly.
31
+ *
32
+ * The shim overrides:
33
+ * - Date (no-arg constructor), Date.now
34
+ * - performance.now
35
+ * - setTimeout, setInterval, clearTimeout, clearInterval
36
+ * - requestAnimationFrame, cancelAnimationFrame
37
+ * - Element.prototype.animate (for WAAPI determinism)
38
+ *
39
+ * It exposes:
40
+ * - window.__VW_ADVANCE_CLOCK__(deltaMs) — advance the virtual clock, fire timers/RAFs, update WAAPI
41
+ * - window.__VW_ENGAGE_VIRTUAL_TIME__() — switch from passthrough to driver-controlled mode
42
+ */
43
+ export declare const TIME_SHIM_SOURCE = "\n(function() {\n // Preserve real implementations\n var RealDate = Date;\n var realDateNow = Date.now.bind(Date);\n var realPerfNow = performance.now.bind(performance);\n var realSetTimeout = window.setTimeout.bind(window);\n var realClearTimeout = window.clearTimeout.bind(window);\n var realSetInterval = window.setInterval.bind(window);\n var realClearInterval = window.clearInterval.bind(window);\n var realRAF = window.requestAnimationFrame.bind(window);\n var realCAF = window.cancelAnimationFrame.bind(window);\n var realElementAnimate = Element.prototype.animate;\n\n // ----- Mode state -----\n // 'passthrough' = boot phase, real timers run normally\n // 'driver' = render capture, driver controls time\n var mode = 'passthrough';\n\n // ----- Passthrough bookkeeping -----\n // Wall-clock reference captured at shim install time\n var shimInstallPerf = realPerfNow();\n var shimInstallDate = realDateNow();\n // Track real timer/RAF IDs with metadata so we can convert them at mode switch\n var realTimerMeta = new Map(); // real setTimeout ID -> { cb, args, fireAtRealMs }\n var realIntervalMeta = new Map(); // real setInterval ID -> { cb, args, intervalMs, nextFireRealMs }\n var realRafMeta = new Map(); // real RAF ID -> { cb }\n\n // ----- Driver-mode state -----\n var virtualMs = 0;\n var nextTimerId = 1;\n var timers = new Map(); // id -> { fireAt, cb, args, repeat? }\n var rafCallbacks = []; // [{ id, cb }]\n var trackedAnimations = []; // [{ animation, startVirtualMs }]\n\n // ----- performance.now -----\n performance.now = function() {\n if (mode === 'passthrough') {\n return realPerfNow() - shimInstallPerf;\n }\n return virtualMs;\n };\n\n // ----- Date -----\n function VirtualDate() {\n if (arguments.length === 0) {\n if (mode === 'passthrough') {\n return new RealDate(realDateNow());\n }\n return new RealDate(shimInstallDate + virtualMs);\n }\n switch (arguments.length) {\n case 1: return new RealDate(arguments[0]);\n case 2: return new RealDate(arguments[0], arguments[1]);\n case 3: return new RealDate(arguments[0], arguments[1], arguments[2]);\n case 4: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3]);\n case 5: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);\n case 6: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);\n default: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);\n }\n }\n VirtualDate.now = function() {\n if (mode === 'passthrough') {\n return realDateNow();\n }\n return Math.floor(shimInstallDate + virtualMs);\n };\n VirtualDate.parse = RealDate.parse;\n VirtualDate.UTC = RealDate.UTC;\n VirtualDate.prototype = RealDate.prototype;\n window.Date = VirtualDate;\n\n // ----- setTimeout / clearTimeout -----\n window.setTimeout = function(cb, ms) {\n if (typeof cb !== 'function') return 0;\n var delay = (ms || 0);\n var args = Array.prototype.slice.call(arguments, 2);\n if (mode === 'passthrough') {\n var fireAtRealMs = realPerfNow() + delay;\n // Wrap the callback so that if the real timer fires before engagement,\n // its metadata is removed -- preventing a double-fire at engagement.\n var realId = realSetTimeout(function() { realTimerMeta.delete(realId); cb.apply(null, args); }, delay);\n realTimerMeta.set(realId, { cb: cb, args: args, fireAtRealMs: fireAtRealMs });\n return realId;\n }\n var id = nextTimerId++;\n timers.set(id, { fireAt: virtualMs + delay, cb: cb, args: args });\n return id;\n };\n window.clearTimeout = function(id) {\n if (mode === 'passthrough') {\n realTimerMeta.delete(id);\n realClearTimeout(id);\n return;\n }\n timers.delete(id);\n };\n\n // ----- setInterval / clearInterval -----\n window.setInterval = function(cb, ms) {\n if (typeof cb !== 'function') return 0;\n if (mode === 'passthrough') {\n var intervalMs = Math.max(ms || 0, 1);\n var args = Array.prototype.slice.call(arguments, 2);\n var meta = { cb: cb, args: args, intervalMs: intervalMs, nextFireRealMs: realPerfNow() + intervalMs };\n // Wrap the callback to update nextFireRealMs each time the real interval\n // fires, so that at engagement we compute an accurate remaining time\n // instead of using the stale initial registration time.\n var realId = realSetInterval(function() { meta.nextFireRealMs = realPerfNow() + intervalMs; cb.apply(null, args); }, intervalMs);\n realIntervalMeta.set(realId, meta);\n return realId;\n }\n var interval = Math.max(ms || 0, 1);\n var id = nextTimerId++;\n var args = Array.prototype.slice.call(arguments, 2);\n timers.set(id, { fireAt: virtualMs + interval, cb: cb, args: args, repeat: interval });\n return id;\n };\n window.clearInterval = function(id) {\n if (mode === 'passthrough') {\n realIntervalMeta.delete(id);\n realClearInterval(id);\n return;\n }\n timers.delete(id);\n };\n\n // ----- requestAnimationFrame / cancelAnimationFrame -----\n window.requestAnimationFrame = function(cb) {\n if (mode === 'passthrough') {\n // Wrap the callback so that if the real rAF fires before engagement,\n // its metadata is removed -- preventing a double-fire at engagement.\n var realId = realRAF(function(timestamp) { realRafMeta.delete(realId); cb(timestamp); });\n realRafMeta.set(realId, { cb: cb });\n return realId;\n }\n var id = nextTimerId++;\n rafCallbacks.push({ id: id, cb: cb });\n return id;\n };\n window.cancelAnimationFrame = function(id) {\n if (mode === 'passthrough') {\n realRafMeta.delete(id);\n realCAF(id);\n return;\n }\n for (var i = 0; i < rafCallbacks.length; i++) {\n if (rafCallbacks[i].id === id) {\n rafCallbacks.splice(i, 1);\n break;\n }\n }\n };\n\n // ----- WAAPI: Element.prototype.animate -----\n Element.prototype.animate = function(keyframes, options) {\n var anim = realElementAnimate.call(this, keyframes, options);\n if (mode === 'passthrough') {\n // Let the animation run normally during boot; it will be captured\n // at mode-switch time if still running.\n return anim;\n }\n // Driver mode: pause and drive via currentTime\n return trackAnimation(anim, options);\n };\n\n function trackAnimation(anim, options) {\n anim.pause();\n anim.currentTime = 0;\n var entry = { animation: anim, startVirtualMs: virtualMs };\n trackedAnimations.push(entry);\n var duration = 0;\n if (typeof options === 'number') {\n duration = options;\n } else if (options && typeof options.duration === 'number') {\n duration = options.duration;\n }\n var delay = 0;\n if (options && typeof options === 'object' && typeof options.delay === 'number') {\n delay = options.delay;\n }\n var totalDuration = delay + duration;\n entry.totalDuration = totalDuration;\n entry.resolved = false;\n var finishedPromise = new Promise(function(resolve) {\n entry.resolve = resolve;\n });\n Object.defineProperty(anim, 'finished', {\n get: function() { return finishedPromise; },\n configurable: true\n });\n return anim;\n }\n\n // ----- Mode switch: passthrough -> driver -----\n window.__VW_ENGAGE_VIRTUAL_TIME__ = function() {\n if (mode === 'driver') return; // already engaged\n\n // Set virtual time to current real elapsed so animations don't jump\n var nowReal = realPerfNow();\n virtualMs = nowReal - shimInstallPerf;\n\n // Convert pending real setTimeouts to virtual timers instead of discarding\n realTimerMeta.forEach(function(meta, realId) {\n realClearTimeout(realId);\n var remaining = Math.max(0, meta.fireAtRealMs - nowReal);\n var id = nextTimerId++;\n timers.set(id, { fireAt: virtualMs + remaining, cb: meta.cb, args: meta.args });\n });\n realTimerMeta.clear();\n\n // Convert pending real setIntervals to virtual intervals\n realIntervalMeta.forEach(function(meta, realId) {\n realClearInterval(realId);\n var remaining = Math.max(0, meta.nextFireRealMs - nowReal);\n var id = nextTimerId++;\n timers.set(id, { fireAt: virtualMs + remaining, cb: meta.cb, args: meta.args, repeat: meta.intervalMs });\n });\n realIntervalMeta.clear();\n\n // Convert pending real rAF callbacks into virtual RAF queue\n realRafMeta.forEach(function(meta, realId) {\n realCAF(realId);\n var id = nextTimerId++;\n rafCallbacks.push({ id: id, cb: meta.cb });\n });\n realRafMeta.clear();\n\n // Capture any WAAPI animations still running from boot and pause+track them\n try {\n var liveAnims = document.getAnimations();\n for (var i = 0; i < liveAnims.length; i++) {\n var a = liveAnims[i];\n if (a.playState === 'running' || a.playState === 'paused') {\n var ct = a.currentTime || 0;\n a.pause();\n a.currentTime = ct;\n var entry = {\n animation: a,\n startVirtualMs: virtualMs - ct,\n totalDuration: undefined,\n resolved: false,\n resolve: undefined\n };\n // Try to read duration from the animation effect\n try {\n var timing = a.effect && a.effect.getTiming ? a.effect.getTiming() : null;\n if (timing) {\n var dur = typeof timing.duration === 'number' ? timing.duration : 0;\n var del = typeof timing.delay === 'number' ? timing.delay : 0;\n entry.totalDuration = del + dur;\n }\n } catch(e) {}\n var finishedPromise = new Promise(function(resolve) {\n entry.resolve = resolve;\n });\n Object.defineProperty(a, 'finished', {\n get: function() { return finishedPromise; },\n configurable: true\n });\n trackedAnimations.push(entry);\n }\n }\n } catch(e) {\n // document.getAnimations may not be available in all contexts\n }\n\n mode = 'driver';\n };\n\n // ----- Clock advance driver -----\n window.__VW_ADVANCE_CLOCK__ = async function(deltaMs) {\n if (mode === 'passthrough') return; // no-op during boot\n\n var target = virtualMs + deltaMs;\n\n // Fire timers in chronological order up to target.\n // The function is async so that microtasks (e.g. Promise resolutions from\n // ctx.hold) drain between timer fires. This ensures awaiting code resumes\n // at virtualMs = fireAt (the timer's actual scheduled time) rather than at\n // the frame boundary (target), eliminating per-hold rounding drift.\n var safety = 10000;\n while (safety-- > 0) {\n var earliestId = null;\n var earliestTimer = null;\n timers.forEach(function(t, id) {\n if (t.fireAt <= target && (earliestTimer === null || t.fireAt < earliestTimer.fireAt)) {\n earliestId = id;\n earliestTimer = t;\n }\n });\n if (!earliestTimer) break;\n\n virtualMs = earliestTimer.fireAt;\n if (earliestTimer.repeat !== undefined) {\n timers.set(earliestId, {\n fireAt: virtualMs + earliestTimer.repeat,\n cb: earliestTimer.cb,\n args: earliestTimer.args,\n repeat: earliestTimer.repeat\n });\n } else {\n timers.delete(earliestId);\n }\n try { earliestTimer.cb.apply(null, earliestTimer.args || []); } catch(e) { console.error('[VW shim] timer error:', e); }\n\n // Drain microtasks so awaiting code (e.g. ctx.hold Promise resolution)\n // resumes at virtualMs = fireAt and registers its next timer relative to\n // fireAt, not target. Multiple awaits handle chained async continuations.\n for (var d = 0; d < 8; d++) await Promise.resolve();\n }\n\n // Final advance to target\n virtualMs = target;\n\n // Fire all queued RAF callbacks\n var queued = rafCallbacks.splice(0);\n for (var i = 0; i < queued.length; i++) {\n try { queued[i].cb(virtualMs); } catch(e) { console.error('[VW shim] RAF error:', e); }\n }\n\n // Update WAAPI animations\n for (var j = trackedAnimations.length - 1; j >= 0; j--) {\n var entry = trackedAnimations[j];\n var anim = entry.animation;\n // Skip cancelled animations\n if (anim.playState === 'idle') {\n trackedAnimations.splice(j, 1);\n continue;\n }\n var elapsed = virtualMs - entry.startVirtualMs;\n try {\n anim.currentTime = elapsed;\n } catch(e) {\n trackedAnimations.splice(j, 1);\n continue;\n }\n // Check if animation is complete\n if (entry.totalDuration !== undefined && elapsed >= entry.totalDuration && !entry.resolved) {\n entry.resolved = true;\n if (entry.resolve) entry.resolve(anim);\n trackedAnimations.splice(j, 1);\n }\n }\n };\n})();\n";
44
+ //# sourceMappingURL=time_shim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time_shim.d.ts","sourceRoot":"","sources":["../../src/cli/time_shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,eAAO,MAAM,gBAAgB,i7ZA0V5B,CAAC"}
@@ -0,0 +1,390 @@
1
+ /**
2
+ * JS time injection shim for deterministic rendering.
3
+ *
4
+ * This module exports the shim source code as a string. The render driver
5
+ * injects it into the page via `page.addInitScript()` BEFORE navigation,
6
+ * so it runs in every frame context before any user code.
7
+ *
8
+ * ## Two-mode design
9
+ *
10
+ * The shim operates in two modes:
11
+ *
12
+ * 1. **Passthrough mode** (default, during boot): Virtual clock advances at
13
+ * wall-clock rate. setTimeout, setInterval, requestAnimationFrame all
14
+ * delegate to the REAL browser implementations so that Vite HMR, module
15
+ * loading, library init (lottie-web, echarts, three.js), and the render
16
+ * entry boot() function can complete normally. `performance.now()` and
17
+ * `Date.now()` return real elapsed time since shim install. Real timer
18
+ * IDs and metadata are tracked so they can be converted to virtual
19
+ * timers at mode switch (preserving pending callbacks).
20
+ *
21
+ * 2. **Driver-controlled mode** (during render capture): The driver controls
22
+ * advancement via `__VW_ADVANCE_CLOCK__(deltaMs)`. Timers and RAF
23
+ * callbacks only fire when the driver advances time. WAAPI animations
24
+ * are paused and driven by explicit currentTime updates.
25
+ *
26
+ * The shim starts in passthrough mode. When the Node driver is ready to
27
+ * start capture (after `__VW_RENDER_READY__` is set), it calls
28
+ * `window.__VW_ENGAGE_VIRTUAL_TIME__()` to switch to driver-controlled
29
+ * mode. From that point, real timers stop firing and the driver advances
30
+ * virtual time explicitly.
31
+ *
32
+ * The shim overrides:
33
+ * - Date (no-arg constructor), Date.now
34
+ * - performance.now
35
+ * - setTimeout, setInterval, clearTimeout, clearInterval
36
+ * - requestAnimationFrame, cancelAnimationFrame
37
+ * - Element.prototype.animate (for WAAPI determinism)
38
+ *
39
+ * It exposes:
40
+ * - window.__VW_ADVANCE_CLOCK__(deltaMs) — advance the virtual clock, fire timers/RAFs, update WAAPI
41
+ * - window.__VW_ENGAGE_VIRTUAL_TIME__() — switch from passthrough to driver-controlled mode
42
+ */
43
+ export const TIME_SHIM_SOURCE = `
44
+ (function() {
45
+ // Preserve real implementations
46
+ var RealDate = Date;
47
+ var realDateNow = Date.now.bind(Date);
48
+ var realPerfNow = performance.now.bind(performance);
49
+ var realSetTimeout = window.setTimeout.bind(window);
50
+ var realClearTimeout = window.clearTimeout.bind(window);
51
+ var realSetInterval = window.setInterval.bind(window);
52
+ var realClearInterval = window.clearInterval.bind(window);
53
+ var realRAF = window.requestAnimationFrame.bind(window);
54
+ var realCAF = window.cancelAnimationFrame.bind(window);
55
+ var realElementAnimate = Element.prototype.animate;
56
+
57
+ // ----- Mode state -----
58
+ // 'passthrough' = boot phase, real timers run normally
59
+ // 'driver' = render capture, driver controls time
60
+ var mode = 'passthrough';
61
+
62
+ // ----- Passthrough bookkeeping -----
63
+ // Wall-clock reference captured at shim install time
64
+ var shimInstallPerf = realPerfNow();
65
+ var shimInstallDate = realDateNow();
66
+ // Track real timer/RAF IDs with metadata so we can convert them at mode switch
67
+ var realTimerMeta = new Map(); // real setTimeout ID -> { cb, args, fireAtRealMs }
68
+ var realIntervalMeta = new Map(); // real setInterval ID -> { cb, args, intervalMs, nextFireRealMs }
69
+ var realRafMeta = new Map(); // real RAF ID -> { cb }
70
+
71
+ // ----- Driver-mode state -----
72
+ var virtualMs = 0;
73
+ var nextTimerId = 1;
74
+ var timers = new Map(); // id -> { fireAt, cb, args, repeat? }
75
+ var rafCallbacks = []; // [{ id, cb }]
76
+ var trackedAnimations = []; // [{ animation, startVirtualMs }]
77
+
78
+ // ----- performance.now -----
79
+ performance.now = function() {
80
+ if (mode === 'passthrough') {
81
+ return realPerfNow() - shimInstallPerf;
82
+ }
83
+ return virtualMs;
84
+ };
85
+
86
+ // ----- Date -----
87
+ function VirtualDate() {
88
+ if (arguments.length === 0) {
89
+ if (mode === 'passthrough') {
90
+ return new RealDate(realDateNow());
91
+ }
92
+ return new RealDate(shimInstallDate + virtualMs);
93
+ }
94
+ switch (arguments.length) {
95
+ case 1: return new RealDate(arguments[0]);
96
+ case 2: return new RealDate(arguments[0], arguments[1]);
97
+ case 3: return new RealDate(arguments[0], arguments[1], arguments[2]);
98
+ case 4: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3]);
99
+ case 5: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
100
+ case 6: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
101
+ default: return new RealDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
102
+ }
103
+ }
104
+ VirtualDate.now = function() {
105
+ if (mode === 'passthrough') {
106
+ return realDateNow();
107
+ }
108
+ return Math.floor(shimInstallDate + virtualMs);
109
+ };
110
+ VirtualDate.parse = RealDate.parse;
111
+ VirtualDate.UTC = RealDate.UTC;
112
+ VirtualDate.prototype = RealDate.prototype;
113
+ window.Date = VirtualDate;
114
+
115
+ // ----- setTimeout / clearTimeout -----
116
+ window.setTimeout = function(cb, ms) {
117
+ if (typeof cb !== 'function') return 0;
118
+ var delay = (ms || 0);
119
+ var args = Array.prototype.slice.call(arguments, 2);
120
+ if (mode === 'passthrough') {
121
+ var fireAtRealMs = realPerfNow() + delay;
122
+ // Wrap the callback so that if the real timer fires before engagement,
123
+ // its metadata is removed -- preventing a double-fire at engagement.
124
+ var realId = realSetTimeout(function() { realTimerMeta.delete(realId); cb.apply(null, args); }, delay);
125
+ realTimerMeta.set(realId, { cb: cb, args: args, fireAtRealMs: fireAtRealMs });
126
+ return realId;
127
+ }
128
+ var id = nextTimerId++;
129
+ timers.set(id, { fireAt: virtualMs + delay, cb: cb, args: args });
130
+ return id;
131
+ };
132
+ window.clearTimeout = function(id) {
133
+ if (mode === 'passthrough') {
134
+ realTimerMeta.delete(id);
135
+ realClearTimeout(id);
136
+ return;
137
+ }
138
+ timers.delete(id);
139
+ };
140
+
141
+ // ----- setInterval / clearInterval -----
142
+ window.setInterval = function(cb, ms) {
143
+ if (typeof cb !== 'function') return 0;
144
+ if (mode === 'passthrough') {
145
+ var intervalMs = Math.max(ms || 0, 1);
146
+ var args = Array.prototype.slice.call(arguments, 2);
147
+ var meta = { cb: cb, args: args, intervalMs: intervalMs, nextFireRealMs: realPerfNow() + intervalMs };
148
+ // Wrap the callback to update nextFireRealMs each time the real interval
149
+ // fires, so that at engagement we compute an accurate remaining time
150
+ // instead of using the stale initial registration time.
151
+ var realId = realSetInterval(function() { meta.nextFireRealMs = realPerfNow() + intervalMs; cb.apply(null, args); }, intervalMs);
152
+ realIntervalMeta.set(realId, meta);
153
+ return realId;
154
+ }
155
+ var interval = Math.max(ms || 0, 1);
156
+ var id = nextTimerId++;
157
+ var args = Array.prototype.slice.call(arguments, 2);
158
+ timers.set(id, { fireAt: virtualMs + interval, cb: cb, args: args, repeat: interval });
159
+ return id;
160
+ };
161
+ window.clearInterval = function(id) {
162
+ if (mode === 'passthrough') {
163
+ realIntervalMeta.delete(id);
164
+ realClearInterval(id);
165
+ return;
166
+ }
167
+ timers.delete(id);
168
+ };
169
+
170
+ // ----- requestAnimationFrame / cancelAnimationFrame -----
171
+ window.requestAnimationFrame = function(cb) {
172
+ if (mode === 'passthrough') {
173
+ // Wrap the callback so that if the real rAF fires before engagement,
174
+ // its metadata is removed -- preventing a double-fire at engagement.
175
+ var realId = realRAF(function(timestamp) { realRafMeta.delete(realId); cb(timestamp); });
176
+ realRafMeta.set(realId, { cb: cb });
177
+ return realId;
178
+ }
179
+ var id = nextTimerId++;
180
+ rafCallbacks.push({ id: id, cb: cb });
181
+ return id;
182
+ };
183
+ window.cancelAnimationFrame = function(id) {
184
+ if (mode === 'passthrough') {
185
+ realRafMeta.delete(id);
186
+ realCAF(id);
187
+ return;
188
+ }
189
+ for (var i = 0; i < rafCallbacks.length; i++) {
190
+ if (rafCallbacks[i].id === id) {
191
+ rafCallbacks.splice(i, 1);
192
+ break;
193
+ }
194
+ }
195
+ };
196
+
197
+ // ----- WAAPI: Element.prototype.animate -----
198
+ Element.prototype.animate = function(keyframes, options) {
199
+ var anim = realElementAnimate.call(this, keyframes, options);
200
+ if (mode === 'passthrough') {
201
+ // Let the animation run normally during boot; it will be captured
202
+ // at mode-switch time if still running.
203
+ return anim;
204
+ }
205
+ // Driver mode: pause and drive via currentTime
206
+ return trackAnimation(anim, options);
207
+ };
208
+
209
+ function trackAnimation(anim, options) {
210
+ anim.pause();
211
+ anim.currentTime = 0;
212
+ var entry = { animation: anim, startVirtualMs: virtualMs };
213
+ trackedAnimations.push(entry);
214
+ var duration = 0;
215
+ if (typeof options === 'number') {
216
+ duration = options;
217
+ } else if (options && typeof options.duration === 'number') {
218
+ duration = options.duration;
219
+ }
220
+ var delay = 0;
221
+ if (options && typeof options === 'object' && typeof options.delay === 'number') {
222
+ delay = options.delay;
223
+ }
224
+ var totalDuration = delay + duration;
225
+ entry.totalDuration = totalDuration;
226
+ entry.resolved = false;
227
+ var finishedPromise = new Promise(function(resolve) {
228
+ entry.resolve = resolve;
229
+ });
230
+ Object.defineProperty(anim, 'finished', {
231
+ get: function() { return finishedPromise; },
232
+ configurable: true
233
+ });
234
+ return anim;
235
+ }
236
+
237
+ // ----- Mode switch: passthrough -> driver -----
238
+ window.__VW_ENGAGE_VIRTUAL_TIME__ = function() {
239
+ if (mode === 'driver') return; // already engaged
240
+
241
+ // Set virtual time to current real elapsed so animations don't jump
242
+ var nowReal = realPerfNow();
243
+ virtualMs = nowReal - shimInstallPerf;
244
+
245
+ // Convert pending real setTimeouts to virtual timers instead of discarding
246
+ realTimerMeta.forEach(function(meta, realId) {
247
+ realClearTimeout(realId);
248
+ var remaining = Math.max(0, meta.fireAtRealMs - nowReal);
249
+ var id = nextTimerId++;
250
+ timers.set(id, { fireAt: virtualMs + remaining, cb: meta.cb, args: meta.args });
251
+ });
252
+ realTimerMeta.clear();
253
+
254
+ // Convert pending real setIntervals to virtual intervals
255
+ realIntervalMeta.forEach(function(meta, realId) {
256
+ realClearInterval(realId);
257
+ var remaining = Math.max(0, meta.nextFireRealMs - nowReal);
258
+ var id = nextTimerId++;
259
+ timers.set(id, { fireAt: virtualMs + remaining, cb: meta.cb, args: meta.args, repeat: meta.intervalMs });
260
+ });
261
+ realIntervalMeta.clear();
262
+
263
+ // Convert pending real rAF callbacks into virtual RAF queue
264
+ realRafMeta.forEach(function(meta, realId) {
265
+ realCAF(realId);
266
+ var id = nextTimerId++;
267
+ rafCallbacks.push({ id: id, cb: meta.cb });
268
+ });
269
+ realRafMeta.clear();
270
+
271
+ // Capture any WAAPI animations still running from boot and pause+track them
272
+ try {
273
+ var liveAnims = document.getAnimations();
274
+ for (var i = 0; i < liveAnims.length; i++) {
275
+ var a = liveAnims[i];
276
+ if (a.playState === 'running' || a.playState === 'paused') {
277
+ var ct = a.currentTime || 0;
278
+ a.pause();
279
+ a.currentTime = ct;
280
+ var entry = {
281
+ animation: a,
282
+ startVirtualMs: virtualMs - ct,
283
+ totalDuration: undefined,
284
+ resolved: false,
285
+ resolve: undefined
286
+ };
287
+ // Try to read duration from the animation effect
288
+ try {
289
+ var timing = a.effect && a.effect.getTiming ? a.effect.getTiming() : null;
290
+ if (timing) {
291
+ var dur = typeof timing.duration === 'number' ? timing.duration : 0;
292
+ var del = typeof timing.delay === 'number' ? timing.delay : 0;
293
+ entry.totalDuration = del + dur;
294
+ }
295
+ } catch(e) {}
296
+ var finishedPromise = new Promise(function(resolve) {
297
+ entry.resolve = resolve;
298
+ });
299
+ Object.defineProperty(a, 'finished', {
300
+ get: function() { return finishedPromise; },
301
+ configurable: true
302
+ });
303
+ trackedAnimations.push(entry);
304
+ }
305
+ }
306
+ } catch(e) {
307
+ // document.getAnimations may not be available in all contexts
308
+ }
309
+
310
+ mode = 'driver';
311
+ };
312
+
313
+ // ----- Clock advance driver -----
314
+ window.__VW_ADVANCE_CLOCK__ = async function(deltaMs) {
315
+ if (mode === 'passthrough') return; // no-op during boot
316
+
317
+ var target = virtualMs + deltaMs;
318
+
319
+ // Fire timers in chronological order up to target.
320
+ // The function is async so that microtasks (e.g. Promise resolutions from
321
+ // ctx.hold) drain between timer fires. This ensures awaiting code resumes
322
+ // at virtualMs = fireAt (the timer's actual scheduled time) rather than at
323
+ // the frame boundary (target), eliminating per-hold rounding drift.
324
+ var safety = 10000;
325
+ while (safety-- > 0) {
326
+ var earliestId = null;
327
+ var earliestTimer = null;
328
+ timers.forEach(function(t, id) {
329
+ if (t.fireAt <= target && (earliestTimer === null || t.fireAt < earliestTimer.fireAt)) {
330
+ earliestId = id;
331
+ earliestTimer = t;
332
+ }
333
+ });
334
+ if (!earliestTimer) break;
335
+
336
+ virtualMs = earliestTimer.fireAt;
337
+ if (earliestTimer.repeat !== undefined) {
338
+ timers.set(earliestId, {
339
+ fireAt: virtualMs + earliestTimer.repeat,
340
+ cb: earliestTimer.cb,
341
+ args: earliestTimer.args,
342
+ repeat: earliestTimer.repeat
343
+ });
344
+ } else {
345
+ timers.delete(earliestId);
346
+ }
347
+ try { earliestTimer.cb.apply(null, earliestTimer.args || []); } catch(e) { console.error('[VW shim] timer error:', e); }
348
+
349
+ // Drain microtasks so awaiting code (e.g. ctx.hold Promise resolution)
350
+ // resumes at virtualMs = fireAt and registers its next timer relative to
351
+ // fireAt, not target. Multiple awaits handle chained async continuations.
352
+ for (var d = 0; d < 8; d++) await Promise.resolve();
353
+ }
354
+
355
+ // Final advance to target
356
+ virtualMs = target;
357
+
358
+ // Fire all queued RAF callbacks
359
+ var queued = rafCallbacks.splice(0);
360
+ for (var i = 0; i < queued.length; i++) {
361
+ try { queued[i].cb(virtualMs); } catch(e) { console.error('[VW shim] RAF error:', e); }
362
+ }
363
+
364
+ // Update WAAPI animations
365
+ for (var j = trackedAnimations.length - 1; j >= 0; j--) {
366
+ var entry = trackedAnimations[j];
367
+ var anim = entry.animation;
368
+ // Skip cancelled animations
369
+ if (anim.playState === 'idle') {
370
+ trackedAnimations.splice(j, 1);
371
+ continue;
372
+ }
373
+ var elapsed = virtualMs - entry.startVirtualMs;
374
+ try {
375
+ anim.currentTime = elapsed;
376
+ } catch(e) {
377
+ trackedAnimations.splice(j, 1);
378
+ continue;
379
+ }
380
+ // Check if animation is complete
381
+ if (entry.totalDuration !== undefined && elapsed >= entry.totalDuration && !entry.resolved) {
382
+ entry.resolved = true;
383
+ if (entry.resolve) entry.resolve(anim);
384
+ trackedAnimations.splice(j, 1);
385
+ }
386
+ }
387
+ };
388
+ })();
389
+ `;
390
+ //# sourceMappingURL=time_shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time_shim.js","sourceRoot":"","sources":["../../src/cli/time_shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0V/B,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Runtime TS module loader for Node using tsx.
3
+ * Used by the CLI to import .ts config and timeline files without a build step.
4
+ */
5
+ /**
6
+ * Dynamically import a TypeScript module from Node at runtime.
7
+ * Returns the module's namespace (use .default for default exports).
8
+ *
9
+ * ## tsx double-wrap heuristic
10
+ *
11
+ * When tsx loads a CommonJS-style module (or a module with `export default`),
12
+ * it may produce a namespace like:
13
+ *
14
+ * { default: { __esModule: true, default: <actual-value> } }
15
+ *
16
+ * This happens because tsx transpiles `export default X` to
17
+ * `module.exports.default = X` + `module.exports.__esModule = true`, then
18
+ * the ESM interop layer wraps the whole `module.exports` as the default export.
19
+ *
20
+ * We detect this pattern (an object default with both __esModule and default keys)
21
+ * and unwrap it so callers always see `{ default: <actual-value>, ... }`.
22
+ *
23
+ * If tsx changes this behavior in future versions, the detection is conservative:
24
+ * we only unwrap when both sentinel keys are present, so a legitimate object
25
+ * with those keys would be the only false positive (extremely unlikely in practice).
26
+ */
27
+ export declare function loadModule(modulePath: string): Promise<Record<string, unknown>>;
28
+ //# sourceMappingURL=ts_loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ts_loader.d.ts","sourceRoot":"","sources":["../../src/cli/ts_loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyDH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAmBrF"}