@bendyline/squisq-cli 2.2.0 → 2.3.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/NOTICE.md +4 -8
- package/README.md +1 -1
- package/THIRD_PARTY_LICENSES.txt +4471 -0
- package/dist/{api-5SX73ZKT.js → api-L5AORQHA.js} +1 -1
- package/dist/api.js +15 -6
- package/dist/{chunk-GYHQHKO5.js → chunk-TPODSM5L.js} +17 -8
- package/dist/index.js +39 -20
- package/dist/squisq-player.global.js +574 -0
- package/package.json +10 -7
package/dist/api.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-Z3BHD4AF.js";
|
|
9
9
|
|
|
10
10
|
// src/api.ts
|
|
11
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
11
12
|
import { resolveMediaSchedule } from "@bendyline/squisq/schemas";
|
|
12
13
|
import { flattenBlocks } from "@bendyline/squisq/doc";
|
|
13
14
|
import { ffmpegGifOutputArgs, generateRenderHtml } from "@bendyline/squisq-video";
|
|
@@ -96,7 +97,7 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
96
97
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
97
98
|
}
|
|
98
99
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
99
|
-
const { writeFile, readFile:
|
|
100
|
+
const { writeFile, readFile: readFile4, mkdir, rm: rm2 } = await import("fs/promises");
|
|
100
101
|
const { join: join3 } = await import("path");
|
|
101
102
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
102
103
|
const { randomBytes: randomBytes2 } = await import("crypto");
|
|
@@ -144,7 +145,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
144
145
|
signal
|
|
145
146
|
});
|
|
146
147
|
signal?.throwIfAborted();
|
|
147
|
-
const data = await
|
|
148
|
+
const data = await readFile4(outputPath);
|
|
148
149
|
signal?.throwIfAborted();
|
|
149
150
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
150
151
|
} finally {
|
|
@@ -663,17 +664,25 @@ function throwIfAborted(signal) {
|
|
|
663
664
|
|
|
664
665
|
// src/api.ts
|
|
665
666
|
import { ConversionError } from "@bendyline/squisq-formats";
|
|
667
|
+
var playerBundlePromise;
|
|
668
|
+
function loadPlayerBundle() {
|
|
669
|
+
playerBundlePromise ??= readFile3(
|
|
670
|
+
new URL("../dist/squisq-player.global.js", import.meta.url),
|
|
671
|
+
"utf8"
|
|
672
|
+
);
|
|
673
|
+
return playerBundlePromise;
|
|
674
|
+
}
|
|
666
675
|
async function convert(source, to, options = {}) {
|
|
667
676
|
return formatsConvert(source, to, {
|
|
668
677
|
registry: createCliRegistry(),
|
|
669
|
-
resolvePlayerScript:
|
|
678
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
670
679
|
...options
|
|
671
680
|
});
|
|
672
681
|
}
|
|
673
682
|
async function prepareConversion(source, options = {}) {
|
|
674
683
|
return formatsPrepareConversion(source, {
|
|
675
684
|
registry: createCliRegistry(),
|
|
676
|
-
resolvePlayerScript:
|
|
685
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
677
686
|
...options
|
|
678
687
|
});
|
|
679
688
|
}
|
|
@@ -732,10 +741,10 @@ async function captureDocFrames(doc, container, options) {
|
|
|
732
741
|
}
|
|
733
742
|
onProgress?.("generating render HTML", 10);
|
|
734
743
|
signal?.throwIfAborted();
|
|
735
|
-
const
|
|
744
|
+
const playerBundle = await loadPlayerBundle();
|
|
736
745
|
signal?.throwIfAborted();
|
|
737
746
|
const renderHtml = generateRenderHtml(doc, {
|
|
738
|
-
playerScript:
|
|
747
|
+
playerScript: playerBundle,
|
|
739
748
|
images,
|
|
740
749
|
audio: audio.size > 0 ? audio : void 0,
|
|
741
750
|
width,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-GGVASRPG.js";
|
|
6
6
|
|
|
7
7
|
// src/api.ts
|
|
8
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
8
9
|
import { resolveMediaSchedule } from "@bendyline/squisq/schemas";
|
|
9
10
|
import { flattenBlocks } from "@bendyline/squisq/doc";
|
|
10
11
|
import { ffmpegGifOutputArgs, generateRenderHtml } from "@bendyline/squisq-video";
|
|
@@ -93,7 +94,7 @@ async function buildMixedAudioTrack(doc, container, ffmpegPath, coverPreRoll, si
|
|
|
93
94
|
return mixTimelineClips(ffmpegPath, usable, signal);
|
|
94
95
|
}
|
|
95
96
|
async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
96
|
-
const { writeFile, readFile:
|
|
97
|
+
const { writeFile, readFile: readFile4, mkdir, rm: rm2 } = await import("fs/promises");
|
|
97
98
|
const { join: join3 } = await import("path");
|
|
98
99
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
99
100
|
const { randomBytes: randomBytes2 } = await import("crypto");
|
|
@@ -141,7 +142,7 @@ async function mixTimelineClips(ffmpegPath, usable, signal) {
|
|
|
141
142
|
signal
|
|
142
143
|
});
|
|
143
144
|
signal?.throwIfAborted();
|
|
144
|
-
const data = await
|
|
145
|
+
const data = await readFile4(outputPath);
|
|
145
146
|
signal?.throwIfAborted();
|
|
146
147
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
147
148
|
} finally {
|
|
@@ -307,7 +308,7 @@ function mp4Format() {
|
|
|
307
308
|
const animationsEnabled = typeof mp4Opts.animationsEnabled === "boolean" ? mp4Opts.animationsEnabled : MP4_DEFAULTS.animationsEnabled;
|
|
308
309
|
const outputPath = join(tmpdir(), `squisq-mp4-${randomBytes(8).toString("hex")}.mp4`);
|
|
309
310
|
try {
|
|
310
|
-
const { renderDocToMp4: renderDocToMp42 } = await import("./api-
|
|
311
|
+
const { renderDocToMp4: renderDocToMp42 } = await import("./api-L5AORQHA.js");
|
|
311
312
|
await renderDocToMp42(input.doc, input.container, {
|
|
312
313
|
outputPath,
|
|
313
314
|
fps,
|
|
@@ -347,7 +348,7 @@ function gifFormat() {
|
|
|
347
348
|
const defaultHeight = portrait ? GIF_DEFAULTS.width : GIF_DEFAULTS.height;
|
|
348
349
|
const outputPath = join(tmpdir(), `squisq-gif-${randomBytes(8).toString("hex")}.gif`);
|
|
349
350
|
try {
|
|
350
|
-
const { renderDocToGif: renderDocToGif2 } = await import("./api-
|
|
351
|
+
const { renderDocToGif: renderDocToGif2 } = await import("./api-L5AORQHA.js");
|
|
351
352
|
const result = await renderDocToGif2(input.doc, input.container, {
|
|
352
353
|
outputPath,
|
|
353
354
|
fps: typeof gifOpts.fps === "number" ? gifOpts.fps : GIF_DEFAULTS.fps,
|
|
@@ -660,17 +661,25 @@ function throwIfAborted(signal) {
|
|
|
660
661
|
|
|
661
662
|
// src/api.ts
|
|
662
663
|
import { ConversionError } from "@bendyline/squisq-formats";
|
|
664
|
+
var playerBundlePromise;
|
|
665
|
+
function loadPlayerBundle() {
|
|
666
|
+
playerBundlePromise ??= readFile3(
|
|
667
|
+
new URL("../dist/squisq-player.global.js", import.meta.url),
|
|
668
|
+
"utf8"
|
|
669
|
+
);
|
|
670
|
+
return playerBundlePromise;
|
|
671
|
+
}
|
|
663
672
|
async function convert(source, to, options = {}) {
|
|
664
673
|
return formatsConvert(source, to, {
|
|
665
674
|
registry: createCliRegistry(),
|
|
666
|
-
resolvePlayerScript:
|
|
675
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
667
676
|
...options
|
|
668
677
|
});
|
|
669
678
|
}
|
|
670
679
|
async function prepareConversion(source, options = {}) {
|
|
671
680
|
return formatsPrepareConversion(source, {
|
|
672
681
|
registry: createCliRegistry(),
|
|
673
|
-
resolvePlayerScript:
|
|
682
|
+
resolvePlayerScript: loadPlayerBundle,
|
|
674
683
|
...options
|
|
675
684
|
});
|
|
676
685
|
}
|
|
@@ -729,10 +738,10 @@ async function captureDocFrames(doc, container, options) {
|
|
|
729
738
|
}
|
|
730
739
|
onProgress?.("generating render HTML", 10);
|
|
731
740
|
signal?.throwIfAborted();
|
|
732
|
-
const
|
|
741
|
+
const playerBundle = await loadPlayerBundle();
|
|
733
742
|
signal?.throwIfAborted();
|
|
734
743
|
const renderHtml = generateRenderHtml(doc, {
|
|
735
|
-
playerScript:
|
|
744
|
+
playerScript: playerBundle,
|
|
736
745
|
images,
|
|
737
746
|
audio: audio.size > 0 ? audio : void 0,
|
|
738
747
|
width,
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
readInput,
|
|
8
8
|
renderDocToGif,
|
|
9
9
|
renderDocToMp4
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TPODSM5L.js";
|
|
11
11
|
import "./chunk-GGVASRPG.js";
|
|
12
12
|
|
|
13
13
|
// src/index.ts
|
|
@@ -17,6 +17,7 @@ import { Command } from "commander";
|
|
|
17
17
|
// src/commands/convert.ts
|
|
18
18
|
import { mkdir } from "fs/promises";
|
|
19
19
|
import { dirname, basename, extname, join, resolve } from "path";
|
|
20
|
+
import { Option } from "commander";
|
|
20
21
|
import { BUILTIN_FORMAT_IDS, ConversionError } from "@bendyline/squisq-formats";
|
|
21
22
|
|
|
22
23
|
// src/util/applyTransformPipeline.ts
|
|
@@ -163,6 +164,12 @@ function parseFormats(value) {
|
|
|
163
164
|
`Unknown format${unknown.length > 1 ? "s" : ""}: ${hints}. Valid: ${VALID_FORMATS.join(", ")}`
|
|
164
165
|
);
|
|
165
166
|
}
|
|
167
|
+
const duplicates = [...new Set(requested.filter((id, index) => requested.indexOf(id) !== index))];
|
|
168
|
+
if (duplicates.length > 0) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Duplicate format${duplicates.length > 1 ? "s" : ""}: ${duplicates.map((id) => `"${id}"`).join(", ")}. Each format may only be requested once.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
166
173
|
return requested;
|
|
167
174
|
}
|
|
168
175
|
function formatFromOutputPath(outputPath) {
|
|
@@ -180,12 +187,21 @@ function formatFromOutputPath(outputPath) {
|
|
|
180
187
|
function registerConvertCommand(program2) {
|
|
181
188
|
program2.command("convert").description(
|
|
182
189
|
"Convert a document to DOCX, PPTX, PDF, HTML, EPUB, MP4, animated GIF, and container formats"
|
|
183
|
-
).argument("<input>", "Path to .md/.docx/.pptx/.pdf/.xlsx/.csv/.html file, .zip/.dbk, or folder").
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"
|
|
188
|
-
|
|
190
|
+
).argument("<input>", "Path to .md/.docx/.pptx/.pdf/.xlsx/.csv/.html file, .zip/.dbk, or folder").addOption(
|
|
191
|
+
new Option(
|
|
192
|
+
"-o, --output <file>",
|
|
193
|
+
"Single output file (format inferred from its extension)"
|
|
194
|
+
).conflicts(["outputDir", "formats"])
|
|
195
|
+
).addOption(
|
|
196
|
+
new Option(
|
|
197
|
+
"-d, --output-dir <dir>",
|
|
198
|
+
"Output directory for multi-format export (default: same as input)"
|
|
199
|
+
).conflicts("output")
|
|
200
|
+
).addOption(
|
|
201
|
+
new Option(
|
|
202
|
+
"-f, --formats <list>",
|
|
203
|
+
`Comma-separated formats to produce (default: a standard set). Valid: ${VALID_FORMATS.join(", ")}`
|
|
204
|
+
).conflicts("output")
|
|
189
205
|
).option("--overwrite", "Replace existing output files (default: refuse and exit non-zero)").option("-t, --theme <id>", "Squisq theme ID to apply (e.g., documentary, cinematic, bold)").option(
|
|
190
206
|
"--transform <style>",
|
|
191
207
|
"Transform style to apply before export (e.g., documentary, magazine, minimal)"
|
|
@@ -296,7 +312,7 @@ var VALID_CAPTIONS = ["off", "standard", "social"];
|
|
|
296
312
|
var VALID_FORMATS2 = ["mp4", "gif"];
|
|
297
313
|
var VALID_GIF_DITHERS = ["bayer", "sierra2_4a", "none"];
|
|
298
314
|
function registerVideoCommand(program2) {
|
|
299
|
-
program2.command("video").description("Render a squisq document to MP4 video or animated GIF").argument("<input>", "Path to .md file, .zip/.dbk container, or folder").argument("[output]", "Output .mp4 or .gif path (default: <input>.<format>)").option("-o, --output <path>", "Output .mp4 or .gif path (format inferred from extension)").option("--format <format>", `Output format: ${VALID_FORMATS2.join(", ")} (default: mp4)`).option("--fps <number>", "Frames per second (default: 30 for MP4, 10 for GIF)").option(
|
|
315
|
+
program2.command("video").description("Render a squisq document to MP4 video or animated GIF").argument("<input>", "Path to .md/.json file, .zip/.dbk container, or folder").argument("[output]", "Output .mp4 or .gif path (default: <input>.<format>)").option("-o, --output <path>", "Output .mp4 or .gif path (format inferred from extension)").option("--format <format>", `Output format: ${VALID_FORMATS2.join(", ")} (default: mp4)`).option("--fps <number>", "Frames per second (default: 30 for MP4, 10 for GIF)").option(
|
|
300
316
|
"--quality <level>",
|
|
301
317
|
`MP4 encoding quality: ${VALID_QUALITIES.join(", ")} (default: normal)`
|
|
302
318
|
).option(
|
|
@@ -322,9 +338,10 @@ function registerVideoCommand(program2) {
|
|
|
322
338
|
`GIF dithering: ${VALID_GIF_DITHERS.join(", ")} (default: sierra2_4a)`
|
|
323
339
|
).option("--bayer-scale <number>", "GIF Bayer dither strength from 0 to 5 (default: 3)").action(async (inputPath, outputArg, opts) => {
|
|
324
340
|
try {
|
|
325
|
-
if (outputArg &&
|
|
326
|
-
|
|
341
|
+
if (outputArg && opts.output) {
|
|
342
|
+
throw new Error("The positional output and --output cannot be used together.");
|
|
327
343
|
}
|
|
344
|
+
if (outputArg) opts.output = outputArg;
|
|
328
345
|
await runVideo(inputPath, opts);
|
|
329
346
|
} catch (err) {
|
|
330
347
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -351,9 +368,9 @@ async function runVideo(inputPath, opts) {
|
|
|
351
368
|
}
|
|
352
369
|
const outputFormat = requestedFormat ?? extensionFormat ?? "mp4";
|
|
353
370
|
const maxFps = outputFormat === "gif" ? 100 : 120;
|
|
354
|
-
const fps =
|
|
355
|
-
if (
|
|
356
|
-
throw new Error(`FPS must be
|
|
371
|
+
const fps = Number(opts.fps ?? (outputFormat === "gif" ? "10" : "30"));
|
|
372
|
+
if (!Number.isSafeInteger(fps) || fps < 1 || fps > maxFps) {
|
|
373
|
+
throw new Error(`FPS must be an integer between 1 and ${maxFps}`);
|
|
357
374
|
}
|
|
358
375
|
const quality = opts.quality ?? "normal";
|
|
359
376
|
if (outputFormat === "gif" && opts.quality !== void 0) {
|
|
@@ -368,16 +385,16 @@ async function runVideo(inputPath, opts) {
|
|
|
368
385
|
`Invalid orientation "${orientation}". Valid: ${VALID_ORIENTATIONS.join(", ")}`
|
|
369
386
|
);
|
|
370
387
|
}
|
|
371
|
-
const width = opts.width === void 0 ? void 0 :
|
|
372
|
-
const height = opts.height === void 0 ? void 0 :
|
|
388
|
+
const width = opts.width === void 0 ? void 0 : Number(opts.width);
|
|
389
|
+
const height = opts.height === void 0 ? void 0 : Number(opts.height);
|
|
373
390
|
validateVideoExportOptions({ width, height, orientation });
|
|
374
391
|
const captions = opts.captions ?? "off";
|
|
375
392
|
if (!VALID_CAPTIONS.includes(captions)) {
|
|
376
393
|
throw new Error(`Invalid captions "${captions}". Valid: ${VALID_CAPTIONS.join(", ")}`);
|
|
377
394
|
}
|
|
378
395
|
const captionStyle = captions === "off" ? void 0 : captions;
|
|
379
|
-
const coverPreRoll =
|
|
380
|
-
if (
|
|
396
|
+
const coverPreRoll = Number(opts.coverPreroll ?? "2");
|
|
397
|
+
if (!Number.isFinite(coverPreRoll) || coverPreRoll < 0) {
|
|
381
398
|
throw new Error("Cover pre-roll must be a number of seconds >= 0");
|
|
382
399
|
}
|
|
383
400
|
const animationsEnabled = opts.animations ?? outputFormat === "mp4";
|
|
@@ -615,9 +632,11 @@ async function runDoctor(runtime = defaultDoctorRuntime) {
|
|
|
615
632
|
// src/index.ts
|
|
616
633
|
var require2 = createRequire(import.meta.url);
|
|
617
634
|
var { version } = require2("../package.json");
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
635
|
+
if (process.stderr.isTTY) {
|
|
636
|
+
console.error(
|
|
637
|
+
`\x1B[36m{[\x1B[0m \x1B[1msquiggly square\x1B[0m \x1B[2m\u2014\x1B[0m \x1B[1msquisq\x1B[0m \x1B[2m\u2014\x1B[0m \x1B[33mv${version}\x1B[0m \x1B[36m]}\x1B[0m`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
621
640
|
var program = new Command();
|
|
622
641
|
program.name("squisq").description("Squisq CLI \u2014 convert and process markdown-based documents").version(version);
|
|
623
642
|
registerConvertCommand(program);
|