@glissade/cli 0.7.0-pre.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -51,7 +51,7 @@ render options:
51
51
  --out <path> output directory for a PNG sequence, or .mp4/.webm (needs ffmpeg). default: ./out
52
52
  --fps <n> frames per second (default: timeline fps, else 60)
53
53
  --range <a..b> integer FRAME indices to render, inclusive (default: whole timeline)
54
- --frame <n> render a single frame index (a still, through the same path)
54
+ --frame <n> render a single frame; --out foo.png writes that one file, --out <dir> writes a PNG into it
55
55
  --format png-seq force a PNG sequence even when --out looks like a video
56
56
  --trace <file> replay an InputTrace and bake it (machine scenes, §A.6)
57
57
  --state <name> render one machine state's timeline linearly
package/dist/render.js CHANGED
@@ -69,8 +69,9 @@ async function render(opts) {
69
69
  }
70
70
  const total = lastFrame - firstFrame + 1;
71
71
  const isVideo = opts.format !== "png-seq" && /\.(mp4|webm)$/i.test(opts.out);
72
+ const singleFile = !isVideo && total === 1 && /\.png$/i.test(opts.out);
72
73
  if (isVideo && !ffmpegAvailable()) throw new Error(`'${opts.out}' needs FFmpeg on PATH and none was found. Render a PNG sequence instead (--out <directory>) or install ffmpeg.`);
73
- const framesDir = isVideo ? mkdtempSync(join(tmpdir(), "glissade-frames-")) : resolve(opts.out);
74
+ const framesDir = isVideo ? mkdtempSync(join(tmpdir(), "glissade-frames-")) : singleFile ? dirname(resolve(opts.out)) : resolve(opts.out);
74
75
  mkdirSync(framesDir, { recursive: true });
75
76
  const backend = new SkiaBackend(scene.size.w, scene.size.h);
76
77
  scene.setTextMeasurer(backend);
@@ -98,7 +99,7 @@ async function render(opts) {
98
99
  }
99
100
  for (let f = firstFrame; f <= lastFrame; f++) {
100
101
  backend.render(withDeterminismGuards("throw", () => evaluate(scene, doc, f / fps)));
101
- writeFileSync(join(framesDir, `frame-${String(f).padStart(5, "0")}.png`), backend.encodePng());
102
+ writeFileSync(singleFile ? resolve(opts.out) : join(framesDir, `frame-${String(f).padStart(5, "0")}.png`), backend.encodePng());
102
103
  opts.onProgress?.(f - firstFrame + 1, total);
103
104
  }
104
105
  backend.dispose();
@@ -114,6 +115,10 @@ async function render(opts) {
114
115
  process.stderr.write(`captions: ${srt}, ${vtt}\n`);
115
116
  };
116
117
  if (!isVideo) {
118
+ if (singleFile) return {
119
+ frames: 1,
120
+ out: resolve(opts.out)
121
+ };
117
122
  if (compiled.audio.length > 0) process.stderr.write("note: PNG-sequence output ignores timeline audio; render to .mp4/.webm to mix it\n");
118
123
  emitSidecars(framesDir);
119
124
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/cli",
3
- "version": "0.7.0-pre.0",
3
+ "version": "0.7.0",
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.7.0-pre.0",
24
- "@glissade/core": "0.7.0-pre.0",
25
- "@glissade/interact": "0.7.0-pre.0",
26
- "@glissade/lottie": "0.7.0-pre.0",
27
- "@glissade/svg": "0.7.0-pre.0",
28
- "@glissade/narrate": "0.7.0-pre.0",
29
- "@glissade/player": "0.7.0-pre.0",
30
- "@glissade/scene": "0.7.0-pre.0",
31
- "@glissade/sfx": "0.7.0-pre.0"
23
+ "@glissade/backend-skia": "0.7.0",
24
+ "@glissade/core": "0.7.0",
25
+ "@glissade/interact": "0.7.0",
26
+ "@glissade/lottie": "0.7.0",
27
+ "@glissade/svg": "0.7.0",
28
+ "@glissade/narrate": "0.7.0",
29
+ "@glissade/player": "0.7.0",
30
+ "@glissade/scene": "0.7.0",
31
+ "@glissade/sfx": "0.7.0"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",