@motionscript/browser 0.1.0-alpha.0 → 0.1.0-alpha.5

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 (90) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/LICENSE +201 -201
  3. package/NOTICE +48 -0
  4. package/README.md +118 -69
  5. package/dist/asset-bytes.d.ts +7 -1
  6. package/dist/asset-bytes.d.ts.map +1 -1
  7. package/dist/asset-bytes.js +14 -1
  8. package/dist/asset-bytes.js.map +1 -1
  9. package/dist/audio/bus-graph.d.ts +12 -3
  10. package/dist/audio/bus-graph.d.ts.map +1 -1
  11. package/dist/audio/bus-graph.js +24 -29
  12. package/dist/audio/bus-graph.js.map +1 -1
  13. package/dist/audio/filter-graph.d.ts +6 -5
  14. package/dist/audio/filter-graph.d.ts.map +1 -1
  15. package/dist/audio/filter-graph.js +29 -23
  16. package/dist/audio/filter-graph.js.map +1 -1
  17. package/dist/audio/index.js.map +1 -1
  18. package/dist/audio/loop-region.d.ts +31 -0
  19. package/dist/audio/loop-region.d.ts.map +1 -0
  20. package/dist/audio/loop-region.js +43 -0
  21. package/dist/audio/loop-region.js.map +1 -0
  22. package/dist/audio/mixer.d.ts.map +1 -1
  23. package/dist/audio/mixer.js +30 -11
  24. package/dist/audio/mixer.js.map +1 -1
  25. package/dist/audio/player.d.ts +4 -4
  26. package/dist/audio/player.d.ts.map +1 -1
  27. package/dist/audio/player.js +19 -15
  28. package/dist/audio/player.js.map +1 -1
  29. package/dist/browser/THIRD_PARTY_LICENSES.txt +432 -0
  30. package/dist/browser/audio.js +1 -1
  31. package/dist/browser/chunks/chunk-2MAFJWBZ.js +2 -0
  32. package/dist/browser/chunks/chunk-2MAFJWBZ.js.map +7 -0
  33. package/dist/browser/index.js +133 -279
  34. package/dist/browser/index.js.map +4 -4
  35. package/dist/browser/manifest.json +19 -2
  36. package/dist/concat.js.map +1 -1
  37. package/dist/decode/image-levels.js.map +1 -1
  38. package/dist/decode/levels.js.map +1 -1
  39. package/dist/decode/video-levels.js.map +1 -1
  40. package/dist/dynamic-nodes.d.ts.map +1 -1
  41. package/dist/dynamic-nodes.js +5 -1
  42. package/dist/dynamic-nodes.js.map +1 -1
  43. package/dist/engine.d.ts.map +1 -1
  44. package/dist/engine.js +18 -5
  45. package/dist/engine.js.map +1 -1
  46. package/dist/exporter.d.ts +10 -2
  47. package/dist/exporter.d.ts.map +1 -1
  48. package/dist/exporter.js +8 -7
  49. package/dist/exporter.js.map +1 -1
  50. package/dist/getter.js.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -27
  54. package/dist/index.js.map +1 -1
  55. package/dist/measure-context.js.map +1 -1
  56. package/dist/mix-timeline-audio.d.ts +2 -2
  57. package/dist/mix-timeline-audio.d.ts.map +1 -1
  58. package/dist/mix-timeline-audio.js +1 -2
  59. package/dist/mix-timeline-audio.js.map +1 -1
  60. package/dist/render-context.d.ts +5 -3
  61. package/dist/render-context.d.ts.map +1 -1
  62. package/dist/render-context.js +7 -3
  63. package/dist/render-context.js.map +1 -1
  64. package/dist/screenshot.d.ts +3 -3
  65. package/dist/screenshot.d.ts.map +1 -1
  66. package/dist/screenshot.js +1 -2
  67. package/dist/screenshot.js.map +1 -1
  68. package/dist/still.d.ts +18 -8
  69. package/dist/still.d.ts.map +1 -1
  70. package/dist/still.js +28 -21
  71. package/dist/still.js.map +1 -1
  72. package/dist/storage-adapter.d.ts +1 -1
  73. package/dist/storage-adapter.d.ts.map +1 -1
  74. package/dist/storage-adapter.js +12 -10
  75. package/dist/storage-adapter.js.map +1 -1
  76. package/dist/three/renderer.d.ts +10 -11
  77. package/dist/three/renderer.d.ts.map +1 -1
  78. package/dist/three/renderer.js +18 -13
  79. package/dist/three/renderer.js.map +1 -1
  80. package/dist/ticker.d.ts +22 -4
  81. package/dist/ticker.d.ts.map +1 -1
  82. package/dist/ticker.js +65 -28
  83. package/dist/ticker.js.map +1 -1
  84. package/package.json +8 -6
  85. package/dist/browser/chunks/chunk-NSFLRI4P.js +0 -2
  86. package/dist/browser/chunks/chunk-NSFLRI4P.js.map +0 -7
  87. package/dist/output.d.ts +0 -7
  88. package/dist/output.d.ts.map +0 -1
  89. package/dist/output.js +0 -6
  90. package/dist/output.js.map +0 -1
package/dist/still.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Registry, type AssetManifest, type ProjectDocument, type SceneDocument, type Size2D, type Theme } from "@motionscript/core";
2
- import { type DrawnFrame, type FrameSpec, type ScreenshotFormat } from "@motionscript/skia-render/export";
1
+ import { Registry, type AssetManifest, type FrameSpec, type ProjectDocument, type VisualDocument, type Size2D, type Theme } from "@motionscript/core";
2
+ import { type DrawnFrame, type ScreenshotFormat } from "@motionscript/skia-render/export";
3
3
  import { getCanvasKit } from "./getter";
4
4
  /** Anything a still can be rendered from: a project, or a lone still or composition as the one track of one. */
5
- export type StillSource = ProjectDocument | SceneDocument;
5
+ export type StillSource = ProjectDocument | VisualDocument;
6
6
  /**
7
7
  * Which frame to draw, in whatever form is most convenient — a global frame
8
8
  * index, the timeline's first or last frame, or a `FrameSpec`.
@@ -41,9 +41,10 @@ export interface RenderStillOptions {
41
41
  /** Seconds into the timeline, converted with the document's fps. Wins over `frame`. */
42
42
  time?: number;
43
43
  /**
44
- * Draw at this pixel ratio instead of the renderer's. **Resizes the canvas and
45
- * rebuilds the Skia surface**, so keep it off the per-edit path — render the
46
- * preview at its normal scale and pass this only when capturing at a higher one.
44
+ * Draw this call at this pixel ratio instead of the renderer's. **Resizes the
45
+ * canvas and rebuilds the Skia 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.
47
48
  */
48
49
  scale?: number;
49
50
  }
@@ -54,7 +55,7 @@ export interface EncodeStillOptions {
54
55
  quality?: number;
55
56
  }
56
57
  /** Normalize the many ways a caller can name a frame into the engine's own form. */
57
- export declare function toFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec;
58
+ export declare function stillFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec;
58
59
  /**
59
60
  * A long-lived single-frame renderer.
60
61
  *
@@ -84,6 +85,11 @@ export declare function toFrameSpec(options: RenderStillOptions | undefined, fps
84
85
  * different themes still each draw correctly; but anything else that calls
85
86
  * `setTheme` between a render and a read of the surface will have been
86
87
  * overwritten by the next render.
88
+ *
89
+ * **One surface, one caller at a time.** A render and the encode of its result
90
+ * are two calls, and the next render draws over the surface between them, so
91
+ * callers that share a renderer without knowing of each other must be queued —
92
+ * as `session.image()` queues its calls.
87
93
  */
88
94
  export declare class StillRenderer {
89
95
  readonly canvas: HTMLCanvasElement;
@@ -95,11 +101,15 @@ export declare class StillRenderer {
95
101
  private readonly ownsCanvas;
96
102
  private viewportValue;
97
103
  private readonly fpsValue;
98
- private scaleValue;
104
+ private readonly scaleValue;
105
+ /** What the canvas is sized at now, which a call's own `scale` changes. */
106
+ private surfaceScale;
99
107
  private manifest;
100
108
  private theme?;
101
109
  private readonly registry;
102
110
  private disposed;
111
+ /** False until a frame has been drawn onto the current surface; a remount clears it. */
112
+ private drawn;
103
113
  /** @internal Use {@link createStillRenderer}, which loads CanvasKit first. */
104
114
  constructor(canvasKit: Awaited<ReturnType<typeof getCanvasKit>>, options: StillRendererOptions);
105
115
  get viewport(): Size2D;
@@ -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
@@ -1,15 +1,13 @@
1
- import { ManifestAssetCatalog, Registry, resolveProjectDocument, setTheme, } from "@motionscript/core";
1
+ import { DEFAULT_OUTPUT, ManifestAssetCatalog, frameSpecAtTime, Registry, resolveProjectDocument, } from "@motionscript/core";
2
2
  import { drawFrameAt, } from "@motionscript/skia-render/export";
3
3
  import { WebRenderContext } from "./render-context";
4
4
  import { WebStorageAdapter } from "./storage-adapter";
5
5
  import { getCanvasKit } from "./getter";
6
- import { DEFAULT_OUTPUT } from "./output";
7
6
  const EMPTY_MANIFEST = { image: {}, video: {}, audio: {}, font: {} };
8
7
  /** Normalize the many ways a caller can name a frame into the engine's own form. */
9
- export function toFrameSpec(options, fps) {
10
- if (options?.time !== undefined) {
11
- return { kind: "frame", frame: Math.round(options.time * fps) };
12
- }
8
+ export function stillFrameSpec(options, fps) {
9
+ if (options?.time !== undefined)
10
+ return frameSpecAtTime(options.time, fps);
13
11
  const frame = options?.frame;
14
12
  if (frame === undefined || frame === "first")
15
13
  return { kind: "first" };
@@ -51,6 +49,11 @@ function mimeOf(format) {
51
49
  * different themes still each draw correctly; but anything else that calls
52
50
  * `setTheme` between a render and a read of the surface will have been
53
51
  * overwritten by the next render.
52
+ *
53
+ * **One surface, one caller at a time.** A render and the encode of its result
54
+ * are two calls, and the next render draws over the surface between them, so
55
+ * callers that share a renderer without knowing of each other must be queued —
56
+ * as `session.image()` queues its calls.
54
57
  */
55
58
  export class StillRenderer {
56
59
  canvas;
@@ -63,16 +66,21 @@ export class StillRenderer {
63
66
  viewportValue;
64
67
  fpsValue;
65
68
  scaleValue;
69
+ /** What the canvas is sized at now, which a call's own `scale` changes. */
70
+ surfaceScale;
66
71
  manifest;
67
72
  theme;
68
73
  registry;
69
74
  disposed = false;
75
+ /** False until a frame has been drawn onto the current surface; a remount clears it. */
76
+ drawn = false;
70
77
  /** @internal Use {@link createStillRenderer}, which loads CanvasKit first. */
71
78
  constructor(canvasKit, options) {
72
79
  this.canvasKit = canvasKit;
73
80
  this.viewportValue = options.viewport ?? DEFAULT_OUTPUT.viewport;
74
81
  this.fpsValue = options.fps ?? DEFAULT_OUTPUT.fps;
75
82
  this.scaleValue = options.scale ?? 1;
83
+ this.surfaceScale = this.scaleValue;
76
84
  this.manifest = options.manifest ?? EMPTY_MANIFEST;
77
85
  this.theme = options.theme;
78
86
  this.registry = options.registry ?? new Registry();
@@ -98,24 +106,21 @@ export class StillRenderer {
98
106
  async render(source, options) {
99
107
  this.assertLive();
100
108
  const scale = options?.scale ?? this.scaleValue;
101
- if (scale !== this.scaleValue) {
102
- this.scaleValue = scale;
103
- this.remount();
104
- }
109
+ if (scale !== this.surfaceScale)
110
+ this.remount(scale);
105
111
  const project = resolveProjectDocument(source, { viewport: this.viewportValue }, { viewport: this.viewportValue, fps: this.fpsValue });
106
- // Re-applied per render, not once at construction: it is a global
107
- // registry, so a second renderer (or the player) may have replaced it
108
- // since. Cheap — a map swap.
109
- setTheme(project.theme ?? this.theme);
110
- return drawFrameAt({
112
+ const frame = await drawFrameAt({
111
113
  project,
112
114
  registry: this.registry,
115
+ theme: this.theme,
113
116
  scale,
114
117
  renderContext: this.renderContext,
115
118
  storageAdapter: this.storageAdapter,
116
119
  assetCatalog: this.catalog,
117
- frame: toFrameSpec(options, project.fps),
120
+ frame: stillFrameSpec(options, project.fps),
118
121
  });
122
+ this.drawn = true;
123
+ return frame;
119
124
  }
120
125
  /** Theme for a document that carries none of its own. */
121
126
  setTheme(theme) { this.theme = theme; }
@@ -146,7 +151,7 @@ export class StillRenderer {
146
151
  this.renderContext.dispose();
147
152
  this.storageAdapter = new WebStorageAdapter(this.canvasKit, this.catalog, viewport, this.fpsValue);
148
153
  this.renderContext = new WebRenderContext(this.canvasKit, this.storageAdapter);
149
- this.remount();
154
+ this.remount(this.scaleValue);
150
155
  }
151
156
  /** Encode the frame currently on the surface. */
152
157
  async toBytes(options) {
@@ -183,7 +188,7 @@ export class StillRenderer {
183
188
  */
184
189
  snapshotToCanvas() {
185
190
  this.assertLive();
186
- const snapshot = this.renderContext.snapshotPixels();
191
+ const snapshot = this.drawn ? this.renderContext.snapshotPixels() : undefined;
187
192
  if (!snapshot)
188
193
  throw new Error("Nothing has been rendered yet.");
189
194
  const canvas = document.createElement("canvas");
@@ -197,11 +202,13 @@ export class StillRenderer {
197
202
  return canvas;
198
203
  }
199
204
  sizeCanvas() {
200
- this.canvas.width = this.viewportValue.width * this.scaleValue;
201
- this.canvas.height = this.viewportValue.height * this.scaleValue;
205
+ this.canvas.width = this.viewportValue.width * this.surfaceScale;
206
+ this.canvas.height = this.viewportValue.height * this.surfaceScale;
202
207
  }
203
208
  /** Resize the backing store and build a fresh Skia surface over it. */
204
- remount() {
209
+ remount(scale) {
210
+ this.surfaceScale = scale;
211
+ this.drawn = false;
205
212
  this.sizeCanvas();
206
213
  this.renderContext.mount(this.canvas);
207
214
  }
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, ManifestAssetCatalog,\n Registry,\n resolveProjectDocument,\n setTheme,\n type AssetManifest,\n type ProjectDocument,\n type SceneDocument,\n type Size2D,\n type Theme,\n} from \"@motionscript/core\";\nimport {\n drawFrameAt,\n type DrawnFrame,\n type FrameSpec,\n type ScreenshotFormat,\n} from \"@motionscript/skia-render/export\";\nimport { WebRenderContext } from \"./render-context\";\nimport { WebStorageAdapter } from \"./storage-adapter\";\nimport { getCanvasKit } from \"./getter\";\nimport { DEFAULT_OUTPUT } from \"./output\";\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 | SceneDocument;\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 {\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 wasmUrl?: string;\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 at this pixel ratio instead of the renderer's. **Resizes the canvas and\n * rebuilds the Skia surface**, so keep it off the per-edit path — render the\n * preview at its normal scale and pass this only when capturing at a higher one.\n */\n scale?: number;\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 toFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec {\n if (options?.time !== undefined) {\n return { kind: \"frame\", frame: Math.round(options.time * fps) };\n }\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 CanvasKit module, the WebGL Skia 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 */\nexport class StillRenderer {\n readonly canvas: HTMLCanvasElement;\n\n private renderContext: WebRenderContext;\n private storageAdapter: WebStorageAdapter;\n private catalog: AssetCatalog;\n\n private readonly canvasKit: Awaited<ReturnType<typeof getCanvasKit>>;\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 scaleValue: number;\n private manifest: AssetManifest;\n private theme?: Theme;\n private readonly registry: Registry;\n private disposed = false;\n\n /** @internal Use {@link createStillRenderer}, which loads CanvasKit first. */\n constructor(\n canvasKit: Awaited<ReturnType<typeof getCanvasKit>>,\n options: StillRendererOptions,\n ) {\n this.canvasKit = canvasKit;\n this.viewportValue = options.viewport ?? DEFAULT_OUTPUT.viewport;\n this.fpsValue = options.fps ?? DEFAULT_OUTPUT.fps;\n this.scaleValue = options.scale ?? 1;\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 = new WebStorageAdapter(\n canvasKit, this.catalog, this.viewportValue, this.fpsValue,\n );\n this.renderContext = new WebRenderContext(canvasKit, 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.scaleValue) {\n this.scaleValue = scale;\n this.remount();\n }\n\n const project = resolveProjectDocument(\n source, { viewport: this.viewportValue }, { viewport: this.viewportValue, fps: this.fpsValue },\n );\n // Re-applied per render, not once at construction: it is a global\n // registry, so a second renderer (or the player) may have replaced it\n // since. Cheap — a map swap.\n setTheme(project.theme ?? this.theme);\n\n return drawFrameAt({\n project,\n registry: this.registry,\n scale,\n renderContext: this.renderContext,\n storageAdapter: this.storageAdapter,\n assetCatalog: this.catalog,\n frame: toFrameSpec(options, project.fps!),\n });\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 = new WebStorageAdapter(\n this.canvasKit, this.catalog, viewport, this.fpsValue,\n );\n this.renderContext = new WebRenderContext(this.canvasKit, this.storageAdapter);\n this.remount();\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: this\n * CanvasKit build ships no wasm image encoders, so the browser's own codecs are\n * the only ones available.\n */\n private snapshotToCanvas(): HTMLCanvasElement {\n this.assertLive();\n const snapshot = this.renderContext.snapshotPixels();\n if (!snapshot) throw new Error(\"Nothing has been rendered yet.\");\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = snapshot.width;\n canvas.height = snapshot.height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Still encode: could not get a 2d context.\");\n // `pixels` is unpremultiplied RGBA8888, which is exactly ImageData's layout.\n ctx.putImageData(\n new ImageData(new Uint8ClampedArray(snapshot.pixels), snapshot.width, snapshot.height),\n 0, 0,\n );\n return canvas;\n }\n\n private sizeCanvas(): void {\n this.canvas.width = this.viewportValue.width * this.scaleValue;\n this.canvas.height = this.viewportValue.height * this.scaleValue;\n }\n\n /** Resize the backing store and build a fresh Skia surface over it. */\n private remount(): void {\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/**\n * Create a {@link StillRenderer}, loading CanvasKit if it isn't already.\n *\n * `getCanvasKit` memoizes, so a second renderer in the same page is cheap.\n */\nexport async function createStillRenderer(\n options: StillRendererOptions = {},\n): Promise<StillRenderer> {\n const canvasKit = await getCanvasKit(options.wasmUrl);\n return new StillRenderer(canvasKit, options);\n}\n"]}
1
+ {"version":3,"file":"still.js","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n AssetCatalog, DEFAULT_OUTPUT, ManifestAssetCatalog,\r\n frameSpecAtTime,\r\n Registry,\r\n resolveProjectDocument,\r\n type AssetManifest,\r\n type FrameSpec,\r\n type ProjectDocument,\r\n type VisualDocument,\r\n type Size2D,\r\n type Theme,\r\n} from \"@motionscript/core\";\r\nimport {\r\n drawFrameAt,\r\n type DrawnFrame,\r\n type ScreenshotFormat,\r\n} from \"@motionscript/skia-render/export\";\r\nimport { WebRenderContext } from \"./render-context\";\r\nimport { WebStorageAdapter } from \"./storage-adapter\";\r\nimport { getCanvasKit } from \"./getter\";\r\n\r\nconst EMPTY_MANIFEST: AssetManifest = { image: {}, video: {}, audio: {}, font: {} };\r\n\r\n/** Anything a still can be rendered from: a project, or a lone still or composition as the one track of one. */\r\nexport type StillSource = ProjectDocument | VisualDocument;\r\n\r\n/**\r\n * Which frame to draw, in whatever form is most convenient — a global frame\r\n * index, the timeline's first or last frame, or a `FrameSpec`.\r\n */\r\nexport type FrameRef = FrameSpec | number | \"first\" | \"last\";\r\n\r\nexport interface StillRendererOptions {\r\n /**\r\n * The canvas to draw into. Omit and the renderer creates its own hidden one\r\n * and removes it on {@link StillRenderer.dispose} — which is what a one-shot\r\n * capture wants, while a live preview passes the canvas it already shows.\r\n */\r\n canvas?: HTMLCanvasElement;\r\n /** Output resolution in pixels, whatever the document asks for. Defaults to 1920×1080. */\r\n viewport?: Size2D;\r\n /** Frame rate for a document that sets none. Defaults to 60. */\r\n fps?: number;\r\n /** Device pixel ratio the surface is sized at. Defaults to 1. */\r\n scale?: number;\r\n manifest?: AssetManifest;\r\n /** Theme for a document that carries none of its own. */\r\n theme?: Theme;\r\n wasmUrl?: string;\r\n /**\r\n * The vocabulary a document is compiled against.\r\n *\r\n * Defaults to a fresh one carrying only what the library ships — a renderer\r\n * owns its registry the way an engine does, so a host with custom node types\r\n * passes the same one it gave its engine rather than relying on whatever a\r\n * module happened to register.\r\n */\r\n registry?: Registry;\r\n}\r\n\r\nexport interface RenderStillOptions {\r\n /** Which frame to draw. Defaults to the timeline's first. */\r\n frame?: FrameRef;\r\n /** Seconds into the timeline, converted with the document's fps. Wins over `frame`. */\r\n time?: number;\r\n /**\r\n * Draw this call at this pixel ratio instead of the renderer's. **Resizes the\r\n * canvas and rebuilds the Skia surface**, and the next call without it rebuilds\r\n * them back, so keep it off the per-edit path — render the preview at its\r\n * normal scale and pass this only when capturing at a higher one.\r\n */\r\n scale?: number;\r\n}\r\n\r\nexport interface EncodeStillOptions {\r\n /** Defaults to `\"png\"`. */\r\n format?: ScreenshotFormat;\r\n /** JPEG quality in [0,1]; ignored for png. Defaults to 0.92. */\r\n quality?: number;\r\n}\r\n\r\n/** Normalize the many ways a caller can name a frame into the engine's own form. */\r\nexport function stillFrameSpec(options: RenderStillOptions | undefined, fps: number): FrameSpec {\r\n if (options?.time !== undefined) return frameSpecAtTime(options.time, fps);\r\n const frame = options?.frame;\r\n if (frame === undefined || frame === \"first\") return { kind: \"first\" };\r\n if (frame === \"last\") return { kind: \"last\" };\r\n if (typeof frame === \"number\") return { kind: \"frame\", frame };\r\n return frame;\r\n}\r\n\r\nfunction mimeOf(format: ScreenshotFormat): string {\r\n return format === \"jpeg\" ? \"image/jpeg\" : \"image/png\";\r\n}\r\n\r\n/**\r\n * A long-lived single-frame renderer.\r\n *\r\n * Where `exportScreenshot` is one frame from a standing start, this keeps the\r\n * expensive things — the CanvasKit module, the WebGL Skia surface, and the\r\n * storage adapter's decoded-asset cache — alive across calls, so re-rendering\r\n * after an edit costs discovering one frame and a draw rather than a new GL\r\n * context. That is\r\n * the difference between a one-off capture and a builder that repaints while the\r\n * user types.\r\n *\r\n * It is also the *same* draw: {@link render} goes through `drawFrameAt`, which is\r\n * the pass the video exporter runs per frame. A preview and its exported image\r\n * cannot drift apart, because there is only one path.\r\n *\r\n * ```ts\r\n * const renderer = await createStillRenderer({ canvas, viewport, manifest, theme });\r\n * await renderer.render({ kind: \"still\", nodes: [\r\n * { id: \"bg\", type: \"rect\", parentId: null, layerOrder: 0, props: { width: 1920, height: 1080, fill: \"bg\" } },\r\n * ] });\r\n * const blob = await renderer.toBlob({ format: \"png\" });\r\n * renderer.dispose();\r\n * ```\r\n *\r\n * **The theme is process-global in core.** {@link render} re-applies the\r\n * document's (or this renderer's) before every draw, so two renderers with\r\n * different themes still each draw correctly; but anything else that calls\r\n * `setTheme` between a render and a read of the surface will have been\r\n * overwritten by the next render.\r\n *\r\n * **One surface, one caller at a time.** A render and the encode of its result\r\n * are two calls, and the next render draws over the surface between them, so\r\n * callers that share a renderer without knowing of each other must be queued —\r\n * as `session.image()` queues its calls.\r\n */\r\nexport class StillRenderer {\r\n readonly canvas: HTMLCanvasElement;\r\n\r\n private renderContext: WebRenderContext;\r\n private storageAdapter: WebStorageAdapter;\r\n private catalog: AssetCatalog;\r\n\r\n private readonly canvasKit: Awaited<ReturnType<typeof getCanvasKit>>;\r\n /** True when we made the canvas and must clean it up. */\r\n private readonly ownsCanvas: boolean;\r\n\r\n private viewportValue: Size2D;\r\n private readonly fpsValue: number;\r\n private readonly scaleValue: number;\r\n /** What the canvas is sized at now, which a call's own `scale` changes. */\r\n private surfaceScale: number;\r\n private manifest: AssetManifest;\r\n private theme?: Theme;\r\n private readonly registry: Registry;\r\n private disposed = false;\r\n /** False until a frame has been drawn onto the current surface; a remount clears it. */\r\n private drawn = false;\r\n\r\n /** @internal Use {@link createStillRenderer}, which loads CanvasKit first. */\r\n constructor(\r\n canvasKit: Awaited<ReturnType<typeof getCanvasKit>>,\r\n options: StillRendererOptions,\r\n ) {\r\n this.canvasKit = canvasKit;\r\n this.viewportValue = options.viewport ?? DEFAULT_OUTPUT.viewport;\r\n this.fpsValue = options.fps ?? DEFAULT_OUTPUT.fps;\r\n this.scaleValue = options.scale ?? 1;\r\n this.surfaceScale = this.scaleValue;\r\n this.manifest = options.manifest ?? EMPTY_MANIFEST;\r\n this.theme = options.theme;\r\n this.registry = options.registry ?? new Registry();\r\n\r\n this.ownsCanvas = options.canvas === undefined;\r\n this.canvas = options.canvas ?? document.createElement(\"canvas\");\r\n if (this.ownsCanvas) {\r\n this.canvas.style.display = \"none\";\r\n document.body.appendChild(this.canvas);\r\n }\r\n this.sizeCanvas();\r\n\r\n this.catalog = new ManifestAssetCatalog(this.manifest);\r\n this.storageAdapter = new WebStorageAdapter(\r\n canvasKit, this.catalog, this.viewportValue, this.fpsValue,\r\n );\r\n this.renderContext = new WebRenderContext(canvasKit, this.storageAdapter);\r\n this.renderContext.mount(this.canvas);\r\n }\r\n\r\n get viewport(): Size2D { return this.viewportValue; }\r\n get fps(): number { return this.fpsValue; }\r\n get scale(): number { return this.scaleValue; }\r\n\r\n /**\r\n * Draw one frame of `source`, at this renderer's viewport — the surface is\r\n * the output, so it wins over any size the document asks for.\r\n */\r\n async render(source: StillSource, options?: RenderStillOptions): Promise<DrawnFrame> {\r\n this.assertLive();\r\n const scale = options?.scale ?? this.scaleValue;\r\n if (scale !== this.surfaceScale) this.remount(scale);\r\n\r\n const project = resolveProjectDocument(\r\n source, { viewport: this.viewportValue }, { viewport: this.viewportValue, fps: this.fpsValue },\r\n );\r\n\r\n const frame = await drawFrameAt({\r\n project,\r\n registry: this.registry,\r\n theme: this.theme,\r\n scale,\r\n renderContext: this.renderContext,\r\n storageAdapter: this.storageAdapter,\r\n assetCatalog: this.catalog,\r\n frame: stillFrameSpec(options, project.fps!),\r\n });\r\n this.drawn = true;\r\n return frame;\r\n }\r\n\r\n /** Theme for a document that carries none of its own. */\r\n setTheme(theme?: Theme): void { this.theme = theme; }\r\n\r\n /** Swap the asset manifest, keeping every decode already cached. */\r\n setManifest(manifest: AssetManifest): void {\r\n this.assertLive();\r\n this.manifest = manifest;\r\n this.catalog = new ManifestAssetCatalog(manifest);\r\n this.storageAdapter.setCatalog(this.catalog);\r\n }\r\n\r\n /**\r\n * Change the output resolution.\r\n *\r\n * A full reset, unlike {@link setManifest}: the viewport clamps the resolution\r\n * images are decoded *at*, so cached decodes made for the old one would be\r\n * soft at the new one, and the render context's handlers captured the adapter\r\n * when they were built — so both are rebuilt. Call this when the output format\r\n * changes, not per edit.\r\n */\r\n setViewport(viewport: Size2D): void {\r\n this.assertLive();\r\n if (viewport.width === this.viewportValue.width && viewport.height === this.viewportValue.height) {\r\n return;\r\n }\r\n this.viewportValue = viewport;\r\n // Disposing the context disposes the adapter it holds, so this releases\r\n // the old surface, its handlers and every decode in one call.\r\n this.renderContext.dispose();\r\n this.storageAdapter = new WebStorageAdapter(\r\n this.canvasKit, this.catalog, viewport, this.fpsValue,\r\n );\r\n this.renderContext = new WebRenderContext(this.canvasKit, this.storageAdapter);\r\n this.remount(this.scaleValue);\r\n }\r\n\r\n /** Encode the frame currently on the surface. */\r\n async toBytes(options?: EncodeStillOptions): Promise<Uint8Array> {\r\n const blob = await this.toBlob(options);\r\n return new Uint8Array(await blob.arrayBuffer());\r\n }\r\n\r\n /** Encode the frame currently on the surface. */\r\n toBlob(options?: EncodeStillOptions): Promise<Blob> {\r\n const canvas = this.snapshotToCanvas();\r\n const { format = \"png\", quality = 0.92 } = options ?? {};\r\n return new Promise((resolve, reject) => {\r\n canvas.toBlob(\r\n (blob) => blob\r\n ? resolve(blob)\r\n : reject(new Error(\"Still encode produced no data.\")),\r\n mimeOf(format),\r\n quality,\r\n );\r\n });\r\n }\r\n\r\n /** Encode the frame currently on the surface. */\r\n toDataURL(options?: EncodeStillOptions): string {\r\n const { format = \"png\", quality = 0.92 } = options ?? {};\r\n return this.snapshotToCanvas().toDataURL(mimeOf(format), quality);\r\n }\r\n\r\n dispose(): void {\r\n if (this.disposed) return;\r\n this.disposed = true;\r\n this.renderContext.dispose();\r\n if (this.ownsCanvas) this.canvas.remove();\r\n }\r\n\r\n /**\r\n * Read the surface back into a 2D canvas, which is what actually encodes: this\r\n * CanvasKit build ships no wasm image encoders, so the browser's own codecs are\r\n * the only ones available.\r\n */\r\n private snapshotToCanvas(): HTMLCanvasElement {\r\n this.assertLive();\r\n const snapshot = this.drawn ? this.renderContext.snapshotPixels() : undefined;\r\n if (!snapshot) throw new Error(\"Nothing has been rendered yet.\");\r\n\r\n const canvas = document.createElement(\"canvas\");\r\n canvas.width = snapshot.width;\r\n canvas.height = snapshot.height;\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) throw new Error(\"Still encode: could not get a 2d context.\");\r\n // `pixels` is unpremultiplied RGBA8888, which is exactly ImageData's layout.\r\n ctx.putImageData(\r\n new ImageData(new Uint8ClampedArray(snapshot.pixels), snapshot.width, snapshot.height),\r\n 0, 0,\r\n );\r\n return canvas;\r\n }\r\n\r\n private sizeCanvas(): void {\r\n this.canvas.width = this.viewportValue.width * this.surfaceScale;\r\n this.canvas.height = this.viewportValue.height * this.surfaceScale;\r\n }\r\n\r\n /** Resize the backing store and build a fresh Skia surface over it. */\r\n private remount(scale: number): void {\r\n this.surfaceScale = scale;\r\n this.drawn = false;\r\n this.sizeCanvas();\r\n this.renderContext.mount(this.canvas);\r\n }\r\n\r\n private assertLive(): void {\r\n if (this.disposed) throw new Error(\"This StillRenderer has been disposed.\");\r\n }\r\n}\r\n\r\n/**\r\n * Create a {@link StillRenderer}, loading CanvasKit if it isn't already.\r\n *\r\n * `getCanvasKit` memoizes, so a second renderer in the same page is cheap.\r\n */\r\nexport async function createStillRenderer(\r\n options: StillRendererOptions = {},\r\n): Promise<StillRenderer> {\r\n const canvasKit = await getCanvasKit(options.wasmUrl);\r\n return new StillRenderer(canvasKit, options);\r\n}\r\n"]}
@@ -170,7 +170,7 @@ export declare class WebStorageAdapter extends StorageAdapter implements SkiaAss
170
170
  * The compressed bytes for `src`, from the host's local store if it has
171
171
  * them and from the network otherwise. Every byte-reading path goes through
172
172
  * this, so a source is local for all of images, video, audio and fonts or
173
- * for none of them.
173
+ * for none of them. A response that is not OK rejects, naming the address.
174
174
  */
175
175
  private fetchBlob;
176
176
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"storage-adapter.d.ts","sourceRoot":"","sources":["../src/storage-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage-adapter.d.ts","sourceRoot":"","sources":["../src/storage-adapter.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { ALL_FORMATS, BlobSource, Input, UrlSource, VideoSampleSink } from "medi
3
3
  import { ParagraphShapeCache } from "@motionscript/skia-render/shapes/paragraph-cache";
4
4
  // Type-only three usage keeps this a real lazy boundary — see three/bridge.ts.
5
5
  import { hasPendingCanvas3D, warmPendingCanvas3D } from "@motionscript/skia-render/three/bridge";
6
- import { localAssetBytes } from "./asset-bytes";
6
+ import { fetchAssetBytes, localAssetBytes } from "./asset-bytes";
7
7
  import { ImageLevelCache } from "./decode/image-levels";
8
8
  import { levelCap, levelSize, MAX_IMAGE_DECODE_EDGE } from "./decode/levels";
9
9
  import { VideoLevel, videoWindowFrames } from "./decode/video-levels";
@@ -366,13 +366,13 @@ export class WebStorageAdapter extends StorageAdapter {
366
366
  * The compressed bytes for `src`, from the host's local store if it has
367
367
  * them and from the network otherwise. Every byte-reading path goes through
368
368
  * this, so a source is local for all of images, video, audio and fonts or
369
- * for none of them.
369
+ * for none of them. A response that is not OK rejects, naming the address.
370
370
  */
371
371
  async fetchBlob(src) {
372
372
  const local = await localAssetBytes(src);
373
373
  if (local)
374
374
  return local;
375
- return (await fetch(src)).blob();
375
+ return fetchAssetBytes(src);
376
376
  }
377
377
  /**
378
378
  * The address `src` is fetched from.
@@ -1197,10 +1197,11 @@ export class WebStorageAdapter extends StorageAdapter {
1197
1197
  return true;
1198
1198
  const metas = this.catalog.getFontFamilyMetas(fontFamily);
1199
1199
  if (metas.length === 0) {
1200
- // Not an error: an undescribed family shapes against the platform
1201
- // fallback. Warned once per family per catalog, since the caller
1202
- // retries on every `loadAt`.
1203
- if (!this.unknownFontFamilies.has(fontFamily)) {
1200
+ // Not an error while previewing: an undescribed family shapes against
1201
+ // the platform fallback. A blocking caller is refused by the base
1202
+ // class instead, so only a preview is warned, once per family per
1203
+ // catalog, since it retries on every `loadAt`.
1204
+ if (this.decodePolicy === "interactive" && !this.unknownFontFamilies.has(fontFamily)) {
1204
1205
  this.unknownFontFamilies.add(fontFamily);
1205
1206
  console.warn(`[WebStorageAdapter] No font manifest entry for "${key}"; shaping against the fallback face. Add it to the manifest font record.`);
1206
1207
  }
@@ -1208,10 +1209,11 @@ export class WebStorageAdapter extends StorageAdapter {
1208
1209
  }
1209
1210
  // Dedupe by src in case the same file is registered under multiple keys.
1210
1211
  const sources = [...new Set(metas.map(m => m.src))];
1211
- await Promise.all(sources.map(async (src) => {
1212
- const bytes = await (await this.fetchBlob(src)).arrayBuffer();
1212
+ // Every file arrives before any registers: a family missing a weight would
1213
+ // shape that weight against another, and the retry would register the rest twice.
1214
+ const files = await Promise.all(sources.map(async (src) => (await this.fetchBlob(src)).arrayBuffer()));
1215
+ for (const bytes of files)
1213
1216
  this.fontMgr.registerFont(new Uint8Array(bytes), fontFamily);
1214
- }));
1215
1217
  this.registeredFontFamilies.add(fontFamily);
1216
1218
  this.unknownFontFamilies.delete(fontFamily);
1217
1219
  this.fontEpoch++;