@motionscript/browser 0.1.0 → 0.1.1-alpha.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 (102) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/asset-bytes.d.ts +12 -50
  3. package/dist/asset-bytes.d.ts.map +1 -1
  4. package/dist/asset-bytes.js +20 -52
  5. package/dist/asset-bytes.js.map +1 -1
  6. package/dist/audio/bus-graph.d.ts +4 -39
  7. package/dist/audio/bus-graph.d.ts.map +1 -1
  8. package/dist/audio/bus-graph.js +4 -38
  9. package/dist/audio/bus-graph.js.map +1 -1
  10. package/dist/audio/effect-graph.d.ts +4 -29
  11. package/dist/audio/effect-graph.d.ts.map +1 -1
  12. package/dist/audio/effect-graph.js +4 -31
  13. package/dist/audio/effect-graph.js.map +1 -1
  14. package/dist/audio/loop-region.d.ts +3 -21
  15. package/dist/audio/loop-region.d.ts.map +1 -1
  16. package/dist/audio/loop-region.js +3 -21
  17. package/dist/audio/loop-region.js.map +1 -1
  18. package/dist/audio/mixer.d.ts +9 -41
  19. package/dist/audio/mixer.d.ts.map +1 -1
  20. package/dist/audio/mixer.js +11 -44
  21. package/dist/audio/mixer.js.map +1 -1
  22. package/dist/audio/player.d.ts +3 -17
  23. package/dist/audio/player.d.ts.map +1 -1
  24. package/dist/audio/player.js +4 -23
  25. package/dist/audio/player.js.map +1 -1
  26. package/dist/browser/audio.js +1 -1
  27. package/dist/browser/chunks/chunk-NDJYMKIV.js +2 -0
  28. package/dist/browser/chunks/chunk-NDJYMKIV.js.map +7 -0
  29. package/dist/browser/index.js +19 -19
  30. package/dist/browser/index.js.map +4 -4
  31. package/dist/browser/manifest.json +1 -1
  32. package/dist/browser/ms_renderer_bg.wasm +0 -0
  33. package/dist/concat.d.ts +25 -84
  34. package/dist/concat.d.ts.map +1 -1
  35. package/dist/concat.js +19 -69
  36. package/dist/concat.js.map +1 -1
  37. package/dist/dynamic-nodes.d.ts +1 -6
  38. package/dist/dynamic-nodes.d.ts.map +1 -1
  39. package/dist/dynamic-nodes.js +3 -24
  40. package/dist/dynamic-nodes.js.map +1 -1
  41. package/dist/engine.d.ts +9 -42
  42. package/dist/engine.d.ts.map +1 -1
  43. package/dist/engine.js +10 -44
  44. package/dist/engine.js.map +1 -1
  45. package/dist/export-audio.d.ts +1 -6
  46. package/dist/export-audio.d.ts.map +1 -1
  47. package/dist/export-audio.js +1 -6
  48. package/dist/export-audio.js.map +1 -1
  49. package/dist/exporter.d.ts +28 -141
  50. package/dist/exporter.d.ts.map +1 -1
  51. package/dist/exporter.js +15 -90
  52. package/dist/exporter.js.map +1 -1
  53. package/dist/frame-loop.d.ts +19 -105
  54. package/dist/frame-loop.d.ts.map +1 -1
  55. package/dist/frame-loop.js +20 -82
  56. package/dist/frame-loop.js.map +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +7 -15
  60. package/dist/index.js.map +1 -1
  61. package/dist/mix-timeline-audio.d.ts +7 -24
  62. package/dist/mix-timeline-audio.d.ts.map +1 -1
  63. package/dist/mix-timeline-audio.js +7 -23
  64. package/dist/mix-timeline-audio.js.map +1 -1
  65. package/dist/native-export.d.ts +29 -0
  66. package/dist/native-export.d.ts.map +1 -0
  67. package/dist/native-export.js +99 -0
  68. package/dist/native-export.js.map +1 -0
  69. package/dist/parallel/export-parallel.d.ts +2 -16
  70. package/dist/parallel/export-parallel.d.ts.map +1 -1
  71. package/dist/parallel/export-parallel.js +1 -11
  72. package/dist/parallel/export-parallel.js.map +1 -1
  73. package/dist/parallel/governor.d.ts +2 -15
  74. package/dist/parallel/governor.d.ts.map +1 -1
  75. package/dist/parallel/governor.js +1 -5
  76. package/dist/parallel/governor.js.map +1 -1
  77. package/dist/parallel/lane.d.ts +1 -5
  78. package/dist/parallel/lane.d.ts.map +1 -1
  79. package/dist/parallel/lane.js.map +1 -1
  80. package/dist/parallel/plan.d.ts +3 -7
  81. package/dist/parallel/plan.d.ts.map +1 -1
  82. package/dist/parallel/plan.js +1 -6
  83. package/dist/parallel/plan.js.map +1 -1
  84. package/dist/parallel/run-lanes.d.ts +4 -18
  85. package/dist/parallel/run-lanes.d.ts.map +1 -1
  86. package/dist/parallel/run-lanes.js +1 -12
  87. package/dist/parallel/run-lanes.js.map +1 -1
  88. package/dist/screenshot.d.ts +1 -12
  89. package/dist/screenshot.d.ts.map +1 -1
  90. package/dist/screenshot.js +1 -12
  91. package/dist/screenshot.js.map +1 -1
  92. package/dist/still.d.ts +17 -74
  93. package/dist/still.d.ts.map +1 -1
  94. package/dist/still.js +13 -50
  95. package/dist/still.js.map +1 -1
  96. package/dist/ticker.d.ts +10 -39
  97. package/dist/ticker.d.ts.map +1 -1
  98. package/dist/ticker.js +13 -45
  99. package/dist/ticker.js.map +1 -1
  100. package/package.json +4 -4
  101. package/dist/browser/chunks/chunk-F3AZKPHU.js +0 -2
  102. package/dist/browser/chunks/chunk-F3AZKPHU.js.map +0 -7
@@ -1,17 +1,6 @@
1
1
  import { DEFAULT_OUTPUT, resolveProjectDocument, } from "@motionscript/core";
2
2
  import { createStillRenderer } from "./still";
3
- /**
4
- * Renders a single frame of a document to an image.
5
- *
6
- * A one-shot wrapper over {@link StillRenderer}: create, render, encode, dispose.
7
- * It therefore takes the same path as a live preview, and as the video export at
8
- * that frame — there is one renderer, not three.
9
- *
10
- * For anything that captures **repeatedly** — a thumbnail builder, a preview that
11
- * repaints as the user edits — hold a {@link StillRenderer} instead. This function
12
- * builds and tears down a WebGPU surface per call, which is the right trade for one
13
- * capture and the wrong one for a hundred.
14
- */
3
+ /** Renders a single frame of a document to an image: a one-shot wrapper over {@link StillRenderer} (create, render, encode, dispose), so it takes the same path as a live preview and the video export at that frame. For repeated capture hold a {@link StillRenderer}: this builds and tears down a WebGPU surface per call. */
15
4
  export async function exportScreenshot(params) {
16
5
  const { registry, scale, manifest, theme, rendererWasmUrl, frame, format = "png", quality = 0.92, } = params;
17
6
  const project = resolveProjectDocument(params.document, params, DEFAULT_OUTPUT);
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,cAAc,EAAE,sBAAsB,GAEzC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAgC7D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAwB;IAC3D,MAAM,EACF,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EACjD,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,IAAI,GACxC,GAAG,MAAM,CAAC;IAEX,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;IAChF,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAE7F,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC;QACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ;KAClG,CAAC,CAAC;IAEH,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;YAAS,CAAC;QACP,QAAQ,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;AACL,CAAC","sourcesContent":["import {\n DEFAULT_OUTPUT, resolveProjectDocument,\n type AssetManifest, type FrameSpec, type ProjectDocument, type Registry, type VisualDocument, type Size2D, type Theme,\n} from \"@motionscript/core\";\nimport type { ScreenshotFormat } from \"./frame-loop\";\nimport type { RendererWasmOption } from \"./renderer\";\nimport { createStillRenderer, type FrameRef } from \"./still\";\n\nexport type { ScreenshotFormat, FrameSpec };\n\nexport type ScreenshotParams = RendererWasmOption & {\n /** A project, or a lone still or composition as the one track of one. */\n document: ProjectDocument | VisualDocument;\n /** The vocabulary its clips compile against. Defaults to what the library ships. */\n registry?: Registry;\n /** Win over the document's own; 1920×1080 at 60 fps when neither says. */\n viewport?: Size2D;\n fps?: number;\n scale?: number;\n manifest?: AssetManifest;\n /** Theme for a document that carries none of its own. */\n theme?: Theme;\n /** Which frame to capture. Defaults to the timeline's first. */\n frame?: FrameRef;\n /** Encoding format (default \"png\"). */\n format?: ScreenshotFormat;\n /** JPEG quality in [0,1] (ignored for png; default 0.92). */\n quality?: number;\n};\n\nexport type ScreenshotResult = {\n /** The project frame that was captured, after clamping. */\n frame: number;\n totalFrames: number;\n /** Encoded image bytes. */\n bytes: Uint8Array;\n};\n\n/**\n * Renders a single frame of a document to an image.\n *\n * A one-shot wrapper over {@link StillRenderer}: create, render, encode, dispose.\n * It therefore takes the same path as a live preview, and as the video export at\n * that frame — there is one renderer, not three.\n *\n * For anything that captures **repeatedly** — a thumbnail builder, a preview that\n * repaints as the user edits — hold a {@link StillRenderer} instead. This function\n * builds and tears down a WebGPU surface per call, which is the right trade for one\n * capture and the wrong one for a hundred.\n */\nexport async function exportScreenshot(params: ScreenshotParams): Promise<ScreenshotResult> {\n const {\n registry, scale, manifest, theme, rendererWasmUrl,\n frame, format = \"png\", quality = 0.92,\n } = params;\n\n const project = resolveProjectDocument(params.document, params, DEFAULT_OUTPUT);\n if (project.tracks.length === 0) throw new Error(\"The project has no tracks to screenshot.\");\n\n const renderer = await createStillRenderer({\n viewport: project.viewport, fps: project.fps, scale, manifest, theme, rendererWasmUrl, registry,\n });\n\n try {\n const drawn = await renderer.render(project, { frame });\n return { ...drawn, bytes: await renderer.toBytes({ format, quality }) };\n } finally {\n renderer.dispose();\n }\n}\n"]}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,cAAc,EAAE,sBAAsB,GAEzC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAgC7D,kUAAkU;AAClU,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAwB;IAC3D,MAAM,EACF,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EACjD,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,IAAI,GACxC,GAAG,MAAM,CAAC;IAEX,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;IAChF,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAE7F,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC;QACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ;KAClG,CAAC,CAAC;IAEH,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;YAAS,CAAC;QACP,QAAQ,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;AACL,CAAC","sourcesContent":["import {\n DEFAULT_OUTPUT, resolveProjectDocument,\n type AssetManifest, type FrameSpec, type ProjectDocument, type Registry, type VisualDocument, type Size2D, type Theme,\n} from \"@motionscript/core\";\nimport type { ScreenshotFormat } from \"./frame-loop\";\nimport type { RendererWasmOption } from \"./renderer\";\nimport { createStillRenderer, type FrameRef } from \"./still\";\n\nexport type { ScreenshotFormat, FrameSpec };\n\nexport type ScreenshotParams = RendererWasmOption & {\n /** A project, or a lone still or composition as the one track of one. */\n document: ProjectDocument | VisualDocument;\n /** The vocabulary its clips compile against. Defaults to what the library ships. */\n registry?: Registry;\n /** Win over the document's own; 1920×1080 at 60 fps when neither says. */\n viewport?: Size2D;\n fps?: number;\n scale?: number;\n manifest?: AssetManifest;\n /** Theme for a document that carries none of its own. */\n theme?: Theme;\n /** Which frame to capture. Defaults to the timeline's first. */\n frame?: FrameRef;\n /** Encoding format (default \"png\"). */\n format?: ScreenshotFormat;\n /** JPEG quality in [0,1] (ignored for png; default 0.92). */\n quality?: number;\n};\n\nexport type ScreenshotResult = {\n /** The project frame that was captured, after clamping. */\n frame: number;\n totalFrames: number;\n /** Encoded image bytes. */\n bytes: Uint8Array;\n};\n\n/** Renders a single frame of a document to an image: a one-shot wrapper over {@link StillRenderer} (create, render, encode, dispose), so it takes the same path as a live preview and the video export at that frame. For repeated capture hold a {@link StillRenderer}: this builds and tears down a WebGPU surface per call. */\nexport async function exportScreenshot(params: ScreenshotParams): Promise<ScreenshotResult> {\n const {\n registry, scale, manifest, theme, rendererWasmUrl,\n frame, format = \"png\", quality = 0.92,\n } = params;\n\n const project = resolveProjectDocument(params.document, params, DEFAULT_OUTPUT);\n if (project.tracks.length === 0) throw new Error(\"The project has no tracks to screenshot.\");\n\n const renderer = await createStillRenderer({\n viewport: project.viewport, fps: project.fps, scale, manifest, theme, rendererWasmUrl, registry,\n });\n\n try {\n const drawn = await renderer.render(project, { frame });\n return { ...drawn, bytes: await renderer.toBytes({ format, quality }) };\n } finally {\n renderer.dispose();\n }\n}\n"]}
package/dist/still.d.ts CHANGED
@@ -4,17 +4,10 @@ import type { BrowserRenderer } from "./engine";
4
4
  import { type RendererWasmOption } from "./renderer";
5
5
  /** Anything a still can be rendered from: a project, or a lone still or composition as the one track of one. */
6
6
  export type StillSource = ProjectDocument | VisualDocument;
7
- /**
8
- * Which frame to draw, in whatever form is most convenient — a global frame
9
- * index, the timeline's first or last frame, or a `FrameSpec`.
10
- */
7
+ /** Which frame to draw: a global frame index, the timeline's `"first"`/`"last"`, or a `FrameSpec`. */
11
8
  export type FrameRef = FrameSpec | number | "first" | "last";
12
9
  export interface StillRendererOptions extends RendererWasmOption {
13
- /**
14
- * The canvas to draw into. Omit and the renderer creates its own hidden one
15
- * and removes it on {@link StillRenderer.dispose} — which is what a one-shot
16
- * capture wants, while a live preview passes the canvas it already shows.
17
- */
10
+ /** Omit and the renderer makes its own hidden canvas and removes it on {@link StillRenderer.dispose}; a live preview passes the one it shows. */
18
11
  canvas?: HTMLCanvasElement;
19
12
  /** Output resolution in pixels, whatever the document asks for. Defaults to 1920×1080. */
20
13
  viewport?: Size2D;
@@ -25,14 +18,7 @@ export interface StillRendererOptions extends RendererWasmOption {
25
18
  manifest?: AssetManifest;
26
19
  /** Theme for a document that carries none of its own. */
27
20
  theme?: Theme;
28
- /**
29
- * The vocabulary a document is compiled against.
30
- *
31
- * Defaults to a fresh one carrying only what the library ships — a renderer
32
- * owns its registry the way an engine does, so a host with custom node types
33
- * passes the same one it gave its engine rather than relying on whatever a
34
- * module happened to register.
35
- */
21
+ /** The vocabulary a document compiles against. Defaults to a fresh one with only what the library ships; a host with custom node types passes the same one it gave its engine. */
36
22
  registry?: Registry;
37
23
  }
38
24
  export interface RenderStillOptions {
@@ -40,58 +26,36 @@ export interface RenderStillOptions {
40
26
  frame?: FrameRef;
41
27
  /** Seconds into the timeline, converted with the document's fps. Wins over `frame`. */
42
28
  time?: number;
43
- /**
44
- * Draw this call at this pixel ratio instead of the renderer's. **Resizes the
45
- * canvas and rebuilds the surface**, and the next call without it rebuilds
46
- * them back, so keep it off the per-edit path — render the preview at its
47
- * normal scale and pass this only when capturing at a higher one.
48
- */
29
+ /** Draw this call at this pixel ratio instead of the renderer's. **Resizes the canvas and rebuilds the surface**, and the next call without it rebuilds back: keep it off the per-edit path. */
49
30
  scale?: number;
50
31
  /** What the renderer drew but not as the document says; a lost GPU device throws `RenderBackendError` instead. */
51
32
  onIssue?: (issue: RenderIssue) => void;
52
33
  }
53
34
  export interface EncodeStillOptions {
54
- /** Defaults to `"png"`. */
55
35
  format?: ScreenshotFormat;
56
36
  /** JPEG quality in [0,1]; ignored for png. Defaults to 0.92. */
57
37
  quality?: number;
58
38
  }
59
- /** Normalize the many ways a caller can name a frame into the engine's own form. */
60
39
  export declare function stillFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec;
61
40
  /**
62
- * A long-lived single-frame renderer.
63
- *
64
- * Where `exportScreenshot` is one frame from a standing start, this keeps the
65
- * expensive things — the renderer module, its GPU surface, and the
66
- * storage adapter's decoded-asset cache — alive across calls, so re-rendering
67
- * after an edit costs discovering one frame and a draw rather than a new GL
68
- * context. That is
69
- * the difference between a one-off capture and a builder that repaints while the
70
- * user types.
71
- *
72
- * It is also the *same* draw: {@link render} goes through `drawFrameAt`, which is
73
- * the pass the video exporter runs per frame. A preview and its exported image
74
- * cannot drift apart, because there is only one path.
41
+ * A long-lived single-frame renderer. Where `exportScreenshot` is one frame from a standing start, this keeps the
42
+ * renderer module, its GPU surface and the storage adapter's decode cache alive, so re-rendering after an edit costs
43
+ * discovering one frame and a draw. {@link render} goes through `drawFrameAt`, the pass the video exporter runs per
44
+ * frame, so a preview and its exported image cannot drift apart.
75
45
  *
76
46
  * ```ts
77
47
  * const renderer = await createStillRenderer({ canvas, viewport, manifest, theme });
78
- * await renderer.render({ kind: "still", nodes: [
79
- * { id: "bg", type: "rect", parentId: null, layerOrder: 0, props: { width: 1920, height: 1080, fill: "bg" } },
80
- * ] });
48
+ * await renderer.render({ kind: "still", nodes: [...] });
81
49
  * const blob = await renderer.toBlob({ format: "png" });
82
50
  * renderer.dispose();
83
51
  * ```
84
52
  *
85
- * **The theme is process-global in core.** {@link render} re-applies the
86
- * document's (or this renderer's) before every draw, so two renderers with
87
- * different themes still each draw correctly; but anything else that calls
88
- * `setTheme` between a render and a read of the surface will have been
89
- * overwritten by the next render.
53
+ * **The theme is process-global in core.** {@link render} re-applies its theme before every draw, so renderers with
54
+ * different themes each draw correctly, but anything else calling `setTheme` between a render and a read of the
55
+ * surface is overwritten by the next render.
90
56
  *
91
- * **One surface, one caller at a time.** A render and the encode of its result
92
- * are two calls, and the next render draws over the surface between them, so
93
- * callers that share a renderer without knowing of each other must be queued —
94
- * as `session.image()` queues its calls.
57
+ * **One surface, one caller at a time.** The next render draws over the surface between a render and the encode of
58
+ * its result, so callers sharing a renderer must be queued (as `session.image()` does).
95
59
  */
96
60
  export declare class StillRenderer {
97
61
  readonly canvas: HTMLCanvasElement;
@@ -99,7 +63,6 @@ export declare class StillRenderer {
99
63
  private storageAdapter;
100
64
  private catalog;
101
65
  private readonly backend;
102
- /** True when we made the canvas and must clean it up. */
103
66
  private readonly ownsCanvas;
104
67
  private viewportValue;
105
68
  private readonly fpsValue;
@@ -117,40 +80,20 @@ export declare class StillRenderer {
117
80
  get viewport(): Size2D;
118
81
  get fps(): number;
119
82
  get scale(): number;
120
- /**
121
- * Draw one frame of `source`, at this renderer's viewport — the surface is
122
- * the output, so it wins over any size the document asks for.
123
- */
83
+ /** The surface is the output, so this renderer's viewport wins over any size the document asks for. */
124
84
  render(source: StillSource, options?: RenderStillOptions): Promise<DrawnFrame>;
125
- /** Theme for a document that carries none of its own. */
126
85
  setTheme(theme?: Theme): void;
127
- /** Swap the asset manifest, keeping every decode already cached. */
128
86
  setManifest(manifest: AssetManifest): void;
129
- /**
130
- * Change the output resolution.
131
- *
132
- * A full reset, unlike {@link setManifest}: the viewport clamps the resolution
133
- * images are decoded *at*, so cached decodes made for the old one would be
134
- * soft at the new one, and the render context's handlers captured the adapter
135
- * when they were built — so both are rebuilt. Call this when the output format
136
- * changes, not per edit.
137
- */
87
+ /** A full reset, unlike {@link setManifest}: cached decodes were made at the old viewport's resolution and the render context's handlers captured the adapter, so both are rebuilt. Call when the output format changes, not per edit. */
138
88
  setViewport(viewport: Size2D): void;
139
89
  /** Encode the frame currently on the surface. */
140
90
  toBytes(options?: EncodeStillOptions): Promise<Uint8Array>;
141
- /** Encode the frame currently on the surface. */
142
91
  toBlob(options?: EncodeStillOptions): Promise<Blob>;
143
- /** Encode the frame currently on the surface. */
144
92
  toDataURL(options?: EncodeStillOptions): string;
145
93
  dispose(): void;
146
- /**
147
- * Read the surface back into a 2D canvas, which is what actually encodes: the
148
- * renderers ship no image encoders, so the browser's own codecs are the only
149
- * ones available.
150
- */
94
+ /** Reads the surface back into a 2D canvas, which does the encoding: the renderers ship no image encoders. */
151
95
  private snapshotToCanvas;
152
96
  private sizeCanvas;
153
- /** Resize the backing store and mount a fresh surface over it. */
154
97
  private remount;
155
98
  private assertLive;
156
99
  }
@@ -1 +1 @@
1
- {"version":3,"file":"still.d.ts","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"still.d.ts","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"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"}
package/dist/still.js CHANGED
@@ -2,7 +2,6 @@ import { DEFAULT_OUTPUT, ManifestAssetCatalog, frameSpecAtTime, Registry, resolv
2
2
  import { drawFrameAt, } from "./frame-loop";
3
3
  import { loadRenderer } from "./renderer";
4
4
  const EMPTY_MANIFEST = { image: {}, video: {}, audio: {}, font: {} };
5
- /** Normalize the many ways a caller can name a frame into the engine's own form. */
6
5
  export function stillFrameSpec(options, fps) {
7
6
  if (options?.time !== undefined)
8
7
  return frameSpecAtTime(options.time, fps);
@@ -19,39 +18,24 @@ function mimeOf(format) {
19
18
  return format === "jpeg" ? "image/jpeg" : "image/png";
20
19
  }
21
20
  /**
22
- * A long-lived single-frame renderer.
23
- *
24
- * Where `exportScreenshot` is one frame from a standing start, this keeps the
25
- * expensive things — the renderer module, its GPU surface, and the
26
- * storage adapter's decoded-asset cache — alive across calls, so re-rendering
27
- * after an edit costs discovering one frame and a draw rather than a new GL
28
- * context. That is
29
- * the difference between a one-off capture and a builder that repaints while the
30
- * user types.
31
- *
32
- * It is also the *same* draw: {@link render} goes through `drawFrameAt`, which is
33
- * the pass the video exporter runs per frame. A preview and its exported image
34
- * cannot drift apart, because there is only one path.
21
+ * A long-lived single-frame renderer. Where `exportScreenshot` is one frame from a standing start, this keeps the
22
+ * renderer module, its GPU surface and the storage adapter's decode cache alive, so re-rendering after an edit costs
23
+ * discovering one frame and a draw. {@link render} goes through `drawFrameAt`, the pass the video exporter runs per
24
+ * frame, so a preview and its exported image cannot drift apart.
35
25
  *
36
26
  * ```ts
37
27
  * const renderer = await createStillRenderer({ canvas, viewport, manifest, theme });
38
- * await renderer.render({ kind: "still", nodes: [
39
- * { id: "bg", type: "rect", parentId: null, layerOrder: 0, props: { width: 1920, height: 1080, fill: "bg" } },
40
- * ] });
28
+ * await renderer.render({ kind: "still", nodes: [...] });
41
29
  * const blob = await renderer.toBlob({ format: "png" });
42
30
  * renderer.dispose();
43
31
  * ```
44
32
  *
45
- * **The theme is process-global in core.** {@link render} re-applies the
46
- * document's (or this renderer's) before every draw, so two renderers with
47
- * different themes still each draw correctly; but anything else that calls
48
- * `setTheme` between a render and a read of the surface will have been
49
- * overwritten by the next render.
33
+ * **The theme is process-global in core.** {@link render} re-applies its theme before every draw, so renderers with
34
+ * different themes each draw correctly, but anything else calling `setTheme` between a render and a read of the
35
+ * surface is overwritten by the next render.
50
36
  *
51
- * **One surface, one caller at a time.** A render and the encode of its result
52
- * are two calls, and the next render draws over the surface between them, so
53
- * callers that share a renderer without knowing of each other must be queued —
54
- * as `session.image()` queues its calls.
37
+ * **One surface, one caller at a time.** The next render draws over the surface between a render and the encode of
38
+ * its result, so callers sharing a renderer must be queued (as `session.image()` does).
55
39
  */
56
40
  export class StillRenderer {
57
41
  canvas;
@@ -59,7 +43,6 @@ export class StillRenderer {
59
43
  storageAdapter;
60
44
  catalog;
61
45
  backend;
62
- /** True when we made the canvas and must clean it up. */
63
46
  ownsCanvas;
64
47
  viewportValue;
65
48
  fpsValue;
@@ -99,10 +82,7 @@ export class StillRenderer {
99
82
  get viewport() { return this.viewportValue; }
100
83
  get fps() { return this.fpsValue; }
101
84
  get scale() { return this.scaleValue; }
102
- /**
103
- * Draw one frame of `source`, at this renderer's viewport — the surface is
104
- * the output, so it wins over any size the document asks for.
105
- */
85
+ /** The surface is the output, so this renderer's viewport wins over any size the document asks for. */
106
86
  async render(source, options) {
107
87
  this.assertLive();
108
88
  const scale = options?.scale ?? this.scaleValue;
@@ -124,24 +104,14 @@ export class StillRenderer {
124
104
  this.drawn = true;
125
105
  return frame;
126
106
  }
127
- /** Theme for a document that carries none of its own. */
128
107
  setTheme(theme) { this.theme = theme; }
129
- /** Swap the asset manifest, keeping every decode already cached. */
130
108
  setManifest(manifest) {
131
109
  this.assertLive();
132
110
  this.manifest = manifest;
133
111
  this.catalog = new ManifestAssetCatalog(manifest);
134
112
  this.storageAdapter.setCatalog(this.catalog);
135
113
  }
136
- /**
137
- * Change the output resolution.
138
- *
139
- * A full reset, unlike {@link setManifest}: the viewport clamps the resolution
140
- * images are decoded *at*, so cached decodes made for the old one would be
141
- * soft at the new one, and the render context's handlers captured the adapter
142
- * when they were built — so both are rebuilt. Call this when the output format
143
- * changes, not per edit.
144
- */
114
+ /** A full reset, unlike {@link setManifest}: cached decodes were made at the old viewport's resolution and the render context's handlers captured the adapter, so both are rebuilt. Call when the output format changes, not per edit. */
145
115
  setViewport(viewport) {
146
116
  this.assertLive();
147
117
  if (viewport.width === this.viewportValue.width && viewport.height === this.viewportValue.height) {
@@ -162,7 +132,6 @@ export class StillRenderer {
162
132
  const blob = await this.toBlob(options);
163
133
  return new Uint8Array(await blob.arrayBuffer());
164
134
  }
165
- /** Encode the frame currently on the surface. */
166
135
  toBlob(options) {
167
136
  const canvas = this.snapshotToCanvas();
168
137
  const { format = "png", quality = 0.92 } = options ?? {};
@@ -172,7 +141,6 @@ export class StillRenderer {
172
141
  : reject(new Error("Still encode produced no data.")), mimeOf(format), quality);
173
142
  });
174
143
  }
175
- /** Encode the frame currently on the surface. */
176
144
  toDataURL(options) {
177
145
  const { format = "png", quality = 0.92 } = options ?? {};
178
146
  return this.snapshotToCanvas().toDataURL(mimeOf(format), quality);
@@ -185,11 +153,7 @@ export class StillRenderer {
185
153
  if (this.ownsCanvas)
186
154
  this.canvas.remove();
187
155
  }
188
- /**
189
- * Read the surface back into a 2D canvas, which is what actually encodes: the
190
- * renderers ship no image encoders, so the browser's own codecs are the only
191
- * ones available.
192
- */
156
+ /** Reads the surface back into a 2D canvas, which does the encoding: the renderers ship no image encoders. */
193
157
  snapshotToCanvas() {
194
158
  this.assertLive();
195
159
  if (!this.drawn)
@@ -203,7 +167,6 @@ export class StillRenderer {
203
167
  this.canvas.width = this.viewportValue.width * this.surfaceScale;
204
168
  this.canvas.height = this.viewportValue.height * this.surfaceScale;
205
169
  }
206
- /** Resize the backing store and mount a fresh surface over it. */
207
170
  remount(scale) {
208
171
  this.surfaceScale = scale;
209
172
  this.drawn = false;
package/dist/still.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"still.js","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AssetCatalog, DEFAULT_OUTPUT, ManifestAssetCatalog,\n frameSpecAtTime,\n Registry,\n resolveProjectDocument,\n type AssetManifest,\n type FrameSpec,\n type RenderIssue,\n type ProjectDocument,\n type VisualDocument,\n type Size2D,\n type StorageAdapter,\n type Theme,\n} from \"@motionscript/core\";\nimport {\n drawFrameAt,\n type DrawnFrame,\n type ScreenshotFormat,\n} from \"./frame-loop\";\nimport type { BrowserRenderContext, BrowserRenderer } from \"./engine\";\nimport { loadRenderer, type RendererWasmOption } from \"./renderer\";\n\nconst EMPTY_MANIFEST: AssetManifest = { image: {}, video: {}, audio: {}, font: {} };\n\n/** Anything a still can be rendered from: a project, or a lone still or composition as the one track of one. */\nexport type StillSource = ProjectDocument | VisualDocument;\n\n/**\n * Which frame to draw, in whatever form is most convenient — a global frame\n * index, the timeline's first or last frame, or a `FrameSpec`.\n */\nexport type FrameRef = FrameSpec | number | \"first\" | \"last\";\n\nexport interface StillRendererOptions extends RendererWasmOption {\n /**\n * The canvas to draw into. Omit and the renderer creates its own hidden one\n * and removes it on {@link StillRenderer.dispose} — which is what a one-shot\n * capture wants, while a live preview passes the canvas it already shows.\n */\n canvas?: HTMLCanvasElement;\n /** Output resolution in pixels, whatever the document asks for. Defaults to 1920×1080. */\n viewport?: Size2D;\n /** Frame rate for a document that sets none. Defaults to 60. */\n fps?: number;\n /** Device pixel ratio the surface is sized at. Defaults to 1. */\n scale?: number;\n manifest?: AssetManifest;\n /** Theme for a document that carries none of its own. */\n theme?: Theme;\n /**\n * The vocabulary a document is compiled against.\n *\n * Defaults to a fresh one carrying only what the library ships — a renderer\n * owns its registry the way an engine does, so a host with custom node types\n * passes the same one it gave its engine rather than relying on whatever a\n * module happened to register.\n */\n registry?: Registry;\n}\n\nexport interface RenderStillOptions {\n /** Which frame to draw. Defaults to the timeline's first. */\n frame?: FrameRef;\n /** Seconds into the timeline, converted with the document's fps. Wins over `frame`. */\n time?: number;\n /**\n * Draw this call at this pixel ratio instead of the renderer's. **Resizes the\n * canvas and rebuilds the surface**, and the next call without it rebuilds\n * them back, so keep it off the per-edit path — render the preview at its\n * normal scale and pass this only when capturing at a higher one.\n */\n scale?: number;\n /** What the renderer drew but not as the document says; a lost GPU device throws `RenderBackendError` instead. */\n onIssue?: (issue: RenderIssue) => void;\n}\n\nexport interface EncodeStillOptions {\n /** Defaults to `\"png\"`. */\n format?: ScreenshotFormat;\n /** JPEG quality in [0,1]; ignored for png. Defaults to 0.92. */\n quality?: number;\n}\n\n/** Normalize the many ways a caller can name a frame into the engine's own form. */\nexport function stillFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec {\n if (options?.time !== undefined) return frameSpecAtTime(options.time, fps);\n const frame = options?.frame;\n if (frame === undefined || frame === \"first\") return { kind: \"first\" };\n if (frame === \"last\") return { kind: \"last\" };\n if (typeof frame === \"number\") return { kind: \"frame\", frame };\n return frame;\n}\n\nfunction mimeOf(format: ScreenshotFormat): string {\n return format === \"jpeg\" ? \"image/jpeg\" : \"image/png\";\n}\n\n/**\n * A long-lived single-frame renderer.\n *\n * Where `exportScreenshot` is one frame from a standing start, this keeps the\n * expensive things — the renderer module, its GPU surface, and the\n * storage adapter's decoded-asset cache — alive across calls, so re-rendering\n * after an edit costs discovering one frame and a draw rather than a new GL\n * context. That is\n * the difference between a one-off capture and a builder that repaints while the\n * user types.\n *\n * It is also the *same* draw: {@link render} goes through `drawFrameAt`, which is\n * the pass the video exporter runs per frame. A preview and its exported image\n * cannot drift apart, because there is only one path.\n *\n * ```ts\n * const renderer = await createStillRenderer({ canvas, viewport, manifest, theme });\n * await renderer.render({ kind: \"still\", nodes: [\n * { id: \"bg\", type: \"rect\", parentId: null, layerOrder: 0, props: { width: 1920, height: 1080, fill: \"bg\" } },\n * ] });\n * const blob = await renderer.toBlob({ format: \"png\" });\n * renderer.dispose();\n * ```\n *\n * **The theme is process-global in core.** {@link render} re-applies the\n * document's (or this renderer's) before every draw, so two renderers with\n * different themes still each draw correctly; but anything else that calls\n * `setTheme` between a render and a read of the surface will have been\n * overwritten by the next render.\n *\n * **One surface, one caller at a time.** A render and the encode of its result\n * are two calls, and the next render draws over the surface between them, so\n * callers that share a renderer without knowing of each other must be queued —\n * as `session.image()` queues its calls.\n */\nexport class StillRenderer {\n readonly canvas: HTMLCanvasElement;\n\n private renderContext: BrowserRenderContext;\n private storageAdapter: StorageAdapter;\n private catalog: AssetCatalog;\n\n private readonly backend: BrowserRenderer;\n /** True when we made the canvas and must clean it up. */\n private readonly ownsCanvas: boolean;\n\n private viewportValue: Size2D;\n private readonly fpsValue: number;\n private readonly scaleValue: number;\n /** What the canvas is sized at now, which a call's own `scale` changes. */\n private surfaceScale: number;\n private manifest: AssetManifest;\n private theme?: Theme;\n private readonly registry: Registry;\n private disposed = false;\n /** False until a frame has been drawn onto the current surface; a remount clears it. */\n private drawn = false;\n\n /** @internal Use {@link createStillRenderer}, which loads the renderer first. */\n constructor(\n backend: BrowserRenderer,\n options: StillRendererOptions,\n ) {\n this.backend = backend;\n this.viewportValue = options.viewport ?? DEFAULT_OUTPUT.viewport;\n this.fpsValue = options.fps ?? DEFAULT_OUTPUT.fps;\n this.scaleValue = options.scale ?? 1;\n this.surfaceScale = this.scaleValue;\n this.manifest = options.manifest ?? EMPTY_MANIFEST;\n this.theme = options.theme;\n this.registry = options.registry ?? new Registry();\n\n this.ownsCanvas = options.canvas === undefined;\n this.canvas = options.canvas ?? document.createElement(\"canvas\");\n if (this.ownsCanvas) {\n this.canvas.style.display = \"none\";\n document.body.appendChild(this.canvas);\n }\n this.sizeCanvas();\n\n this.catalog = new ManifestAssetCatalog(this.manifest);\n this.storageAdapter = this.backend.createStorageAdapter({\n catalog: this.catalog, viewport: this.viewportValue, fps: this.fpsValue,\n });\n this.renderContext = this.backend.createRenderContext(this.storageAdapter);\n this.renderContext.mount(this.canvas);\n }\n\n get viewport(): Size2D { return this.viewportValue; }\n get fps(): number { return this.fpsValue; }\n get scale(): number { return this.scaleValue; }\n\n /**\n * Draw one frame of `source`, at this renderer's viewport — the surface is\n * the output, so it wins over any size the document asks for.\n */\n async render(source: StillSource, options?: RenderStillOptions): Promise<DrawnFrame> {\n this.assertLive();\n const scale = options?.scale ?? this.scaleValue;\n if (scale !== this.surfaceScale) this.remount(scale);\n await this.renderContext.whenMounted?.();\n\n const project = resolveProjectDocument(\n source, { viewport: this.viewportValue }, { viewport: this.viewportValue, fps: this.fpsValue },\n );\n\n const frame = await drawFrameAt({\n project,\n registry: this.registry,\n theme: this.theme,\n scale,\n renderContext: this.renderContext,\n storageAdapter: this.storageAdapter,\n assetCatalog: this.catalog,\n frame: stillFrameSpec(options, project.fps!),\n onIssue: options?.onIssue,\n });\n this.drawn = true;\n return frame;\n }\n\n /** Theme for a document that carries none of its own. */\n setTheme(theme?: Theme): void { this.theme = theme; }\n\n /** Swap the asset manifest, keeping every decode already cached. */\n setManifest(manifest: AssetManifest): void {\n this.assertLive();\n this.manifest = manifest;\n this.catalog = new ManifestAssetCatalog(manifest);\n this.storageAdapter.setCatalog(this.catalog);\n }\n\n /**\n * Change the output resolution.\n *\n * A full reset, unlike {@link setManifest}: the viewport clamps the resolution\n * images are decoded *at*, so cached decodes made for the old one would be\n * soft at the new one, and the render context's handlers captured the adapter\n * when they were built — so both are rebuilt. Call this when the output format\n * changes, not per edit.\n */\n setViewport(viewport: Size2D): void {\n this.assertLive();\n if (viewport.width === this.viewportValue.width && viewport.height === this.viewportValue.height) {\n return;\n }\n this.viewportValue = viewport;\n // Disposing the context disposes the adapter it holds, so this releases\n // the old surface, its handlers and every decode in one call.\n this.renderContext.dispose();\n this.storageAdapter = this.backend.createStorageAdapter({\n catalog: this.catalog, viewport, fps: this.fpsValue,\n });\n this.renderContext = this.backend.createRenderContext(this.storageAdapter);\n this.remount(this.scaleValue);\n }\n\n /** Encode the frame currently on the surface. */\n async toBytes(options?: EncodeStillOptions): Promise<Uint8Array> {\n const blob = await this.toBlob(options);\n return new Uint8Array(await blob.arrayBuffer());\n }\n\n /** Encode the frame currently on the surface. */\n toBlob(options?: EncodeStillOptions): Promise<Blob> {\n const canvas = this.snapshotToCanvas();\n const { format = \"png\", quality = 0.92 } = options ?? {};\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => blob\n ? resolve(blob)\n : reject(new Error(\"Still encode produced no data.\")),\n mimeOf(format),\n quality,\n );\n });\n }\n\n /** Encode the frame currently on the surface. */\n toDataURL(options?: EncodeStillOptions): string {\n const { format = \"png\", quality = 0.92 } = options ?? {};\n return this.snapshotToCanvas().toDataURL(mimeOf(format), quality);\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.renderContext.dispose();\n if (this.ownsCanvas) this.canvas.remove();\n }\n\n /**\n * Read the surface back into a 2D canvas, which is what actually encodes: the\n * renderers ship no image encoders, so the browser's own codecs are the only\n * ones available.\n */\n private snapshotToCanvas(): HTMLCanvasElement {\n this.assertLive();\n if (!this.drawn) throw new Error(\"Nothing has been rendered yet.\");\n const canvas = this.renderContext.snapshotCanvas?.();\n if (!canvas) throw new Error(\"Still encode: could not read the surface into a 2d canvas.\");\n return canvas;\n }\n\n private sizeCanvas(): void {\n this.canvas.width = this.viewportValue.width * this.surfaceScale;\n this.canvas.height = this.viewportValue.height * this.surfaceScale;\n }\n\n /** Resize the backing store and mount a fresh surface over it. */\n private remount(scale: number): void {\n this.surfaceScale = scale;\n this.drawn = false;\n this.sizeCanvas();\n this.renderContext.mount(this.canvas);\n }\n\n private assertLive(): void {\n if (this.disposed) throw new Error(\"This StillRenderer has been disposed.\");\n }\n}\n\n/** Create a {@link StillRenderer}; rejects with `WebGpuUnavailableError` without WebGPU. */\nexport async function createStillRenderer(\n options: StillRendererOptions = {},\n): Promise<StillRenderer> {\n return new StillRenderer(await loadRenderer(options), options);\n}\n"]}
1
+ {"version":3,"file":"still.js","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AssetCatalog, DEFAULT_OUTPUT, ManifestAssetCatalog,\n frameSpecAtTime,\n Registry,\n resolveProjectDocument,\n type AssetManifest,\n type FrameSpec,\n type RenderIssue,\n type ProjectDocument,\n type VisualDocument,\n type Size2D,\n type StorageAdapter,\n type Theme,\n} from \"@motionscript/core\";\nimport {\n drawFrameAt,\n type DrawnFrame,\n type ScreenshotFormat,\n} from \"./frame-loop\";\nimport type { BrowserRenderContext, BrowserRenderer } from \"./engine\";\nimport { loadRenderer, type RendererWasmOption } from \"./renderer\";\n\nconst EMPTY_MANIFEST: AssetManifest = { image: {}, video: {}, audio: {}, font: {} };\n\n/** Anything a still can be rendered from: a project, or a lone still or composition as the one track of one. */\nexport type StillSource = ProjectDocument | VisualDocument;\n\n/** Which frame to draw: a global frame index, the timeline's `\"first\"`/`\"last\"`, or a `FrameSpec`. */\nexport type FrameRef = FrameSpec | number | \"first\" | \"last\";\n\nexport interface StillRendererOptions extends RendererWasmOption {\n /** Omit and the renderer makes its own hidden canvas and removes it on {@link StillRenderer.dispose}; a live preview passes the one it shows. */\n canvas?: HTMLCanvasElement;\n /** Output resolution in pixels, whatever the document asks for. Defaults to 1920×1080. */\n viewport?: Size2D;\n /** Frame rate for a document that sets none. Defaults to 60. */\n fps?: number;\n /** Device pixel ratio the surface is sized at. Defaults to 1. */\n scale?: number;\n manifest?: AssetManifest;\n /** Theme for a document that carries none of its own. */\n theme?: Theme;\n /** The vocabulary a document compiles against. Defaults to a fresh one with only what the library ships; a host with custom node types passes the same one it gave its engine. */\n registry?: Registry;\n}\n\nexport interface RenderStillOptions {\n /** Which frame to draw. Defaults to the timeline's first. */\n frame?: FrameRef;\n /** Seconds into the timeline, converted with the document's fps. Wins over `frame`. */\n time?: number;\n /** Draw this call at this pixel ratio instead of the renderer's. **Resizes the canvas and rebuilds the surface**, and the next call without it rebuilds back: keep it off the per-edit path. */\n scale?: number;\n /** What the renderer drew but not as the document says; a lost GPU device throws `RenderBackendError` instead. */\n onIssue?: (issue: RenderIssue) => void;\n}\n\nexport interface EncodeStillOptions {\n format?: ScreenshotFormat;\n /** JPEG quality in [0,1]; ignored for png. Defaults to 0.92. */\n quality?: number;\n}\n\nexport function stillFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec {\n if (options?.time !== undefined) return frameSpecAtTime(options.time, fps);\n const frame = options?.frame;\n if (frame === undefined || frame === \"first\") return { kind: \"first\" };\n if (frame === \"last\") return { kind: \"last\" };\n if (typeof frame === \"number\") return { kind: \"frame\", frame };\n return frame;\n}\n\nfunction mimeOf(format: ScreenshotFormat): string {\n return format === \"jpeg\" ? \"image/jpeg\" : \"image/png\";\n}\n\n/**\n * A long-lived single-frame renderer. Where `exportScreenshot` is one frame from a standing start, this keeps the\n * renderer module, its GPU surface and the storage adapter's decode cache alive, so re-rendering after an edit costs\n * discovering one frame and a draw. {@link render} goes through `drawFrameAt`, the pass the video exporter runs per\n * frame, so a preview and its exported image cannot drift apart.\n *\n * ```ts\n * const renderer = await createStillRenderer({ canvas, viewport, manifest, theme });\n * await renderer.render({ kind: \"still\", nodes: [...] });\n * const blob = await renderer.toBlob({ format: \"png\" });\n * renderer.dispose();\n * ```\n *\n * **The theme is process-global in core.** {@link render} re-applies its theme before every draw, so renderers with\n * different themes each draw correctly, but anything else calling `setTheme` between a render and a read of the\n * surface is overwritten by the next render.\n *\n * **One surface, one caller at a time.** The next render draws over the surface between a render and the encode of\n * its result, so callers sharing a renderer must be queued (as `session.image()` does).\n */\nexport class StillRenderer {\n readonly canvas: HTMLCanvasElement;\n\n private renderContext: BrowserRenderContext;\n private storageAdapter: StorageAdapter;\n private catalog: AssetCatalog;\n\n private readonly backend: BrowserRenderer;\n private readonly ownsCanvas: boolean;\n\n private viewportValue: Size2D;\n private readonly fpsValue: number;\n private readonly scaleValue: number;\n /** What the canvas is sized at now, which a call's own `scale` changes. */\n private surfaceScale: number;\n private manifest: AssetManifest;\n private theme?: Theme;\n private readonly registry: Registry;\n private disposed = false;\n /** False until a frame has been drawn onto the current surface; a remount clears it. */\n private drawn = false;\n\n /** @internal Use {@link createStillRenderer}, which loads the renderer first. */\n constructor(\n backend: BrowserRenderer,\n options: StillRendererOptions,\n ) {\n this.backend = backend;\n this.viewportValue = options.viewport ?? DEFAULT_OUTPUT.viewport;\n this.fpsValue = options.fps ?? DEFAULT_OUTPUT.fps;\n this.scaleValue = options.scale ?? 1;\n this.surfaceScale = this.scaleValue;\n this.manifest = options.manifest ?? EMPTY_MANIFEST;\n this.theme = options.theme;\n this.registry = options.registry ?? new Registry();\n\n this.ownsCanvas = options.canvas === undefined;\n this.canvas = options.canvas ?? document.createElement(\"canvas\");\n if (this.ownsCanvas) {\n this.canvas.style.display = \"none\";\n document.body.appendChild(this.canvas);\n }\n this.sizeCanvas();\n\n this.catalog = new ManifestAssetCatalog(this.manifest);\n this.storageAdapter = this.backend.createStorageAdapter({\n catalog: this.catalog, viewport: this.viewportValue, fps: this.fpsValue,\n });\n this.renderContext = this.backend.createRenderContext(this.storageAdapter);\n this.renderContext.mount(this.canvas);\n }\n\n get viewport(): Size2D { return this.viewportValue; }\n get fps(): number { return this.fpsValue; }\n get scale(): number { return this.scaleValue; }\n\n /** The surface is the output, so this renderer's viewport wins over any size the document asks for. */\n async render(source: StillSource, options?: RenderStillOptions): Promise<DrawnFrame> {\n this.assertLive();\n const scale = options?.scale ?? this.scaleValue;\n if (scale !== this.surfaceScale) this.remount(scale);\n await this.renderContext.whenMounted?.();\n\n const project = resolveProjectDocument(\n source, { viewport: this.viewportValue }, { viewport: this.viewportValue, fps: this.fpsValue },\n );\n\n const frame = await drawFrameAt({\n project,\n registry: this.registry,\n theme: this.theme,\n scale,\n renderContext: this.renderContext,\n storageAdapter: this.storageAdapter,\n assetCatalog: this.catalog,\n frame: stillFrameSpec(options, project.fps!),\n onIssue: options?.onIssue,\n });\n this.drawn = true;\n return frame;\n }\n\n setTheme(theme?: Theme): void { this.theme = theme; }\n\n setManifest(manifest: AssetManifest): void {\n this.assertLive();\n this.manifest = manifest;\n this.catalog = new ManifestAssetCatalog(manifest);\n this.storageAdapter.setCatalog(this.catalog);\n }\n\n /** A full reset, unlike {@link setManifest}: cached decodes were made at the old viewport's resolution and the render context's handlers captured the adapter, so both are rebuilt. Call when the output format changes, not per edit. */\n setViewport(viewport: Size2D): void {\n this.assertLive();\n if (viewport.width === this.viewportValue.width && viewport.height === this.viewportValue.height) {\n return;\n }\n this.viewportValue = viewport;\n // Disposing the context disposes the adapter it holds, so this releases\n // the old surface, its handlers and every decode in one call.\n this.renderContext.dispose();\n this.storageAdapter = this.backend.createStorageAdapter({\n catalog: this.catalog, viewport, fps: this.fpsValue,\n });\n this.renderContext = this.backend.createRenderContext(this.storageAdapter);\n this.remount(this.scaleValue);\n }\n\n /** Encode the frame currently on the surface. */\n async toBytes(options?: EncodeStillOptions): Promise<Uint8Array> {\n const blob = await this.toBlob(options);\n return new Uint8Array(await blob.arrayBuffer());\n }\n\n toBlob(options?: EncodeStillOptions): Promise<Blob> {\n const canvas = this.snapshotToCanvas();\n const { format = \"png\", quality = 0.92 } = options ?? {};\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => blob\n ? resolve(blob)\n : reject(new Error(\"Still encode produced no data.\")),\n mimeOf(format),\n quality,\n );\n });\n }\n\n toDataURL(options?: EncodeStillOptions): string {\n const { format = \"png\", quality = 0.92 } = options ?? {};\n return this.snapshotToCanvas().toDataURL(mimeOf(format), quality);\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.renderContext.dispose();\n if (this.ownsCanvas) this.canvas.remove();\n }\n\n /** Reads the surface back into a 2D canvas, which does the encoding: the renderers ship no image encoders. */\n private snapshotToCanvas(): HTMLCanvasElement {\n this.assertLive();\n if (!this.drawn) throw new Error(\"Nothing has been rendered yet.\");\n const canvas = this.renderContext.snapshotCanvas?.();\n if (!canvas) throw new Error(\"Still encode: could not read the surface into a 2d canvas.\");\n return canvas;\n }\n\n private sizeCanvas(): void {\n this.canvas.width = this.viewportValue.width * this.surfaceScale;\n this.canvas.height = this.viewportValue.height * this.surfaceScale;\n }\n\n private remount(scale: number): void {\n this.surfaceScale = scale;\n this.drawn = false;\n this.sizeCanvas();\n this.renderContext.mount(this.canvas);\n }\n\n private assertLive(): void {\n if (this.disposed) throw new Error(\"This StillRenderer has been disposed.\");\n }\n}\n\n/** Create a {@link StillRenderer}; rejects with `WebGpuUnavailableError` without WebGPU. */\nexport async function createStillRenderer(\n options: StillRendererOptions = {},\n): Promise<StillRenderer> {\n return new StillRenderer(await loadRenderer(options), options);\n}\n"]}
package/dist/ticker.d.ts CHANGED
@@ -1,16 +1,5 @@
1
1
  import { Ticker } from "@motionscript/core";
2
- /**
3
- * Ticker backed by a Web Audio AudioContext. Clip time follows
4
- * `audioContext.currentTime` (within a few ms, see `startLoop`), so any
5
- * AudioBufferSourceNodes scheduled on the same context play in sync.
6
- *
7
- * When paused we suspend the context (audio stops, currentTime freezes);
8
- * when playing we resume and a rAF loop publishes ticks at the target fps.
9
- *
10
- * A single silent buffer covering the full timeline is started on play(),
11
- * which keeps the context active and gives the browser a stable audio
12
- * graph to schedule against even on scenes that contain no audio nodes.
13
- */
2
+ /** Ticker backed by a Web Audio AudioContext: clip time follows `audioContext.currentTime` (within a few ms, see `startLoop`), so AudioBufferSourceNodes on the same context play in sync. Paused suspends the context (currentTime freezes); playing resumes it and a rAF loop publishes ticks at the target fps. A silent buffer covering the full timeline starts on play() to keep the context active even for scenes with no audio nodes. */
14
3
  export declare class WebTicker extends Ticker {
15
4
  private readonly contextOf;
16
5
  private readonly ownsContext;
@@ -24,11 +13,7 @@ export declare class WebTicker extends Ticker {
24
13
  /** Bumped by every `seek`, so the loop can tell that a listener or tick callback moved the playhead under it. */
25
14
  private seekEpoch;
26
15
  private silentSource;
27
- /**
28
- * `context` may be a getter, called on the first `play`: a session hands
29
- * over its audio sink's, which is created on first use, so a session that
30
- * never plays never opens the audio device.
31
- */
16
+ /** `context` may be a getter, called on the first `play`, so a session that never plays never opens the audio device. */
32
17
  constructor(opts: {
33
18
  context?: AudioContext | (() => AudioContext);
34
19
  fps: number;
@@ -36,36 +21,22 @@ export declare class WebTicker extends Ticker {
36
21
  getContext(): AudioContext;
37
22
  private get context();
38
23
  seek(t: number): void;
39
- /**
40
- * Anchors elapsed-time tracking to the current `context.currentTime`, starts the silent ticker
41
- * + rAF tick loop and asks a suspended context to resume.
42
- *
43
- * Synchronous, as {@link Ticker.play} is: a `pause` or `seek` in the same turn must find a
44
- * running clock to stop. A suspended context's `currentTime` is frozen, so the clock holds at
45
- * the anchor until the resume lands — outside a user gesture that is never.
46
- */
24
+ /** Anchors elapsed-time tracking to `context.currentTime`, starts the silent ticker and rAF loop, and asks a suspended context to resume. Synchronous, as {@link Ticker.play} is: a `pause` or `seek` in the same turn must find a running clock to stop. A suspended context's `currentTime` is frozen, so the clock holds at the anchor until the resume lands (outside a user gesture, never). */
47
25
  play(speed?: number, reverse?: boolean): void;
48
26
  /** Stops the tick loop and silent ticker, then suspends the context — freezing `currentTime` so elapsed-time math stays correct across the pause. */
49
27
  pause(): void;
50
- /** (Re)starts the silent buffer source covering the remaining timeline — keeps the AudioContext active and gives a stable graph for other AudioBufferSourceNodes to schedule against, per the class-level note. */
28
+ /** (Re)starts the silent buffer source covering the remaining timeline (see the class note). */
51
29
  private startSilentTicker;
52
30
  private stopSilentTicker;
53
31
  /** Where the playhead is now, from elapsed `context.currentTime` (so it stays sample-accurate), and whether that is the end of the run. */
54
32
  private position;
55
33
  /**
56
- * Drives ticks off `requestAnimationFrame`, firing `tick()` when the playhead reaches another
57
- * project frame — and once more at the end of the run, whether or not it landed on a boundary.
58
- *
59
- * `context.currentTime` moves in whole render quanta handed over in bursts: at 48 kHz on a
60
- * 60 Hz display it advances 8, 10.67, 18.67 or 21.33 ms between animation frames, and a
61
- * playhead read straight off it skipped up to a third of the frames. So the playhead advances
62
- * by each animation frame's own interval, pulled 5% of the way to the audio clock every frame
63
- * and snapped to it when they disagree by more than 50 ms (a stall, a seek). It stays within
64
- * a few ms of the audio and moves one frame per frame.
65
- *
66
- * A listener may `seek` (a host looping wraps to 0 when it hears the end), so the end is
67
- * re-read after the listeners and the tick callbacks have run: a run that was seeked out of
68
- * its end carries on, and one that was not pauses itself.
34
+ * Drives ticks off `requestAnimationFrame`, firing `tick()` when the playhead reaches another project frame, and
35
+ * once more at the end of the run. `context.currentTime` moves in whole render quanta handed over in bursts (at
36
+ * 48 kHz on a 60 Hz display 8, 10.67, 18.67 or 21.33 ms between frames), so a playhead read straight off it skipped
37
+ * up to a third of the frames. The playhead therefore advances by each animation frame's own interval, pulled 5% of
38
+ * the way to the audio clock every frame and snapped to it past 50 ms apart (a stall, a seek). A listener may `seek`
39
+ * (a host looping wraps to 0 on the end), so the end is re-read after listeners and tick callbacks run.
69
40
  */
70
41
  private startLoop;
71
42
  private stopLoop;
@@ -1 +1 @@
1
- {"version":3,"file":"ticker.d.ts","sourceRoot":"","sources":["../src/ticker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,qBAAa,SAAU,SAAQ,MAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,KAAK,CAAuB;IACpC,iHAAiH;IACjH,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,YAAY,CAAsC;IAE1D;;;;OAIG;gBACS,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,YAAY,GAAG,CAAC,MAAM,YAAY,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAQhF,UAAU,IAAI,YAAY;IAK1B,OAAO,KAAK,OAAO,GAElB;IAED,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IASrB;;;;;;;OAOG;IACH,IAAI,CAAC,KAAK,GAAE,MAAU,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI;IAwBvD,qJAAqJ;IACrJ,KAAK,IAAI,IAAI;IAUb,mNAAmN;IACnN,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,gBAAgB;IAaxB,2IAA2I;IAC3I,OAAO,CAAC,QAAQ;IAShB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,SAAS;IA6DjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,IAAI,IAAI;CAQlB"}
1
+ {"version":3,"file":"ticker.d.ts","sourceRoot":"","sources":["../src/ticker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,kbAAkb;AAClb,qBAAa,SAAU,SAAQ,MAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,GAAG,CAAS;IAEpB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,KAAK,CAAuB;IACpC,iHAAiH;IACjH,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,YAAY,CAAsC;IAE1D,yHAAyH;gBAC7G,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,YAAY,GAAG,CAAC,MAAM,YAAY,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAQhF,UAAU,IAAI,YAAY;IAK1B,OAAO,KAAK,OAAO,GAElB;IAED,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IASrB,qYAAqY;IACrY,IAAI,CAAC,KAAK,GAAE,MAAU,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI;IAwBvD,qJAAqJ;IACrJ,KAAK,IAAI,IAAI;IAUb,gGAAgG;IAChG,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,gBAAgB;IAYxB,2IAA2I;IAC3I,OAAO,CAAC,QAAQ;IAShB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;IA2DjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,IAAI,IAAI;CAQlB"}