@glissade/cli 0.8.1 → 0.9.0-pre.1

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/dist/cli.js CHANGED
@@ -61,6 +61,7 @@ render options:
61
61
  --music <m> auto (default): mix a sibling *.music.timing.json bed, ducked under narration | off
62
62
  --sfx <m> auto (default): mix effect hits from a sibling *.sfx.timing.json | off
63
63
  --chapters <m> vtt: also write WebVTT chapters from cue markers (cues.json is always written when cues exist)
64
+ --strict fail on an unregistered font family or an uncovered glyph (§3.6; default: warn)
64
65
 
65
66
  dev options:
66
67
  --record add a Record button; writes .trace.json sidecars next to the module
@@ -191,6 +192,7 @@ async function main() {
191
192
  ...flags.has("trace") ? { trace: flags.get("trace") } : {},
192
193
  ...flags.has("state") ? { state: flags.get("state") } : {},
193
194
  ...flags.has("force") ? { force: true } : {},
195
+ ...flags.has("strict") ? { strictFonts: true } : {},
194
196
  captions: parseCaptionsModeOrFail(flags.get("captions")),
195
197
  narration: flags.get("narration") === "off" ? "off" : "auto",
196
198
  music: flags.get("music") === "off" ? "off" : "auto",
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Scene, SceneModule, VideoFrameSource } from "@glissade/scene";
2
1
  import { AudioClip, Key, Timeline } from "@glissade/core";
2
+ import { Scene, SceneModule, VideoFrameSource } from "@glissade/scene";
3
3
  import { Image } from "@napi-rs/canvas";
4
4
 
5
5
  //#region src/render.d.ts
@@ -32,6 +32,8 @@ interface RenderOptions {
32
32
  sfx?: 'auto' | 'off';
33
33
  /** also write WebVTT chapters from cue markers ('vtt'); cues.json is always written when cues exist. */
34
34
  chapters?: 'vtt' | 'off';
35
+ /** --strict: font validation throws on an unregistered family / missing glyph (§3.6). Default dev-warn. */
36
+ strictFonts?: boolean;
35
37
  onProgress?: (frame: number, total: number) => void;
36
38
  }
37
39
  declare class SceneModuleError extends Error {
package/dist/machines.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime.js";
2
2
  import { readFileSync } from "node:fs";
3
- import "@glissade/scene";
4
3
  import { compileTimeline, timeline } from "@glissade/core";
4
+ import "@glissade/scene";
5
5
  import { bakeTrace, createMachine } from "@glissade/interact";
6
6
  //#region src/machines.ts
7
7
  /**
package/dist/render.js CHANGED
@@ -3,8 +3,10 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, isAbsolute, join, resolve } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
+ import { readFile } from "node:fs/promises";
6
7
  import { createJiti } from "jiti";
7
- import { evaluate, withDeterminismGuards } from "@glissade/scene";
8
+ import { buildFontRegistry } from "@glissade/core";
9
+ import { evaluate, validateSceneFonts, withDeterminismGuards } from "@glissade/scene";
8
10
  import { SkiaBackend } from "@glissade/backend-skia";
9
11
  //#region src/render.ts
10
12
  /**
@@ -81,22 +83,30 @@ async function render(opts) {
81
83
  await loadYogaLayoutEngine();
82
84
  }
83
85
  const videoSources = [];
84
- for (const [assetId, ref] of Object.entries(doc.assets ?? {})) {
85
- const { resolveAssetPath } = await import("./audioMix.js").then((n) => n.r);
86
- if (ref.kind === "font") {
87
- const { GlobalFonts } = await import("@napi-rs/canvas");
88
- GlobalFonts.registerFromPath(resolveAssetPath(ref.url, opts.modulePath), assetId);
89
- } else if (ref.kind === "image") {
90
- const { loadImage } = await import("@napi-rs/canvas");
91
- backend.setImageAsset(assetId, await loadImage(resolveAssetPath(ref.url, opts.modulePath)));
92
- } else if (ref.kind === "video") {
93
- if (!ffmpegAvailable()) throw new Error(`video asset '${assetId}' needs FFmpeg on PATH for frame extraction (§5.4)`);
94
- const { FfmpegVideoFrameSource } = await import("./videoSource.js").then((n) => n.i);
95
- const source = new FfmpegVideoFrameSource(resolveAssetPath(ref.url, opts.modulePath));
96
- await source.warm(0, source.duration);
97
- backend.setVideoAsset(assetId, source);
98
- videoSources.push(source);
86
+ const { resolveAssetPath: resolveAsset } = await import("./audioMix.js").then((n) => n.r);
87
+ const fontRegistry = buildFontRegistry(doc.assets);
88
+ if (fontRegistry.faces().length > 0) {
89
+ const { GlobalFonts } = await import("@napi-rs/canvas");
90
+ for (const face of fontRegistry.faces()) GlobalFonts.registerFromPath(resolveAsset(face.url, opts.modulePath), face.family);
91
+ }
92
+ await validateSceneFonts(scene, doc, async (url) => {
93
+ try {
94
+ const buf = await readFile(resolveAsset(url, opts.modulePath));
95
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
96
+ } catch {
97
+ return;
99
98
  }
99
+ }, { mode: opts.strictFonts ? "strict" : "dev" });
100
+ for (const [assetId, ref] of Object.entries(doc.assets ?? {})) if (ref.kind === "font") {} else if (ref.kind === "image") {
101
+ const { loadImage } = await import("@napi-rs/canvas");
102
+ backend.setImageAsset(assetId, await loadImage(resolveAsset(ref.url, opts.modulePath)));
103
+ } else if (ref.kind === "video") {
104
+ if (!ffmpegAvailable()) throw new Error(`video asset '${assetId}' needs FFmpeg on PATH for frame extraction (§5.4)`);
105
+ const { FfmpegVideoFrameSource } = await import("./videoSource.js").then((n) => n.i);
106
+ const source = new FfmpegVideoFrameSource(resolveAsset(ref.url, opts.modulePath));
107
+ await source.warm(0, source.duration);
108
+ backend.setVideoAsset(assetId, source);
109
+ videoSources.push(source);
100
110
  }
101
111
  for (let f = firstFrame; f <= lastFrame; f++) {
102
112
  backend.render(withDeterminismGuards("throw", () => evaluate(scene, doc, f / fps)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0-pre.1",
4
4
  "description": "glissade CLI: headless rendering via backend-skia (+ FFmpeg mux).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -20,15 +20,15 @@
20
20
  "@napi-rs/canvas": "^0.1.65",
21
21
  "esbuild": "^0.28.0",
22
22
  "jiti": "^2.4.2",
23
- "@glissade/backend-skia": "0.8.1",
24
- "@glissade/core": "0.8.1",
25
- "@glissade/interact": "0.8.1",
26
- "@glissade/lottie": "0.8.1",
27
- "@glissade/svg": "0.8.1",
28
- "@glissade/narrate": "0.8.1",
29
- "@glissade/player": "0.8.1",
30
- "@glissade/scene": "0.8.1",
31
- "@glissade/sfx": "0.8.1"
23
+ "@glissade/backend-skia": "0.9.0-pre.1",
24
+ "@glissade/core": "0.9.0-pre.1",
25
+ "@glissade/interact": "0.9.0-pre.1",
26
+ "@glissade/lottie": "0.9.0-pre.1",
27
+ "@glissade/svg": "0.9.0-pre.1",
28
+ "@glissade/narrate": "0.9.0-pre.1",
29
+ "@glissade/player": "0.9.0-pre.1",
30
+ "@glissade/scene": "0.9.0-pre.1",
31
+ "@glissade/sfx": "0.9.0-pre.1"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",