recordable 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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/actions.d.ts +140 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +184 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/audio/track.d.ts +45 -0
  8. package/dist/audio/track.d.ts.map +1 -0
  9. package/dist/audio/track.js +61 -0
  10. package/dist/audio/track.js.map +1 -0
  11. package/dist/browser/cursor.d.ts +33 -0
  12. package/dist/browser/cursor.d.ts.map +1 -0
  13. package/dist/browser/cursor.js +118 -0
  14. package/dist/browser/cursor.js.map +1 -0
  15. package/dist/browser/dom.d.ts +31 -0
  16. package/dist/browser/dom.d.ts.map +1 -0
  17. package/dist/browser/dom.js +134 -0
  18. package/dist/browser/dom.js.map +1 -0
  19. package/dist/browser/play-button.d.ts +11 -0
  20. package/dist/browser/play-button.d.ts.map +1 -0
  21. package/dist/browser/play-button.js +87 -0
  22. package/dist/browser/play-button.js.map +1 -0
  23. package/dist/browser/runtime.d.ts +66 -0
  24. package/dist/browser/runtime.d.ts.map +1 -0
  25. package/dist/browser/runtime.js +271 -0
  26. package/dist/browser/runtime.js.map +1 -0
  27. package/dist/cli.d.ts +3 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +131 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/compose/mix.d.ts +13 -0
  32. package/dist/compose/mix.d.ts.map +1 -0
  33. package/dist/compose/mix.js +50 -0
  34. package/dist/compose/mix.js.map +1 -0
  35. package/dist/compose/recordable.d.ts +149 -0
  36. package/dist/compose/recordable.d.ts.map +1 -0
  37. package/dist/compose/recordable.js +337 -0
  38. package/dist/compose/recordable.js.map +1 -0
  39. package/dist/compose/session.d.ts +38 -0
  40. package/dist/compose/session.d.ts.map +1 -0
  41. package/dist/compose/session.js +122 -0
  42. package/dist/compose/session.js.map +1 -0
  43. package/dist/config.d.ts +93 -0
  44. package/dist/config.d.ts.map +1 -0
  45. package/dist/config.js +64 -0
  46. package/dist/config.js.map +1 -0
  47. package/dist/errors.d.ts +13 -0
  48. package/dist/errors.d.ts.map +1 -0
  49. package/dist/errors.js +21 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/ffmpeg.d.ts +8 -0
  52. package/dist/ffmpeg.d.ts.map +1 -0
  53. package/dist/ffmpeg.js +55 -0
  54. package/dist/ffmpeg.js.map +1 -0
  55. package/dist/formats/json.d.ts +12 -0
  56. package/dist/formats/json.d.ts.map +1 -0
  57. package/dist/formats/json.js +20 -0
  58. package/dist/formats/json.js.map +1 -0
  59. package/dist/formats/markdown/method.d.ts +25 -0
  60. package/dist/formats/markdown/method.d.ts.map +1 -0
  61. package/dist/formats/markdown/method.js +48 -0
  62. package/dist/formats/markdown/method.js.map +1 -0
  63. package/dist/formats/markdown/parse.d.ts +44 -0
  64. package/dist/formats/markdown/parse.d.ts.map +1 -0
  65. package/dist/formats/markdown/parse.js +143 -0
  66. package/dist/formats/markdown/parse.js.map +1 -0
  67. package/dist/fs.d.ts +9 -0
  68. package/dist/fs.d.ts.map +1 -0
  69. package/dist/fs.js +30 -0
  70. package/dist/fs.js.map +1 -0
  71. package/dist/index.d.ts +10 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +6 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/logger.d.ts +21 -0
  76. package/dist/logger.d.ts.map +1 -0
  77. package/dist/logger.js +45 -0
  78. package/dist/logger.js.map +1 -0
  79. package/dist/schema.d.ts +5 -0
  80. package/dist/schema.d.ts.map +1 -0
  81. package/dist/schema.js +100 -0
  82. package/dist/schema.js.map +1 -0
  83. package/dist/script.d.ts +21 -0
  84. package/dist/script.d.ts.map +1 -0
  85. package/dist/script.js +26 -0
  86. package/dist/script.js.map +1 -0
  87. package/dist/targets.d.ts +6 -0
  88. package/dist/targets.d.ts.map +1 -0
  89. package/dist/targets.js +13 -0
  90. package/dist/targets.js.map +1 -0
  91. package/dist/timing.d.ts +41 -0
  92. package/dist/timing.d.ts.map +1 -0
  93. package/dist/timing.js +149 -0
  94. package/dist/timing.js.map +1 -0
  95. package/dist/utils.d.ts +3 -0
  96. package/dist/utils.d.ts.map +1 -0
  97. package/dist/utils.js +8 -0
  98. package/dist/utils.js.map +1 -0
  99. package/dist/validate.d.ts +8 -0
  100. package/dist/validate.d.ts.map +1 -0
  101. package/dist/validate.js +54 -0
  102. package/dist/validate.js.map +1 -0
  103. package/dist/video/recorder.d.ts +57 -0
  104. package/dist/video/recorder.d.ts.map +1 -0
  105. package/dist/video/recorder.js +238 -0
  106. package/dist/video/recorder.js.map +1 -0
  107. package/dist/video/stitch.d.ts +15 -0
  108. package/dist/video/stitch.d.ts.map +1 -0
  109. package/dist/video/stitch.js +111 -0
  110. package/dist/video/stitch.js.map +1 -0
  111. package/dist/voiceover/alignment.d.ts +14 -0
  112. package/dist/voiceover/alignment.d.ts.map +1 -0
  113. package/dist/voiceover/alignment.js +13 -0
  114. package/dist/voiceover/alignment.js.map +1 -0
  115. package/dist/voiceover/cache.d.ts +22 -0
  116. package/dist/voiceover/cache.d.ts.map +1 -0
  117. package/dist/voiceover/cache.js +55 -0
  118. package/dist/voiceover/cache.js.map +1 -0
  119. package/dist/voiceover/compile.d.ts +35 -0
  120. package/dist/voiceover/compile.d.ts.map +1 -0
  121. package/dist/voiceover/compile.js +194 -0
  122. package/dist/voiceover/compile.js.map +1 -0
  123. package/dist/voiceover/elevenlabs.d.ts +16 -0
  124. package/dist/voiceover/elevenlabs.d.ts.map +1 -0
  125. package/dist/voiceover/elevenlabs.js +66 -0
  126. package/dist/voiceover/elevenlabs.js.map +1 -0
  127. package/dist/voiceover/index.d.ts +7 -0
  128. package/dist/voiceover/index.d.ts.map +1 -0
  129. package/dist/voiceover/index.js +8 -0
  130. package/dist/voiceover/index.js.map +1 -0
  131. package/dist/voiceover/mock.d.ts +15 -0
  132. package/dist/voiceover/mock.d.ts.map +1 -0
  133. package/dist/voiceover/mock.js +41 -0
  134. package/dist/voiceover/mock.js.map +1 -0
  135. package/dist/voiceover/types.d.ts +31 -0
  136. package/dist/voiceover/types.d.ts.map +1 -0
  137. package/dist/voiceover/types.js +10 -0
  138. package/dist/voiceover/types.js.map +1 -0
  139. package/package.json +86 -0
  140. package/recordable.schema.json +738 -0
@@ -0,0 +1,149 @@
1
+ import { type GoToOptions } from "puppeteer";
2
+ import { type AudioOptions, type InsertOptions, type RecordableConfig, type WaitForOptions } from "../config.js";
3
+ import { type Script } from "../script.js";
4
+ export declare class Recordable {
5
+ private cfg;
6
+ private readonly userConfig;
7
+ private readonly log;
8
+ private readonly recorder;
9
+ private readonly audioTrack;
10
+ private readonly runtime;
11
+ private queue;
12
+ private pending;
13
+ private recording;
14
+ constructor(config?: RecordableConfig);
15
+ /** Load a JSON script — an array of actions, a `{ config, actions }` object, or a
16
+ * raw JSON string — enqueuing each action. Returns `this` to chain into `.run()`. */
17
+ fromJSON(script: Script | string): this;
18
+ /**
19
+ * Load a Markdown document — synchronous and chainable. The `voiceover`
20
+ * frontmatter key picks the path: present → synthesize narration + computed
21
+ * waits (the add-on, dynamically imported so a no-audio run never loads TTS),
22
+ * deferred to `run()`; absent → flatten markers to a plain chain now. Caller
23
+ * reads the file (and loads any `.env`).
24
+ */
25
+ fromMarkdown(md: string): this;
26
+ /** Synthesize a voiceover document and splice its actions into the queue at the
27
+ * position `fromMarkdown` was called (so chaining order is preserved). */
28
+ private _stageVoiceover;
29
+ /** Merge config mid-sequence — enqueued so it takes effect at this point. */
30
+ setConfig(config: RecordableConfig): this;
31
+ /**
32
+ * Stop capturing. The chain keeps running — anything between `pause()` and the
33
+ * next resume executes off-camera (page loads, logins, data setup) and is
34
+ * omitted from the final video. Place it first to skip the cold open.
35
+ */
36
+ pause(): this;
37
+ /** Resume capturing in a fresh segment, immediately. */
38
+ resume(): this;
39
+ /**
40
+ * Resume capturing, but only once the user *plays* — clicks the in-page ▶ Play
41
+ * button (or presses Enter in the terminal). Use for manual actions such as a
42
+ * login: `pause()` first, sign in by hand (headful), then `resumeOnInput()`.
43
+ */
44
+ resumeOnInput(message?: string): this;
45
+ /**
46
+ * Splice an external video clip into the timeline at this point — first call =
47
+ * intro, last = outro, anywhere between = mid-roll. The clip is normalized to
48
+ * the recording's resolution / fps / codec so the join stays seamless.
49
+ *
50
+ * Pass `fadeIn` / `fadeOut` (ms) to cross-fade with the neighbouring footage
51
+ * (or from/to black at the timeline ends). Omit them for a hard cut.
52
+ * Auto-segments: no pause/resume needed.
53
+ */
54
+ insert(path: string, options?: InsertOptions): this;
55
+ /**
56
+ * Lay an existing audio file (mp3/wav) onto the timeline at this point —
57
+ * narration, music bed, sound effect. Mixed onto the silent capture at
58
+ * finalise, positioned by where this call lands in *recorded* time (off-camera
59
+ * pauses excluded). By default the chain blocks until the clip finishes
60
+ * (`{ wait: false }` to play it over following actions); `{ volume }` gains it.
61
+ * Don't `pause()` mid-clip — paused time is dropped, desyncing the audio.
62
+ */
63
+ audio(path: string, options?: AudioOptions): this;
64
+ /** Navigate to a URL and wait for the page to settle. */
65
+ visit(url: string, options?: GoToOptions): this;
66
+ /**
67
+ * Wait for an element to reach a given state before continuing. Useful for async
68
+ * content, or as an automatic gate after a manual step. `target` accepts a CSS
69
+ * selector or a `text:` prefix; see {@link WaitForOptions} for `state`/`timeout`.
70
+ */
71
+ waitFor(target: string, options?: WaitForOptions): this;
72
+ /**
73
+ * Click an element. Accepts a CSS selector (`#id`, `.class`, `input[name="…"]`)
74
+ * or a `text:` prefix (`"text:Next"`, matched by visible text).
75
+ */
76
+ click(target: string): this;
77
+ /**
78
+ * Move the mouse (and cursor overlay) onto an element without clicking, so any
79
+ * `:hover` state — tooltips, dropdowns, menus — is revealed.
80
+ */
81
+ hover(target: string): this;
82
+ /**
83
+ * Type into an element with human-like timing. Accepts a CSS selector or a
84
+ * `text:` prefix. Timing is **jittered yet deterministic in total**: keystroke
85
+ * delays vary but always sum to `typingDuration(text, speed)`, so the voiceover
86
+ * compiler can predict this action's length. Pass `{ duration }` (ms) to
87
+ * override that total.
88
+ */
89
+ type(target: string, text: string, options?: {
90
+ duration?: number;
91
+ }): this;
92
+ /** Clear an input or textarea (select-all + delete). Handy before re-typing. */
93
+ clear(target: string): this;
94
+ /**
95
+ * Choose an option in a native `<select>` by its `value`. The cursor animates to
96
+ * the control like `click()`. Note: the open option list is OS-drawn, *outside*
97
+ * the page, so it can't be captured — build a custom dropdown from `click()`s
98
+ * for an on-camera menu.
99
+ */
100
+ select(target: string, value: string): this;
101
+ /** Press a keyboard key (e.g. "Escape", "Enter", "Tab"). */
102
+ key(key: string): this;
103
+ /**
104
+ * Move the mouse (and cursor overlay) to a target or coordinates — a CSS
105
+ * selector / plain text (element centre) or `{ x, y }` (viewport coords).
106
+ */
107
+ mouse(target: string | {
108
+ x: number;
109
+ y: number;
110
+ }): this;
111
+ /**
112
+ * Smooth-scroll to an element or position: `"top"`/`"bottom"`, a CSS selector or
113
+ * plain text (centred), or a number (absolute Y). `duration` (ms) overrides the
114
+ * default animation length.
115
+ */
116
+ scroll(target: string | number, options?: {
117
+ duration?: number;
118
+ }): this;
119
+ /**
120
+ * Smoothly scale the viewport to `level` from a transform origin. Calling zoom()
121
+ * again while zoomed transitions both scale and origin at once — no reset needed.
122
+ * `origin` accepts position keywords, percentages, a CSS selector, or `text:`.
123
+ * `duration` overrides `zoomDuration` for this call.
124
+ */
125
+ zoom(level: number, options?: {
126
+ origin?: string;
127
+ duration?: number;
128
+ }): this;
129
+ /** Smoothly reset zoom back to 1. */
130
+ resetZoom(options?: {
131
+ duration?: number;
132
+ }): this;
133
+ /** Pause the sequence for `ms` milliseconds. */
134
+ wait(ms: number): this;
135
+ /** Execute the queued action sequence, then finalise the recording. */
136
+ run(): Promise<void>;
137
+ /** Resolve a local asset path (insert/audio clip) against `baseDir` so a
138
+ * Markdown/JSON script and its clips travel together; absolute paths pass
139
+ * through. `baseDir` empty → resolve against cwd (the programmatic default). */
140
+ private _resolveFile;
141
+ private _enqueue;
142
+ /** Recompute `cfg` as defaults < content config < constructor config, then
143
+ * resolve a relative `outputDir`/`assetsDir` against `baseDir`. */
144
+ private _applyContentConfig;
145
+ /** Validate each action against the manifest and enqueue it by calling its method
146
+ * (relative `visit` URLs resolve against `baseDir` first). */
147
+ private _loadActions;
148
+ }
149
+ //# sourceMappingURL=recordable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recordable.d.ts","sourceRoot":"","sources":["../../src/compose/recordable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { DEFAULT_CONFIG, } from "../config.js";
3
+ import { sleep, truncate } from "../utils.js";
4
+ import { createLogger } from "../logger.js";
5
+ import { parseConfig } from "../validate.js";
6
+ import { Recorder } from "../video/recorder.js";
7
+ import { AudioTrack } from "../audio/track.js";
8
+ import { Runtime } from "../browser/runtime.js";
9
+ import { Session } from "./session.js";
10
+ import { buildArgs, validateAction } from "../actions.js";
11
+ import { resolveVisitUrls, splitScript } from "../script.js";
12
+ import { extractActions, parseMarkdown } from "../formats/markdown/parse.js";
13
+ // ─── Compose layer: the builder ──────────────────────────────────────────────
14
+ //
15
+ // The public fluent API. Chain methods *describe* a recording by enqueuing
16
+ // actions; they perform no page work themselves — interaction is delegated to the
17
+ // Runtime, and execution to the Session at run(). Also owns config resolution and
18
+ // the declarative loaders (JSON / Markdown).
19
+ export class Recordable {
20
+ cfg = { ...DEFAULT_CONFIG };
21
+ // Constructor config — always wins over config from a loaded document
22
+ // (frontmatter / script `config`), which layers underneath.
23
+ userConfig;
24
+ log = createLogger(() => this.cfg.silent);
25
+ recorder = new Recorder(() => this.cfg, this.log);
26
+ audioTrack = new AudioTrack();
27
+ runtime = new Runtime(() => this.cfg, this.log);
28
+ queue = [];
29
+ // Deferred load work (voiceover synthesis), run by run() — so `fromMarkdown`
30
+ // stays synchronous and building a script never triggers synthesis.
31
+ pending = [];
32
+ // Recording state. `recording` is the *intent* (should we be capturing?); the
33
+ // recorder starts segments lazily so a leading pause() never makes a clip.
34
+ recording = true;
35
+ constructor(config = {}) {
36
+ this.userConfig = parseConfig(config);
37
+ this._applyContentConfig({}); // sets cfg = defaults < userConfig, resolving paths
38
+ }
39
+ // ─── Loaders ───────────────────────────────────────────────────────────────
40
+ //
41
+ // Turn declarative content into queued actions, between construction and run().
42
+ /** Load a JSON script — an array of actions, a `{ config, actions }` object, or a
43
+ * raw JSON string — enqueuing each action. Returns `this` to chain into `.run()`. */
44
+ fromJSON(script) {
45
+ const parsed = typeof script === "string" ? JSON.parse(script) : script;
46
+ const { config, actions } = splitScript(parsed);
47
+ if (!Array.isArray(actions))
48
+ throw new Error("Script must be an array of actions, or { actions: [...] }");
49
+ this._applyContentConfig(config ?? {});
50
+ this._loadActions(actions);
51
+ return this;
52
+ }
53
+ /**
54
+ * Load a Markdown document — synchronous and chainable. The `voiceover`
55
+ * frontmatter key picks the path: present → synthesize narration + computed
56
+ * waits (the add-on, dynamically imported so a no-audio run never loads TTS),
57
+ * deferred to `run()`; absent → flatten markers to a plain chain now. Caller
58
+ * reads the file (and loads any `.env`).
59
+ */
60
+ fromMarkdown(md) {
61
+ const parsed = parseMarkdown(md);
62
+ this._applyContentConfig(parsed.config);
63
+ if (!parsed.voiceover) {
64
+ this._loadActions(extractActions(parsed.blocks));
65
+ return this;
66
+ }
67
+ // Defer TTS to run(); remember where these actions belong in the queue.
68
+ const insertAt = this.queue.length;
69
+ this.pending.push(() => this._stageVoiceover(md, insertAt));
70
+ return this;
71
+ }
72
+ /** Synthesize a voiceover document and splice its actions into the queue at the
73
+ * position `fromMarkdown` was called (so chaining order is preserved). */
74
+ async _stageVoiceover(md, insertAt) {
75
+ // Pick up secrets (ELEVENLABS_API_KEY) from a .env beside the document.
76
+ if (this.cfg.baseDir) {
77
+ try {
78
+ process.loadEnvFile(resolve(this.cfg.baseDir, ".env"));
79
+ }
80
+ catch {
81
+ // No .env beside the document — fine, secrets may already be in the env.
82
+ }
83
+ }
84
+ const { compileMarkdown } = await import("../voiceover/index.js");
85
+ const compiled = await compileMarkdown(md, {
86
+ assetsDir: this.cfg.assetsDir,
87
+ configOverride: this.cfg,
88
+ log: this.log,
89
+ });
90
+ this.cfg = { ...this.cfg, actionDelay: 0 }; // computed waits assume no inter-action delay
91
+ // Build the compiled actions in isolation (the chain methods push to `queue`),
92
+ // then splice them in. Safe: this runs during run()'s await, single-threaded.
93
+ const saved = this.queue;
94
+ this.queue = [];
95
+ this._loadActions(compiled.actions);
96
+ const items = this.queue;
97
+ this.queue = saved;
98
+ this.queue.splice(insertAt, 0, ...items);
99
+ }
100
+ /** Merge config mid-sequence — enqueued so it takes effect at this point. */
101
+ setConfig(config) {
102
+ return this._enqueue(async () => {
103
+ this.cfg = { ...this.cfg, ...parseConfig(config) };
104
+ });
105
+ }
106
+ // ─── Recording control ───────────────────────────────────────────────────────
107
+ //
108
+ // Recording is ON from the top by default and finalises automatically when
109
+ // run() ends — there is no start()/stop(). Use pause()/resume() to carve
110
+ // off-camera gaps; every captured segment is stitched into one seamless MP4.
111
+ /**
112
+ * Stop capturing. The chain keeps running — anything between `pause()` and the
113
+ * next resume executes off-camera (page loads, logins, data setup) and is
114
+ * omitted from the final video. Place it first to skip the cold open.
115
+ */
116
+ pause() {
117
+ return this._enqueue(async () => {
118
+ this.recording = false;
119
+ await this.recorder.end();
120
+ }, true);
121
+ }
122
+ /** Resume capturing in a fresh segment, immediately. */
123
+ resume() {
124
+ return this._enqueue(async (page) => {
125
+ this.recording = true;
126
+ await this.recorder.begin(page);
127
+ }, true);
128
+ }
129
+ /**
130
+ * Resume capturing, but only once the user *plays* — clicks the in-page ▶ Play
131
+ * button (or presses Enter in the terminal). Use for manual actions such as a
132
+ * login: `pause()` first, sign in by hand (headful), then `resumeOnInput()`.
133
+ */
134
+ resumeOnInput(message = "Press ▶ Play when you're ready to record") {
135
+ return this._enqueue(async (page) => {
136
+ await this.runtime.waitForPlay(page, message);
137
+ this.recording = true;
138
+ await this.recorder.begin(page);
139
+ // The page may have navigated during the manual step — re-inject cursor.
140
+ await this.runtime.injectCursor(page);
141
+ }, true);
142
+ }
143
+ /**
144
+ * Splice an external video clip into the timeline at this point — first call =
145
+ * intro, last = outro, anywhere between = mid-roll. The clip is normalized to
146
+ * the recording's resolution / fps / codec so the join stays seamless.
147
+ *
148
+ * Pass `fadeIn` / `fadeOut` (ms) to cross-fade with the neighbouring footage
149
+ * (or from/to black at the timeline ends). Omit them for a hard cut.
150
+ * Auto-segments: no pause/resume needed.
151
+ */
152
+ insert(path, options = {}) {
153
+ return this._enqueue(async () => {
154
+ const file = this._resolveFile(path);
155
+ this.log("Insert", file);
156
+ await this.recorder.insert(file, options);
157
+ }, true);
158
+ }
159
+ /**
160
+ * Lay an existing audio file (mp3/wav) onto the timeline at this point —
161
+ * narration, music bed, sound effect. Mixed onto the silent capture at
162
+ * finalise, positioned by where this call lands in *recorded* time (off-camera
163
+ * pauses excluded). By default the chain blocks until the clip finishes
164
+ * (`{ wait: false }` to play it over following actions); `{ volume }` gains it.
165
+ * Don't `pause()` mid-clip — paused time is dropped, desyncing the audio.
166
+ */
167
+ audio(path, options = {}) {
168
+ return this._enqueue(async () => {
169
+ const { wait = true, volume } = options;
170
+ const file = this._resolveFile(path);
171
+ // Pin the clip to where we are in recorded time (the video timeline), then
172
+ // hand it to the audio layer to probe + collect for the final mix.
173
+ const startMs = this.recorder.currentTimelineMs();
174
+ const { durationMs } = await this.audioTrack.add(file, startMs, { volume });
175
+ this.log("Audio", `${truncate(file)} @ ${Math.round(startMs)}ms (${Math.round(durationMs)}ms)`);
176
+ if (wait)
177
+ await sleep(durationMs);
178
+ });
179
+ }
180
+ // ─── Navigation ────────────────────────────────────────────────────────────
181
+ /** Navigate to a URL and wait for the page to settle. */
182
+ visit(url, options) {
183
+ return this._enqueue((page) => this.runtime.visit(page, url, options));
184
+ }
185
+ /**
186
+ * Wait for an element to reach a given state before continuing. Useful for async
187
+ * content, or as an automatic gate after a manual step. `target` accepts a CSS
188
+ * selector or a `text:` prefix; see {@link WaitForOptions} for `state`/`timeout`.
189
+ */
190
+ waitFor(target, options = {}) {
191
+ return this._enqueue((page) => this.runtime.waitFor(page, target, options));
192
+ }
193
+ // ─── Interactions ──────────────────────────────────────────────────────────
194
+ /**
195
+ * Click an element. Accepts a CSS selector (`#id`, `.class`, `input[name="…"]`)
196
+ * or a `text:` prefix (`"text:Next"`, matched by visible text).
197
+ */
198
+ click(target) {
199
+ return this._enqueue((page) => this.runtime.click(page, target));
200
+ }
201
+ /**
202
+ * Move the mouse (and cursor overlay) onto an element without clicking, so any
203
+ * `:hover` state — tooltips, dropdowns, menus — is revealed.
204
+ */
205
+ hover(target) {
206
+ return this._enqueue((page) => this.runtime.hover(page, target));
207
+ }
208
+ /**
209
+ * Type into an element with human-like timing. Accepts a CSS selector or a
210
+ * `text:` prefix. Timing is **jittered yet deterministic in total**: keystroke
211
+ * delays vary but always sum to `typingDuration(text, speed)`, so the voiceover
212
+ * compiler can predict this action's length. Pass `{ duration }` (ms) to
213
+ * override that total.
214
+ */
215
+ type(target, text, options = {}) {
216
+ return this._enqueue((page) => this.runtime.type(page, target, text, options));
217
+ }
218
+ /** Clear an input or textarea (select-all + delete). Handy before re-typing. */
219
+ clear(target) {
220
+ return this._enqueue((page) => this.runtime.clear(page, target));
221
+ }
222
+ /**
223
+ * Choose an option in a native `<select>` by its `value`. The cursor animates to
224
+ * the control like `click()`. Note: the open option list is OS-drawn, *outside*
225
+ * the page, so it can't be captured — build a custom dropdown from `click()`s
226
+ * for an on-camera menu.
227
+ */
228
+ select(target, value) {
229
+ return this._enqueue((page) => this.runtime.select(page, target, value));
230
+ }
231
+ /** Press a keyboard key (e.g. "Escape", "Enter", "Tab"). */
232
+ key(key) {
233
+ return this._enqueue((page) => this.runtime.key(page, key));
234
+ }
235
+ /**
236
+ * Move the mouse (and cursor overlay) to a target or coordinates — a CSS
237
+ * selector / plain text (element centre) or `{ x, y }` (viewport coords).
238
+ */
239
+ mouse(target) {
240
+ return this._enqueue((page) => this.runtime.mouse(page, target));
241
+ }
242
+ // ─── Scrolling ─────────────────────────────────────────────────────────────
243
+ /**
244
+ * Smooth-scroll to an element or position: `"top"`/`"bottom"`, a CSS selector or
245
+ * plain text (centred), or a number (absolute Y). `duration` (ms) overrides the
246
+ * default animation length.
247
+ */
248
+ scroll(target, options = {}) {
249
+ return this._enqueue((page) => this.runtime.scroll(page, target, options));
250
+ }
251
+ // ─── Zoom ──────────────────────────────────────────────────────────────────
252
+ /**
253
+ * Smoothly scale the viewport to `level` from a transform origin. Calling zoom()
254
+ * again while zoomed transitions both scale and origin at once — no reset needed.
255
+ * `origin` accepts position keywords, percentages, a CSS selector, or `text:`.
256
+ * `duration` overrides `zoomDuration` for this call.
257
+ */
258
+ zoom(level, options = {}) {
259
+ return this._enqueue((page) => this.runtime.zoomTo(page, level, options));
260
+ }
261
+ /** Smoothly reset zoom back to 1. */
262
+ resetZoom(options = {}) {
263
+ return this._enqueue((page) => this.runtime.resetZoom(page, options));
264
+ }
265
+ // ─── Timing ────────────────────────────────────────────────────────────────
266
+ /** Pause the sequence for `ms` milliseconds. */
267
+ wait(ms) {
268
+ return this._enqueue(async () => {
269
+ this.log("Wait", `${ms}ms`);
270
+ await sleep(ms);
271
+ });
272
+ }
273
+ // ─── Execution ─────────────────────────────────────────────────────────────
274
+ /** Execute the queued action sequence, then finalise the recording. */
275
+ async run() {
276
+ // Finish any deferred load work (voiceover synthesis) before recording.
277
+ for (const job of this.pending)
278
+ await job();
279
+ this.pending = [];
280
+ // Expose just what the session needs; cfg/recording are read live via arrow
281
+ // getters (this instance mutates them as control actions run).
282
+ const comp = {
283
+ queue: this.queue,
284
+ log: this.log,
285
+ recorder: this.recorder,
286
+ audioTrack: this.audioTrack,
287
+ runtime: this.runtime,
288
+ };
289
+ Object.defineProperty(comp, "cfg", { get: () => this.cfg, enumerable: true });
290
+ Object.defineProperty(comp, "recording", {
291
+ get: () => this.recording,
292
+ enumerable: true,
293
+ });
294
+ await new Session(comp).run();
295
+ }
296
+ // ─── Private ───────────────────────────────────────────────────────────────
297
+ /** Resolve a local asset path (insert/audio clip) against `baseDir` so a
298
+ * Markdown/JSON script and its clips travel together; absolute paths pass
299
+ * through. `baseDir` empty → resolve against cwd (the programmatic default). */
300
+ _resolveFile(path) {
301
+ return isAbsolute(path) ? path : resolve(this.cfg.baseDir, path);
302
+ }
303
+ _enqueue(run, control = false) {
304
+ this.queue.push({ run, control });
305
+ return this;
306
+ }
307
+ /** Recompute `cfg` as defaults < content config < constructor config, then
308
+ * resolve a relative `outputDir`/`assetsDir` against `baseDir`. */
309
+ _applyContentConfig(content) {
310
+ this.cfg = { ...DEFAULT_CONFIG, ...parseConfig(content), ...this.userConfig };
311
+ const base = this.cfg.baseDir;
312
+ if (base) {
313
+ if (!isAbsolute(this.cfg.outputDir))
314
+ this.cfg.outputDir = resolve(base, this.cfg.outputDir);
315
+ if (!isAbsolute(this.cfg.assetsDir))
316
+ this.cfg.assetsDir = resolve(base, this.cfg.assetsDir);
317
+ }
318
+ }
319
+ /** Validate each action against the manifest and enqueue it by calling its method
320
+ * (relative `visit` URLs resolve against `baseDir` first). */
321
+ _loadActions(actions) {
322
+ resolveVisitUrls(actions, this.cfg.baseDir);
323
+ actions.forEach((step, i) => {
324
+ const where = `step ${i} (${step?.action ?? "?"})`;
325
+ if (!step || typeof step !== "object")
326
+ throw new Error(`${where}: not an object`);
327
+ try {
328
+ validateAction(step);
329
+ }
330
+ catch (err) {
331
+ throw new Error(`${where}: ${err.message}`, { cause: err });
332
+ }
333
+ this[step.action](...buildArgs(step, step.action));
334
+ });
335
+ }
336
+ }
337
+ //# sourceMappingURL=recordable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recordable.js","sourceRoot":"","sources":["../../src/compose/recordable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type Page } from "puppeteer";
2
+ import { type Logger } from "../logger.js";
3
+ import type { ResolvedConfig } from "../config.js";
4
+ import { type Recorder } from "../video/recorder.js";
5
+ import { type AudioTrack } from "../audio/track.js";
6
+ import { type Runtime } from "../browser/runtime.js";
7
+ /** One queued action. `control` actions (pause/resume/insert) run without forcing a
8
+ * capture segment to begin. */
9
+ export interface QueueItem {
10
+ run: (page: Page) => Promise<void>;
11
+ control: boolean;
12
+ }
13
+ /** What the session needs from the builder to execute a composed recording.
14
+ * `cfg` and `recording` are read live (the builder mutates them mid-run). */
15
+ export interface Composition {
16
+ readonly queue: QueueItem[];
17
+ readonly log: Logger;
18
+ readonly recorder: Recorder;
19
+ readonly audioTrack: AudioTrack;
20
+ readonly runtime: Runtime;
21
+ readonly cfg: ResolvedConfig;
22
+ readonly recording: boolean;
23
+ }
24
+ export declare class Session {
25
+ private readonly comp;
26
+ private browser;
27
+ private outputPath;
28
+ private finalised;
29
+ constructor(comp: Composition);
30
+ /** Execute the queued action sequence, then finalise the recording. */
31
+ run(): Promise<void>;
32
+ private _cleanup;
33
+ /** Seal the recording: stitch the captured/inserted segments (video layer),
34
+ * then mix the audio track onto them (audio layer). The video defines the
35
+ * length. Idempotent — a signal handler and the normal path both call it. */
36
+ private _finalize;
37
+ }
38
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/compose/session.ts"],"names":[],"mappings":"AAAA,OAAkB,EAAgB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAI/D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AASrD;gCACgC;AAChC,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;8EAC8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,OAAO;IAKN,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,SAAS,CAAS;gBAEG,IAAI,EAAE,WAAW;IAE9C,uEAAuE;IACjE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAyEZ,QAAQ;IAStB;;kFAE8E;YAChE,SAAS;CA0BxB"}