@maravilla-labs/frames 0.3.3 → 0.5.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.
package/README.md CHANGED
@@ -115,15 +115,25 @@ a WAAPI `Animation` object via `Element.getAnimations()`, and drives
115
115
  its `currentTime` exactly like every other slot. The visual is the
116
116
  class's native CSS animation; the timing is yours to control.
117
117
 
118
- The stylesheet that defines the keyframes must be loaded before
119
- `defineTimeline` runs. The simplest pattern is to `await` the page
120
- `load` event first inside a `<script type="module">`:
118
+ The stylesheet that defines the keyframes must be loaded before the
119
+ animations are materialised but **you don't need to do anything for
120
+ that**. `defineTimeline` waits internally for render-blocking
121
+ stylesheets and fonts to be ready before it reads the keyframes, so you
122
+ can just call it as soon as your module runs:
121
123
 
122
124
  ```ts
123
- await new Promise(r => addEventListener("load", r, { once: true }));
124
125
  defineTimeline({ /* ... */ });
125
126
  ```
126
127
 
128
+ > **Do not** gate `defineTimeline` behind the window `load` event
129
+ > (`await new Promise(r => addEventListener("load", r))`). `load` is also
130
+ > held open by `<video preload>` and `<img>`, which on a media-heavy page
131
+ > can take long enough to blow past the Maravilla renderer's readiness
132
+ > budget and fail the render with "timeline never became available."
133
+ > `defineTimeline` already waits for exactly what it needs (stylesheets +
134
+ > fonts) and nothing it doesn't (media). If you need that signal yourself,
135
+ > import `stylesheetsReady()`.
136
+
127
137
  This works for any library that exposes its motion as CSS classes
128
138
  plus shipped `@keyframes` — Animate.css, Magic, your own custom
129
139
  keyframes — not just Animate.css specifically.
package/dist/index.d.ts CHANGED
@@ -30,6 +30,15 @@ export type VideoInstr = {
30
30
  seek?: number;
31
31
  /** CSS selector for the `<video>` element. */
32
32
  selector: string;
33
+ /**
34
+ * Storage key of a pre-extracted frame-set manifest for this clip (the
35
+ * `output_key` returned by `platform.env.TRANSFORMS.extractFrames`). When
36
+ * present, the headless renderer swaps the live `<video>` for the matching
37
+ * frame image per captured frame (required under begin-frame-control, which
38
+ * can't seek a live video). Optional — without it the renderer falls back to
39
+ * extracting frames at render time, and the screenshot path seeks live video.
40
+ */
41
+ frameset?: string;
33
42
  };
34
43
  /**
35
44
  * Drive a CSS-class-triggered animation (Animate.css, Magic, your own
@@ -59,13 +68,61 @@ export type TimelineSchema = {
59
68
  duration: number;
60
69
  instructions: Instr[];
61
70
  };
71
+ /** A `<video>` slot as exposed to the renderer engine (worker). The engine
72
+ * pre-extracts each clip to frames with ffmpeg and swaps the live `<video>`
73
+ * for the matching frame image per captured frame — so the live video is never
74
+ * seeked under begin-frame-control (which crashes headless Chromium). */
75
+ export type VideoSlotInfo = {
76
+ /** CSS selector for the `<video>`. */
77
+ selector: string;
78
+ /** Timeline start, ms. */
79
+ at: number;
80
+ /** How long the clip plays on the timeline, ms. */
81
+ duration: number;
82
+ /** Start offset within the clip, ms. */
83
+ seek: number;
84
+ /** Resolved clip URL (`currentSrc` || `src`). */
85
+ src: string;
86
+ /** Pre-extracted frame-set manifest storage key, if the app provided one. */
87
+ frameset?: string;
88
+ };
62
89
  declare global {
63
90
  interface Window {
64
91
  __mvFrames: {
65
92
  applyState(t: number): Promise<void>;
93
+ /** Video slots in this timeline — read by the renderer engine to
94
+ * swap video frames out-of-band. Empty when no video. */
95
+ videos: VideoSlotInfo[];
66
96
  };
97
+ /** Set true by the headless renderer when it captures (`__mvFramesRendererPresent`). */
67
98
  __mvFramesRendererPresent?: boolean;
99
+ /** Set true by the renderer ONLY on the begin-frame-control capture path,
100
+ * where the live `<video>` must not be seeked (the engine swaps in
101
+ * pre-extracted frame images instead). When unset — the screenshot capture
102
+ * path and the live editor/preview — `applyState` seeks the live video. */
103
+ __mvFramesNoVideoSeek?: boolean;
68
104
  }
69
105
  }
106
+ /**
107
+ * Declare the page's timeline. **Safe to call as soon as your module runs** —
108
+ * it internally waits for render-blocking stylesheets and fonts to be ready
109
+ * (so CSS keyframes, e.g. Animate.css, are present before animations are
110
+ * materialised) and only then registers `window.__mvFrames`.
111
+ *
112
+ * It deliberately does **NOT** wait for the window `load` event or for media
113
+ * (`<video preload>`, `<img>`). Those can take a long time — long enough to
114
+ * blow past the Maravilla renderer's readiness budget — and aren't needed to
115
+ * build the timeline. **Do not** gate this behind
116
+ * `window.addEventListener('load', …)`; that was an old recommendation and is
117
+ * the classic cause of "renderer timed out waiting for the timeline" on pages
118
+ * with video. `defineTimeline` now owns that readiness so every app gets it
119
+ * right by default.
120
+ */
70
121
  export declare function defineTimeline(schema: TimelineSchema): void;
122
+ /**
123
+ * Resolve once the document's render-blocking stylesheets are applied and web
124
+ * fonts are ready — **without** waiting on the window `load` event or media.
125
+ * Exposed for advanced callers; [`defineTimeline`] already awaits it.
126
+ */
127
+ export declare function stylesheetsReady(): Promise<void>;
71
128
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,yDAAyD;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,uDAAuD;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,gCAAgC;IAChC,IAAI,EAAE,KAAK,CAAC;IACZ,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC;AAEzE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE;YAAE,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;SAAE,CAAC;QACrD,yBAAyB,CAAC,EAAE,OAAO,CAAC;KACrC;CACF;AAaD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAsI3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,yDAAyD;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,uDAAuD;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,gCAAgC;IAChC,IAAI,EAAE,KAAK,CAAC;IACZ,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC;AAEzE;;;yEAGyE;AACzE,MAAM,MAAM,aAAa,GAAG;IAC1B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE;YACV,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YACrC;qEACyD;YACzD,MAAM,EAAE,aAAa,EAAE,CAAC;SACzB,CAAC;QACF,wFAAwF;QACxF,yBAAyB,CAAC,EAAE,OAAO,CAAC;QACpC;;;mFAG2E;QAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAC;KACjC;CACF;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAW3D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BhD"}
package/dist/index.js CHANGED
@@ -7,7 +7,60 @@
7
7
  * - Inside the Maravilla renderer, the timeline is stepped frame by
8
8
  * frame to produce deterministic video output.
9
9
  */
10
+ /**
11
+ * Declare the page's timeline. **Safe to call as soon as your module runs** —
12
+ * it internally waits for render-blocking stylesheets and fonts to be ready
13
+ * (so CSS keyframes, e.g. Animate.css, are present before animations are
14
+ * materialised) and only then registers `window.__mvFrames`.
15
+ *
16
+ * It deliberately does **NOT** wait for the window `load` event or for media
17
+ * (`<video preload>`, `<img>`). Those can take a long time — long enough to
18
+ * blow past the Maravilla renderer's readiness budget — and aren't needed to
19
+ * build the timeline. **Do not** gate this behind
20
+ * `window.addEventListener('load', …)`; that was an old recommendation and is
21
+ * the classic cause of "renderer timed out waiting for the timeline" on pages
22
+ * with video. `defineTimeline` now owns that readiness so every app gets it
23
+ * right by default.
24
+ */
10
25
  export function defineTimeline(schema) {
26
+ stylesheetsReady()
27
+ .then(() => installTimeline(schema))
28
+ .catch((err) => {
29
+ // Surface in the page console (the renderer captures console output) so
30
+ // a bad selector / missing keyframes is debuggable. On failure
31
+ // `__mvFrames` simply never registers and the renderer reports a clean
32
+ // readiness timeout rather than hanging.
33
+ // eslint-disable-next-line no-console
34
+ console.error("[@maravilla-labs/frames] defineTimeline failed:", err);
35
+ });
36
+ }
37
+ /**
38
+ * Resolve once the document's render-blocking stylesheets are applied and web
39
+ * fonts are ready — **without** waiting on the window `load` event or media.
40
+ * Exposed for advanced callers; [`defineTimeline`] already awaits it.
41
+ */
42
+ export function stylesheetsReady() {
43
+ if (typeof document === "undefined")
44
+ return Promise.resolve();
45
+ const domReady = document.readyState === "loading"
46
+ ? new Promise((resolve) => document.addEventListener("DOMContentLoaded", () => resolve(), { once: true }))
47
+ : Promise.resolve();
48
+ return domReady
49
+ .then(() => {
50
+ // A <link rel="stylesheet"> has a null `.sheet` until it has loaded +
51
+ // parsed. Wait for any still-pending ones; `error` resolves too so a
52
+ // missing/broken stylesheet can't hang the timeline.
53
+ const pending = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
54
+ .filter((link) => !link.sheet)
55
+ .map((link) => new Promise((resolve) => {
56
+ link.addEventListener("load", () => resolve(), { once: true });
57
+ link.addEventListener("error", () => resolve(), { once: true });
58
+ }));
59
+ return Promise.all(pending);
60
+ })
61
+ .then(() => (document.fonts ? document.fonts.ready.then(() => undefined) : undefined));
62
+ }
63
+ function installTimeline(schema) {
11
64
  const rendererPresent = typeof window !== "undefined" && window.__mvFramesRendererPresent === true;
12
65
  const slots = schema.instructions.flatMap((i) => {
13
66
  const el = document.querySelector(i.selector);
@@ -21,8 +74,25 @@ export function defineTimeline(schema) {
21
74
  const naturalMs = isFinite(el.duration) ? el.duration * 1000 : Infinity;
22
75
  const duration = i.duration ?? Math.min(naturalMs, schema.duration - i.at);
23
76
  el.pause();
24
- el.currentTime = seek / 1000;
25
- return [{ kind: "video", at: i.at, duration, seek, el }];
77
+ // In renderer mode the engine drives video by swapping in pre-extracted
78
+ // frame images; we must NOT seek the live <video> (seeking under
79
+ // begin-frame-control crashes headless Chromium). In browser preview the
80
+ // setTimeout scheduler below plays it normally.
81
+ if (!rendererPresent) {
82
+ el.currentTime = seek / 1000;
83
+ }
84
+ return [
85
+ {
86
+ kind: "video",
87
+ at: i.at,
88
+ duration,
89
+ seek,
90
+ el,
91
+ selector: i.selector,
92
+ src: el.currentSrc || el.src,
93
+ frameset: i.frameset,
94
+ },
95
+ ];
26
96
  }
27
97
  if (i.kind === "css") {
28
98
  if (!(el instanceof HTMLElement)) {
@@ -78,30 +148,40 @@ export function defineTimeline(schema) {
78
148
  anim.pause();
79
149
  return [{ kind: "animation", at: i.at, duration, anim }];
80
150
  });
151
+ // Metadata for the renderer engine: which `<video>`s to pre-extract + swap.
152
+ const videos = slots
153
+ .filter((s) => s.kind === "video")
154
+ .map((s) => ({
155
+ selector: s.selector,
156
+ at: s.at,
157
+ duration: s.duration,
158
+ seek: s.seek,
159
+ src: s.src,
160
+ frameset: s.frameset,
161
+ }));
81
162
  window.__mvFrames = {
163
+ videos,
82
164
  async applyState(t) {
83
- const videoWaits = [];
84
165
  for (const s of slots) {
85
166
  if (s.kind === "css-pending")
86
167
  continue; // only relevant in browser preview
87
- const local = clamp(t - s.at, 0, s.duration);
88
168
  if (s.kind === "video") {
89
- s.el.pause();
90
- const target = (s.seek + local) / 1000;
91
- // Seeking is async: the decoded frame for `target` is NOT ready the
92
- // instant we assign `currentTime`. The renderer captures each frame
93
- // with a synchronous CDP `beginFrame` right after `applyState`
94
- // resolves, so we MUST wait for the seek to land and the frame to
95
- // decode otherwise we capture a stale/blank frame, or (under
96
- // deterministic `--enable-begin-frame-control`) race the media
97
- // pipeline and wedge/crash the headless tab.
98
- videoWaits.push(seekVideo(s.el, target));
99
- }
100
- else {
101
- s.anim.currentTime = local;
169
+ const local = clamp(t - s.at, 0, s.duration);
170
+ // Seek the live <video> EXCEPT on the renderer's begin-frame-control
171
+ // capture path (where seeking crashes the headless tab the engine
172
+ // swaps in pre-extracted frame images instead, flagged via
173
+ // `__mvFramesNoVideoSeek`). So the editor, live preview, AND the
174
+ // renderer's screenshot path all seek normally frame-accurate
175
+ // "time travel" (StagePreview drives this via applyState).
176
+ if (!window.__mvFramesNoVideoSeek) {
177
+ s.el.currentTime = (s.seek + local) / 1000;
178
+ }
179
+ continue;
102
180
  }
181
+ const local = clamp(t - s.at, 0, s.duration);
182
+ s.anim.currentTime = local;
103
183
  }
104
- await Promise.all([document.fonts.ready, ...videoWaits]);
184
+ await document.fonts.ready;
105
185
  },
106
186
  };
107
187
  if (!rendererPresent) {
@@ -141,51 +221,4 @@ export function defineTimeline(schema) {
141
221
  function clamp(v, lo, hi) {
142
222
  return v < lo ? lo : v > hi ? hi : v;
143
223
  }
144
- /**
145
- * Seek `el` to `targetSec` and resolve only once the seeked frame has
146
- * actually decoded — so a subsequent capture grabs the right frame instead
147
- * of racing an in-flight seek.
148
- *
149
- * Resolution waits for the `seeked` event, then for one decoded video frame
150
- * via `requestVideoFrameCallback` where available (fallback: a microtask, so
151
- * we still yield to the decode). A timeout guards against `seeked` never
152
- * firing (e.g. seeking to the exact current position, or an unsupported
153
- * codec) so `applyState` can't hang the whole render. Already-satisfied
154
- * seeks (`readyState` high enough and time already at target) resolve fast.
155
- */
156
- function seekVideo(el, targetSec) {
157
- const HAVE_CURRENT_DATA = 2;
158
- // If we're already at (or within a frame of) the target with data ready,
159
- // there's nothing to wait for — assigning currentTime won't fire `seeked`.
160
- const alreadyThere = Math.abs(el.currentTime - targetSec) < 1e-3 && el.readyState >= HAVE_CURRENT_DATA;
161
- el.currentTime = targetSec;
162
- if (alreadyThere)
163
- return Promise.resolve();
164
- return new Promise((resolve) => {
165
- let done = false;
166
- const finish = () => {
167
- if (done)
168
- return;
169
- done = true;
170
- el.removeEventListener("seeked", onSeeked);
171
- clearTimeout(timer);
172
- resolve();
173
- };
174
- const afterSeeked = () => {
175
- // Wait for one decoded frame to be presented when the API exists.
176
- const rvfc = el.requestVideoFrameCallback;
177
- if (typeof rvfc === "function") {
178
- rvfc.call(el, () => finish());
179
- }
180
- else {
181
- // No rVFC: yield a microtask so the decode can settle, then finish.
182
- Promise.resolve().then(finish);
183
- }
184
- };
185
- const onSeeked = () => afterSeeked();
186
- el.addEventListener("seeked", onSeeked, { once: true });
187
- // Guard: never let a missing `seeked` stall the render.
188
- const timer = setTimeout(finish, 2000);
189
- });
190
- }
191
224
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyEH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,eAAe,GACnB,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,yBAAyB,KAAK,IAAI,CAAC;IAE7E,MAAM,KAAK,GAAW,MAAM,CAAC,YAAY,CAAC,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE;QAC5D,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE7E,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACvB,IAAI,CAAC,CAAC,EAAE,YAAY,gBAAgB,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,QAAQ,2BAA2B,CAAC,CAAC;YAC5E,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;YACzB,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;YACxE,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3E,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,EAAE,CAAC,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;YAC7B,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,CAAC,EAAE,YAAY,WAAW,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,QAAQ,yCAAyC,CAAC,CAAC;YAC1F,CAAC;YACD,mEAAmE;YACnE,0DAA0D;YAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,OAAO,CAAC;wBACN,IAAI,EAAE,aAAa;wBACnB,EAAE,EAAE,CAAC,CAAC,EAAE;wBACR,EAAE;wBACF,OAAO,EAAE,CAAC,CAAC,OAAO;wBAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;qBAC7B,CAAC,CAAC;YACL,CAAC;YACD,+DAA+D;YAC/D,mEAAmE;YACnE,kBAAkB;YAClB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;YAC3C,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YAC/B,MAAM,QAAQ,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,4BAA4B,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,QAAQ,IAAI;oBAC1F,mFAAmF,CACtF,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBACzC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;gBAChC,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAC1B,CAAC,EAAE,CAAC,CAAC,CAAC;YACN,oEAAoE;YACpE,iEAAiE;YACjE,2CAA2C;YAC3C,qEAAqE;YACrE,qEAAqE;YACrE,iEAAiE;YACjE,kEAAkE;YAClE,8BAA8B;YAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7F,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAAE,QAAQ,GAAG,IAAI,EAAE,WAAW,CAAC,CAAC;YACzE,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC;YAChC,OAAO,QAAQ,CAAC,GAAG,CAAW,CAAC,IAAI,EAAE,EAAE;gBACrC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;gBACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,UAAU,GAAG;QAClB,KAAK,CAAC,UAAU,CAAC,CAAS;YACxB,MAAM,UAAU,GAAoB,EAAE,CAAC;YACvC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;oBAAE,SAAS,CAAC,mCAAmC;gBAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAC7C,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACvB,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;oBACb,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC;oBACvC,oEAAoE;oBACpE,oEAAoE;oBACpE,+DAA+D;oBAC/D,kEAAkE;oBAClE,+DAA+D;oBAC/D,+DAA+D;oBAC/D,6CAA6C;oBAC7C,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC3C,CAAC;qBAAM,CAAC;oBACN,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;gBAC7B,CAAC;YACH,CAAC;YACD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;QAC3D,CAAC;KACF,CAAC;IAEF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;gBACrB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACvB,CAAC,CAAC,EAAE,CAAC,WAAW,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;oBACjC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;gBACnB,CAAC;qBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;oBACpC,6DAA6D;oBAC7D,iDAAiD;oBACjD,8DAA8D;oBAC9D,6DAA6D;oBAC7D,6DAA6D;oBAC7D,iBAAiB;oBACjB,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;wBACxB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,CAAC,QAAQ,GAAG,IAAI,EAAE,WAAW,CAAC,CAAC;oBAC/E,CAAC;oBACD,mDAAmD;oBACnD,6DAA6D;oBAC7D,wDAAwD;oBACxD,yDAAyD;oBACzD,2DAA2D;oBAC3D,yDAAyD;oBACzD,gDAAgD;oBAChD,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC;oBAClC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;gBACnC,CAAC;qBAAM,CAAC;oBACN,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU;IAC9C,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,SAAS,CAAC,EAAoB,EAAE,SAAiB;IACxD,MAAM,iBAAiB,GAAG,CAAC,CAAC;IAC5B,yEAAyE;IACzE,2EAA2E;IAC3E,MAAM,YAAY,GAChB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACpF,EAAE,CAAC,WAAW,GAAG,SAAS,CAAC;IAC3B,IAAI,YAAY;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAE3C,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,kEAAkE;YAClE,MAAM,IAAI,GAAI,EAEZ,CAAC,yBAAyB,CAAC;YAC7B,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,oEAAoE;gBACpE,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,CAAC;QACH,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,wDAAwD;QACxD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maravilla-labs/frames",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "Tiny vanilla helper for declaring Web Animations API timelines that the Maravilla runtime renderer drives frame-accurately.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -32,6 +32,15 @@ export type VideoInstr = {
32
32
  seek?: number;
33
33
  /** CSS selector for the `<video>` element. */
34
34
  selector: string;
35
+ /**
36
+ * Storage key of a pre-extracted frame-set manifest for this clip (the
37
+ * `output_key` returned by `platform.env.TRANSFORMS.extractFrames`). When
38
+ * present, the headless renderer swaps the live `<video>` for the matching
39
+ * frame image per captured frame (required under begin-frame-control, which
40
+ * can't seek a live video). Optional — without it the renderer falls back to
41
+ * extracting frames at render time, and the screenshot path seeks live video.
42
+ */
43
+ frameset?: string;
35
44
  };
36
45
 
37
46
  /**
@@ -61,15 +70,54 @@ export type CssClassInstr = {
61
70
  export type Instr = AnimationInstr | VideoInstr | CssClassInstr;
62
71
  export type TimelineSchema = { duration: number; instructions: Instr[] };
63
72
 
73
+ /** A `<video>` slot as exposed to the renderer engine (worker). The engine
74
+ * pre-extracts each clip to frames with ffmpeg and swaps the live `<video>`
75
+ * for the matching frame image per captured frame — so the live video is never
76
+ * seeked under begin-frame-control (which crashes headless Chromium). */
77
+ export type VideoSlotInfo = {
78
+ /** CSS selector for the `<video>`. */
79
+ selector: string;
80
+ /** Timeline start, ms. */
81
+ at: number;
82
+ /** How long the clip plays on the timeline, ms. */
83
+ duration: number;
84
+ /** Start offset within the clip, ms. */
85
+ seek: number;
86
+ /** Resolved clip URL (`currentSrc` || `src`). */
87
+ src: string;
88
+ /** Pre-extracted frame-set manifest storage key, if the app provided one. */
89
+ frameset?: string;
90
+ };
91
+
64
92
  declare global {
65
93
  interface Window {
66
- __mvFrames: { applyState(t: number): Promise<void> };
94
+ __mvFrames: {
95
+ applyState(t: number): Promise<void>;
96
+ /** Video slots in this timeline — read by the renderer engine to
97
+ * swap video frames out-of-band. Empty when no video. */
98
+ videos: VideoSlotInfo[];
99
+ };
100
+ /** Set true by the headless renderer when it captures (`__mvFramesRendererPresent`). */
67
101
  __mvFramesRendererPresent?: boolean;
102
+ /** Set true by the renderer ONLY on the begin-frame-control capture path,
103
+ * where the live `<video>` must not be seeked (the engine swaps in
104
+ * pre-extracted frame images instead). When unset — the screenshot capture
105
+ * path and the live editor/preview — `applyState` seeks the live video. */
106
+ __mvFramesNoVideoSeek?: boolean;
68
107
  }
69
108
  }
70
109
 
71
110
  type AnimSlot = { kind: "animation"; at: number; duration: number; anim: Animation };
72
- type VideoSlot = { kind: "video"; at: number; duration: number; seek: number; el: HTMLVideoElement };
111
+ type VideoSlot = {
112
+ kind: "video";
113
+ at: number;
114
+ duration: number;
115
+ seek: number;
116
+ el: HTMLVideoElement;
117
+ selector: string;
118
+ src: string;
119
+ frameset?: string;
120
+ };
73
121
  type CssPendingSlot = {
74
122
  kind: "css-pending";
75
123
  at: number;
@@ -79,7 +127,69 @@ type CssPendingSlot = {
79
127
  };
80
128
  type Slot = AnimSlot | VideoSlot | CssPendingSlot;
81
129
 
130
+ /**
131
+ * Declare the page's timeline. **Safe to call as soon as your module runs** —
132
+ * it internally waits for render-blocking stylesheets and fonts to be ready
133
+ * (so CSS keyframes, e.g. Animate.css, are present before animations are
134
+ * materialised) and only then registers `window.__mvFrames`.
135
+ *
136
+ * It deliberately does **NOT** wait for the window `load` event or for media
137
+ * (`<video preload>`, `<img>`). Those can take a long time — long enough to
138
+ * blow past the Maravilla renderer's readiness budget — and aren't needed to
139
+ * build the timeline. **Do not** gate this behind
140
+ * `window.addEventListener('load', …)`; that was an old recommendation and is
141
+ * the classic cause of "renderer timed out waiting for the timeline" on pages
142
+ * with video. `defineTimeline` now owns that readiness so every app gets it
143
+ * right by default.
144
+ */
82
145
  export function defineTimeline(schema: TimelineSchema): void {
146
+ stylesheetsReady()
147
+ .then(() => installTimeline(schema))
148
+ .catch((err) => {
149
+ // Surface in the page console (the renderer captures console output) so
150
+ // a bad selector / missing keyframes is debuggable. On failure
151
+ // `__mvFrames` simply never registers and the renderer reports a clean
152
+ // readiness timeout rather than hanging.
153
+ // eslint-disable-next-line no-console
154
+ console.error("[@maravilla-labs/frames] defineTimeline failed:", err);
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Resolve once the document's render-blocking stylesheets are applied and web
160
+ * fonts are ready — **without** waiting on the window `load` event or media.
161
+ * Exposed for advanced callers; [`defineTimeline`] already awaits it.
162
+ */
163
+ export function stylesheetsReady(): Promise<void> {
164
+ if (typeof document === "undefined") return Promise.resolve();
165
+ const domReady =
166
+ document.readyState === "loading"
167
+ ? new Promise<void>((resolve) =>
168
+ document.addEventListener("DOMContentLoaded", () => resolve(), { once: true }),
169
+ )
170
+ : Promise.resolve();
171
+ return domReady
172
+ .then(() => {
173
+ // A <link rel="stylesheet"> has a null `.sheet` until it has loaded +
174
+ // parsed. Wait for any still-pending ones; `error` resolves too so a
175
+ // missing/broken stylesheet can't hang the timeline.
176
+ const pending = Array.from(
177
+ document.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]'),
178
+ )
179
+ .filter((link) => !link.sheet)
180
+ .map(
181
+ (link) =>
182
+ new Promise<void>((resolve) => {
183
+ link.addEventListener("load", () => resolve(), { once: true });
184
+ link.addEventListener("error", () => resolve(), { once: true });
185
+ }),
186
+ );
187
+ return Promise.all(pending);
188
+ })
189
+ .then(() => (document.fonts ? document.fonts.ready.then(() => undefined) : undefined));
190
+ }
191
+
192
+ function installTimeline(schema: TimelineSchema): void {
83
193
  const rendererPresent =
84
194
  typeof window !== "undefined" && window.__mvFramesRendererPresent === true;
85
195
 
@@ -95,8 +205,25 @@ export function defineTimeline(schema: TimelineSchema): void {
95
205
  const naturalMs = isFinite(el.duration) ? el.duration * 1000 : Infinity;
96
206
  const duration = i.duration ?? Math.min(naturalMs, schema.duration - i.at);
97
207
  el.pause();
98
- el.currentTime = seek / 1000;
99
- return [{ kind: "video", at: i.at, duration, seek, el }];
208
+ // In renderer mode the engine drives video by swapping in pre-extracted
209
+ // frame images; we must NOT seek the live <video> (seeking under
210
+ // begin-frame-control crashes headless Chromium). In browser preview the
211
+ // setTimeout scheduler below plays it normally.
212
+ if (!rendererPresent) {
213
+ el.currentTime = seek / 1000;
214
+ }
215
+ return [
216
+ {
217
+ kind: "video",
218
+ at: i.at,
219
+ duration,
220
+ seek,
221
+ el,
222
+ selector: i.selector,
223
+ src: el.currentSrc || el.src,
224
+ frameset: i.frameset,
225
+ },
226
+ ];
100
227
  }
101
228
 
102
229
  if (i.kind === "css") {
@@ -157,28 +284,40 @@ export function defineTimeline(schema: TimelineSchema): void {
157
284
  return [{ kind: "animation", at: i.at, duration, anim }];
158
285
  });
159
286
 
287
+ // Metadata for the renderer engine: which `<video>`s to pre-extract + swap.
288
+ const videos: VideoSlotInfo[] = slots
289
+ .filter((s): s is VideoSlot => s.kind === "video")
290
+ .map((s) => ({
291
+ selector: s.selector,
292
+ at: s.at,
293
+ duration: s.duration,
294
+ seek: s.seek,
295
+ src: s.src,
296
+ frameset: s.frameset,
297
+ }));
298
+
160
299
  window.__mvFrames = {
300
+ videos,
161
301
  async applyState(t: number) {
162
- const videoWaits: Promise<void>[] = [];
163
302
  for (const s of slots) {
164
303
  if (s.kind === "css-pending") continue; // only relevant in browser preview
165
- const local = clamp(t - s.at, 0, s.duration);
166
304
  if (s.kind === "video") {
167
- s.el.pause();
168
- const target = (s.seek + local) / 1000;
169
- // Seeking is async: the decoded frame for `target` is NOT ready the
170
- // instant we assign `currentTime`. The renderer captures each frame
171
- // with a synchronous CDP `beginFrame` right after `applyState`
172
- // resolves, so we MUST wait for the seek to land and the frame to
173
- // decode otherwise we capture a stale/blank frame, or (under
174
- // deterministic `--enable-begin-frame-control`) race the media
175
- // pipeline and wedge/crash the headless tab.
176
- videoWaits.push(seekVideo(s.el, target));
177
- } else {
178
- s.anim.currentTime = local;
305
+ const local = clamp(t - s.at, 0, s.duration);
306
+ // Seek the live <video> EXCEPT on the renderer's begin-frame-control
307
+ // capture path (where seeking crashes the headless tab the engine
308
+ // swaps in pre-extracted frame images instead, flagged via
309
+ // `__mvFramesNoVideoSeek`). So the editor, live preview, AND the
310
+ // renderer's screenshot path all seek normally frame-accurate
311
+ // "time travel" (StagePreview drives this via applyState).
312
+ if (!window.__mvFramesNoVideoSeek) {
313
+ s.el.currentTime = (s.seek + local) / 1000;
314
+ }
315
+ continue;
179
316
  }
317
+ const local = clamp(t - s.at, 0, s.duration);
318
+ s.anim.currentTime = local;
180
319
  }
181
- await Promise.all([document.fonts.ready, ...videoWaits]);
320
+ await document.fonts.ready;
182
321
  },
183
322
  };
184
323
 
@@ -218,52 +357,3 @@ export function defineTimeline(schema: TimelineSchema): void {
218
357
  function clamp(v: number, lo: number, hi: number): number {
219
358
  return v < lo ? lo : v > hi ? hi : v;
220
359
  }
221
-
222
- /**
223
- * Seek `el` to `targetSec` and resolve only once the seeked frame has
224
- * actually decoded — so a subsequent capture grabs the right frame instead
225
- * of racing an in-flight seek.
226
- *
227
- * Resolution waits for the `seeked` event, then for one decoded video frame
228
- * via `requestVideoFrameCallback` where available (fallback: a microtask, so
229
- * we still yield to the decode). A timeout guards against `seeked` never
230
- * firing (e.g. seeking to the exact current position, or an unsupported
231
- * codec) so `applyState` can't hang the whole render. Already-satisfied
232
- * seeks (`readyState` high enough and time already at target) resolve fast.
233
- */
234
- function seekVideo(el: HTMLVideoElement, targetSec: number): Promise<void> {
235
- const HAVE_CURRENT_DATA = 2;
236
- // If we're already at (or within a frame of) the target with data ready,
237
- // there's nothing to wait for — assigning currentTime won't fire `seeked`.
238
- const alreadyThere =
239
- Math.abs(el.currentTime - targetSec) < 1e-3 && el.readyState >= HAVE_CURRENT_DATA;
240
- el.currentTime = targetSec;
241
- if (alreadyThere) return Promise.resolve();
242
-
243
- return new Promise<void>((resolve) => {
244
- let done = false;
245
- const finish = () => {
246
- if (done) return;
247
- done = true;
248
- el.removeEventListener("seeked", onSeeked);
249
- clearTimeout(timer);
250
- resolve();
251
- };
252
- const afterSeeked = () => {
253
- // Wait for one decoded frame to be presented when the API exists.
254
- const rvfc = (el as unknown as {
255
- requestVideoFrameCallback?: (cb: () => void) => number;
256
- }).requestVideoFrameCallback;
257
- if (typeof rvfc === "function") {
258
- rvfc.call(el, () => finish());
259
- } else {
260
- // No rVFC: yield a microtask so the decode can settle, then finish.
261
- Promise.resolve().then(finish);
262
- }
263
- };
264
- const onSeeked = () => afterSeeked();
265
- el.addEventListener("seeked", onSeeked, { once: true });
266
- // Guard: never let a missing `seeked` stall the render.
267
- const timer = setTimeout(finish, 2000);
268
- });
269
- }