@motionscript/browser 0.1.0-alpha.5 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +5 -5
- package/dist/browser/index.js +24 -24
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/manifest.json +1 -1
- package/dist/dynamic-nodes.d.ts.map +1 -1
- package/dist/dynamic-nodes.js +6 -12
- package/dist/dynamic-nodes.js.map +1 -1
- package/dist/engine.d.ts +6 -6
- package/dist/engine.js.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-nodes.d.ts","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"dynamic-nodes.d.ts","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA8C5D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,SAAS,EAC/C,eAAe,CAAC,EAAE,MAAM,GACzB,iBAAiB,CAMnB"}
|
package/dist/dynamic-nodes.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as sdk from "@motionscript/core/sdk";
|
|
2
|
-
import * as jsxRuntime from "@motionscript/core/jsx/jsx-runtime";
|
|
3
2
|
import * as esbuild from "esbuild-wasm";
|
|
4
3
|
/**
|
|
5
4
|
* `browser`'s real, working default for `Registry.addNodeFromSource` — the
|
|
6
|
-
* `sdk
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* `sdk` half is cheap (re-exports, no WASM) and imported eagerly; the compiler
|
|
6
|
+
* is the expensive half, and stays behind a lazy `import()` so an app that
|
|
7
|
+
* never registers a source-string node never pays for it, however long the
|
|
8
|
+
* engine has been running.
|
|
10
9
|
*
|
|
11
10
|
* `core` has no compiler dependency of its own and cannot import the published
|
|
12
11
|
* `@motionscript/sdk` barrel (that package depends on `core`, so the reverse
|
|
@@ -30,14 +29,10 @@ function defaultCompiler(wasmUrl) {
|
|
|
30
29
|
await esbuild.initialize({ wasmURL: wasmUrl ?? defaultCompilerWasmUrl() });
|
|
31
30
|
return async (source) => {
|
|
32
31
|
const { code } = await esbuild.transform(source, {
|
|
33
|
-
|
|
32
|
+
// A component is written with calls: there is no JSX to compile.
|
|
33
|
+
loader: "ts",
|
|
34
34
|
format: "cjs",
|
|
35
35
|
target: "es2020",
|
|
36
|
-
// JSX becomes `require("@motionscript/sdk/jsx-runtime")`, which the
|
|
37
|
-
// registry links to `jsxRuntime`. A pragma in the script still wins.
|
|
38
|
-
jsx: "automatic",
|
|
39
|
-
jsxImportSource: "@motionscript/sdk",
|
|
40
|
-
tsconfigRaw: { compilerOptions: { experimentalDecorators: true } },
|
|
41
36
|
});
|
|
42
37
|
return code;
|
|
43
38
|
};
|
|
@@ -52,7 +47,6 @@ function defaultCompiler(wasmUrl) {
|
|
|
52
47
|
export function resolveDynamicNodes(partial, compilerWasmUrl) {
|
|
53
48
|
return {
|
|
54
49
|
sdk: partial?.sdk ?? sdk,
|
|
55
|
-
jsxRuntime: partial?.jsxRuntime ?? jsxRuntime,
|
|
56
50
|
modules: partial?.modules,
|
|
57
51
|
compile: partial?.compile ?? (async (source) => (await defaultCompiler(compilerWasmUrl))(source)),
|
|
58
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-nodes.js","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"dynamic-nodes.js","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;;;;GAaG;AAEH,mFAAmF;AACnF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,SAAS,sBAAsB;IAC3B,OAAO,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,IAAI,eAAyE,CAAC;AAE9E,iFAAiF;AACjF,SAAS,eAAe,CAAC,OAAgB;IACrC,OAAO,CAAC,eAAe,KAAK,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,EAAE,CAAC,CAAC;QAC3E,OAAO,KAAK,EAAE,MAAc,EAAE,EAAE;YAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE;gBAC7C,iEAAiE;gBACjE,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,QAAQ;aACnB,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC;IACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACV,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAC/B,OAA+C,EAC/C,eAAwB;IAExB,OAAO;QACH,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG;QACxB,OAAO,EAAE,OAAO,EAAE,OAAO;QACzB,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;KACpG,CAAC;AACN,CAAC","sourcesContent":["import type { DynamicNodeConfig } from \"@motionscript/core\";\r\nimport * as sdk from \"@motionscript/core/sdk\";\r\nimport * as esbuild from \"esbuild-wasm\";\r\n\r\n/**\r\n * `browser`'s real, working default for `Registry.addNodeFromSource` — the\r\n * `sdk` half is cheap (re-exports, no WASM) and imported eagerly; the compiler\r\n * is the expensive half, and stays behind a lazy `import()` so an app that\r\n * never registers a source-string node never pays for it, however long the\r\n * engine has been running.\r\n *\r\n * `core` has no compiler dependency of its own and cannot import the published\r\n * `@motionscript/sdk` barrel (that package depends on `core`, so the reverse\r\n * would be circular) — `browser` is where this concern actually belongs: the\r\n * package whose entire job is \"what running in an actual browser tab looks\r\n * like\" already owns `getCanvasKit`/`WebAudioSink`/`WebTicker` on exactly that\r\n * basis.\r\n */\r\n\r\n// Default to the wasm shipped by esbuild-wasm, resolved lazily — `import.meta.url`\r\n// is empty in a non-ESM output (an IIFE bundle, which is what the headless page\r\n// loads), and `new URL(\"…\", \"\")` throws on import, before a caller has had the\r\n// chance to pass an explicit URL that would have made the default irrelevant.\r\n// Mirrors `getCanvasKit`'s own `defaultWasmUrl` in `getter.ts`.\r\nfunction defaultCompilerWasmUrl(): string {\r\n return new URL(\"esbuild-wasm/esbuild.wasm\", import.meta.url).href;\r\n}\r\n\r\nlet compilerPromise: Promise<(source: string) => Promise<string>> | undefined;\r\n\r\n/** Loaded once, memoized process-wide — mirrors how `getCanvasKit` is shared. */\r\nfunction defaultCompiler(wasmUrl?: string): Promise<(source: string) => Promise<string>> {\r\n return (compilerPromise ??= (async () => {\r\n await esbuild.initialize({ wasmURL: wasmUrl ?? defaultCompilerWasmUrl() });\r\n return async (source: string) => {\r\n const { code } = await esbuild.transform(source, {\r\n // A component is written with calls: there is no JSX to compile.\r\n loader: \"ts\",\r\n format: \"cjs\",\r\n target: \"es2020\",\r\n });\r\n return code;\r\n };\r\n })());\r\n}\r\n\r\n/**\r\n * Resolves a full `DynamicNodeConfig`, filling in whatever the caller left\r\n * unset with `browser`'s own default. Synchronous — the WASM import happens\r\n * on `compile`'s first actual call, not here, so attaching this to every\r\n * engine costs nothing until a source string is genuinely registered.\r\n */\r\nexport function resolveDynamicNodes(\r\n partial: Partial<DynamicNodeConfig> | undefined,\r\n compilerWasmUrl?: string,\r\n): DynamicNodeConfig {\r\n return {\r\n sdk: partial?.sdk ?? sdk,\r\n modules: partial?.modules,\r\n compile: partial?.compile ?? (async (source) => (await defaultCompiler(compilerWasmUrl))(source)),\r\n };\r\n}\r\n"]}
|
package/dist/engine.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { WebStorageAdapter } from "./storage-adapter";
|
|
|
3
3
|
import { type AudioEncoderOptions, type VideoEncoderOptions } from "./exporter";
|
|
4
4
|
export interface BrowserEngineOptions extends Omit<EngineOptions, "nodes" | "dynamicNodes"> {
|
|
5
5
|
/**
|
|
6
|
-
* Node types any document opened here may name —
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* Node types any document opened here may name — components as an array
|
|
7
|
+
* or a record naming each key outright, same as `EngineOptions`, plus a
|
|
8
|
+
* TS source string in the **array** form only. A string default-exports a
|
|
9
|
+
* component, whose `key` is its key — the record form takes no string, so
|
|
10
|
+
* there is exactly one place a dynamic node's key comes from, not two that
|
|
11
|
+
* could disagree.
|
|
12
12
|
*/
|
|
13
13
|
nodes?: readonly (NodeConstructor | string)[] | NodeTypeMap;
|
|
14
14
|
/**
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CanvasKit } from \"@motionscript/canvaskit\";\r\nimport {\r\n AudioSink,\r\n Engine,\r\n EngineOptions,\r\n EnginePlatform,\r\n EngineRenderContext,\r\n Exporter,\r\n ExportStillRequest,\r\n ExportVideoRequest,\r\n ExportedStill,\r\n Registry,\r\n Ticker,\r\n MeasureContext2D,\r\n PlatformDeps,\r\n type DynamicNodeConfig,\r\n type NodeConstructor,\r\n type NodeTypeMap,\r\n} from \"@motionscript/core\";\r\n\r\nimport { getCanvasKit } from \"./getter\";\r\nimport { resolveDynamicNodes } from \"./dynamic-nodes\";\r\nimport { WebAudioSink } from \"./audio/player\";\r\nimport { WebTicker } from \"./ticker\";\r\nimport { WebMeasureContext } from \"./measure-context\";\r\nimport { WebRenderContext } from \"./render-context\";\r\nimport { WebStorageAdapter } from \"./storage-adapter\";\r\nimport { exportVideo as exportVideoFile, type AudioEncoderOptions, type VideoEncoderOptions } from \"./exporter\";\r\nimport { StillRenderer } from \"./still\";\r\n\r\nexport interface BrowserEngineOptions extends Omit<EngineOptions, \"nodes\" | \"dynamicNodes\"> {\r\n /**\r\n * Node types any document opened here may name — decorated classes as an\r\n * array or a record naming each key outright, same as `EngineOptions`,\r\n * plus a TS/JSX source string in the **array** form only. A string\r\n * self-declares its key via `@node(...)` in its own source — the record\r\n * form stays class-only, so there is exactly one place a dynamic node's\r\n * key comes from, not two that could disagree.\r\n */\r\n nodes?: readonly (NodeConstructor | string)[] | NodeTypeMap;\r\n /**\r\n * Enables a source string in `nodes`, and `engine.registerNode(source)`\r\n * at any point afterward. Any field left unset falls back to `browser`'s\r\n * own default — `@motionscript/core/sdk` for `sdk`, a lazily-loaded\r\n * `esbuild-wasm` for `compile` — so this never needs to be passed at all\r\n * for the common case. `modules` has no default: only the host knows\r\n * which loaded packages a script it compiles may reach.\r\n */\r\n dynamicNodes?: Partial<DynamicNodeConfig>;\r\n /**\r\n * Where `esbuild-wasm`'s own `.wasm` binary is served — only read if the\r\n * default compiler is actually used. Mirrors `wasmUrl` for CanvasKit.\r\n */\r\n compilerWasmUrl?: string;\r\n /** URL of the CanvasKit `.wasm` binary, forwarded to `getCanvasKit`. */\r\n wasmUrl?: string;\r\n /** Default video encoder settings for `session.export()`. Overridable per call. */\r\n video?: VideoEncoderOptions;\r\n /** Default audio encoder settings for `session.export()`. Overridable per call. */\r\n audio?: AudioEncoderOptions;\r\n}\r\n\r\n/** What `session.export({ encode })` accepts in this environment. */\r\nexport interface BrowserEncodeOptions {\r\n video?: VideoEncoderOptions;\r\n audio?: AudioEncoderOptions;\r\n supersample?: number;\r\n /** Set false to get the bytes back instead of triggering a browser download. */\r\n download?: boolean;\r\n filename?: string;\r\n}\r\n\r\n/**\r\n * The browser's {@link Exporter}: WebCodecs + mediabunny for video, the\r\n * browser's own image codecs for a still.\r\n *\r\n * **It does not reuse a session's storage adapter, and that is deliberate.** An\r\n * adapter's viewport clamps the resolution images decode at — `StorageAdapter`\r\n * documents that it has no setter for exactly this reason — so handing an export\r\n * the preview's adapter would paint a 2× render from decodes sized for 1×, and\r\n * the result would be soft in a way nothing reports. What is safely shared is\r\n * CanvasKit itself, which `getCanvasKit` already memoizes process-wide.\r\n */\r\nclass WebExporter implements Exporter {\r\n private still: StillRenderer | null = null;\r\n private stillRegistry: Registry | null = null;\r\n /** Settles once every still asked for so far has been encoded. */\r\n private stillQueue: Promise<unknown> = Promise.resolve();\r\n\r\n constructor(\r\n private readonly canvasKit: CanvasKit,\r\n private readonly defaults: { video?: VideoEncoderOptions; audio?: AudioEncoderOptions },\r\n private readonly wasmUrl?: string,\r\n ) { }\r\n\r\n async exportVideo(request: ExportVideoRequest): Promise<Uint8Array | void> {\r\n const encode = (request.encode ?? {}) as BrowserEncodeOptions;\r\n return exportVideoFile({\r\n document: request.project,\r\n registry: request.registry,\r\n scale: request.scale,\r\n manifest: request.manifest,\r\n includeAudio: request.includeAudio,\r\n frames: request.frames,\r\n video: encode.video ?? this.defaults.video,\r\n audio: encode.audio ?? this.defaults.audio,\r\n supersample: encode.supersample,\r\n filename: encode.filename,\r\n // A `Session` asks for bytes; only an explicit `download: true` takes\r\n // the DOM anchor path this function offers a page.\r\n returnBytes: encode.download !== true,\r\n onProgress: request.onProgress,\r\n signal: request.signal,\r\n wasmUrl: this.wasmUrl,\r\n });\r\n }\r\n\r\n /**\r\n * Queued, rather than a renderer per call: the one renderer is what keeps a\r\n * GL context and the decode cache warm between stills, and its draw and the\r\n * encode of it are two steps another call's draw would land between.\r\n */\r\n exportStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const still = this.stillQueue.then(() => this.drawStill(request));\r\n this.stillQueue = still.catch(() => undefined);\r\n return still;\r\n }\r\n\r\n dispose(): void {\r\n this.still?.dispose();\r\n this.still = null;\r\n }\r\n\r\n private async drawStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const renderer = this.renderer(request);\r\n const drawn = await renderer.render(request.project, {\r\n scale: request.scale,\r\n ...frameOption(request.frame),\r\n });\r\n const bytes = await renderer.toBytes({\r\n format: request.format,\r\n quality: request.quality,\r\n });\r\n return { bytes, frame: drawn.frame, totalFrames: drawn.totalFrames };\r\n }\r\n\r\n /**\r\n * One `StillRenderer`, reused across calls and re-viewported when the ask\r\n * changes — `setViewport` is a no-op when the size already matches, and a\r\n * full reset when it does not, which is the same trade the class documents.\r\n */\r\n private renderer(request: ExportStillRequest): StillRenderer {\r\n const { project, registry } = request;\r\n if (project.viewport === undefined || project.fps === undefined) {\r\n throw new Error(\"exportStill takes a project whose fps and viewport are resolved — see Session.\");\r\n }\r\n // A renderer compiles against the registry it was built with.\r\n if (this.still && this.stillRegistry !== registry) this.dispose();\r\n this.still ??= new StillRenderer(this.canvasKit, {\r\n viewport: project.viewport,\r\n fps: project.fps,\r\n manifest: request.manifest,\r\n registry,\r\n });\r\n this.stillRegistry = registry;\r\n this.still.setViewport(project.viewport);\r\n if (request.manifest) this.still.setManifest(request.manifest);\r\n return this.still;\r\n }\r\n}\r\n\r\n/** `ExportStillRequest.frame` in the shape `StillRenderer.render` accepts. */\r\nfunction frameOption(frame: ExportStillRequest[\"frame\"]): { frame?: number | \"first\" | \"last\" } {\r\n if (frame.kind === \"frame\") return { frame: frame.frame };\r\n return { frame: frame.kind };\r\n}\r\n\r\n/**\r\n * An engine backed by the browser: WebGL/Skia surfaces, Web Audio, WebCodecs.\r\n *\r\n * Async because CanvasKit is a wasm module and every seam below needs it. Await\r\n * once, at app start, and share the result — the caches underneath (registered\r\n * fonts, decoded images, shaped paragraphs) are the point of keeping one engine\r\n * rather than one per canvas.\r\n *\r\n * ```ts\r\n * const engine = await createBrowserEngine({ nodes: { pulseRing: PulseRing } });\r\n * const session = engine.open({ document, target: canvasEl });\r\n * session.playback!.play();\r\n * ```\r\n */\r\nexport async function createBrowserEngine(\r\n options: BrowserEngineOptions = {},\r\n): Promise<Engine<WebStorageAdapter>> {\r\n const rawNodes = options.nodes;\r\n // `Array.isArray` doesn't narrow a `readonly T[] | U` union cleanly against\r\n // its `arg is any[]` (mutable) predicate — the `.filter` predicate below\r\n // already guarantees no string survives, so the cast is just recovering\r\n // what the runtime check already proved.\r\n const classNodes = (\r\n Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is NodeConstructor => typeof n !== \"string\")\r\n : rawNodes\r\n ) as NodeTypeMap | readonly NodeConstructor[] | undefined;\r\n const sourceNodes = Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is string => typeof n === \"string\")\r\n : [];\r\n // Cheap — see dynamic-nodes.ts: no WASM import happens here, only on\r\n // `compile`'s first actual call. Attached unconditionally so a later\r\n // `engine.registerNode(source)` works even when `nodes` carried no\r\n // string at boot.\r\n const dynamicNodes = resolveDynamicNodes(options.dynamicNodes, options.compilerWasmUrl);\r\n\r\n const canvasKit = await getCanvasKit(options.wasmUrl);\r\n const exporter = new WebExporter(\r\n canvasKit,\r\n { video: options.video, audio: options.audio },\r\n options.wasmUrl,\r\n );\r\n\r\n const platform: EnginePlatform<WebStorageAdapter> = {\r\n createStorageAdapter({ catalog, viewport, fps }: PlatformDeps): WebStorageAdapter {\r\n return new WebStorageAdapter(canvasKit, catalog, viewport, fps);\r\n },\r\n createRenderContext(storage: WebStorageAdapter): EngineRenderContext {\r\n return new WebRenderContext(canvasKit, storage);\r\n },\r\n createMeasureContext(storage: WebStorageAdapter): MeasureContext2D {\r\n return new WebMeasureContext(storage);\r\n },\r\n createAudioSink(): AudioSink {\r\n return new WebAudioSink();\r\n },\r\n createTicker(fps: number, audioDevice: AudioSink): Ticker {\r\n // Shares the device's own `AudioContext`: two independent timebases\r\n // in one session drift against each other, and sound is the one that\r\n // cannot be nudged back.\r\n return new WebTicker({\r\n context: (audioDevice as WebAudioSink).getContext(),\r\n fps,\r\n });\r\n },\r\n createExporter(): Exporter {\r\n return exporter;\r\n },\r\n dispose(): void {\r\n exporter.dispose();\r\n },\r\n };\r\n\r\n const engine = new Engine<WebStorageAdapter>(platform, { ...options, nodes: classNodes, dynamicNodes });\r\n for (const source of sourceNodes) {\r\n // Sequential — one script's error shouldn't mask another's, and\r\n // registration order should match array order for determinism.\r\n await engine.registerNode(source);\r\n }\r\n return engine;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CanvasKit } from \"@motionscript/canvaskit\";\r\nimport {\r\n AudioSink,\r\n Engine,\r\n EngineOptions,\r\n EnginePlatform,\r\n EngineRenderContext,\r\n Exporter,\r\n ExportStillRequest,\r\n ExportVideoRequest,\r\n ExportedStill,\r\n Registry,\r\n Ticker,\r\n MeasureContext2D,\r\n PlatformDeps,\r\n type DynamicNodeConfig,\r\n type NodeConstructor,\r\n type NodeTypeMap,\r\n} from \"@motionscript/core\";\r\n\r\nimport { getCanvasKit } from \"./getter\";\r\nimport { resolveDynamicNodes } from \"./dynamic-nodes\";\r\nimport { WebAudioSink } from \"./audio/player\";\r\nimport { WebTicker } from \"./ticker\";\r\nimport { WebMeasureContext } from \"./measure-context\";\r\nimport { WebRenderContext } from \"./render-context\";\r\nimport { WebStorageAdapter } from \"./storage-adapter\";\r\nimport { exportVideo as exportVideoFile, type AudioEncoderOptions, type VideoEncoderOptions } from \"./exporter\";\r\nimport { StillRenderer } from \"./still\";\r\n\r\nexport interface BrowserEngineOptions extends Omit<EngineOptions, \"nodes\" | \"dynamicNodes\"> {\r\n /**\r\n * Node types any document opened here may name — components as an array\r\n * or a record naming each key outright, same as `EngineOptions`, plus a\r\n * TS source string in the **array** form only. A string default-exports a\r\n * component, whose `key` is its key — the record form takes no string, so\r\n * there is exactly one place a dynamic node's key comes from, not two that\r\n * could disagree.\r\n */\r\n nodes?: readonly (NodeConstructor | string)[] | NodeTypeMap;\r\n /**\r\n * Enables a source string in `nodes`, and `engine.registerNode(source)`\r\n * at any point afterward. Any field left unset falls back to `browser`'s\r\n * own default — `@motionscript/core/sdk` for `sdk`, a lazily-loaded\r\n * `esbuild-wasm` for `compile` — so this never needs to be passed at all\r\n * for the common case. `modules` has no default: only the host knows\r\n * which loaded packages a script it compiles may reach.\r\n */\r\n dynamicNodes?: Partial<DynamicNodeConfig>;\r\n /**\r\n * Where `esbuild-wasm`'s own `.wasm` binary is served — only read if the\r\n * default compiler is actually used. Mirrors `wasmUrl` for CanvasKit.\r\n */\r\n compilerWasmUrl?: string;\r\n /** URL of the CanvasKit `.wasm` binary, forwarded to `getCanvasKit`. */\r\n wasmUrl?: string;\r\n /** Default video encoder settings for `session.export()`. Overridable per call. */\r\n video?: VideoEncoderOptions;\r\n /** Default audio encoder settings for `session.export()`. Overridable per call. */\r\n audio?: AudioEncoderOptions;\r\n}\r\n\r\n/** What `session.export({ encode })` accepts in this environment. */\r\nexport interface BrowserEncodeOptions {\r\n video?: VideoEncoderOptions;\r\n audio?: AudioEncoderOptions;\r\n supersample?: number;\r\n /** Set false to get the bytes back instead of triggering a browser download. */\r\n download?: boolean;\r\n filename?: string;\r\n}\r\n\r\n/**\r\n * The browser's {@link Exporter}: WebCodecs + mediabunny for video, the\r\n * browser's own image codecs for a still.\r\n *\r\n * **It does not reuse a session's storage adapter, and that is deliberate.** An\r\n * adapter's viewport clamps the resolution images decode at — `StorageAdapter`\r\n * documents that it has no setter for exactly this reason — so handing an export\r\n * the preview's adapter would paint a 2× render from decodes sized for 1×, and\r\n * the result would be soft in a way nothing reports. What is safely shared is\r\n * CanvasKit itself, which `getCanvasKit` already memoizes process-wide.\r\n */\r\nclass WebExporter implements Exporter {\r\n private still: StillRenderer | null = null;\r\n private stillRegistry: Registry | null = null;\r\n /** Settles once every still asked for so far has been encoded. */\r\n private stillQueue: Promise<unknown> = Promise.resolve();\r\n\r\n constructor(\r\n private readonly canvasKit: CanvasKit,\r\n private readonly defaults: { video?: VideoEncoderOptions; audio?: AudioEncoderOptions },\r\n private readonly wasmUrl?: string,\r\n ) { }\r\n\r\n async exportVideo(request: ExportVideoRequest): Promise<Uint8Array | void> {\r\n const encode = (request.encode ?? {}) as BrowserEncodeOptions;\r\n return exportVideoFile({\r\n document: request.project,\r\n registry: request.registry,\r\n scale: request.scale,\r\n manifest: request.manifest,\r\n includeAudio: request.includeAudio,\r\n frames: request.frames,\r\n video: encode.video ?? this.defaults.video,\r\n audio: encode.audio ?? this.defaults.audio,\r\n supersample: encode.supersample,\r\n filename: encode.filename,\r\n // A `Session` asks for bytes; only an explicit `download: true` takes\r\n // the DOM anchor path this function offers a page.\r\n returnBytes: encode.download !== true,\r\n onProgress: request.onProgress,\r\n signal: request.signal,\r\n wasmUrl: this.wasmUrl,\r\n });\r\n }\r\n\r\n /**\r\n * Queued, rather than a renderer per call: the one renderer is what keeps a\r\n * GL context and the decode cache warm between stills, and its draw and the\r\n * encode of it are two steps another call's draw would land between.\r\n */\r\n exportStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const still = this.stillQueue.then(() => this.drawStill(request));\r\n this.stillQueue = still.catch(() => undefined);\r\n return still;\r\n }\r\n\r\n dispose(): void {\r\n this.still?.dispose();\r\n this.still = null;\r\n }\r\n\r\n private async drawStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const renderer = this.renderer(request);\r\n const drawn = await renderer.render(request.project, {\r\n scale: request.scale,\r\n ...frameOption(request.frame),\r\n });\r\n const bytes = await renderer.toBytes({\r\n format: request.format,\r\n quality: request.quality,\r\n });\r\n return { bytes, frame: drawn.frame, totalFrames: drawn.totalFrames };\r\n }\r\n\r\n /**\r\n * One `StillRenderer`, reused across calls and re-viewported when the ask\r\n * changes — `setViewport` is a no-op when the size already matches, and a\r\n * full reset when it does not, which is the same trade the class documents.\r\n */\r\n private renderer(request: ExportStillRequest): StillRenderer {\r\n const { project, registry } = request;\r\n if (project.viewport === undefined || project.fps === undefined) {\r\n throw new Error(\"exportStill takes a project whose fps and viewport are resolved — see Session.\");\r\n }\r\n // A renderer compiles against the registry it was built with.\r\n if (this.still && this.stillRegistry !== registry) this.dispose();\r\n this.still ??= new StillRenderer(this.canvasKit, {\r\n viewport: project.viewport,\r\n fps: project.fps,\r\n manifest: request.manifest,\r\n registry,\r\n });\r\n this.stillRegistry = registry;\r\n this.still.setViewport(project.viewport);\r\n if (request.manifest) this.still.setManifest(request.manifest);\r\n return this.still;\r\n }\r\n}\r\n\r\n/** `ExportStillRequest.frame` in the shape `StillRenderer.render` accepts. */\r\nfunction frameOption(frame: ExportStillRequest[\"frame\"]): { frame?: number | \"first\" | \"last\" } {\r\n if (frame.kind === \"frame\") return { frame: frame.frame };\r\n return { frame: frame.kind };\r\n}\r\n\r\n/**\r\n * An engine backed by the browser: WebGL/Skia surfaces, Web Audio, WebCodecs.\r\n *\r\n * Async because CanvasKit is a wasm module and every seam below needs it. Await\r\n * once, at app start, and share the result — the caches underneath (registered\r\n * fonts, decoded images, shaped paragraphs) are the point of keeping one engine\r\n * rather than one per canvas.\r\n *\r\n * ```ts\r\n * const engine = await createBrowserEngine({ nodes: { pulseRing: PulseRing } });\r\n * const session = engine.open({ document, target: canvasEl });\r\n * session.playback!.play();\r\n * ```\r\n */\r\nexport async function createBrowserEngine(\r\n options: BrowserEngineOptions = {},\r\n): Promise<Engine<WebStorageAdapter>> {\r\n const rawNodes = options.nodes;\r\n // `Array.isArray` doesn't narrow a `readonly T[] | U` union cleanly against\r\n // its `arg is any[]` (mutable) predicate — the `.filter` predicate below\r\n // already guarantees no string survives, so the cast is just recovering\r\n // what the runtime check already proved.\r\n const classNodes = (\r\n Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is NodeConstructor => typeof n !== \"string\")\r\n : rawNodes\r\n ) as NodeTypeMap | readonly NodeConstructor[] | undefined;\r\n const sourceNodes = Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is string => typeof n === \"string\")\r\n : [];\r\n // Cheap — see dynamic-nodes.ts: no WASM import happens here, only on\r\n // `compile`'s first actual call. Attached unconditionally so a later\r\n // `engine.registerNode(source)` works even when `nodes` carried no\r\n // string at boot.\r\n const dynamicNodes = resolveDynamicNodes(options.dynamicNodes, options.compilerWasmUrl);\r\n\r\n const canvasKit = await getCanvasKit(options.wasmUrl);\r\n const exporter = new WebExporter(\r\n canvasKit,\r\n { video: options.video, audio: options.audio },\r\n options.wasmUrl,\r\n );\r\n\r\n const platform: EnginePlatform<WebStorageAdapter> = {\r\n createStorageAdapter({ catalog, viewport, fps }: PlatformDeps): WebStorageAdapter {\r\n return new WebStorageAdapter(canvasKit, catalog, viewport, fps);\r\n },\r\n createRenderContext(storage: WebStorageAdapter): EngineRenderContext {\r\n return new WebRenderContext(canvasKit, storage);\r\n },\r\n createMeasureContext(storage: WebStorageAdapter): MeasureContext2D {\r\n return new WebMeasureContext(storage);\r\n },\r\n createAudioSink(): AudioSink {\r\n return new WebAudioSink();\r\n },\r\n createTicker(fps: number, audioDevice: AudioSink): Ticker {\r\n // Shares the device's own `AudioContext`: two independent timebases\r\n // in one session drift against each other, and sound is the one that\r\n // cannot be nudged back.\r\n return new WebTicker({\r\n context: (audioDevice as WebAudioSink).getContext(),\r\n fps,\r\n });\r\n },\r\n createExporter(): Exporter {\r\n return exporter;\r\n },\r\n dispose(): void {\r\n exporter.dispose();\r\n },\r\n };\r\n\r\n const engine = new Engine<WebStorageAdapter>(platform, { ...options, nodes: classNodes, dynamicNodes });\r\n for (const source of sourceNodes) {\r\n // Sequential — one script's error shouldn't mask another's, and\r\n // registration order should match array order for determinism.\r\n await engine.registerNode(source);\r\n }\r\n return engine;\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motionscript/browser",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browser rendering backend for Motion Script, powered by Skia/CanvasKit.",
|
|
6
6
|
"repository": {
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@motionscript/canvaskit": "^0.1.0-alpha.
|
|
41
|
-
"@motionscript/core": "^0.1.0-alpha.
|
|
40
|
+
"@motionscript/canvaskit": "^0.1.0-alpha.7",
|
|
41
|
+
"@motionscript/core": "^0.1.0-alpha.7"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/three": "0.185.1",
|
|
@@ -55,13 +55,13 @@
|
|
|
55
55
|
"typescript": "^6.0.3",
|
|
56
56
|
"typescript-eslint": "^8.48.0",
|
|
57
57
|
"vitest": "^4.1.5",
|
|
58
|
-
"@motionscript/canvaskit": "0.1.0-alpha.
|
|
59
|
-
"@motionscript/core": "0.1.0-alpha.
|
|
58
|
+
"@motionscript/canvaskit": "0.1.0-alpha.7",
|
|
59
|
+
"@motionscript/core": "0.1.0-alpha.7"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"esbuild-wasm": "^0.28.1",
|
|
63
63
|
"mediabunny": "^1.40.1",
|
|
64
|
-
"@motionscript/skia-render": "^0.1.0-alpha.
|
|
64
|
+
"@motionscript/skia-render": "^0.1.0-alpha.7"
|
|
65
65
|
},
|
|
66
66
|
"motionscriptBrowser": {
|
|
67
67
|
"assets": {
|