@bendyline/squisq 2.11.13 → 2.11.14
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 +20 -0
- package/dist/{Doc-BWtfp7dT.d.ts → Doc-C-cv1g4G.d.ts} +8 -0
- package/dist/{ImageEditDoc-CYRQK8so.d.ts → ImageEditDoc-3J4PRw8N.d.ts} +1 -1
- package/dist/{chunk-EWO2VLOS.js → chunk-4P557IPH.js} +284 -14
- package/dist/{chunk-BR7ZMDWW.js → chunk-6H2HBBFC.js} +1 -1
- package/dist/{chunk-S4427XKY.js → chunk-GDF2FWL6.js} +9 -5
- package/dist/{chunk-X2TDC4E5.js → chunk-HHGVTXHR.js} +2 -2
- package/dist/{chunk-RLBHQSUG.js → chunk-UVQPK6A3.js} +123 -0
- package/dist/{chunk-GIYWXYCK.js → chunk-XSOIWNZL.js} +8 -0
- package/dist/{chunk-42IU2SJY.js → chunk-YPI2VJSB.js} +190 -45
- package/dist/doc/index.d.ts +11 -7
- package/dist/doc/index.js +3 -3
- package/dist/fence/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/imageEdit/index.d.ts +3 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +9 -7
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/markdown/index.d.ts +12 -3
- package/dist/markdown/index.js +3 -1
- package/dist/{materializePageSection-Db7pABI0.d.ts → materializePageSection-Dmy_-AUQ.d.ts} +1 -1
- package/dist/mediaEdit/index.d.ts +2 -2
- package/dist/narration/index.d.ts +1 -1
- package/dist/narration/index.js +3 -3
- package/dist/schemas/index.d.ts +4 -4
- package/dist/storage/index.js +1 -1
- package/dist/{themeLibrary-Bphz1CRp.d.ts → themeLibrary-CROfw3YK.d.ts} +1 -1
- package/dist/timing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +8 -2
- package/dist/transform/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,26 @@ console.log(doc.children); // AST nodes
|
|
|
67
67
|
const md = stringifyMarkdown(doc);
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
### Condense imported Markdown
|
|
71
|
+
|
|
72
|
+
`condenseMarkdownSource(source)` removes table alignment padding, shortens
|
|
73
|
+
GFM delimiter rows, and reduces decorative thematic breaks to three markers.
|
|
74
|
+
It preserves table cells and alignment, code, math, HTML, links, footnotes,
|
|
75
|
+
prose, and frontmatter. The output is reparsed and structurally compared with
|
|
76
|
+
the original before returning; the API throws if equivalence cannot be proven.
|
|
77
|
+
It is idempotent and does not summarize articles or remove citations.
|
|
78
|
+
|
|
79
|
+
Run condensation after link/image edits, which can re-serialize padded tables.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { condenseMarkdownSource } from '@bendyline/squisq/markdown';
|
|
83
|
+
|
|
84
|
+
const compactMarkdown = condenseMarkdownSource(importedMarkdown);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The shared source-transform registry also exposes `condense`, available from
|
|
88
|
+
the editor's Transform menu and `squisq transform file.md --ops condense`.
|
|
89
|
+
|
|
70
90
|
### Author an ASCII Timeline
|
|
71
91
|
|
|
72
92
|
Timeline fences are source-of-truth markdown. Unicode marker rails are
|
|
@@ -739,6 +739,10 @@ interface BaseTemplateBlock {
|
|
|
739
739
|
* Provenance only — NOT a time (see sourceStartTime for seconds).
|
|
740
740
|
*/
|
|
741
741
|
sourceCharOffset?: number;
|
|
742
|
+
/** Projection-only summary policy; keeps visual features on their owning slide. */
|
|
743
|
+
summaryMode?: 'brief' | 'headings-and-features';
|
|
744
|
+
/** Automatic title-and-feature composition; omitted for authored templates. */
|
|
745
|
+
summaryLayout?: 'feature';
|
|
742
746
|
/**
|
|
743
747
|
* Per-block override for the theme's photographic image grade
|
|
744
748
|
* (`theme.style.imageTreatment`). `'none'` opts this block's imagery out;
|
|
@@ -2482,6 +2486,10 @@ interface Block {
|
|
|
2482
2486
|
sourceBlockIds?: string[];
|
|
2483
2487
|
/** Char offset of the extraction inside the source block's plain text. */
|
|
2484
2488
|
sourceCharOffset?: number;
|
|
2489
|
+
/** Projection-only summary policy; keeps visual features on their owning slide. */
|
|
2490
|
+
summaryMode?: 'brief' | 'headings-and-features';
|
|
2491
|
+
/** Automatic title-and-feature composition; omitted for authored templates. */
|
|
2492
|
+
summaryLayout?: 'feature';
|
|
2485
2493
|
/**
|
|
2486
2494
|
* Pre-computed visual layers, rendered back-to-front.
|
|
2487
2495
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-
|
|
1
|
+
import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-C-cv1g4G.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ImageEditDoc — JSON document describing a layered, editable image.
|
|
@@ -4,8 +4,16 @@ import {
|
|
|
4
4
|
stripMarkdown
|
|
5
5
|
} from "./chunk-SVN7T6BK.js";
|
|
6
6
|
import {
|
|
7
|
+
coerceTemplateParams,
|
|
8
|
+
deriveTemplateInputs,
|
|
9
|
+
flattenRenderableBlocks,
|
|
7
10
|
markdownToDoc
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YPI2VJSB.js";
|
|
12
|
+
import {
|
|
13
|
+
isAsciiDiagramFence,
|
|
14
|
+
isAsciiTimelineFence,
|
|
15
|
+
isTreeFence
|
|
16
|
+
} from "./chunk-C3NAY5YP.js";
|
|
9
17
|
import {
|
|
10
18
|
cloneAndFreezeData,
|
|
11
19
|
isTemplateBlock
|
|
@@ -13,9 +21,13 @@ import {
|
|
|
13
21
|
import {
|
|
14
22
|
parseMarkdown
|
|
15
23
|
} from "./chunk-HUXK7EOT.js";
|
|
24
|
+
import {
|
|
25
|
+
isContainerTemplate
|
|
26
|
+
} from "./chunk-TMOHAFYE.js";
|
|
16
27
|
import {
|
|
17
28
|
extractPlainText,
|
|
18
|
-
getChildren
|
|
29
|
+
getChildren,
|
|
30
|
+
stringifyHtmlNodes
|
|
19
31
|
} from "./chunk-X7BNDUNI.js";
|
|
20
32
|
import {
|
|
21
33
|
estimateTimeFromText
|
|
@@ -99,26 +111,45 @@ var narrativeStyle = {
|
|
|
99
111
|
var minimalStyle = {
|
|
100
112
|
id: "minimal",
|
|
101
113
|
name: "Minimal",
|
|
102
|
-
description: "
|
|
114
|
+
description: "Short highlights and a single brief line per section",
|
|
115
|
+
contentMode: "brief",
|
|
103
116
|
minConfidence: 0.6,
|
|
104
117
|
transformRatio: 0.2,
|
|
105
118
|
preferredTypes: ["stat", "quote"],
|
|
106
119
|
colorSchemes: ["blue", "green"],
|
|
107
120
|
insertSectionHeaders: false,
|
|
108
121
|
interleaveImages: false,
|
|
109
|
-
blocksPerSection: { max:
|
|
122
|
+
blocksPerSection: { max: 1 },
|
|
110
123
|
transitionStyle: "fade",
|
|
111
124
|
suggestedThemeId: "minimalist",
|
|
112
125
|
page: { spacing: "generous", emphasisCurve: "even" }
|
|
113
126
|
};
|
|
114
127
|
|
|
128
|
+
// src/transform/styles/headingsAndFeatures.ts
|
|
129
|
+
var headingsAndFeaturesStyle = {
|
|
130
|
+
id: "headings-and-features",
|
|
131
|
+
name: "Headings and features",
|
|
132
|
+
description: "Headings with their images, videos, and diagrams; no body prose",
|
|
133
|
+
contentMode: "headings-and-features",
|
|
134
|
+
minConfidence: 1,
|
|
135
|
+
transformRatio: 0,
|
|
136
|
+
preferredTypes: [],
|
|
137
|
+
colorSchemes: ["blue", "green"],
|
|
138
|
+
insertSectionHeaders: false,
|
|
139
|
+
interleaveImages: false,
|
|
140
|
+
blocksPerSection: { max: 1 },
|
|
141
|
+
transitionStyle: "fade",
|
|
142
|
+
page: { spacing: "generous", emphasisCurve: "even" }
|
|
143
|
+
};
|
|
144
|
+
|
|
115
145
|
// src/transform/registry.ts
|
|
116
146
|
var TRANSFORM_STYLES = Object.freeze({
|
|
117
147
|
[documentaryStyle.id]: cloneAndFreezeData(documentaryStyle),
|
|
118
148
|
[magazineStyle.id]: cloneAndFreezeData(magazineStyle),
|
|
119
149
|
[dataDrivenStyle.id]: cloneAndFreezeData(dataDrivenStyle),
|
|
120
150
|
[narrativeStyle.id]: cloneAndFreezeData(narrativeStyle),
|
|
121
|
-
[minimalStyle.id]: cloneAndFreezeData(minimalStyle)
|
|
151
|
+
[minimalStyle.id]: cloneAndFreezeData(minimalStyle),
|
|
152
|
+
[headingsAndFeaturesStyle.id]: cloneAndFreezeData(headingsAndFeaturesStyle)
|
|
122
153
|
});
|
|
123
154
|
var DEFAULT_TRANSFORM_STYLE_ID = "documentary";
|
|
124
155
|
var EXTRACTION_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -148,6 +179,9 @@ function validateTransformStyle(style) {
|
|
|
148
179
|
fail(`${key} must be a finite number between 0 and 1`);
|
|
149
180
|
}
|
|
150
181
|
}
|
|
182
|
+
if (candidate.contentMode !== void 0 && candidate.contentMode !== "brief" && candidate.contentMode !== "headings-and-features") {
|
|
183
|
+
fail("contentMode must be brief or headings-and-features");
|
|
184
|
+
}
|
|
151
185
|
if (!Array.isArray(candidate.preferredTypes) || candidate.preferredTypes.some((type) => !EXTRACTION_TYPES.has(type))) {
|
|
152
186
|
fail("preferredTypes contains an unknown extraction type");
|
|
153
187
|
}
|
|
@@ -328,7 +362,9 @@ function collectLeafBlocks(blocks) {
|
|
|
328
362
|
return result;
|
|
329
363
|
}
|
|
330
364
|
function analyzeBlocks(blocks, options) {
|
|
331
|
-
|
|
365
|
+
return analyzeFlatBlocks(collectLeafBlocks(blocks), options);
|
|
366
|
+
}
|
|
367
|
+
function analyzeFlatBlocks(flat, options) {
|
|
332
368
|
const results = [];
|
|
333
369
|
for (const block of flat) {
|
|
334
370
|
let plainText = "";
|
|
@@ -353,6 +389,223 @@ function analyzeBlocks(blocks, options) {
|
|
|
353
389
|
return results;
|
|
354
390
|
}
|
|
355
391
|
|
|
392
|
+
// src/transform/contentSummary.ts
|
|
393
|
+
var NATIVE_FEATURE_TEMPLATES = /* @__PURE__ */ new Set([
|
|
394
|
+
"diagram",
|
|
395
|
+
"tree",
|
|
396
|
+
"timeline",
|
|
397
|
+
"map",
|
|
398
|
+
"drawing",
|
|
399
|
+
"layout",
|
|
400
|
+
"dataTable",
|
|
401
|
+
"barChart",
|
|
402
|
+
"columnChart",
|
|
403
|
+
"pieChart",
|
|
404
|
+
"donutChart",
|
|
405
|
+
"lineChart",
|
|
406
|
+
"areaChart",
|
|
407
|
+
"scatterChart"
|
|
408
|
+
]);
|
|
409
|
+
var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
|
|
410
|
+
var PROSE_KEYS = /* @__PURE__ */ new Set([
|
|
411
|
+
"body",
|
|
412
|
+
"subtitle",
|
|
413
|
+
"description",
|
|
414
|
+
"explanation",
|
|
415
|
+
"definition",
|
|
416
|
+
"detail",
|
|
417
|
+
"caption",
|
|
418
|
+
"quote",
|
|
419
|
+
"text"
|
|
420
|
+
]);
|
|
421
|
+
function briefText(text) {
|
|
422
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
423
|
+
const sentence = normalized.match(/^.*?[.!?](?:["'”’])?(?=\s|$)/)?.[0] ?? normalized;
|
|
424
|
+
const words = sentence.split(/\s+/).filter(Boolean);
|
|
425
|
+
let brief = words.slice(0, 24).join(" ");
|
|
426
|
+
const truncated = words.length > 24 || brief.length > 180;
|
|
427
|
+
if (brief.length > 179 && truncated) {
|
|
428
|
+
brief = brief.slice(0, 179).replace(/\s+\S*$/, "").trimEnd();
|
|
429
|
+
}
|
|
430
|
+
return truncated ? `${brief.replace(/[.,;:!?]+$/, "")}\u2026` : brief;
|
|
431
|
+
}
|
|
432
|
+
function htmlFeatures(nodes) {
|
|
433
|
+
return nodes.flatMap((node) => {
|
|
434
|
+
if (node.type !== "htmlElement") return [];
|
|
435
|
+
const tag = node.tagName.toLowerCase();
|
|
436
|
+
if (tag === "img") return [node];
|
|
437
|
+
if (tag === "video") {
|
|
438
|
+
return [
|
|
439
|
+
{
|
|
440
|
+
...node,
|
|
441
|
+
children: node.children.filter(
|
|
442
|
+
(child) => child.type === "htmlElement" && ["source", "track"].includes(child.tagName.toLowerCase())
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
];
|
|
446
|
+
}
|
|
447
|
+
return htmlFeatures(node.children);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function extractFeatureContents(contents, keepData = false) {
|
|
451
|
+
const features = [];
|
|
452
|
+
const visit = (node) => {
|
|
453
|
+
if (node.type === "code") {
|
|
454
|
+
if (node.lang?.trim().toLowerCase() === "mermaid" || isAsciiDiagramFence(node) || isTreeFence(node) || isAsciiTimelineFence(node) || keepData && /^(json|ya?ml)$/i.test(node.lang ?? "") && /\bdata\b/i.test(node.meta ?? ""))
|
|
455
|
+
features.push(node);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (node.type === "table") {
|
|
459
|
+
if (keepData) features.push(node);
|
|
460
|
+
else for (const child of getChildren(node)) visit(child);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (node.type === "image" || node.type === "link" && VIDEO_FILE_RE.test(node.url)) {
|
|
464
|
+
features.push({ type: "paragraph", children: [node] });
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (node.type === "htmlBlock" || node.type === "htmlInline") {
|
|
468
|
+
const htmlChildren = htmlFeatures(node.htmlChildren);
|
|
469
|
+
if (htmlChildren.length) {
|
|
470
|
+
features.push({
|
|
471
|
+
type: "htmlBlock",
|
|
472
|
+
htmlChildren,
|
|
473
|
+
rawHtml: stringifyHtmlNodes(htmlChildren)
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
for (const child of getChildren(node)) visit(child);
|
|
479
|
+
};
|
|
480
|
+
for (const node of contents ?? []) visit(node);
|
|
481
|
+
return features;
|
|
482
|
+
}
|
|
483
|
+
function withoutProse(record) {
|
|
484
|
+
return record && Object.fromEntries(Object.entries(record).filter(([key]) => !PROSE_KEYS.has(key)));
|
|
485
|
+
}
|
|
486
|
+
function typedMediaContents(block) {
|
|
487
|
+
const record = {
|
|
488
|
+
...block,
|
|
489
|
+
...block.templateData,
|
|
490
|
+
...coerceTemplateParams(block.template ?? "", block.templateOverrides ?? {}).input
|
|
491
|
+
};
|
|
492
|
+
const contents = [];
|
|
493
|
+
const addImage = (src, alt) => {
|
|
494
|
+
if (typeof src === "string" && src) {
|
|
495
|
+
contents.push({
|
|
496
|
+
type: "paragraph",
|
|
497
|
+
children: [{ type: "image", url: src, alt: typeof alt === "string" ? alt : "" }]
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
addImage(record.imageSrc, record.imageAlt);
|
|
502
|
+
for (const key of ["accentImage", "backgroundImage"]) {
|
|
503
|
+
const image = record[key];
|
|
504
|
+
if (image && typeof image === "object") {
|
|
505
|
+
const data = image;
|
|
506
|
+
addImage(data.src, data.alt);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (Array.isArray(record.images)) {
|
|
510
|
+
for (const image of record.images) {
|
|
511
|
+
if (image && typeof image === "object") {
|
|
512
|
+
const data = image;
|
|
513
|
+
addImage(data.src, data.alt);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (typeof record.videoSrc === "string" && record.videoSrc) {
|
|
518
|
+
const htmlChildren = [
|
|
519
|
+
{
|
|
520
|
+
type: "htmlElement",
|
|
521
|
+
tagName: "video",
|
|
522
|
+
selfClosing: false,
|
|
523
|
+
children: [],
|
|
524
|
+
attributes: {
|
|
525
|
+
src: record.videoSrc,
|
|
526
|
+
...typeof record.posterSrc === "string" ? { poster: record.posterSrc } : {}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
];
|
|
530
|
+
contents.push({ type: "htmlBlock", htmlChildren, rawHtml: stringifyHtmlNodes(htmlChildren) });
|
|
531
|
+
}
|
|
532
|
+
return contents;
|
|
533
|
+
}
|
|
534
|
+
function summarizeBlock(block, mode) {
|
|
535
|
+
const title = block.sourceHeading ? extractPlainText(block.sourceHeading) : block.title ?? "";
|
|
536
|
+
const nativeFeature = NATIVE_FEATURE_TEMPLATES.has(block.template ?? "");
|
|
537
|
+
const authoredTemplate = mode === "headings-and-features" && !!block.template && !block.autoTemplate && (!block.sourceHeading || !!block.sourceHeading.templateAnnotation || !!block.promotedBodyAnnotation);
|
|
538
|
+
const keepTemplate = nativeFeature || authoredTemplate;
|
|
539
|
+
const tableInputs = keepTemplate ? null : deriveTemplateInputs("dataTable", title, block.contents);
|
|
540
|
+
const featureContents = extractFeatureContents(block.contents, keepTemplate || !!tableInputs);
|
|
541
|
+
const brief = mode === "brief" ? briefText(
|
|
542
|
+
(block.contents ?? []).filter((node) => ["paragraph", "list", "blockquote"].includes(node.type)).map(extractPlainText).join(" ")
|
|
543
|
+
) : "";
|
|
544
|
+
const template = keepTemplate ? block.template : tableInputs ? "dataTable" : brief ? "content" : "sectionHeader";
|
|
545
|
+
const contents = [
|
|
546
|
+
...brief && !nativeFeature && !tableInputs ? [{ type: "paragraph", children: [{ type: "text", value: brief }] }] : [],
|
|
547
|
+
...featureContents,
|
|
548
|
+
...typedMediaContents(block)
|
|
549
|
+
];
|
|
550
|
+
const base = {
|
|
551
|
+
id: block.id,
|
|
552
|
+
startTime: block.startTime,
|
|
553
|
+
duration: block.duration,
|
|
554
|
+
audioSegment: block.audioSegment,
|
|
555
|
+
title,
|
|
556
|
+
template,
|
|
557
|
+
contents,
|
|
558
|
+
summaryMode: mode,
|
|
559
|
+
...mode === "headings-and-features" && !keepTemplate && !tableInputs && contents.length ? { summaryLayout: "feature" } : {},
|
|
560
|
+
sourceBlockId: block.id,
|
|
561
|
+
sourceCharOffset: 0,
|
|
562
|
+
sourceStartTime: block.startTime,
|
|
563
|
+
sourceDuration: block.duration,
|
|
564
|
+
transition: block.transition,
|
|
565
|
+
media: block.media,
|
|
566
|
+
...block.sourceHeading ? { sourceHeading: { ...block.sourceHeading, templateAnnotation: { template } } } : {}
|
|
567
|
+
};
|
|
568
|
+
if (!keepTemplate && !tableInputs) return base;
|
|
569
|
+
const headingInputs = authoredTemplate ? {
|
|
570
|
+
body: "",
|
|
571
|
+
subtitle: "",
|
|
572
|
+
description: template === "statHighlight" ? title : "",
|
|
573
|
+
explanation: "",
|
|
574
|
+
definition: "",
|
|
575
|
+
detail: "",
|
|
576
|
+
...template === "list" ? { items: [] } : {},
|
|
577
|
+
...template === "imageWithCaption" || template === "videoWithCaption" ? { caption: title } : {},
|
|
578
|
+
...template === "quote" ? { quote: title } : {},
|
|
579
|
+
...template === "pullQuote" || template === "fullBleedQuote" ? { text: title } : {}
|
|
580
|
+
} : {};
|
|
581
|
+
return {
|
|
582
|
+
...keepTemplate ? withoutProse(block) : {},
|
|
583
|
+
...base,
|
|
584
|
+
...headingInputs,
|
|
585
|
+
children: keepTemplate && isContainerTemplate(template) ? block.children : void 0,
|
|
586
|
+
templateData: tableInputs ?? { ...withoutProse(block.templateData), ...headingInputs },
|
|
587
|
+
templateOverrides: keepTemplate ? withoutProse(
|
|
588
|
+
template === "list" ? Object.fromEntries(
|
|
589
|
+
Object.entries(block.templateOverrides ?? {}).filter(([key]) => key !== "items")
|
|
590
|
+
) : block.templateOverrides
|
|
591
|
+
) : void 0
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function summarizeHighlight(block, source) {
|
|
595
|
+
const summary = {
|
|
596
|
+
...block,
|
|
597
|
+
summaryMode: "brief",
|
|
598
|
+
contents: [...extractFeatureContents(source.contents), ...typedMediaContents(source)],
|
|
599
|
+
media: source.media
|
|
600
|
+
};
|
|
601
|
+
if (summary.template === "quote") return { ...summary, quote: briefText(summary.quote) };
|
|
602
|
+
if (summary.template === "statHighlight")
|
|
603
|
+
return { ...summary, description: briefText(summary.description), detail: void 0 };
|
|
604
|
+
if (summary.template === "fullBleedQuote" || summary.template === "pullQuote")
|
|
605
|
+
return { ...summary, text: briefText(summary.text) };
|
|
606
|
+
return summary;
|
|
607
|
+
}
|
|
608
|
+
|
|
356
609
|
// src/transform/templateSelector.ts
|
|
357
610
|
var ACCENT_POSITIONS = [
|
|
358
611
|
"left-strip",
|
|
@@ -446,7 +699,8 @@ function buildBlockSequence(analyzed, selected, config, images, rng) {
|
|
|
446
699
|
const ab = analyzed[i];
|
|
447
700
|
const extractions = blockExtractions.get(i);
|
|
448
701
|
if (!extractions || extractions.length === 0) {
|
|
449
|
-
blocks.push(ab.block);
|
|
702
|
+
blocks.push(config.contentMode === "brief" ? summarizeBlock(ab.block, "brief") : ab.block);
|
|
703
|
+
if (config.contentMode === "brief") transformedCount++;
|
|
450
704
|
continue;
|
|
451
705
|
}
|
|
452
706
|
if (config.insertSectionHeaders && ab.block.title) {
|
|
@@ -495,6 +749,9 @@ function buildBlockSequence(analyzed, selected, config, images, rng) {
|
|
|
495
749
|
const translated = translateTemplateBlock(templateBlock, remapTarget);
|
|
496
750
|
if (translated) templateBlock = translated;
|
|
497
751
|
}
|
|
752
|
+
if (config.contentMode === "brief") {
|
|
753
|
+
templateBlock = summarizeHighlight(templateBlock, ab.block);
|
|
754
|
+
}
|
|
498
755
|
if (config.transitionStyle !== "cut") {
|
|
499
756
|
const transitionType = config.transitionStyle === "mixed" ? rng.pick(["fade", "dissolve"]) ?? "fade" : config.transitionStyle;
|
|
500
757
|
templateBlock.transition = {
|
|
@@ -780,12 +1037,15 @@ function applyTransform(doc, style, options) {
|
|
|
780
1037
|
const config = options?.overrides ? { ...baseConfig, ...options.overrides, id: baseConfig.id } : baseConfig;
|
|
781
1038
|
const seed = options?.seed ?? hashString(doc.articleId || "transform");
|
|
782
1039
|
const images = options?.images ?? extractDocImages(doc.blocks);
|
|
783
|
-
const analyzed =
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1040
|
+
const analyzed = (config.contentMode ? analyzeFlatBlocks : analyzeBlocks)(
|
|
1041
|
+
config.contentMode ? flattenRenderableBlocks(doc.blocks) : doc.blocks,
|
|
1042
|
+
{
|
|
1043
|
+
minConfidence: config.minConfidence,
|
|
1044
|
+
types: config.preferredTypes
|
|
1045
|
+
}
|
|
1046
|
+
);
|
|
787
1047
|
const hasContent = analyzed.some((ab) => ab.bodyWordCount >= 5);
|
|
788
|
-
if (!hasContent) {
|
|
1048
|
+
if (!hasContent && !config.contentMode) {
|
|
789
1049
|
return {
|
|
790
1050
|
doc: { ...doc },
|
|
791
1051
|
stats: {
|
|
@@ -795,7 +1055,11 @@ function applyTransform(doc, style, options) {
|
|
|
795
1055
|
}
|
|
796
1056
|
};
|
|
797
1057
|
}
|
|
798
|
-
const selection =
|
|
1058
|
+
const selection = config.contentMode === "headings-and-features" ? {
|
|
1059
|
+
blocks: analyzed.filter((ab) => ab.block.sourceHeading || ab.block.title).map((ab) => summarizeBlock(ab.block, "headings-and-features")),
|
|
1060
|
+
transformedCount: analyzed.filter((ab) => ab.block.sourceHeading || ab.block.title).length,
|
|
1061
|
+
insertedCount: 0
|
|
1062
|
+
} : selectAndBuild(analyzed, config, images, seed);
|
|
799
1063
|
let paced = selection.blocks;
|
|
800
1064
|
let pacingInserted = 0;
|
|
801
1065
|
const docTitle = firstTitle(doc);
|
|
@@ -826,7 +1090,13 @@ function applyTransform(doc, style, options) {
|
|
|
826
1090
|
const transformedDoc = {
|
|
827
1091
|
...doc,
|
|
828
1092
|
blocks: timedBlocks,
|
|
829
|
-
themeId: options?.themeId ?? doc.themeId ?? config.suggestedThemeId
|
|
1093
|
+
themeId: options?.themeId ?? doc.themeId ?? config.suggestedThemeId,
|
|
1094
|
+
...config.contentMode && doc.startBlock ? {
|
|
1095
|
+
startBlock: {
|
|
1096
|
+
...doc.startBlock,
|
|
1097
|
+
subtitle: config.contentMode === "brief" ? briefText(doc.startBlock.subtitle ?? "") : void 0
|
|
1098
|
+
}
|
|
1099
|
+
} : {}
|
|
830
1100
|
};
|
|
831
1101
|
return {
|
|
832
1102
|
doc: transformedDoc,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildNarrationScript,
|
|
3
3
|
parseNarrationTimingJson
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6H2HBBFC.js";
|
|
5
5
|
import {
|
|
6
6
|
SHAPE_NAMES,
|
|
7
7
|
buildChartData,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
templateRegistry,
|
|
33
33
|
writeCustomTemplatesToFrontmatter,
|
|
34
34
|
writeCustomThemesToFrontmatter
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-YPI2VJSB.js";
|
|
36
36
|
import {
|
|
37
37
|
ASCII_TREE_VOCAB,
|
|
38
38
|
ASCII_VOCAB,
|
|
@@ -954,8 +954,10 @@ function blockToSlide(block, index, knownTemplates, documentTitle) {
|
|
|
954
954
|
// authored rich elements (Mermaid fences today; other media can follow)
|
|
955
955
|
// independently of the selected visual template.
|
|
956
956
|
...block.contents ? { contents: block.contents } : {},
|
|
957
|
-
// Custom templates
|
|
958
|
-
|
|
957
|
+
// Custom templates consume child blocks through tokens. Compact summaries
|
|
958
|
+
// also retain the children owned by a diagram/drawing feature.
|
|
959
|
+
...block.children && (isCustomTemplate || block.summaryMode && isContainerTemplate(template)) ? { children: block.children } : {},
|
|
960
|
+
...block.summaryMode && block.layers ? { layers: block.layers } : {},
|
|
959
961
|
...defaults,
|
|
960
962
|
...extraFields,
|
|
961
963
|
// Structured body data (```json data fences, GFM tables for dataTable)
|
|
@@ -1031,7 +1033,9 @@ function buildPreviewDoc(doc, options) {
|
|
|
1031
1033
|
const block = flat[i];
|
|
1032
1034
|
const blockImages = extractBlockImages(block.contents);
|
|
1033
1035
|
const slide = blockToSlide(block, i, knownTemplates, documentTitle);
|
|
1034
|
-
if (
|
|
1036
|
+
if (block.summaryMode) {
|
|
1037
|
+
for (const img of blockImages) usedImageSrcs.add(img.src);
|
|
1038
|
+
} else if (blockImages.length > 0 && slide.template === "sectionHeader") {
|
|
1035
1039
|
const img = blockImages[0];
|
|
1036
1040
|
usedImageSrcs.add(img.src);
|
|
1037
1041
|
slide.template = "imageWithCaption";
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
buildNarrationScript,
|
|
8
8
|
expectedSyllablesAt,
|
|
9
9
|
wordPosAtExpectedSyllables
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6H2HBBFC.js";
|
|
11
11
|
import {
|
|
12
12
|
flattenRenderableBlocks
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YPI2VJSB.js";
|
|
14
14
|
|
|
15
15
|
// src/narration/types.ts
|
|
16
16
|
var DEFAULT_FEATURE_CONFIG = Object.freeze({
|
|
@@ -393,6 +393,103 @@ function tooLarge(maxBytes) {
|
|
|
393
393
|
);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
// src/markdown/condenseMarkdown.ts
|
|
397
|
+
function cellSource(source) {
|
|
398
|
+
let text = trimPadding(source);
|
|
399
|
+
if (text.startsWith("|")) text = text.slice(1);
|
|
400
|
+
if (text.endsWith("|")) {
|
|
401
|
+
let backslashes = 0;
|
|
402
|
+
for (let i = text.length - 2; i >= 0 && text[i] === "\\"; i--) backslashes++;
|
|
403
|
+
if (backslashes % 2 === 0) text = text.slice(0, -1);
|
|
404
|
+
}
|
|
405
|
+
return trimPadding(text);
|
|
406
|
+
}
|
|
407
|
+
function trimPadding(source) {
|
|
408
|
+
let start = 0;
|
|
409
|
+
let end = source.length;
|
|
410
|
+
while (start < end && (source[start] === " " || source[start] === " ")) start++;
|
|
411
|
+
while (end > start && (source[end - 1] === " " || source[end - 1] === " ")) end--;
|
|
412
|
+
return source.slice(start, end);
|
|
413
|
+
}
|
|
414
|
+
function tableEdits(source, table) {
|
|
415
|
+
const edits = [];
|
|
416
|
+
for (const row of table.children) {
|
|
417
|
+
const start2 = row.position?.start.offset;
|
|
418
|
+
const end2 = row.position?.end.offset;
|
|
419
|
+
if (start2 === void 0 || end2 === void 0) throw new Error("Table row has no source span");
|
|
420
|
+
let boundary = start2;
|
|
421
|
+
const cells2 = [];
|
|
422
|
+
for (const cell of row.children) {
|
|
423
|
+
const a = cell.position?.start.offset;
|
|
424
|
+
const b = cell.position?.end.offset;
|
|
425
|
+
if (a !== boundary || b === void 0 || b < a) {
|
|
426
|
+
throw new Error("Table cell spans do not cover their row");
|
|
427
|
+
}
|
|
428
|
+
cells2.push(cellSource(source.slice(a, b)));
|
|
429
|
+
boundary = b;
|
|
430
|
+
}
|
|
431
|
+
if (boundary !== end2) throw new Error("Table cell spans do not cover their row");
|
|
432
|
+
edits.push({ start: start2, end: end2, text: `| ${cells2.join(" | ")} |` });
|
|
433
|
+
}
|
|
434
|
+
const headerEnd = table.children[0]?.position?.end.offset;
|
|
435
|
+
if (headerEnd === void 0) throw new Error("Table has no header span");
|
|
436
|
+
const start = source.indexOf("\n", headerEnd) + 1;
|
|
437
|
+
const newline = source.indexOf("\n", start);
|
|
438
|
+
let end = newline === -1 ? source.length : newline;
|
|
439
|
+
if (source[end - 1] === "\r") end--;
|
|
440
|
+
const divider = source.slice(start, end);
|
|
441
|
+
const prefix = /^[ \t>]*/.exec(divider)?.[0] ?? "";
|
|
442
|
+
if (start === 0 || !/^[|:\- \t]+$/.test(divider.slice(prefix.length))) {
|
|
443
|
+
throw new Error("Table has no recognizable delimiter row");
|
|
444
|
+
}
|
|
445
|
+
const columns = table.children[0]?.children.length ?? 0;
|
|
446
|
+
const cells = Array.from({ length: columns }, (_, i) => {
|
|
447
|
+
const align = table.align?.[i];
|
|
448
|
+
return `${align === "left" || align === "center" ? ":" : ""}---${align === "right" || align === "center" ? ":" : ""}`;
|
|
449
|
+
});
|
|
450
|
+
edits.push({ start: start + prefix.length, end, text: `| ${cells.join(" | ")} |` });
|
|
451
|
+
return edits;
|
|
452
|
+
}
|
|
453
|
+
function condenseMarkdownSource(source) {
|
|
454
|
+
const { frontmatter, body } = splitFrontmatterBlock(source);
|
|
455
|
+
const options = { frontmatter: false, parseHtml: false };
|
|
456
|
+
const document = parseMarkdown(body, options);
|
|
457
|
+
const edits = [];
|
|
458
|
+
const visit = (node) => {
|
|
459
|
+
if (node.type === "table") {
|
|
460
|
+
edits.push(...tableEdits(body, node));
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (node.type === "thematicBreak") {
|
|
464
|
+
const start = node.position?.start.offset;
|
|
465
|
+
const end = node.position?.end.offset;
|
|
466
|
+
if (start !== void 0 && end !== void 0) {
|
|
467
|
+
const marker = body.slice(start, end).match(/[-*_]/)?.[0];
|
|
468
|
+
if (marker) edits.push({ start, end, text: marker.repeat(3) });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
for (const child of getChildren(node)) visit(child);
|
|
472
|
+
};
|
|
473
|
+
visit(document);
|
|
474
|
+
const parts = [];
|
|
475
|
+
let boundary = 0;
|
|
476
|
+
let changed = false;
|
|
477
|
+
for (const edit of edits.sort((a, b) => a.start - b.start)) {
|
|
478
|
+
if (edit.start < boundary) throw new Error("Overlapping Markdown condensation edits");
|
|
479
|
+
parts.push(body.slice(boundary, edit.start), edit.text);
|
|
480
|
+
changed || (changed = body.slice(edit.start, edit.end) !== edit.text);
|
|
481
|
+
boundary = edit.end;
|
|
482
|
+
}
|
|
483
|
+
if (!changed) return source;
|
|
484
|
+
parts.push(body.slice(boundary));
|
|
485
|
+
const output = parts.join("");
|
|
486
|
+
const withoutPositions = (key, value) => key === "position" ? void 0 : value;
|
|
487
|
+
if (JSON.stringify(document, withoutPositions) !== JSON.stringify(parseMarkdown(output, options), withoutPositions)) {
|
|
488
|
+
throw new Error("Condensed Markdown no longer parses to an equivalent document");
|
|
489
|
+
}
|
|
490
|
+
return (frontmatter ?? "") + output;
|
|
491
|
+
}
|
|
492
|
+
|
|
396
493
|
// src/markdown/sourceTransforms.ts
|
|
397
494
|
var DEFAULT_WRAP_WIDTH = 80;
|
|
398
495
|
var MIN_WRAP_WIDTH = 20;
|
|
@@ -861,6 +958,25 @@ ${cleanedBody}` : fm;
|
|
|
861
958
|
edits: [{ start: 0, end: source.length, text: output }]
|
|
862
959
|
};
|
|
863
960
|
}
|
|
961
|
+
function condenseTransform(source, options) {
|
|
962
|
+
try {
|
|
963
|
+
const output = condenseMarkdownSource(source);
|
|
964
|
+
if (output === source) return noopResult(source, false);
|
|
965
|
+
return {
|
|
966
|
+
output,
|
|
967
|
+
changed: true,
|
|
968
|
+
degraded: false,
|
|
969
|
+
edits: [{ start: 0, end: source.length, text: output }]
|
|
970
|
+
};
|
|
971
|
+
} catch (err) {
|
|
972
|
+
return degrade(
|
|
973
|
+
"condense",
|
|
974
|
+
source,
|
|
975
|
+
options?.strict,
|
|
976
|
+
err instanceof Error ? err.message : String(err)
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
864
980
|
var MARKDOWN_SOURCE_TRANSFORMS = Object.freeze([
|
|
865
981
|
{
|
|
866
982
|
id: "unwrap",
|
|
@@ -879,6 +995,12 @@ var MARKDOWN_SOURCE_TRANSFORMS = Object.freeze([
|
|
|
879
995
|
label: "Clean up formatting",
|
|
880
996
|
description: "Re-serialize through the canonical house style: bullets, emphasis, headings, table padding, spacing.",
|
|
881
997
|
apply: (source, options) => cleanupTransform(source, options)
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
id: "condense",
|
|
1001
|
+
label: "Condense formatting",
|
|
1002
|
+
description: "Compact table padding and decorative dividers while preserving all document content.",
|
|
1003
|
+
apply: (source, options) => condenseTransform(source, options)
|
|
882
1004
|
}
|
|
883
1005
|
]);
|
|
884
1006
|
function applyMarkdownSourceTransform(id, source, options) {
|
|
@@ -1144,6 +1266,7 @@ export {
|
|
|
1144
1266
|
ResourcePolicyError,
|
|
1145
1267
|
isResourceUrlAllowed,
|
|
1146
1268
|
fetchResourceBytes,
|
|
1269
|
+
condenseMarkdownSource,
|
|
1147
1270
|
DEFAULT_WRAP_WIDTH,
|
|
1148
1271
|
MARKDOWN_SOURCE_TRANSFORMS,
|
|
1149
1272
|
applyMarkdownSourceTransform,
|