@bendyline/squisq-cli 1.0.0 → 1.0.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/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @bendyline/squisq-cli
2
+
3
+ Command-line tool for converting Squisq documents to multiple formats and rendering them to video. Reads Markdown files, ZIP/DBK containers, or folders as input.
4
+
5
+ Part of the [Squisq](https://github.com/bendyline/squisq) monorepo.
6
+
7
+ [![npm](https://img.shields.io/npm/v/@bendyline/squisq-cli)](https://www.npmjs.com/package/@bendyline/squisq-cli)
8
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bendyline/squisq/blob/main/LICENSE)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g @bendyline/squisq-cli
14
+ ```
15
+
16
+ ## Commands
17
+
18
+ ### `squisq convert`
19
+
20
+ Convert a Squisq document to DOCX, PPTX, PDF, HTML, or DBK (container ZIP):
21
+
22
+ ```bash
23
+ squisq convert input.md --formats docx,pptx,pdf
24
+ squisq convert project.dbk --output-dir ./out --formats html,docx
25
+ squisq convert ./my-folder --formats pdf --theme documentary
26
+ squisq convert input.md --theme cinematic --transform magazine --formats pptx
27
+ ```
28
+
29
+ | Option | Description | Default |
30
+ | -------------- | ------------------------------------------------ | ----------- |
31
+ | `--output-dir` | Output directory | current dir |
32
+ | `--formats` | Comma-separated list: docx, pptx, pdf, html, dbk | all |
33
+ | `--theme` | Squisq theme ID (e.g., documentary, cinematic) | none |
34
+ | `--transform` | Transform style (e.g., documentary, magazine) | none |
35
+
36
+ ### `squisq video`
37
+
38
+ Render a Squisq document to MP4 video using Playwright for frame capture and native ffmpeg for encoding:
39
+
40
+ ```bash
41
+ squisq video input.md output.mp4
42
+ squisq video project.dbk --quality high --fps 30
43
+ squisq video ./my-folder --orientation portrait --captions social
44
+ ```
45
+
46
+ | Option | Description | Default |
47
+ | --------------- | ------------------------- | --------- |
48
+ | `--fps` | Frames per second (1–120) | 30 |
49
+ | `--quality` | draft, normal, or high | normal |
50
+ | `--orientation` | landscape or portrait | landscape |
51
+ | `--captions` | off, standard, or social | off |
52
+ | `--width` | Override width in pixels | auto |
53
+ | `--height` | Override height in pixels | auto |
54
+
55
+ **Requires:** [ffmpeg](https://ffmpeg.org/) installed and available on your PATH.
56
+
57
+ ## Input Formats
58
+
59
+ The CLI accepts three types of input:
60
+
61
+ | Input | Description |
62
+ | ------------- | --------------------------------------------------------- |
63
+ | `.md` file | Plain Markdown file |
64
+ | `.zip`/`.dbk` | Container archive with document + embedded media |
65
+ | Folder | Directory with a Markdown file and associated media files |
66
+
67
+ ## Related Packages
68
+
69
+ | Package | Description |
70
+ | ------------------------------------------------------------------------------------ | ----------------------------------------------- |
71
+ | [@bendyline/squisq](https://www.npmjs.com/package/@bendyline/squisq) | Headless core — schemas, templates, markdown |
72
+ | [@bendyline/squisq-formats](https://www.npmjs.com/package/@bendyline/squisq-formats) | DOCX, PDF, HTML import/export (used by convert) |
73
+ | [@bendyline/squisq-video](https://www.npmjs.com/package/@bendyline/squisq-video) | Headless video rendering (used by video) |
74
+ | [@bendyline/squisq-react](https://www.npmjs.com/package/@bendyline/squisq-react) | React components for rendering docs |
75
+
76
+ ## License
77
+
78
+ [MIT](https://github.com/bendyline/squisq/blob/main/LICENSE)
@@ -0,0 +1,7 @@
1
+ /**
2
+ * convert command integration tests
3
+ *
4
+ * Exercises the full convert pipeline: input → all formats → output validation.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=convert.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/convert.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * convert command integration tests
3
+ *
4
+ * Exercises the full convert pipeline: input → all formats → output validation.
5
+ */
6
+ import { describe, it, before, after, beforeEach, afterEach } from 'mocha';
7
+ import { expect } from 'chai';
8
+ import { execFile } from 'node:child_process';
9
+ import { promisify } from 'node:util';
10
+ import { readFile, mkdir, rm, stat, writeFile as fsWriteFile } from 'node:fs/promises';
11
+ import { join } from 'node:path';
12
+ import { tmpdir } from 'node:os';
13
+ import { containerToZip } from '@bendyline/squisq-formats/container';
14
+ import { MemoryContentContainer } from '@bendyline/squisq/storage';
15
+ import { zipToContainer } from '@bendyline/squisq-formats/container';
16
+ const exec = promisify(execFile);
17
+ const CLI_PATH = join(import.meta.dirname, '..', '..', 'dist', 'index.js');
18
+ const FIXTURE_MD = join(import.meta.dirname, 'fixtures', 'test.md');
19
+ /**
20
+ * Run the CLI with the given args and return stdout/stderr.
21
+ */
22
+ async function runCli(...args) {
23
+ return exec('node', [CLI_PATH, ...args], { timeout: 30000 });
24
+ }
25
+ describe('convert command', () => {
26
+ let tempDir;
27
+ beforeEach(async () => {
28
+ tempDir = join(tmpdir(), `squisq-convert-${Date.now()}-${Math.random().toString(36).slice(2)}`);
29
+ await mkdir(tempDir, { recursive: true });
30
+ });
31
+ afterEach(async () => {
32
+ await rm(tempDir, { recursive: true, force: true });
33
+ });
34
+ it('converts a .md file to all formats', async () => {
35
+ const { stderr } = await runCli('convert', FIXTURE_MD, '-o', tempDir);
36
+ expect(stderr).to.include('Done.');
37
+ // Verify all four output files exist and are non-empty
38
+ for (const ext of ['docx', 'pdf', 'html', 'dbk']) {
39
+ const outPath = join(tempDir, `test.${ext}`);
40
+ const info = await stat(outPath);
41
+ expect(info.size).to.be.greaterThan(0);
42
+ }
43
+ });
44
+ it('respects --formats to produce only selected formats', async () => {
45
+ await runCli('convert', FIXTURE_MD, '-o', tempDir, '-f', 'docx,pdf');
46
+ // These should exist
47
+ const docxStat = await stat(join(tempDir, 'test.docx'));
48
+ expect(docxStat.size).to.be.greaterThan(0);
49
+ const pdfStat = await stat(join(tempDir, 'test.pdf'));
50
+ expect(pdfStat.size).to.be.greaterThan(0);
51
+ // These should NOT exist
52
+ for (const ext of ['html', 'dbk']) {
53
+ try {
54
+ await stat(join(tempDir, `test.${ext}`));
55
+ expect.fail(`test.${ext} should not exist`);
56
+ }
57
+ catch (err) {
58
+ expect(err.code).to.equal('ENOENT');
59
+ }
60
+ }
61
+ });
62
+ it('converts a .dbk container to all formats', async () => {
63
+ // Create a minimal .dbk container
64
+ const container = new MemoryContentContainer();
65
+ await container.writeDocument('# Container Doc\n\nSome content here.');
66
+ const blob = await containerToZip(container);
67
+ const buf = Buffer.from(await blob.arrayBuffer());
68
+ const dbkPath = join(tempDir, 'input.dbk');
69
+ await fsWriteFile(dbkPath, buf);
70
+ // Convert to a separate output dir to avoid overwriting the input .dbk
71
+ const outDir = join(tempDir, 'out');
72
+ const { stderr } = await runCli('convert', dbkPath, '-o', outDir);
73
+ expect(stderr).to.include('Done.');
74
+ for (const ext of ['docx', 'pdf', 'html', 'dbk']) {
75
+ const info = await stat(join(outDir, `input.${ext}`));
76
+ expect(info.size).to.be.greaterThan(0);
77
+ }
78
+ });
79
+ it('exits with error for nonexistent input', async () => {
80
+ try {
81
+ await runCli('convert', '/nonexistent/file.md', '-o', tempDir);
82
+ expect.fail('Expected a non-zero exit');
83
+ }
84
+ catch (err) {
85
+ // execFile rejects on non-zero exit
86
+ expect(err.stderr).to.include('Error');
87
+ }
88
+ });
89
+ describe('output validation', () => {
90
+ let validationDir;
91
+ before(async () => {
92
+ validationDir = join(tmpdir(), `squisq-validate-${Date.now()}-${Math.random().toString(36).slice(2)}`);
93
+ await mkdir(validationDir, { recursive: true });
94
+ await runCli('convert', FIXTURE_MD, '-o', validationDir);
95
+ });
96
+ after(async () => {
97
+ await rm(validationDir, { recursive: true, force: true });
98
+ });
99
+ it('DOCX is a valid ZIP with WordprocessingML content', async () => {
100
+ const data = await readFile(join(validationDir, 'test.docx'));
101
+ // ZIP files start with PK (0x50 0x4B)
102
+ expect(data[0]).to.equal(0x50);
103
+ expect(data[1]).to.equal(0x4b);
104
+ // Should contain Content_Types and document.xml inside
105
+ const asString = data.toString('latin1');
106
+ expect(asString).to.include('[Content_Types].xml');
107
+ expect(asString).to.include('word/document.xml');
108
+ });
109
+ it('PDF starts with %PDF- magic bytes', async () => {
110
+ const data = await readFile(join(validationDir, 'test.pdf'));
111
+ const header = data.subarray(0, 5).toString('ascii');
112
+ expect(header).to.equal('%PDF-');
113
+ });
114
+ it('HTML contains SquisqPlayer and root element', async () => {
115
+ const html = await readFile(join(validationDir, 'test.html'), 'utf-8');
116
+ expect(html).to.include('SquisqPlayer');
117
+ expect(html).to.include('squisq-root');
118
+ });
119
+ it('DBK container round-trips through zipToContainer', async () => {
120
+ const data = await readFile(join(validationDir, 'test.dbk'));
121
+ const container = await zipToContainer(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
122
+ const md = await container.readDocument();
123
+ expect(md).to.be.a('string').that.includes('# Test Document');
124
+ });
125
+ });
126
+ });
127
+ //# sourceMappingURL=convert.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.test.js","sourceRoot":"","sources":["../../src/__tests__/convert.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * readInput unit tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=readInput.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readInput.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/readInput.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * readInput unit tests
3
+ */
4
+ import { describe, it, beforeEach, afterEach } from 'mocha';
5
+ import { expect } from 'chai';
6
+ import { readInput } from '../util/readInput.js';
7
+ import { writeFile, mkdir, rm } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+ import { tmpdir } from 'node:os';
10
+ import { containerToZip } from '@bendyline/squisq-formats/container';
11
+ import { MemoryContentContainer } from '@bendyline/squisq/storage';
12
+ const FIXTURE_MD = join(import.meta.dirname, 'fixtures', 'test.md');
13
+ describe('readInput', () => {
14
+ let tempDir;
15
+ beforeEach(async () => {
16
+ tempDir = join(tmpdir(), `squisq-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
17
+ await mkdir(tempDir, { recursive: true });
18
+ });
19
+ afterEach(async () => {
20
+ await rm(tempDir, { recursive: true, force: true });
21
+ });
22
+ it('reads a .md file into a container and MarkdownDocument', async () => {
23
+ const result = await readInput(FIXTURE_MD);
24
+ expect(result.markdownDoc).to.have.property('type', 'document');
25
+ expect(result.markdownDoc.children).to.be.an('array');
26
+ expect(result.markdownDoc.children.length).to.be.greaterThan(0);
27
+ const docContent = await result.container.readDocument();
28
+ expect(docContent).to.be.a('string').that.includes('# Test Document');
29
+ });
30
+ it('reads a .zip container', async () => {
31
+ // Build a minimal container ZIP
32
+ const container = new MemoryContentContainer();
33
+ await container.writeDocument('# From ZIP\n\nHello from a ZIP container.');
34
+ const blob = await containerToZip(container);
35
+ const buf = Buffer.from(await blob.arrayBuffer());
36
+ const zipPath = join(tempDir, 'test.zip');
37
+ await writeFile(zipPath, buf);
38
+ const result = await readInput(zipPath);
39
+ expect(result.markdownDoc.type).to.equal('document');
40
+ const doc = await result.container.readDocument();
41
+ expect(doc).to.include('# From ZIP');
42
+ });
43
+ it('reads a .dbk container (same as .zip)', async () => {
44
+ const container = new MemoryContentContainer();
45
+ await container.writeDocument('# From DBK\n\nHello from a DBK container.');
46
+ const blob = await containerToZip(container);
47
+ const buf = Buffer.from(await blob.arrayBuffer());
48
+ const dbkPath = join(tempDir, 'test.dbk');
49
+ await writeFile(dbkPath, buf);
50
+ const result = await readInput(dbkPath);
51
+ expect(result.markdownDoc.type).to.equal('document');
52
+ const doc = await result.container.readDocument();
53
+ expect(doc).to.include('# From DBK');
54
+ });
55
+ it('reads a folder with markdown and media', async () => {
56
+ const folderPath = join(tempDir, 'mydoc');
57
+ await mkdir(folderPath, { recursive: true });
58
+ await writeFile(join(folderPath, 'index.md'), '# Folder Doc\n\nContent here.');
59
+ await writeFile(join(folderPath, 'image.txt'), 'placeholder');
60
+ const result = await readInput(folderPath);
61
+ expect(result.markdownDoc.type).to.equal('document');
62
+ const doc = await result.container.readDocument();
63
+ expect(doc).to.include('# Folder Doc');
64
+ // Media files are also in the container
65
+ const files = await result.container.listFiles();
66
+ const paths = files.map((f) => f.path);
67
+ expect(paths).to.include('image.txt');
68
+ });
69
+ it('throws on folder with no markdown file', async () => {
70
+ const folderPath = join(tempDir, 'empty');
71
+ await mkdir(folderPath, { recursive: true });
72
+ await writeFile(join(folderPath, 'image.png'), Buffer.from([0x89, 0x50]));
73
+ try {
74
+ await readInput(folderPath);
75
+ expect.fail('Expected an error');
76
+ }
77
+ catch (err) {
78
+ expect(err).to.be.instanceOf(Error);
79
+ expect(err.message).to.include('No markdown document found');
80
+ }
81
+ });
82
+ });
83
+ //# sourceMappingURL=readInput.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readInput.test.js","sourceRoot":"","sources":["../../src/__tests__/readInput.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * convert command
3
+ *
4
+ * Reads a markdown file, ZIP/DBK container, or folder and exports to
5
+ * all supported formats: DOCX, PPTX, PDF, HTML, and container ZIP (.dbk).
6
+ *
7
+ * Supports optional --theme and --transform flags to apply a squisq theme
8
+ * and/or transform style before exporting.
9
+ *
10
+ * Usage:
11
+ * squisq convert <input> [--output-dir <dir>] [--formats <list>] [--theme <id>] [--transform <style>]
12
+ */
13
+ import type { Command } from 'commander';
14
+ export declare function registerConvertCommand(program: Command): void;
15
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/commands/convert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwB7D"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * convert command
3
+ *
4
+ * Reads a markdown file, ZIP/DBK container, or folder and exports to
5
+ * all supported formats: DOCX, PPTX, PDF, HTML, and container ZIP (.dbk).
6
+ *
7
+ * Supports optional --theme and --transform flags to apply a squisq theme
8
+ * and/or transform style before exporting.
9
+ *
10
+ * Usage:
11
+ * squisq convert <input> [--output-dir <dir>] [--formats <list>] [--theme <id>] [--transform <style>]
12
+ */
13
+ import { writeFile, mkdir } from 'node:fs/promises';
14
+ import { dirname, basename, extname, join, resolve } from 'node:path';
15
+ import { readInput } from '../util/readInput.js';
16
+ const ALL_FORMATS = ['docx', 'pptx', 'pdf', 'html', 'dbk'];
17
+ function parseFormats(value) {
18
+ const requested = value.split(',').map((s) => s.trim().toLowerCase());
19
+ const valid = [];
20
+ for (const r of requested) {
21
+ if (ALL_FORMATS.includes(r)) {
22
+ valid.push(r);
23
+ }
24
+ else {
25
+ console.warn(`Unknown format "${r}" — skipping. Valid: ${ALL_FORMATS.join(', ')}`);
26
+ }
27
+ }
28
+ if (valid.length === 0) {
29
+ throw new Error(`No valid formats specified. Valid: ${ALL_FORMATS.join(', ')}`);
30
+ }
31
+ return valid;
32
+ }
33
+ export function registerConvertCommand(program) {
34
+ program
35
+ .command('convert')
36
+ .description('Convert a markdown document to DOCX, PPTX, PDF, HTML, and DBK container formats')
37
+ .argument('<input>', 'Path to .md file, .zip/.dbk container, or folder')
38
+ .option('-o, --output-dir <dir>', 'Output directory (default: same as input)')
39
+ .option('-f, --formats <list>', `Comma-separated formats to produce (default: all). Valid: ${ALL_FORMATS.join(', ')}`)
40
+ .option('-t, --theme <id>', 'Squisq theme ID to apply (e.g., documentary, cinematic, bold)')
41
+ .option('--transform <style>', 'Transform style to apply before export (e.g., documentary, magazine, minimal)')
42
+ .action(async (inputPath, opts) => {
43
+ try {
44
+ await runConvert(inputPath, opts);
45
+ }
46
+ catch (err) {
47
+ const message = err instanceof Error ? err.message : String(err);
48
+ console.error(`Error: ${message}`);
49
+ process.exitCode = 1;
50
+ }
51
+ });
52
+ }
53
+ async function runConvert(inputPath, opts) {
54
+ const resolvedInput = resolve(inputPath);
55
+ // Determine which formats to produce
56
+ const formats = opts.formats ? parseFormats(opts.formats) : [...ALL_FORMATS];
57
+ // Determine output directory and base filename
58
+ const outputDir = opts.outputDir ? resolve(opts.outputDir) : dirname(resolvedInput);
59
+ const inputBasename = basename(resolvedInput);
60
+ const inputExt = extname(inputBasename);
61
+ const baseName = inputExt ? inputBasename.slice(0, -inputExt.length) : inputBasename;
62
+ // Ensure output directory exists
63
+ await mkdir(outputDir, { recursive: true });
64
+ // Validate theme and transform IDs
65
+ if (opts.theme) {
66
+ const { getAvailableThemes } = await import('@bendyline/squisq/schemas');
67
+ const themes = getAvailableThemes();
68
+ if (!themes.includes(opts.theme)) {
69
+ throw new Error(`Unknown theme "${opts.theme}". Available: ${themes.join(', ')}`);
70
+ }
71
+ }
72
+ if (opts.transform) {
73
+ const { getTransformStyleIds } = await import('@bendyline/squisq/transform');
74
+ const styles = getTransformStyleIds();
75
+ if (!styles.includes(opts.transform)) {
76
+ throw new Error(`Unknown transform style "${opts.transform}". Available: ${styles.join(', ')}`);
77
+ }
78
+ }
79
+ console.error(`Reading: ${resolvedInput}`);
80
+ const { container, markdownDoc } = await readInput(resolvedInput);
81
+ // Apply transform if requested
82
+ let exportMarkdownDoc = markdownDoc;
83
+ if (opts.transform) {
84
+ exportMarkdownDoc = await applyTransformToMarkdown(markdownDoc, container, opts.transform, opts.theme);
85
+ console.error(` Applied transform: ${opts.transform}`);
86
+ }
87
+ const themeId = opts.theme;
88
+ for (const format of formats) {
89
+ const outPath = join(outputDir, `${baseName}.${format}`);
90
+ switch (format) {
91
+ case 'docx': {
92
+ const { markdownDocToDocx } = await import('@bendyline/squisq-formats/docx');
93
+ const buf = await markdownDocToDocx(exportMarkdownDoc, { themeId });
94
+ await writeFile(outPath, Buffer.from(buf));
95
+ break;
96
+ }
97
+ case 'pptx': {
98
+ const { markdownDocToPptx } = await import('@bendyline/squisq-formats/pptx');
99
+ // Collect images from container for PPTX embedding
100
+ const images = await collectContainerImages(container);
101
+ const buf = await markdownDocToPptx(exportMarkdownDoc, { themeId, images });
102
+ await writeFile(outPath, Buffer.from(buf));
103
+ break;
104
+ }
105
+ case 'pdf': {
106
+ const { markdownDocToPdf } = await import('@bendyline/squisq-formats/pdf');
107
+ const buf = await markdownDocToPdf(exportMarkdownDoc, { themeId });
108
+ await writeFile(outPath, Buffer.from(buf));
109
+ break;
110
+ }
111
+ case 'html': {
112
+ const { markdownToDoc } = await import('@bendyline/squisq/doc');
113
+ const { docToHtml, collectImagePaths } = await import('@bendyline/squisq-formats/html');
114
+ const { PLAYER_BUNDLE } = await import('@bendyline/squisq-react/standalone-source');
115
+ const doc = markdownToDoc(exportMarkdownDoc);
116
+ // Gather images referenced by the doc from the container
117
+ const imagePaths = collectImagePaths(doc);
118
+ const images = new Map();
119
+ for (const imgPath of imagePaths) {
120
+ const data = await container.readFile(imgPath);
121
+ if (data) {
122
+ images.set(imgPath, data);
123
+ }
124
+ }
125
+ const html = docToHtml(doc, {
126
+ playerScript: PLAYER_BUNDLE,
127
+ images,
128
+ title: baseName,
129
+ mode: 'static',
130
+ themeId,
131
+ });
132
+ await writeFile(outPath, html, 'utf-8');
133
+ break;
134
+ }
135
+ case 'dbk': {
136
+ const { containerToZip } = await import('@bendyline/squisq-formats/container');
137
+ const blob = await containerToZip(container);
138
+ const buf = await blob.arrayBuffer();
139
+ await writeFile(outPath, Buffer.from(buf));
140
+ break;
141
+ }
142
+ }
143
+ console.error(` ✓ ${outPath}`);
144
+ }
145
+ console.error('Done.');
146
+ }
147
+ // ============================================
148
+ // Transform Pipeline
149
+ // ============================================
150
+ /**
151
+ * Apply a transform style to a MarkdownDocument.
152
+ * Pipeline: MarkdownDocument → Doc → applyTransform → docToMarkdown → MarkdownDocument
153
+ */
154
+ async function applyTransformToMarkdown(markdownDoc, container, transformStyle, themeId) {
155
+ const { markdownToDoc, docToMarkdown } = await import('@bendyline/squisq/doc');
156
+ const { applyTransform, extractDocImages } = await import('@bendyline/squisq/transform');
157
+ const doc = markdownToDoc(markdownDoc);
158
+ // Extract image metadata from the doc for transform interleaving
159
+ const images = extractDocImages(doc.blocks);
160
+ const result = applyTransform(doc, transformStyle, {
161
+ themeId,
162
+ images,
163
+ });
164
+ return docToMarkdown(result.doc);
165
+ }
166
+ /**
167
+ * Collect all image files from a container as a Map<path, ArrayBuffer>.
168
+ */
169
+ async function collectContainerImages(container) {
170
+ const images = new Map();
171
+ const files = await container.listFiles();
172
+ for (const file of files) {
173
+ if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|avif)$/i.test(file.path)) {
174
+ const data = await container.readFile(file.path);
175
+ if (data) {
176
+ images.set(file.path, data);
177
+ }
178
+ }
179
+ }
180
+ return images;
181
+ }
182
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/commands/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * video command
3
+ *
4
+ * Renders a squisq document to MP4 video by:
5
+ * 1. Parsing the input (markdown, container, or folder)
6
+ * 2. Generating a self-contained render HTML
7
+ * 3. Capturing frames via Playwright headless browser
8
+ * 4. Encoding to MP4 using native ffmpeg (fast) or ffmpeg.wasm (fallback)
9
+ *
10
+ * Usage:
11
+ * squisq video <input> [-o output.mp4] [--fps 30] [--quality normal] [--orientation landscape]
12
+ */
13
+ import type { Command } from 'commander';
14
+ export declare function registerVideoCommand(program: Command): void;
15
+ //# sourceMappingURL=video.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../src/commands/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsC3D"}