@bendyline/squisq-cli 1.2.1 → 2.0.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @bendyline/squisq-cli
2
2
 
3
- Command-line tool and programmatic API for converting Squisq documents between DOCX, PPTX, PDF, XLSX, CSV, HTML, EPUB, Markdown, and container ZIP — and rendering them to MP4 video. Reads Markdown, binary documents (`.docx`/`.pptx`/`.pdf`/`.xlsx`/`.csv`/`.html`), ZIP/`.dbk` containers, folders, or pre-built Doc JSON as input.
3
+ Command-line tool and programmatic API for converting Squisq documents between DOCX, PPTX, PDF, XLSX, CSV, HTML, EPUB, Markdown, and container ZIP — and rendering them to MP4 video or animated GIF. Reads Markdown, binary documents (`.docx`/`.pptx`/`.pdf`/`.xlsx`/`.csv`/`.html`), ZIP/`.dbk` containers, folders, or pre-built Doc JSON as input.
4
4
 
5
5
  Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
6
6
 
@@ -13,7 +13,7 @@ Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
13
13
  npm install -g @bendyline/squisq-cli
14
14
  ```
15
15
 
16
- For `squisq video` (and `convert` to `mp4`) you also need:
16
+ For `squisq video` (and `convert` to `mp4` or `gif`) you also need:
17
17
 
18
18
  - ffmpeg — resolved from the `SQUISQ_FFMPEG` env var, then your `PATH` (`brew install ffmpeg` / `apt install ffmpeg` / `winget install ffmpeg`), then an optionally-installed `ffmpeg-static` package.
19
19
  - A Playwright-managed Chromium for headless frame capture (e.g. `npx playwright install chromium`)
@@ -29,55 +29,62 @@ Convert a document to one or more formats. Input can be Markdown or a binary doc
29
29
  ```bash
30
30
  squisq convert input.md -o report.docx # single file; format inferred from extension
31
31
  squisq convert report.docx -o report.md # binary input → markdown
32
- squisq convert input.md --format pptx # single format to the default output dir
32
+ squisq convert input.md --formats pptx # single format to the default output dir
33
33
  squisq convert input.md --formats docx,pptx,pdf # multiple formats
34
34
  squisq convert project.dbk --output-dir ./out --formats html,docx
35
- squisq convert input.md --theme cinematic --transform magazine --format pptx
35
+ squisq convert input.md --theme cinematic --transform magazine --formats pptx
36
36
  ```
37
37
 
38
- | Option | Description | Default |
39
- | --------------------- | ------------------------------------------------------------------------------------------------------- | ------------- |
40
- | `-o, --output <file>` | **Single** output file; format inferred from its extension (cannot combine with `--formats`/`--format`) | — |
41
- | `-d, --output-dir` | Output directory (multi-format mode) | same as input |
42
- | `-f, --formats` | Comma-separated: `docx`, `pptx`, `pdf`, `html`, `htmlzip`, `epub`, `dbk`, `md`, `xlsx`, `csv`, `mp4` | default set |
43
- | `--format <id>` | Produce a single format (alias for a one-entry `--formats`) | |
44
- | `-t, --theme` | Squisq theme id built-in or a custom theme inlined in the doc's frontmatter | none |
45
- | `--transform` | Transform style applied before export (e.g. `documentary`, `magazine`, `minimal`) | none |
46
- | `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
38
+ | Option | Description | Default |
39
+ | --------------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
40
+ | `-o, --output <file>` | **Single** output file; format inferred from its extension (cannot combine with `--formats`) | — |
41
+ | `-d, --output-dir` | Output directory (multi-format mode) | same as input |
42
+ | `-f, --formats` | Comma-separated: `docx`, `pptx`, `pdf`, `html`, `htmlzip`, `epub`, `dbk`, `md`, `xlsx`, `csv`, `mp4`, `gif` | default set |
43
+ | `-t, --theme` | Squisq theme id built-in or a custom theme inlined in the doc's frontmatter | none |
44
+ | `--transform` | Transform style applied before export (e.g. `documentary`, `magazine`, `minimal`) | none |
45
+ | `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
47
46
 
48
- > **v1.5 breaking flag change:** `-o` is now a **single-file** destination (format inferred from the extension). The old `-o` output-**directory** behavior is now `-d, --output-dir`. A bare `convert <input>` with no `-o`/`--format`/`--formats` writes a default set that deliberately excludes `md`/`xlsx`/`csv`/`mp4`.
47
+ > **v1.5 breaking flag change:** `-o` is now a **single-file** destination (format inferred from the extension). The old `-o` output-**directory** behavior is now `-d, --output-dir`. A bare `convert <input>` with no `-o`/`--formats` writes a default set that deliberately excludes `md`/`xlsx`/`csv`/`mp4`/`gif`.
49
48
 
50
49
  Notes:
51
50
 
52
51
  - `html` produces a single self-contained file with the standalone player inlined (static mode); `htmlzip` produces a `<name>.html.zip` archive with external assets and optional audio.
53
52
  - `epub` embeds images from the input container and, when the doc has narration segments, generates EPUB 3 Media Overlays.
54
- - `xlsx` export is tables-only; `csv` export emits the first table; `mp4` requires the video toolchain (see Install).
53
+ - `xlsx` export is tables-only; `csv` export emits the first table; `mp4` and `gif` require the render toolchain (see Install).
55
54
  - `dbk` re-serializes the input container as a ZIP.
56
55
 
57
56
  ### `squisq video <input> [output]`
58
57
 
59
- Render a document to MP4: Playwright captures frames from a headless player page, native ffmpeg encodes them (H.264 + AAC when the doc has audio).
58
+ Render a document to MP4 or animated GIF. Playwright captures deterministic frames from a headless player page; native ffmpeg encodes H.264 + AAC for MP4 or a generated global palette for GIF. GIF has no audio track.
60
59
 
61
60
  ```bash
62
61
  squisq video input.md output.mp4
62
+ squisq video input.md output.gif
63
+ squisq video input.md --format gif --no-animations
63
64
  squisq video project.dbk --quality high --fps 30
64
65
  squisq video ./my-folder --orientation portrait --captions social
65
66
  squisq video input.md -t documentary --transform magazine --cover-preroll 1.5
66
67
  squisq video doc.json -o out.mp4
67
68
  ```
68
69
 
69
- | Option | Description | Default |
70
- | ---------------------- | -------------------------------------------------------------------- | --------------- |
71
- | `-o, --output` | Output MP4 path (positional `[output]` wins over the flag) | `<input>.mp4` |
72
- | `--fps` | Frames per second (1–120) | 30 |
73
- | `--quality` | `draft`, `normal`, or `high` | normal |
74
- | `--orientation` | `landscape` (1920×1080) or `portrait` (1080×1920) | landscape |
75
- | `--captions` | `off`, `standard`, or `social` | off |
76
- | `-t, --theme` | Squisq theme id to apply | none |
77
- | `--transform` | Transform style to apply before rendering | none |
78
- | `--cover-preroll` | Seconds of cover-slide pre-roll before the story starts | 2 |
79
- | `--width` / `--height` | Dimension overrides in pixels | per orientation |
80
- | `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
70
+ | Option | Description | Default |
71
+ | ---------------------------------- | -------------------------------------------------------------------- | ------------------------- |
72
+ | `-o, --output` | Output `.mp4` or `.gif` path; extension selects the format | `<input>.mp4` |
73
+ | `--format` | `mp4` or `gif` | inferred, otherwise `mp4` |
74
+ | `--fps` | Frames per second (MP4 1–120; GIF 1–100) | MP4 30; GIF 10 |
75
+ | `--quality` | MP4-only quality: `draft`, `normal`, or `high` | normal |
76
+ | `--orientation` | `landscape` or `portrait` | landscape |
77
+ | `--captions` | `off`, `standard`, or `social` | off |
78
+ | `--animations` / `--no-animations` | Enable/disable layer animations and block transitions | MP4 enabled; GIF disabled |
79
+ | `--loop` | GIF repeat count (`0` forever, `-1` no loop) | 0 |
80
+ | `--max-colors` | GIF palette size, 2–256 | 256 |
81
+ | `--dither` | GIF dithering: `bayer`, `sierra2_4a`, or `none` | `sierra2_4a` |
82
+ | `--bayer-scale` | Ordered Bayer strength, 0–5 | 3 |
83
+ | `-t, --theme` | Squisq theme id to apply | none |
84
+ | `--transform` | Transform style to apply before rendering | none |
85
+ | `--cover-preroll` | Seconds of cover-slide pre-roll before the story starts | 2 |
86
+ | `--width` / `--height` | Dimension overrides in pixels | MP4 1080p; GIF 960×540 |
87
+ | `--no-auto-templates` | Disable content-aware template auto-picking for unannotated headings | (auto on) |
81
88
 
82
89
  ### `squisq doctor`
83
90
 
@@ -118,11 +125,11 @@ All commands accept:
118
125
 
119
126
  ## Programmatic API
120
127
 
121
- Import `@bendyline/squisq-cli/api` to use the same pipeline as a library from Node.js — no shell-out required. Like the CLI, `renderDocToMp4` requires ffmpeg and a Playwright-managed Chromium.
128
+ Import `@bendyline/squisq-cli/api` to use the same pipeline as a library from Node.js — no shell-out required. Like the CLI, `renderDocToMp4` and `renderDocToGif` require ffmpeg and a Playwright-managed Chromium.
122
129
 
123
130
  ### `convert()`
124
131
 
125
- A pre-bound wrapper over `@bendyline/squisq-formats`' `convert()` that injects the CLI's format registry (every built-in exporter plus the CLI-only `mp4` format) and a default `resolvePlayerScript` (so HTML/player-embedding exports work out of the box). Both are overridable via `options`. `createCliRegistry()` returns that same registry for direct use.
132
+ A pre-bound wrapper over `@bendyline/squisq-formats`' `convert()` that injects the CLI's format registry (every built-in exporter plus the CLI-only `mp4` and `gif` formats) and a default `resolvePlayerScript` (so HTML/player-embedding exports work out of the box). Both are overridable via `options`. `createCliRegistry()` returns that same registry for direct use.
126
133
 
127
134
  ```ts
128
135
  import { convert, createCliRegistry, ConversionError } from '@bendyline/squisq-cli/api';
@@ -153,6 +160,7 @@ const result = await renderDocToMp4(doc, input.container, {
153
160
  quality: 'high', // 'draft' | 'normal' | 'high' (default 'normal')
154
161
  orientation: 'landscape', // 'landscape' | 'portrait' (default 'landscape')
155
162
  captionStyle: 'social', // 'standard' | 'social' — omit for no captions
163
+ animationsEnabled: false, // static slide changes; media/timing remain active
156
164
  coverPreRoll: 2, // seconds of cover-slide pre-roll (default 0)
157
165
  onProgress: (phase, pct) => console.log(`${phase}: ${pct}%`),
158
166
  });
@@ -162,6 +170,49 @@ console.log(`Rendered ${result.frameCount} frames (${result.duration}s) → ${re
162
170
 
163
171
  The container's audio segments become the MP4's audio track; timed media clips and block videos found in the doc are embedded and mixed at their scheduled positions.
164
172
 
173
+ ### `renderDocToGif`
174
+
175
+ ```ts
176
+ import { renderDocToGif } from '@bendyline/squisq-cli/api';
177
+
178
+ const result = await renderDocToGif(doc, input.container, {
179
+ outputPath: './output.gif',
180
+ fps: 10,
181
+ animationsEnabled: false,
182
+ maxColors: 256,
183
+ loop: 0,
184
+ });
185
+ ```
186
+
187
+ GIF defaults to 960×540 landscape (540×960 portrait), 10 fps, an infinite
188
+ loop, and disabled slide animations/transitions. Embedded video still advances,
189
+ but all audio is omitted and reported through `result.warnings`.
190
+
191
+ ### Native frame encoding
192
+
193
+ For callers that already have PNG frames and do not need Playwright capture,
194
+ the API exposes the native FFmpeg layer directly:
195
+
196
+ ```ts
197
+ import { framesToGifNativeBytes, framesToMp4NativeBytes } from '@bendyline/squisq-cli/api';
198
+
199
+ const mp4 = await framesToMp4NativeBytes('/usr/bin/ffmpeg', pngFrames, audioBytes, {
200
+ fps: 30,
201
+ quality: 'high',
202
+ });
203
+
204
+ const gif = await framesToGifNativeBytes('/usr/bin/ffmpeg', pngFrames, {
205
+ fps: 10,
206
+ width: 960,
207
+ height: 540,
208
+ });
209
+ ```
210
+
211
+ The path variants write to a caller-supplied output; each `Bytes` variant returns
212
+ a `Uint8Array`. MP4 pads short audio with silence so narration cannot truncate
213
+ the timeline. GIF uses a compression-friendly diff palette and changed-rectangle
214
+ application.
215
+
165
216
  ### `extractThumbnails`
166
217
 
167
218
  Extract JPEG thumbnails from the first frame of a rendered video:
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ConversionError,
4
+ MemoryContentContainer,
5
+ convert,
6
+ createCliRegistry,
7
+ extractThumbnails,
8
+ readInput,
9
+ renderDocToGif,
10
+ renderDocToMp4
11
+ } from "./chunk-JEE26SZS.js";
12
+ import {
13
+ GIF_EXPORT_DEFAULTS,
14
+ framesToGifNative,
15
+ framesToGifNativeBytes,
16
+ framesToMp4Native,
17
+ framesToMp4NativeBytes
18
+ } from "./chunk-VM3ZFP3J.js";
19
+ export {
20
+ ConversionError,
21
+ GIF_EXPORT_DEFAULTS,
22
+ MemoryContentContainer,
23
+ convert,
24
+ createCliRegistry,
25
+ extractThumbnails,
26
+ framesToGifNative,
27
+ framesToGifNativeBytes,
28
+ framesToMp4Native,
29
+ framesToMp4NativeBytes,
30
+ readInput,
31
+ renderDocToGif,
32
+ renderDocToMp4
33
+ };
34
+ //# sourceMappingURL=api-6KN3F6IQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/api.d.ts CHANGED
@@ -1,12 +1,51 @@
1
1
  import { Doc } from '@bendyline/squisq/schemas';
2
2
  import { ContentContainer } from '@bendyline/squisq/storage';
3
3
  export { MemoryContentContainer } from '@bendyline/squisq/storage';
4
- import { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
5
- export { VideoOrientation, VideoQuality } from '@bendyline/squisq-video';
6
- import { FormatId, FormatRegistry, ConvertSource, ConvertOptions, ConversionResult } from '@bendyline/squisq-formats';
4
+ import { VideoQuality, VideoOrientation, GifDither, VideoExportOptions } from '@bendyline/squisq-video';
5
+ export { GifDither, VideoOrientation, VideoQuality } from '@bendyline/squisq-video';
6
+ import { FormatRegistry, FormatId, ConvertOptions, ConvertSource, ConversionResult } from '@bendyline/squisq-formats';
7
7
  export { ConversionError, ConversionErrorCode, ConversionErrorOptions, ConversionResult, ConvertOptions, ConvertSource, FormatDefinition, FormatId, FormatRegistry, NormalizedInput } from '@bendyline/squisq-formats';
8
8
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
9
9
 
10
+ /**
11
+ * CLI format registry.
12
+ *
13
+ * The CLI extends the formats package's default registry with `mp4` and `gif`
14
+ * formats so `convert()` can produce rendered media like DOCX/PDF/HTML. These
15
+ * exports are Node-only (Playwright + FFmpeg), which is why they live here
16
+ * rather than in the browser-pure formats package.
17
+ */
18
+
19
+ interface Mp4FormatOptions {
20
+ fps?: number;
21
+ quality?: VideoQuality;
22
+ orientation?: VideoOrientation;
23
+ width?: number;
24
+ height?: number;
25
+ captionStyle?: 'standard' | 'social';
26
+ coverPreRoll?: number;
27
+ animationsEnabled?: boolean;
28
+ }
29
+ /** Per-export options for `convert(..., 'gif')`. */
30
+ interface GifFormatOptions {
31
+ fps?: number;
32
+ orientation?: VideoOrientation;
33
+ width?: number;
34
+ height?: number;
35
+ captionStyle?: 'standard' | 'social';
36
+ coverPreRoll?: number;
37
+ animationsEnabled?: boolean;
38
+ loop?: number;
39
+ maxColors?: number;
40
+ dither?: GifDither;
41
+ bayerScale?: number;
42
+ }
43
+ /**
44
+ * Build the CLI's format registry: every built-in format from
45
+ * `@bendyline/squisq-formats` plus the CLI-only rendered-media exporters.
46
+ */
47
+ declare function createCliRegistry(): FormatRegistry;
48
+
10
49
  /**
11
50
  * readInput
12
51
  *
@@ -37,33 +76,95 @@ interface ReadInputResult {
37
76
  /** Detected source format id (registry id, or `md`/`json`/`dbk`/`folder`). */
38
77
  sourceFormat: FormatId;
39
78
  }
79
+ interface ReadInputOptions {
80
+ /**
81
+ * Infer a Squisq theme from an OOXML source's theme part (PPTX today).
82
+ * Default true — the importer carries it as frontmatter.
83
+ */
84
+ inferTheme?: boolean;
85
+ /** Derive custom layout templates from PPTX slide layouts. Default true. */
86
+ inferLayouts?: boolean;
87
+ }
40
88
  /**
41
89
  * Read input from a file path (markdown, JSON Doc, ZIP/DBK container, folder,
42
90
  * or an importable binary format) and resolve it to a populated
43
91
  * {@link ReadInputResult}.
44
92
  */
45
- declare function readInput(inputPath: string): Promise<ReadInputResult>;
93
+ declare function readInput(inputPath: string, options?: ReadInputOptions): Promise<ReadInputResult>;
46
94
 
47
95
  /**
48
- * CLI format registry.
96
+ * Native FFmpeg Encoder
97
+ *
98
+ * Encodes PNG frames to MP4 or animated GIF using a locally installed ffmpeg
99
+ * binary. Writes frames to a temporary directory, invokes ffmpeg as a child
100
+ * process, and reads the resulting media file.
49
101
  *
50
- * The CLI extends the formats package's default registry with an `mp4` format
51
- * so `convert()` can produce video the same way it produces DOCX/PDF/HTML. MP4
52
- * export is Node-only (it needs Playwright + FFmpeg), which is why it lives
53
- * here in the CLI rather than in the browser-pure formats package.
102
+ * This is the Node fast path used after the CLI detects native FFmpeg. Browser
103
+ * callers use the WebCodecs/ffmpeg.wasm paths in the video packages instead.
54
104
  */
55
105
 
106
+ /** Options for native animated-GIF encoding. */
107
+ interface GifExportOptions {
108
+ /** Frames per second (default: 10; GIF timing is centisecond-based). */
109
+ fps?: number;
110
+ /** Output width in pixels (default: 960 landscape / 540 portrait). */
111
+ width?: number;
112
+ /** Output height in pixels (default: 540 landscape / 960 portrait). */
113
+ height?: number;
114
+ /** Viewport orientation used for default dimensions. */
115
+ orientation?: VideoOrientation;
116
+ /** Number of repeats; 0 loops forever and -1 disables looping. */
117
+ loop?: number;
118
+ /** Palette size, between 2 and 256 (default: 256). */
119
+ maxColors?: number;
120
+ /** Palette dithering algorithm (default: sierra2_4a). */
121
+ dither?: GifDither;
122
+ /** Bayer strength when dither is bayer (0-5, default: 3). */
123
+ bayerScale?: number;
124
+ /** Encoding progress callback. */
125
+ onProgress?: (percent: number, phase: string) => void;
126
+ }
127
+ /** Compression-friendly defaults for animated GIF output. */
128
+ declare const GIF_EXPORT_DEFAULTS: {
129
+ readonly fps: 10;
130
+ readonly width: 960;
131
+ readonly height: 540;
132
+ readonly loop: 0;
133
+ readonly maxColors: 256;
134
+ readonly dither: GifDither;
135
+ };
56
136
  /**
57
- * Build the CLI's format registry: every built-in format from
58
- * `@bendyline/squisq-formats` plus the CLI-only `mp4` exporter.
137
+ * Encode frame PNGs to MP4 using native ffmpeg.
138
+ *
139
+ * @param ffmpegPath - Absolute path to the ffmpeg binary
140
+ * @param frames - Array of PNG image bytes (one per frame)
141
+ * @param audio - Optional audio file bytes to mux
142
+ * @param outputPath - Where to write the final MP4
143
+ * @param options - Encoding options (fps, quality, dimensions, progress)
59
144
  */
60
- declare function createCliRegistry(): FormatRegistry;
145
+ declare function framesToMp4Native(ffmpegPath: string, frames: Uint8Array[], audio: Uint8Array | null, outputPath: string, options?: VideoExportOptions): Promise<void>;
146
+ /**
147
+ * Encode frames using native ffmpeg and return the MP4 bytes (instead of writing to disk).
148
+ * Useful when the caller needs the bytes in memory.
149
+ */
150
+ declare function framesToMp4NativeBytes(ffmpegPath: string, frames: Uint8Array[], audio: Uint8Array | null, options?: VideoExportOptions): Promise<Uint8Array>;
151
+ /**
152
+ * Encode PNG frames to an animated GIF using native FFmpeg.
153
+ *
154
+ * The shared palette graph uses changed-frame statistics and rectangle-scoped
155
+ * palette application so static slide regions remain stable and compress well.
156
+ * GIF has no audio track; callers should surface that limitation before calling.
157
+ */
158
+ declare function framesToGifNative(ffmpegPath: string, frames: Uint8Array[], outputPath: string, options?: GifExportOptions): Promise<void>;
159
+ /** Encode frames with native FFmpeg and return animated-GIF bytes. */
160
+ declare function framesToGifNativeBytes(ffmpegPath: string, frames: Uint8Array[], options?: GifExportOptions): Promise<Uint8Array>;
61
161
 
62
162
  /**
63
- * Programmatic Video API
163
+ * Programmatic Rendered-Media API
64
164
  *
65
- * Provides a library-style entry point for rendering Squisq documents to MP4
66
- * from Node.js callers (e.g., Qualla's pipeline). This avoids the need to shell
165
+ * Provides library-style entry points for rendering Squisq documents to MP4
166
+ * or animated GIF from Node.js callers (e.g., Qualla's pipeline). This avoids
167
+ * the need to shell
67
168
  * out to the `squisq video` CLI and gives callers full control over the Doc,
68
169
  * MemoryContentContainer, and encoding options.
69
170
  *
@@ -78,26 +179,39 @@ declare function createCliRegistry(): FormatRegistry;
78
179
  * quality: 'normal',
79
180
  * orientation: 'landscape',
80
181
  * });
182
+ *
183
+ * await renderDocToGif(doc, container, {
184
+ * outputPath: '/tmp/output.gif',
185
+ * animationsEnabled: false,
186
+ * });
81
187
  */
82
188
 
189
+ /** Convert options with the CLI-only rendered-media adapters strongly typed. */
190
+ type CliConvertOptions = Omit<ConvertOptions, 'formatOptions'> & {
191
+ formatOptions?: ConvertOptions['formatOptions'] & {
192
+ mp4?: Mp4FormatOptions;
193
+ gif?: GifFormatOptions;
194
+ };
195
+ };
196
+
83
197
  /**
84
198
  * Convert a document to a target format using the CLI's format registry.
85
199
  *
86
200
  * This is a thin, pre-bound wrapper over `convert()` from
87
201
  * `@bendyline/squisq-formats`: it injects the CLI registry (which adds the
88
- * `mp4` format on top of every built-in exporter) and a default
202
+ * `mp4` and `gif` formats on top of every built-in exporter) and a default
89
203
  * `resolvePlayerScript` that lazily loads the standalone player IIFE bundle
90
204
  * (required for HTML/EPUB-style exports). Callers may override either via
91
205
  * `options`.
92
206
  *
93
207
  * @param source - A bytes / markdown / doc {@link ConvertSource}.
94
- * @param to - Target format id (`docx`, `pdf`, `pptx`, `html`, `mp4`, …).
208
+ * @param to - Target format id (`docx`, `pdf`, `pptx`, `html`, `mp4`, `gif`, …).
95
209
  * @param options - Conversion options; `registry` and `resolvePlayerScript`
96
210
  * default to the CLI's values but can be overridden.
97
211
  * @returns The encoded bytes plus mime type, suggested filename, and warnings.
98
212
  * @throws {@link ConversionError} on any failure, with a stable `code`.
99
213
  */
100
- declare function convert(source: ConvertSource, to: FormatId, options?: ConvertOptions): Promise<ConversionResult>;
214
+ declare function convert(source: ConvertSource, to: FormatId, options?: CliConvertOptions): Promise<ConversionResult>;
101
215
  /** Options for renderDocToMp4. */
102
216
  interface RenderDocToMp4Options {
103
217
  /** Output file path for the MP4. */
@@ -114,6 +228,8 @@ interface RenderDocToMp4Options {
114
228
  height?: number;
115
229
  /** Caption style to bake into the video (default: none). */
116
230
  captionStyle?: 'standard' | 'social';
231
+ /** Render layer animations and block transitions (default: true). */
232
+ animationsEnabled?: boolean;
117
233
  /**
118
234
  * Seconds of cover-slide pre-roll before the story starts (default: 0).
119
235
  *
@@ -127,6 +243,35 @@ interface RenderDocToMp4Options {
127
243
  */
128
244
  onProgress?: (phase: string, percent: number) => void;
129
245
  }
246
+ /** Options for rendering a document to an animated GIF. */
247
+ interface RenderDocToGifOptions {
248
+ /** Output file path for the GIF. */
249
+ outputPath: string;
250
+ /** Frames per second (default: 10). */
251
+ fps?: number;
252
+ /** Viewport orientation (default: landscape). */
253
+ orientation?: VideoOrientation;
254
+ /** Override output width (default: 960 landscape / 540 portrait). */
255
+ width?: number;
256
+ /** Override output height (default: 540 landscape / 960 portrait). */
257
+ height?: number;
258
+ /** Caption style to bake into the GIF (default: none). */
259
+ captionStyle?: 'standard' | 'social';
260
+ /** Seconds of cover-slide pre-roll (default: 0). */
261
+ coverPreRoll?: number;
262
+ /** Render layer animations and block transitions (default: false). */
263
+ animationsEnabled?: boolean;
264
+ /** Number of repeats; 0 loops forever and -1 disables looping. */
265
+ loop?: number;
266
+ /** Palette size, from 2 through 256 (default: 256). */
267
+ maxColors?: number;
268
+ /** Palette dithering algorithm (default: sierra2_4a). */
269
+ dither?: GifDither;
270
+ /** Bayer strength when dither is bayer (0-5, default: 3). */
271
+ bayerScale?: number;
272
+ /** Progress callback. */
273
+ onProgress?: (phase: string, percent: number) => void;
274
+ }
130
275
  /** Result returned by renderDocToMp4. */
131
276
  interface RenderDocToMp4Result {
132
277
  /** Duration of the rendered video in seconds (including pre-roll). */
@@ -136,24 +281,15 @@ interface RenderDocToMp4Result {
136
281
  /** Output file path. */
137
282
  outputPath: string;
138
283
  }
139
- /**
140
- * Render a Doc + media container to an MP4 video file.
141
- *
142
- * The container should contain audio and image files referenced by the Doc's
143
- * audio.segments[].src and block image paths. Files are embedded as base64
144
- * data URIs in a self-contained render HTML page.
145
- *
146
- * Requires:
147
- * - Playwright (chromium) — for headless frame capture
148
- * - FFmpeg — for video encoding (resolved from SQUISQ_FFMPEG, PATH, or an
149
- * optionally installed `ffmpeg-static` package — see detectFfmpeg)
150
- *
151
- * @param doc - The Doc structure to render
152
- * @param container - MemoryContentContainer with audio/image files
153
- * @param options - Rendering and encoding options
154
- * @returns Result with duration and frame count
155
- */
284
+ /** Result returned by renderDocToGif. */
285
+ interface RenderDocToGifResult extends RenderDocToMp4Result {
286
+ /** Non-fatal fidelity warnings, including GIF's lack of audio. */
287
+ warnings: string[];
288
+ }
289
+ /** Render a Doc + media container to an MP4 video file. */
156
290
  declare function renderDocToMp4(doc: Doc, container: ContentContainer, options: RenderDocToMp4Options): Promise<RenderDocToMp4Result>;
291
+ /** Render a Doc + media container to a silent animated GIF. */
292
+ declare function renderDocToGif(doc: Doc, container: ContentContainer, options: RenderDocToGifOptions): Promise<RenderDocToGifResult>;
157
293
  /** A thumbnail size specification. */
158
294
  interface ThumbnailSpec {
159
295
  /** Label for the thumbnail (used in filename: `{slug}-{width}x{height}.jpg`). */
@@ -184,4 +320,4 @@ interface ExtractThumbnailsOptions {
184
320
  */
185
321
  declare function extractThumbnails(options: ExtractThumbnailsOptions): Promise<void>;
186
322
 
187
- export { type ExtractThumbnailsOptions, type ReadInputResult, type RenderDocToMp4Options, type RenderDocToMp4Result, type ThumbnailSpec, convert, createCliRegistry, extractThumbnails, readInput, renderDocToMp4 };
323
+ export { type CliConvertOptions, type ExtractThumbnailsOptions, GIF_EXPORT_DEFAULTS, type GifExportOptions, type GifFormatOptions, type Mp4FormatOptions, type ReadInputResult, type RenderDocToGifOptions, type RenderDocToGifResult, type RenderDocToMp4Options, type RenderDocToMp4Result, type ThumbnailSpec, convert, createCliRegistry, extractThumbnails, framesToGifNative, framesToGifNativeBytes, framesToMp4Native, framesToMp4NativeBytes, readInput, renderDocToGif, renderDocToMp4 };