@bendyline/squisq 2.11.13 → 2.11.15
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-S4427XKY.js → chunk-4IQ2DHCI.js} +83 -10
- package/dist/{chunk-EWO2VLOS.js → chunk-4P557IPH.js} +284 -14
- package/dist/{chunk-X2TDC4E5.js → chunk-GQWIUBJU.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/{chunk-BR7ZMDWW.js → chunk-Z35TG4WR.js} +11 -6
- package/dist/doc/index.d.ts +17 -7
- package/dist/doc/index.js +4 -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 +6 -6
- package/dist/index.js +12 -8
- 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 +28 -8
- package/dist/narration/index.js +5 -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/timing/index.js +1 -1
- package/dist/transform/index.d.ts +8 -2
- package/dist/transform/index.js +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-44BJ42GT.js → chunk-4OQFLZ6G.js} +0 -0
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.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildNarrationScript,
|
|
3
3
|
parseNarrationTimingJson
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z35TG4WR.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,
|
|
@@ -98,6 +98,9 @@ import {
|
|
|
98
98
|
parseHtmlToNodes,
|
|
99
99
|
readFrontmatterThemeId
|
|
100
100
|
} from "./chunk-X7BNDUNI.js";
|
|
101
|
+
import {
|
|
102
|
+
buildCaptionTrack
|
|
103
|
+
} from "./chunk-4OQFLZ6G.js";
|
|
101
104
|
import {
|
|
102
105
|
estimateTimeFromText
|
|
103
106
|
} from "./chunk-GHOPHGOK.js";
|
|
@@ -954,8 +957,12 @@ function blockToSlide(block, index, knownTemplates, documentTitle) {
|
|
|
954
957
|
// authored rich elements (Mermaid fences today; other media can follow)
|
|
955
958
|
// independently of the selected visual template.
|
|
956
959
|
...block.contents ? { contents: block.contents } : {},
|
|
957
|
-
// Custom templates
|
|
958
|
-
|
|
960
|
+
// Custom templates consume child blocks through tokens, and container
|
|
961
|
+
// templates (diagram, drawing, layout) draw their children as the canvas:
|
|
962
|
+
// without them a drawing renders as an empty frame in the slideshow,
|
|
963
|
+
// video export, and PPTX. They are never flattened into their own slides.
|
|
964
|
+
...block.children && (isCustomTemplate || isContainerTemplate(template)) ? { children: block.children } : {},
|
|
965
|
+
...block.summaryMode && block.layers ? { layers: block.layers } : {},
|
|
959
966
|
...defaults,
|
|
960
967
|
...extraFields,
|
|
961
968
|
// Structured body data (```json data fences, GFM tables for dataTable)
|
|
@@ -1031,7 +1038,9 @@ function buildPreviewDoc(doc, options) {
|
|
|
1031
1038
|
const block = flat[i];
|
|
1032
1039
|
const blockImages = extractBlockImages(block.contents);
|
|
1033
1040
|
const slide = blockToSlide(block, i, knownTemplates, documentTitle);
|
|
1034
|
-
if (
|
|
1041
|
+
if (block.summaryMode) {
|
|
1042
|
+
for (const img of blockImages) usedImageSrcs.add(img.src);
|
|
1043
|
+
} else if (blockImages.length > 0 && slide.template === "sectionHeader") {
|
|
1035
1044
|
const img = blockImages[0];
|
|
1036
1045
|
usedImageSrcs.add(img.src);
|
|
1037
1046
|
slide.template = "imageWithCaption";
|
|
@@ -4293,6 +4302,34 @@ function mapRangesToPlayed(ranges, map2) {
|
|
|
4293
4302
|
}
|
|
4294
4303
|
return mapped;
|
|
4295
4304
|
}
|
|
4305
|
+
function narrationCaptions(doc, clip, timing, timeMap, playedDuration) {
|
|
4306
|
+
if (timing.bookmarks.length === 0 || !(playedDuration > 0)) return null;
|
|
4307
|
+
const bookmarks = [];
|
|
4308
|
+
for (const bookmark of timing.bookmarks) {
|
|
4309
|
+
if (!timeMap) {
|
|
4310
|
+
if (bookmark.time <= playedDuration) bookmarks.push(bookmark);
|
|
4311
|
+
} else if (timeMap.sourceToPlayed(bookmark.time) !== null) {
|
|
4312
|
+
bookmarks.push({ ...bookmark, time: timeMap.snapSourceToPlayed(bookmark.time) });
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
if (bookmarks.length === 0) return null;
|
|
4316
|
+
bookmarks.sort((a, b) => a.time - b.time);
|
|
4317
|
+
const track = buildCaptionTrack(
|
|
4318
|
+
[
|
|
4319
|
+
{
|
|
4320
|
+
segment: {
|
|
4321
|
+
src: clip.src,
|
|
4322
|
+
name: "narration",
|
|
4323
|
+
startTime: clip.startAt,
|
|
4324
|
+
duration: playedDuration
|
|
4325
|
+
},
|
|
4326
|
+
timing: { sourceText: timing.sourceText, bookmarks, duration: playedDuration }
|
|
4327
|
+
}
|
|
4328
|
+
],
|
|
4329
|
+
doc.captions?.generatedAt ? { generatedAt: doc.captions.generatedAt } : {}
|
|
4330
|
+
);
|
|
4331
|
+
return track.phrases.length > 0 ? track : null;
|
|
4332
|
+
}
|
|
4296
4333
|
function retimeBlocks(blocks, ctx) {
|
|
4297
4334
|
return blocks.map((block) => {
|
|
4298
4335
|
const range = ctx.ranges.get(block.id);
|
|
@@ -4349,10 +4386,12 @@ async function applyNarrationTiming(doc, container) {
|
|
|
4349
4386
|
};
|
|
4350
4387
|
const blocks = retimeBlocks(doc.blocks, ctx);
|
|
4351
4388
|
const duration = Math.max(clip.startAt + playedDuration, ctx.cursor);
|
|
4389
|
+
const captions = narrationCaptions(doc, clip, timing, timeMap, playedDuration);
|
|
4352
4390
|
const retimed = {
|
|
4353
4391
|
...doc,
|
|
4354
4392
|
blocks,
|
|
4355
4393
|
duration,
|
|
4394
|
+
...captions ? { captions } : {},
|
|
4356
4395
|
...ctx.diagnostics.length > 0 ? { diagnostics: [...doc.diagnostics ?? [], ...ctx.diagnostics] } : {}
|
|
4357
4396
|
};
|
|
4358
4397
|
return { doc: retimed, applied: true, clipSrc: clip.src };
|
|
@@ -4809,6 +4848,10 @@ function collectKnownTemplates(doc, options) {
|
|
|
4809
4848
|
function isDrawingBlock(block) {
|
|
4810
4849
|
return resolveTemplateName(block.template ?? "") === "drawing";
|
|
4811
4850
|
}
|
|
4851
|
+
function isLayoutBlock(block) {
|
|
4852
|
+
return resolveTemplateName(block.template ?? "") === "layout";
|
|
4853
|
+
}
|
|
4854
|
+
var LAYOUT_IMAGE_LAYERS = /* @__PURE__ */ new Set(["image", "img"]);
|
|
4812
4855
|
function checkTemplate(block, parent, known, diagnostics) {
|
|
4813
4856
|
const requested = block.sourceHeading?.templateAnnotation?.template ?? block.sourceAnnotation?.template;
|
|
4814
4857
|
if (!requested) return;
|
|
@@ -4826,11 +4869,37 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
4826
4869
|
});
|
|
4827
4870
|
return;
|
|
4828
4871
|
}
|
|
4872
|
+
if (parent && isLayoutBlock(parent)) {
|
|
4873
|
+
if (LAYOUT_IMAGE_LAYERS.has(requested.toLowerCase())) {
|
|
4874
|
+
if (!block.templateOverrides?.src) {
|
|
4875
|
+
diagnostics.push({
|
|
4876
|
+
severity: "warning",
|
|
4877
|
+
code: "layout-image-missing-src",
|
|
4878
|
+
message: `Layout image "${block.id}" has no src= \u2014 the layer will be skipped`,
|
|
4879
|
+
blockId: block.id,
|
|
4880
|
+
...lineOf(block)
|
|
4881
|
+
});
|
|
4882
|
+
}
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
if (normalizeShapeKind(block.template ?? requested) || normalizeShapeKind(requested)) return;
|
|
4886
|
+
const suggestion2 = nearestName(requested, /* @__PURE__ */ new Set([...SHAPE_NAMES, "image"]), {
|
|
4887
|
+
map: resolveTemplateName
|
|
4888
|
+
});
|
|
4889
|
+
diagnostics.push({
|
|
4890
|
+
severity: "warning",
|
|
4891
|
+
code: "unknown-layer",
|
|
4892
|
+
message: `"${requested}" is not a layout layer \u2014 the child will be skipped. Layers are text, image, or a drawing shape` + (suggestion2 ? `. Did you mean "${suggestion2}"?` : ""),
|
|
4893
|
+
blockId: block.id,
|
|
4894
|
+
...lineOf(block)
|
|
4895
|
+
});
|
|
4896
|
+
return;
|
|
4897
|
+
}
|
|
4829
4898
|
if (isShapeName(requested)) {
|
|
4830
4899
|
diagnostics.push({
|
|
4831
4900
|
severity: "warning",
|
|
4832
4901
|
code: "shape-outside-drawing",
|
|
4833
|
-
message: `Shape "${requested}" is only recognized on a child heading of a {[drawing]} block`,
|
|
4902
|
+
message: `Shape "${requested}" is only recognized on a child heading of a {[drawing]} or {[layout]} block`,
|
|
4834
4903
|
blockId: block.id,
|
|
4835
4904
|
...lineOf(block)
|
|
4836
4905
|
});
|
|
@@ -4849,7 +4918,7 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
4849
4918
|
function checkTemplateInputs(block, parent, customNames, diagnostics) {
|
|
4850
4919
|
const requested = block.sourceHeading?.templateAnnotation?.template;
|
|
4851
4920
|
if (!requested) return;
|
|
4852
|
-
if (parent && isDrawingBlock(parent)) return;
|
|
4921
|
+
if (parent && (isDrawingBlock(parent) || isLayoutBlock(parent))) return;
|
|
4853
4922
|
if (isShapeName(requested)) return;
|
|
4854
4923
|
const canonical = resolveTemplateName(requested);
|
|
4855
4924
|
if (customNames.has(requested) || customNames.has(canonical)) return;
|
|
@@ -4874,6 +4943,7 @@ function isInputSatisfiable(canonical, block, key) {
|
|
|
4874
4943
|
return derived != null && key in derived;
|
|
4875
4944
|
}
|
|
4876
4945
|
var NUMERIC_SHAPE_KEYS = ["x", "y", "width", "height", "w", "h", "strokeWidth", "borderRadius"];
|
|
4946
|
+
var NUMERIC_LAYER_KEYS = [...NUMERIC_SHAPE_KEYS, "fontSize", "lineHeight", "padding"];
|
|
4877
4947
|
function checkAttributeCoercion(block, parent, diagnostics) {
|
|
4878
4948
|
const params = block.sourceHeading?.attributes?.params;
|
|
4879
4949
|
if (params) {
|
|
@@ -4888,9 +4958,12 @@ function checkAttributeCoercion(block, parent, diagnostics) {
|
|
|
4888
4958
|
});
|
|
4889
4959
|
}
|
|
4890
4960
|
}
|
|
4891
|
-
|
|
4961
|
+
const inDrawing = parent !== void 0 && isDrawingBlock(parent) && isShapeName(block.template);
|
|
4962
|
+
const inLayout = parent !== void 0 && isLayoutBlock(parent);
|
|
4963
|
+
if (inDrawing || inLayout) {
|
|
4892
4964
|
const overrides = block.templateOverrides ?? {};
|
|
4893
|
-
|
|
4965
|
+
const label = inLayout ? "Layout layer" : "Drawing shape";
|
|
4966
|
+
for (const key of inLayout ? NUMERIC_LAYER_KEYS : NUMERIC_SHAPE_KEYS) {
|
|
4894
4967
|
const raw = overrides[key];
|
|
4895
4968
|
if (raw == null) continue;
|
|
4896
4969
|
const value = raw.replace(/,\s*$/, "").trim();
|
|
@@ -4898,7 +4971,7 @@ function checkAttributeCoercion(block, parent, diagnostics) {
|
|
|
4898
4971
|
diagnostics.push({
|
|
4899
4972
|
severity: "warning",
|
|
4900
4973
|
code: "invalid-attribute",
|
|
4901
|
-
message:
|
|
4974
|
+
message: `${label} "${block.id}": "${key}" is not a number (${JSON.stringify(raw)})`,
|
|
4902
4975
|
blockId: block.id,
|
|
4903
4976
|
...lineOf(block)
|
|
4904
4977
|
});
|
|
@@ -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,
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
buildNarrationScript,
|
|
8
8
|
expectedSyllablesAt,
|
|
9
9
|
wordPosAtExpectedSyllables
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Z35TG4WR.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({
|