@bendyline/squisq 2.13.10 → 2.13.12
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/dist/{Doc-DqYt_b2b.d.ts → Doc-BhvotZx_.d.ts} +48 -2
- package/dist/{ImageEditDoc-CXXiXVJ-.d.ts → ImageEditDoc-D8hYio6b.d.ts} +1 -1
- package/dist/{chunk-S76W65BB.js → chunk-5DFFVW75.js} +1 -1
- package/dist/{chunk-DOOUVVOM.js → chunk-7MS26OXD.js} +4 -0
- package/dist/{chunk-XDOSE5M3.js → chunk-C4SB3NN4.js} +1 -1
- package/dist/{chunk-FXQ7C2FF.js → chunk-CQSCYJQU.js} +1 -1
- package/dist/{chunk-SG3HXPMF.js → chunk-DBVCSVNV.js} +2 -1
- package/dist/{chunk-WK6LEZ3U.js → chunk-IFZZI4XY.js} +132 -11
- package/dist/{chunk-WNEH3BZP.js → chunk-JNUNNQ74.js} +3 -3
- package/dist/{chunk-KRMAEYNM.js → chunk-OUSEEUEC.js} +389 -217
- package/dist/{chunk-LWNT6M4N.js → chunk-QJQQO3K4.js} +1857 -386
- package/dist/{chunk-DIY3NBIS.js → chunk-SGNWO7OF.js} +22 -1
- package/dist/{chunk-GLWQTZ4C.js → chunk-XEQALK5B.js} +1 -1
- package/dist/doc/index.d.ts +124 -9
- package/dist/doc/index.js +19 -7
- 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 +23 -11
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/jsonForm/index.js +3 -3
- package/dist/markdown/index.js +3 -3
- package/dist/{materializePageSection-S2hxZ_lO.d.ts → materializePageSection-bLfqIy6j.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 +5 -5
- package/dist/recommend/index.js +1 -1
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/index.js +2 -2
- package/dist/{themeLibrary-CFJbeItn.d.ts → themeLibrary-BIYE6aLw.d.ts} +1 -1
- package/dist/timing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +23 -14
- package/dist/transform/index.js +6 -6
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildNarrationScript,
|
|
3
|
+
buildSourceText,
|
|
3
4
|
coerceTemplateParams,
|
|
5
|
+
conceptBuildStepTexts,
|
|
4
6
|
flattenRenderableBlocks,
|
|
5
7
|
getPinnedBlockMeta,
|
|
6
8
|
markdownToDoc
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QJQQO3K4.js";
|
|
8
10
|
import {
|
|
9
11
|
deriveTemplateInputs,
|
|
10
12
|
extractEmbeddedVideos,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
isStandaloneHtmlVideo,
|
|
20
22
|
isTreeFence,
|
|
21
23
|
matchNumberHighlight
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-SGNWO7OF.js";
|
|
23
25
|
import {
|
|
24
26
|
extractContent,
|
|
25
27
|
mapElementToBlock,
|
|
@@ -29,13 +31,13 @@ import {
|
|
|
29
31
|
cloneAndFreezeData,
|
|
30
32
|
cloneData,
|
|
31
33
|
isTemplateBlock
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-JNUNNQ74.js";
|
|
33
35
|
import {
|
|
34
36
|
parseMarkdown
|
|
35
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-C4SB3NN4.js";
|
|
36
38
|
import {
|
|
37
39
|
isContainerTemplate
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-7MS26OXD.js";
|
|
39
41
|
import {
|
|
40
42
|
extractPlainText,
|
|
41
43
|
findBlockVideoEmbed,
|
|
@@ -67,7 +69,11 @@ var PRESENTATION_LAYOUTS = [
|
|
|
67
69
|
"list",
|
|
68
70
|
"comparison",
|
|
69
71
|
"steps",
|
|
70
|
-
"image"
|
|
72
|
+
"image",
|
|
73
|
+
"cards",
|
|
74
|
+
"process",
|
|
75
|
+
"cycle",
|
|
76
|
+
"layers"
|
|
71
77
|
];
|
|
72
78
|
var AUTHORED_PRESENTATION_LAYOUT = "authored";
|
|
73
79
|
var EMPHASIS_CHARACTERS = 60;
|
|
@@ -83,7 +89,12 @@ function presentationLayoutFits(layout, headline, points, passage) {
|
|
|
83
89
|
switch (layout) {
|
|
84
90
|
case "list":
|
|
85
91
|
case "steps":
|
|
92
|
+
case "cards":
|
|
93
|
+
case "process":
|
|
86
94
|
return points.length >= 2;
|
|
95
|
+
case "cycle":
|
|
96
|
+
case "layers":
|
|
97
|
+
return points.length >= 3;
|
|
87
98
|
case "comparison":
|
|
88
99
|
return points.length === 2;
|
|
89
100
|
case "definition":
|
|
@@ -289,96 +300,6 @@ function usesDynamicPresentation(doc) {
|
|
|
289
300
|
return typeof style === "string" && style.trim().toLowerCase().replace(/\s+/g, "-") === DYNAMIC_PRESENTATION_STYLE;
|
|
290
301
|
}
|
|
291
302
|
|
|
292
|
-
// src/transform/blockAnalyzer.ts
|
|
293
|
-
function contentsToPlainText(contents) {
|
|
294
|
-
return contents.map((node) => extractPlainText(node)).join("\n");
|
|
295
|
-
}
|
|
296
|
-
var PROSE_NODE_TYPES = /* @__PURE__ */ new Set(["paragraph", "blockquote", "heading"]);
|
|
297
|
-
function maskNonProse(stripped, contents) {
|
|
298
|
-
let masked = stripped;
|
|
299
|
-
let cursor = 0;
|
|
300
|
-
for (const node of contents) {
|
|
301
|
-
const text3 = stripMarkdown(extractPlainText(node));
|
|
302
|
-
if (!text3) continue;
|
|
303
|
-
const at = stripped.indexOf(text3, cursor);
|
|
304
|
-
if (at < 0) continue;
|
|
305
|
-
cursor = at + text3.length;
|
|
306
|
-
if (PROSE_NODE_TYPES.has(node.type)) continue;
|
|
307
|
-
masked = masked.slice(0, at) + text3.replace(/\S/g, " ") + masked.slice(cursor);
|
|
308
|
-
}
|
|
309
|
-
return masked;
|
|
310
|
-
}
|
|
311
|
-
function extractBlockImages(block, blockIndex) {
|
|
312
|
-
return extractImages(block.contents).map(({ src, alt }) => ({
|
|
313
|
-
src,
|
|
314
|
-
alt,
|
|
315
|
-
sourceBlockIndex: blockIndex
|
|
316
|
-
}));
|
|
317
|
-
}
|
|
318
|
-
function extractDocImages(blocks) {
|
|
319
|
-
const seen = /* @__PURE__ */ new Set();
|
|
320
|
-
const images = [];
|
|
321
|
-
function walk(blockList) {
|
|
322
|
-
for (const block of blockList) {
|
|
323
|
-
for (const img of extractImages(block.contents)) {
|
|
324
|
-
if (!seen.has(img.src)) {
|
|
325
|
-
seen.add(img.src);
|
|
326
|
-
images.push({ src: img.src, alt: img.alt });
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (block.children) {
|
|
330
|
-
walk(block.children);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
walk(blocks);
|
|
335
|
-
return images;
|
|
336
|
-
}
|
|
337
|
-
function collectLeafBlocks(blocks) {
|
|
338
|
-
const result = [];
|
|
339
|
-
for (const block of blocks) {
|
|
340
|
-
if (block.children && block.children.length > 0) {
|
|
341
|
-
if (!block.contents || block.contents.length === 0) {
|
|
342
|
-
result.push(block);
|
|
343
|
-
}
|
|
344
|
-
result.push(...collectLeafBlocks(block.children));
|
|
345
|
-
} else {
|
|
346
|
-
result.push(block);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return result;
|
|
350
|
-
}
|
|
351
|
-
function analyzeBlocks(blocks, options) {
|
|
352
|
-
return analyzeFlatBlocks(collectLeafBlocks(blocks), options);
|
|
353
|
-
}
|
|
354
|
-
function analyzeFlatBlocks(flat, options) {
|
|
355
|
-
const results = [];
|
|
356
|
-
for (const block of flat) {
|
|
357
|
-
let plainText = "";
|
|
358
|
-
if (block.contents && block.contents.length > 0) {
|
|
359
|
-
plainText = contentsToPlainText(block.contents);
|
|
360
|
-
} else if (block.title) {
|
|
361
|
-
plainText = block.title;
|
|
362
|
-
}
|
|
363
|
-
const stripped = stripMarkdown(plainText);
|
|
364
|
-
const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
|
|
365
|
-
const extraction = extractContent(
|
|
366
|
-
block.contents && block.contents.length > 0 ? maskNonProse(stripped, block.contents) : stripped,
|
|
367
|
-
options
|
|
368
|
-
);
|
|
369
|
-
const images = extractBlockImages(block, results.length);
|
|
370
|
-
results.push({
|
|
371
|
-
block,
|
|
372
|
-
plainText: stripped,
|
|
373
|
-
extractions: extraction.elements,
|
|
374
|
-
hasChildren: !!(block.children && block.children.length > 0),
|
|
375
|
-
bodyWordCount,
|
|
376
|
-
images
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
return results;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
303
|
// src/transform/styles/dynamicSlides.ts
|
|
383
304
|
var dynamicSlidesStyle = {
|
|
384
305
|
id: "dynamic-slides",
|
|
@@ -656,6 +577,96 @@ function getTransformStyleSummaries(registry) {
|
|
|
656
577
|
});
|
|
657
578
|
}
|
|
658
579
|
|
|
580
|
+
// src/transform/blockAnalyzer.ts
|
|
581
|
+
function contentsToPlainText(contents) {
|
|
582
|
+
return contents.map((node) => extractPlainText(node)).join("\n");
|
|
583
|
+
}
|
|
584
|
+
var PROSE_NODE_TYPES = /* @__PURE__ */ new Set(["paragraph", "blockquote", "heading"]);
|
|
585
|
+
function maskNonProse(stripped, contents) {
|
|
586
|
+
let masked = stripped;
|
|
587
|
+
let cursor = 0;
|
|
588
|
+
for (const node of contents) {
|
|
589
|
+
const text3 = stripMarkdown(extractPlainText(node));
|
|
590
|
+
if (!text3) continue;
|
|
591
|
+
const at = stripped.indexOf(text3, cursor);
|
|
592
|
+
if (at < 0) continue;
|
|
593
|
+
cursor = at + text3.length;
|
|
594
|
+
if (PROSE_NODE_TYPES.has(node.type)) continue;
|
|
595
|
+
masked = masked.slice(0, at) + text3.replace(/\S/g, " ") + masked.slice(cursor);
|
|
596
|
+
}
|
|
597
|
+
return masked;
|
|
598
|
+
}
|
|
599
|
+
function extractBlockImages(block, blockIndex) {
|
|
600
|
+
return extractImages(block.contents).map(({ src, alt }) => ({
|
|
601
|
+
src,
|
|
602
|
+
alt,
|
|
603
|
+
sourceBlockIndex: blockIndex
|
|
604
|
+
}));
|
|
605
|
+
}
|
|
606
|
+
function extractDocImages(blocks) {
|
|
607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
608
|
+
const images = [];
|
|
609
|
+
function walk(blockList) {
|
|
610
|
+
for (const block of blockList) {
|
|
611
|
+
for (const img of extractImages(block.contents)) {
|
|
612
|
+
if (!seen.has(img.src)) {
|
|
613
|
+
seen.add(img.src);
|
|
614
|
+
images.push({ src: img.src, alt: img.alt });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (block.children) {
|
|
618
|
+
walk(block.children);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
walk(blocks);
|
|
623
|
+
return images;
|
|
624
|
+
}
|
|
625
|
+
function collectLeafBlocks(blocks) {
|
|
626
|
+
const result = [];
|
|
627
|
+
for (const block of blocks) {
|
|
628
|
+
if (block.children && block.children.length > 0) {
|
|
629
|
+
if (!block.contents || block.contents.length === 0) {
|
|
630
|
+
result.push(block);
|
|
631
|
+
}
|
|
632
|
+
result.push(...collectLeafBlocks(block.children));
|
|
633
|
+
} else {
|
|
634
|
+
result.push(block);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
function analyzeBlocks(blocks, options) {
|
|
640
|
+
return analyzeFlatBlocks(collectLeafBlocks(blocks), options);
|
|
641
|
+
}
|
|
642
|
+
function analyzeFlatBlocks(flat, options) {
|
|
643
|
+
const results = [];
|
|
644
|
+
for (const block of flat) {
|
|
645
|
+
let plainText = "";
|
|
646
|
+
if (block.contents && block.contents.length > 0) {
|
|
647
|
+
plainText = contentsToPlainText(block.contents);
|
|
648
|
+
} else if (block.title) {
|
|
649
|
+
plainText = block.title;
|
|
650
|
+
}
|
|
651
|
+
const stripped = stripMarkdown(plainText);
|
|
652
|
+
const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
|
|
653
|
+
const extraction = extractContent(
|
|
654
|
+
block.contents && block.contents.length > 0 ? maskNonProse(stripped, block.contents) : stripped,
|
|
655
|
+
options
|
|
656
|
+
);
|
|
657
|
+
const images = extractBlockImages(block, results.length);
|
|
658
|
+
results.push({
|
|
659
|
+
block,
|
|
660
|
+
plainText: stripped,
|
|
661
|
+
extractions: extraction.elements,
|
|
662
|
+
hasChildren: !!(block.children && block.children.length > 0),
|
|
663
|
+
bodyWordCount,
|
|
664
|
+
images
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
return results;
|
|
668
|
+
}
|
|
669
|
+
|
|
659
670
|
// src/transform/timingAllocator.ts
|
|
660
671
|
var MIN_BLOCK_DURATION = 3;
|
|
661
672
|
var MAX_BLOCK_DURATION = 20;
|
|
@@ -1708,7 +1719,23 @@ function videoBlock(video, index, segmentId, duration) {
|
|
|
1708
1719
|
}
|
|
1709
1720
|
|
|
1710
1721
|
// src/transform/presentationMedia.ts
|
|
1722
|
+
function presentationBlockImages(doc) {
|
|
1723
|
+
const own = /* @__PURE__ */ new Map();
|
|
1724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1725
|
+
const claim = (block) => [
|
|
1726
|
+
...extractImages(block.contents).filter((image) => {
|
|
1727
|
+
if (seen.has(image.src)) return false;
|
|
1728
|
+
seen.add(image.src);
|
|
1729
|
+
return true;
|
|
1730
|
+
}),
|
|
1731
|
+
...(block.children ?? []).flatMap(claim)
|
|
1732
|
+
];
|
|
1733
|
+
for (const block of [...flattenRenderableBlocks(doc.blocks)].reverse())
|
|
1734
|
+
own.set(block.id, claim(block));
|
|
1735
|
+
return own;
|
|
1736
|
+
}
|
|
1711
1737
|
function mediaKind(node) {
|
|
1738
|
+
if (node.type === "table") return { kind: "table", continuous: true };
|
|
1712
1739
|
if (node.type === "code")
|
|
1713
1740
|
return isPictureFence(node) || isExplicitDiagramLang(node.lang) || isExplicitTreeLang(node.lang) || isExplicitTimelineLang(node.lang) ? { kind: "diagram", continuous: true } : null;
|
|
1714
1741
|
if (isStandaloneHtmlVideo(node)) return { kind: "video", continuous: true };
|
|
@@ -1719,17 +1746,39 @@ function mediaKind(node) {
|
|
|
1719
1746
|
function collectPresentationMedia(doc, sectionIds) {
|
|
1720
1747
|
const sections = new Set(sectionIds);
|
|
1721
1748
|
const result = /* @__PURE__ */ new Map();
|
|
1749
|
+
const pictures = presentationBlockImages(doc);
|
|
1722
1750
|
let owner = sectionIds[0];
|
|
1723
1751
|
for (const block of flattenRenderableBlocks(doc.blocks)) {
|
|
1724
1752
|
if (sections.has(block.id)) owner = block.id;
|
|
1725
1753
|
if (!owner) continue;
|
|
1754
|
+
const items = result.get(owner) ?? [];
|
|
1755
|
+
const count = (kind) => items.filter((item) => item.kind === kind).length;
|
|
1726
1756
|
for (const node of block.contents ?? []) {
|
|
1727
1757
|
const kind = mediaKind(node);
|
|
1728
1758
|
if (!kind) continue;
|
|
1729
|
-
const
|
|
1730
|
-
items.push({ ...kind, ref
|
|
1731
|
-
result.set(owner, items);
|
|
1759
|
+
const ref = kind.kind === "table" ? `${owner}#table-${count("table")}` : `${owner}#${count("video") + count("diagram")}`;
|
|
1760
|
+
items.push({ ...kind, ref, node });
|
|
1732
1761
|
}
|
|
1762
|
+
for (const { src, alt, width, height } of pictures.get(block.id) ?? [])
|
|
1763
|
+
items.push({
|
|
1764
|
+
kind: "image",
|
|
1765
|
+
continuous: true,
|
|
1766
|
+
ref: `${owner}#image-${count("image")}`,
|
|
1767
|
+
src,
|
|
1768
|
+
node: {
|
|
1769
|
+
type: "paragraph",
|
|
1770
|
+
children: [
|
|
1771
|
+
{
|
|
1772
|
+
type: "image",
|
|
1773
|
+
url: src,
|
|
1774
|
+
alt,
|
|
1775
|
+
...width !== void 0 ? { width } : {},
|
|
1776
|
+
...height !== void 0 ? { height } : {}
|
|
1777
|
+
}
|
|
1778
|
+
]
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
if (items.length) result.set(owner, items);
|
|
1733
1782
|
}
|
|
1734
1783
|
return result;
|
|
1735
1784
|
}
|
|
@@ -1759,7 +1808,23 @@ function presentationMediaNode(item, elapsed) {
|
|
|
1759
1808
|
// src/transform/presentationPlanner.ts
|
|
1760
1809
|
var SENTENCE_END = /[.!?][”"')\]]?$/u;
|
|
1761
1810
|
var SENTENCE_START = /^[\p{Lu}\p{N}"“‘'([]/u;
|
|
1762
|
-
var SEQUENCE_CUE = /^(?:(?:first|second|third|fourth)(?:ly)?|then|next|after that|
|
|
1811
|
+
var SEQUENCE_CUE = /^(?:(?:first|second|third|fourth)(?:ly)?|then|next|after\b|afterwards|at that point|eventually|finally|lastly)\b|^\d+[.)]\s/iu;
|
|
1812
|
+
var ORDINAL_OPENER = /^(?:(?:first|second|third|fourth)(?:ly)?|then|next|after that|afterwards|finally|lastly),?\s+/iu;
|
|
1813
|
+
function stagePoint(sentence) {
|
|
1814
|
+
const rest = sentence.replace(ORDINAL_OPENER, "");
|
|
1815
|
+
return pointText(rest === sentence ? sentence : `${rest[0].toUpperCase()}${rest.slice(1)}`);
|
|
1816
|
+
}
|
|
1817
|
+
var CYCLE_CUE = /\b(?:cycles?|cyclical|repeats?|over and over|again and again|loop)\b/iu;
|
|
1818
|
+
var DEFINES = /^(?:the |an? )?((?:[\p{Lu}][\p{L}'’-]*|[\p{Ll}][\p{L}'’-]*)(?: [\p{L}'’()-]+){0,3}?) (?:is|are|was|were) (?:an? |the )?(.{12,})$/u;
|
|
1819
|
+
var PRONOUN = /^(?:it|its|this|that|these|those|there|they|their|he|his|she|her|we|our|you|your|i|my|which|one|each|some|to|so|but|and|or)$/iu;
|
|
1820
|
+
function labelledPoint(sentence) {
|
|
1821
|
+
const match = sentence.replace(/\s+/gu, " ").trim().match(DEFINES);
|
|
1822
|
+
if (match && !PRONOUN.test(match[1].split(" ")[0])) {
|
|
1823
|
+
const label = match[1];
|
|
1824
|
+
return clip(`${label[0].toUpperCase()}${label.slice(1)}: ${match[2]}`, Infinity, 160);
|
|
1825
|
+
}
|
|
1826
|
+
return clip(sentence, Infinity, 160);
|
|
1827
|
+
}
|
|
1763
1828
|
function clip(text3, words, characters) {
|
|
1764
1829
|
const clean = text3.replace(/\s+/gu, " ").trim();
|
|
1765
1830
|
const parts = clean.split(" ");
|
|
@@ -1774,6 +1839,17 @@ function clip(text3, words, characters) {
|
|
|
1774
1839
|
var headlineText = (text3) => clip(text3, 12, 110);
|
|
1775
1840
|
var pointText = (text3) => clip(text3, Infinity, 180);
|
|
1776
1841
|
var captionText = (text3) => clip(text3, 18, 110);
|
|
1842
|
+
var FEATURE_POINTS = 2;
|
|
1843
|
+
var FEATURE_POINT_CHARACTERS = 100;
|
|
1844
|
+
function featurePointText(text3) {
|
|
1845
|
+
const clean = text3.replace(/\s+/gu, " ").trim();
|
|
1846
|
+
if (clean.length <= FEATURE_POINT_CHARACTERS) return clean;
|
|
1847
|
+
const head = clean.slice(0, FEATURE_POINT_CHARACTERS);
|
|
1848
|
+
const clause = Math.max(
|
|
1849
|
+
...[",", ";", ":", " \u2013", " \u2014", " - "].map((mark) => head.lastIndexOf(mark))
|
|
1850
|
+
);
|
|
1851
|
+
return clause > FEATURE_POINT_CHARACTERS * 0.4 ? clean.slice(0, clause).replace(/[\s,;:–—-]+$/u, "") + "\u2026" : clip(clean, Infinity, FEATURE_POINT_CHARACTERS);
|
|
1852
|
+
}
|
|
1777
1853
|
function sentencesIn(sourceText, start, end) {
|
|
1778
1854
|
const passage = sourceText.slice(start, end);
|
|
1779
1855
|
const sentences = [];
|
|
@@ -1816,24 +1892,31 @@ function chunkWords(words, limit) {
|
|
|
1816
1892
|
}
|
|
1817
1893
|
return chunks;
|
|
1818
1894
|
}
|
|
1819
|
-
function
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1895
|
+
function chunkPassage(words, limit, feature) {
|
|
1896
|
+
const prose = words.flatMap((word, index) => feature(word) ? [] : [index]);
|
|
1897
|
+
if (!prose.length) return words.length ? [[0, words.length]] : [];
|
|
1898
|
+
const chunks = chunkWords(
|
|
1899
|
+
prose.map((index) => words[index]),
|
|
1900
|
+
limit
|
|
1901
|
+
).map(([first, last]) => [prose[first], prose[last - 1] + 1]);
|
|
1902
|
+
chunks[0][0] = 0;
|
|
1903
|
+
chunks.forEach((chunk, index) => {
|
|
1904
|
+
chunk[1] = chunks[index + 1]?.[0] ?? words.length;
|
|
1905
|
+
});
|
|
1906
|
+
return chunks;
|
|
1907
|
+
}
|
|
1908
|
+
function featureSpans(sourceText, from, to, items) {
|
|
1909
|
+
const spans = [];
|
|
1910
|
+
let cursor = from;
|
|
1911
|
+
for (const item of items) {
|
|
1912
|
+
if (item.kind === "image") continue;
|
|
1913
|
+
const text3 = extractPlainText(item.node).trim();
|
|
1914
|
+
const at = text3 ? sourceText.indexOf(text3, cursor) : -1;
|
|
1915
|
+
if (at < 0 || at + text3.length > to) continue;
|
|
1916
|
+
spans.push([at, at + text3.length]);
|
|
1917
|
+
cursor = at + text3.length;
|
|
1835
1918
|
}
|
|
1836
|
-
return
|
|
1919
|
+
return spans;
|
|
1837
1920
|
}
|
|
1838
1921
|
function field(block, key) {
|
|
1839
1922
|
if (!block || typeof block !== "object") return void 0;
|
|
@@ -1848,7 +1931,7 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1848
1931
|
throw new Error(
|
|
1849
1932
|
"This document is too long for one presentation. Split it into shorter documents."
|
|
1850
1933
|
);
|
|
1851
|
-
const
|
|
1934
|
+
const sectionMedia = collectPresentationMedia(
|
|
1852
1935
|
doc,
|
|
1853
1936
|
script.blocks.map((block) => block.blockId)
|
|
1854
1937
|
);
|
|
@@ -1881,8 +1964,22 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1881
1964
|
continue;
|
|
1882
1965
|
}
|
|
1883
1966
|
const preferred = hint?.layout;
|
|
1884
|
-
const
|
|
1885
|
-
const
|
|
1967
|
+
const media = sectionMedia.get(block.blockId) ?? [];
|
|
1968
|
+
const spans = featureSpans(script.sourceText, block.charStart, block.charEnd, media);
|
|
1969
|
+
const inFeature = (from, to) => spans.some(([s, e]) => from < e && to > s);
|
|
1970
|
+
const chunks = chunkPassage(
|
|
1971
|
+
words,
|
|
1972
|
+
wordLimit,
|
|
1973
|
+
(word) => inFeature(block.charStart + word.index, block.charStart + word.index + word[0].length)
|
|
1974
|
+
);
|
|
1975
|
+
for (let k = chunks.length - 1; k > 0; k--) {
|
|
1976
|
+
const opening = script.sourceText.slice(block.charStart + words[chunks[k][0]].index);
|
|
1977
|
+
if (SEQUENCE_CUE.test(opening) && chunks[k][1] - chunks[k - 1][0] <= wordLimit * 2) {
|
|
1978
|
+
chunks[k - 1][1] = chunks[k][1];
|
|
1979
|
+
chunks.splice(k, 1);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
const images = media.filter((item) => item.kind === "image");
|
|
1886
1983
|
const opensDeck = beats.length === 0;
|
|
1887
1984
|
for (const [chunk, [first, last]] of chunks.entries()) {
|
|
1888
1985
|
const fromEnd = chunks.length - 1 - chunk;
|
|
@@ -1890,8 +1987,18 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1890
1987
|
const start = block.charStart + words[first].index;
|
|
1891
1988
|
const end = block.charStart + words[last - 1].index + words[last - 1][0].length;
|
|
1892
1989
|
const passage = script.sourceText.slice(start, end);
|
|
1990
|
+
let prose = "";
|
|
1991
|
+
let cursor = start;
|
|
1992
|
+
for (const [s, e] of spans) {
|
|
1993
|
+
if (e <= cursor || s >= end) continue;
|
|
1994
|
+
prose += script.sourceText.slice(cursor, Math.max(cursor, s)) + "\n";
|
|
1995
|
+
cursor = Math.min(end, e);
|
|
1996
|
+
}
|
|
1997
|
+
prose = (prose + script.sourceText.slice(cursor, end)).trim() || passage;
|
|
1893
1998
|
const index = beats.length;
|
|
1894
|
-
const sentences = sentencesIn(script.sourceText, start, end)
|
|
1999
|
+
const sentences = sentencesIn(script.sourceText, start, end).filter(
|
|
2000
|
+
(sentence) => !inFeature(sentence.start, sentence.end)
|
|
2001
|
+
);
|
|
1895
2002
|
const body = sentences.filter((sentence) => sentence.start >= headingEnd);
|
|
1896
2003
|
let headline;
|
|
1897
2004
|
let shown;
|
|
@@ -1899,12 +2006,12 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1899
2006
|
headline = headlineText(heading);
|
|
1900
2007
|
shown = body.filter((sentence) => comparable(sentence.text) !== comparable(heading));
|
|
1901
2008
|
} else {
|
|
1902
|
-
const
|
|
2009
|
+
const suggested2 = transformNarratedSegment(
|
|
1903
2010
|
{
|
|
1904
2011
|
articleId: doc.articleId,
|
|
1905
2012
|
segmentId: `presentation-${index}`,
|
|
1906
2013
|
title: "",
|
|
1907
|
-
text:
|
|
2014
|
+
text: prose,
|
|
1908
2015
|
duration: Math.max(4, (last - first) / 2.5),
|
|
1909
2016
|
audioSrc: "",
|
|
1910
2017
|
includeTitleBlock: false
|
|
@@ -1912,23 +2019,24 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1912
2019
|
{ slidesPerMinute: 4, minConfidence: 0.25, style: "documentary" }
|
|
1913
2020
|
).doc.blocks[0];
|
|
1914
2021
|
headline = headlineText(
|
|
1915
|
-
field(
|
|
2022
|
+
field(suggested2, "fact") ?? field(suggested2, "description") ?? field(suggested2, "quote") ?? sentences[0]?.text ?? prose
|
|
1916
2023
|
);
|
|
1917
|
-
if (!headline) headline = headlineText(
|
|
2024
|
+
if (!headline) headline = headlineText(prose);
|
|
1918
2025
|
shown = body.filter((sentence) => !repeats(sentence.text, headline));
|
|
1919
2026
|
}
|
|
1920
2027
|
const steps = shown.filter((sentence) => SEQUENCE_CUE.test(sentence.text));
|
|
1921
2028
|
const sequence = steps.length >= 2;
|
|
1922
2029
|
const textPoints = shown.slice(0, 3).map((item) => pointText(item.text));
|
|
1923
|
-
const stepPoints = sequence ? steps.slice(0,
|
|
2030
|
+
const stepPoints = sequence ? steps.slice(0, 4).map((item) => stagePoint(item.text)) : textPoints;
|
|
2031
|
+
const cardPoints = shown.slice(0, 4).map((item) => labelledPoint(item.text));
|
|
1924
2032
|
const custom = chunk === 0 ? wording : void 0;
|
|
1925
2033
|
if (custom?.headline) headline = custom.headline;
|
|
1926
|
-
const pointsFor = (layout2) => custom?.points ?? (layout2 === "
|
|
2034
|
+
const pointsFor = (layout2) => custom?.points ?? (["steps", "process", "cycle"].includes(layout2) ? stepPoints : layout2 === "cards" ? cardPoints : textPoints);
|
|
1927
2035
|
const quote = custom?.headline ? void 0 : body.find((sentence) => sentence.text.length <= 110)?.text;
|
|
1928
2036
|
const headlineFor = (layout2) => layout2 === "quote" && quote ? quote : headline;
|
|
1929
|
-
let layout = index === 0 ? "title" : textPoints.length >= 2
|
|
1930
|
-
if (sequence) layout = "steps";
|
|
1931
|
-
else if (textPoints.length === 2 && /\b(versus|whereas|compared|instead)\b/iu.test(
|
|
2037
|
+
let layout = index === 0 ? "title" : textPoints.length >= 2 ? "cards" : "statement";
|
|
2038
|
+
if (sequence) layout = steps.length >= 3 && CYCLE_CUE.test(prose) ? "cycle" : "steps";
|
|
2039
|
+
else if (textPoints.length === 2 && /\b(versus|whereas|compared|instead)\b/iu.test(prose))
|
|
1932
2040
|
layout = "comparison";
|
|
1933
2041
|
if (image) layout = "image";
|
|
1934
2042
|
if (preferred && (preferred === "image" ? !!image : presentationLayoutFits(
|
|
@@ -1945,7 +2053,7 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1945
2053
|
layout,
|
|
1946
2054
|
headline: headlineFor(layout),
|
|
1947
2055
|
points: pointsFor(layout),
|
|
1948
|
-
...layout === "image" && image ? { imageSrc: image.src } : {}
|
|
2056
|
+
...layout === "image" && image?.src ? { imageSrc: image.src } : {}
|
|
1949
2057
|
};
|
|
1950
2058
|
if (quote) quotes.set(beat.id, quote);
|
|
1951
2059
|
let split;
|
|
@@ -1997,28 +2105,6 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
1997
2105
|
))
|
|
1998
2106
|
beat.hidden = true;
|
|
1999
2107
|
}
|
|
2000
|
-
const sectionMedia = collectPresentationMedia(
|
|
2001
|
-
doc,
|
|
2002
|
-
script.blocks.map((block) => block.blockId)
|
|
2003
|
-
);
|
|
2004
|
-
for (const block of script.blocks) {
|
|
2005
|
-
const items = sectionMedia.get(block.blockId);
|
|
2006
|
-
if (!items?.length) continue;
|
|
2007
|
-
const own = beats.filter(
|
|
2008
|
-
(beat) => beat.sourceStart >= block.charStart && beat.sourceStart < block.charEnd && beat.layout !== AUTHORED_PRESENTATION_LAYOUT && !beat.hidden
|
|
2009
|
-
);
|
|
2010
|
-
if (!own.length) continue;
|
|
2011
|
-
const open = own.filter((beat) => beat !== beats[0] && beat.layout !== "image");
|
|
2012
|
-
const slots = open.length ? open : own;
|
|
2013
|
-
const shown = /* @__PURE__ */ new Set();
|
|
2014
|
-
slots.forEach((beat, slot) => {
|
|
2015
|
-
const from = Math.floor(slot * items.length / slots.length);
|
|
2016
|
-
const to = Math.max(from + 1, Math.floor((slot + 1) * items.length / slots.length));
|
|
2017
|
-
const refs = items.slice(from, to).filter((item) => item.continuous || !shown.has(item.ref)).slice(0, PRESENTATION_LIMITS.mediaPerBeat).map((item) => item.ref);
|
|
2018
|
-
for (const ref of refs) shown.add(ref);
|
|
2019
|
-
if (refs.length) beat.media = refs;
|
|
2020
|
-
});
|
|
2021
|
-
}
|
|
2022
2108
|
const closing = beats[beats.length - 1];
|
|
2023
2109
|
if (beats.length > 2 && closing?.layout === "statement" && !hints.blocks.some(
|
|
2024
2110
|
(hint) => hint.layout && script.blocks.some(
|
|
@@ -2026,19 +2112,24 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
2026
2112
|
)
|
|
2027
2113
|
))
|
|
2028
2114
|
closing.layout = "title";
|
|
2115
|
+
const sectionOf = (beat) => script.blocks.find(
|
|
2116
|
+
(block) => beat.sourceStart >= block.charStart && beat.sourceStart < block.charEnd
|
|
2117
|
+
);
|
|
2118
|
+
const hintFor = (beat) => hints.blocks.find((item) => item.blockId === sectionOf(beat)?.blockId);
|
|
2119
|
+
const worded = /* @__PURE__ */ new Set();
|
|
2120
|
+
const suggested = /* @__PURE__ */ new Set();
|
|
2029
2121
|
let origin = "automatic";
|
|
2030
2122
|
for (const beat of beats) {
|
|
2031
2123
|
if (beat.layout === AUTHORED_PRESENTATION_LAYOUT) continue;
|
|
2032
|
-
const section =
|
|
2033
|
-
|
|
2034
|
-
);
|
|
2035
|
-
const hint = hints.blocks.find((item) => item.blockId === section?.blockId);
|
|
2124
|
+
const section = sectionOf(beat);
|
|
2125
|
+
const hint = hintFor(beat);
|
|
2036
2126
|
const passage = script.sourceText.slice(beat.sourceStart, beat.sourceEnd);
|
|
2037
2127
|
const cached = hint?.ai?.find((item) => item.source === passage);
|
|
2038
2128
|
if (cached && (cached.layout !== "image" || beat.imageSrc)) {
|
|
2039
2129
|
beat.headline = cached.headline;
|
|
2040
2130
|
beat.points = [...cached.points];
|
|
2041
2131
|
beat.layout = cached.layout;
|
|
2132
|
+
suggested.add(beat.id);
|
|
2042
2133
|
origin = "ai";
|
|
2043
2134
|
}
|
|
2044
2135
|
const preferred = hint?.layout === AUTHORED_PRESENTATION_LAYOUT ? void 0 : hint?.layout;
|
|
@@ -2052,12 +2143,49 @@ function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
|
2052
2143
|
if (section && beat.sourceStart === section.charStart && hint?.wording?.source === script.sourceText.slice(section.charStart, section.charEnd)) {
|
|
2053
2144
|
beat.headline = hint.wording.headline ?? beat.headline;
|
|
2054
2145
|
beat.points = hint.wording.points ?? beat.points;
|
|
2146
|
+
if (hint.wording.points) worded.add(beat.id);
|
|
2055
2147
|
}
|
|
2056
|
-
if (beat.media && beat.layout === "steps" && preferred !== "steps") beat.layout = "list";
|
|
2057
2148
|
if (beat.layout !== "image" && !presentationLayoutFits(beat.layout, beat.headline, beat.points, passage))
|
|
2058
2149
|
beat.layout = "statement";
|
|
2059
2150
|
if (beat.layout !== "image") delete beat.imageSrc;
|
|
2060
2151
|
}
|
|
2152
|
+
const fullBleed = new Set(beats.flatMap((beat) => beat.imageSrc ? [beat.imageSrc] : []));
|
|
2153
|
+
for (const block of script.blocks) {
|
|
2154
|
+
const items = (sectionMedia.get(block.blockId) ?? []).filter(
|
|
2155
|
+
(item) => item.kind !== "image" || !fullBleed.has(item.src)
|
|
2156
|
+
);
|
|
2157
|
+
if (!items.length) continue;
|
|
2158
|
+
const own = beats.filter(
|
|
2159
|
+
(beat) => beat.sourceStart >= block.charStart && beat.sourceStart < block.charEnd && beat.layout !== AUTHORED_PRESENTATION_LAYOUT && !beat.hidden
|
|
2160
|
+
);
|
|
2161
|
+
if (!own.length) continue;
|
|
2162
|
+
const open = own.filter((beat) => beat !== beats[0] && beat.layout !== "image");
|
|
2163
|
+
const slots = open.length ? open : own;
|
|
2164
|
+
const shown = /* @__PURE__ */ new Set();
|
|
2165
|
+
slots.forEach((beat, slot) => {
|
|
2166
|
+
const from = Math.floor(slot * items.length / slots.length);
|
|
2167
|
+
const to = Math.max(from + 1, Math.floor((slot + 1) * items.length / slots.length));
|
|
2168
|
+
const refs = items.slice(from, to).filter((item) => item.continuous || !shown.has(item.ref)).slice(0, PRESENTATION_LIMITS.mediaPerBeat).map((item) => item.ref);
|
|
2169
|
+
for (const ref of refs) shown.add(ref);
|
|
2170
|
+
if (refs.length) beat.media = refs;
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
for (const beat of beats) {
|
|
2174
|
+
if (!beat.media) continue;
|
|
2175
|
+
if (!worded.has(beat.id)) {
|
|
2176
|
+
const points = beat.points.slice(0, FEATURE_POINTS);
|
|
2177
|
+
beat.points = suggested.has(beat.id) ? points : points.map(featurePointText);
|
|
2178
|
+
}
|
|
2179
|
+
if (beat.layout === "steps" && hintFor(beat)?.layout !== "steps") beat.layout = "list";
|
|
2180
|
+
const passage = script.sourceText.slice(beat.sourceStart, beat.sourceEnd);
|
|
2181
|
+
if (!presentationLayoutFits(
|
|
2182
|
+
beat.layout,
|
|
2183
|
+
beat.headline,
|
|
2184
|
+
beat.points,
|
|
2185
|
+
passage
|
|
2186
|
+
))
|
|
2187
|
+
beat.layout = "statement";
|
|
2188
|
+
}
|
|
2061
2189
|
return { version: 1, sourceText: script.sourceText, origin, beats };
|
|
2062
2190
|
}
|
|
2063
2191
|
|
|
@@ -2078,7 +2206,7 @@ function compilePresentationPlan(doc, plan) {
|
|
|
2078
2206
|
);
|
|
2079
2207
|
if (narration && narration.sourceText !== checked.sourceText)
|
|
2080
2208
|
throw new Error(
|
|
2081
|
-
"The narration no longer matches the document.
|
|
2209
|
+
"The narration no longer matches the document. Record or generate narration again before presenting."
|
|
2082
2210
|
);
|
|
2083
2211
|
if (narration && flattenRenderableBlocks(doc.blocks).some((block) => {
|
|
2084
2212
|
const pin = getPinnedBlockMeta(block);
|
|
@@ -2102,7 +2230,7 @@ function compilePresentationPlan(doc, plan) {
|
|
|
2102
2230
|
);
|
|
2103
2231
|
if (checked.beats.some((beat) => beat.media?.some((ref) => !media.has(ref))))
|
|
2104
2232
|
throw new Error(
|
|
2105
|
-
"A presentation video or diagram is no longer in the document. Make the presentation again."
|
|
2233
|
+
"A presentation table, picture, video or diagram is no longer in the document. Make the presentation again."
|
|
2106
2234
|
);
|
|
2107
2235
|
const duration = narration ? narration.startTime + narration.duration : Math.max(4, script.tokens.length / 2.5);
|
|
2108
2236
|
const bookmarks = (narration?.bookmarks ?? []).filter(
|
|
@@ -2129,20 +2257,17 @@ function compilePresentationPlan(doc, plan) {
|
|
|
2129
2257
|
const share = span > 0 ? (tokensBefore(offset) - tokensBefore(from.charOffset)) / span : 0;
|
|
2130
2258
|
return from.time + share * Math.max(0, to.time - from.time);
|
|
2131
2259
|
};
|
|
2260
|
+
const timeAt = (offset) => {
|
|
2261
|
+
const marked = markedTime(offset);
|
|
2262
|
+
if (marked !== void 0) return marked;
|
|
2263
|
+
if (narration && bookmarks.length)
|
|
2264
|
+
return bookmarks.find((word) => word.charOffset >= offset)?.time ?? duration;
|
|
2265
|
+
return (narration?.startTime ?? 0) + tokensBefore(offset) / Math.max(1, script.tokens.length) * (narration?.duration ?? duration);
|
|
2266
|
+
};
|
|
2132
2267
|
let previous = 0;
|
|
2133
2268
|
const starts = checked.beats.map((beat, index) => {
|
|
2134
2269
|
if (!index) return 0;
|
|
2135
|
-
|
|
2136
|
-
const marked = markedTime(beat.sourceStart);
|
|
2137
|
-
if (marked !== void 0) {
|
|
2138
|
-
time = marked;
|
|
2139
|
-
} else if (narration && bookmarks.length) {
|
|
2140
|
-
time = bookmarks.find((word) => word.charOffset >= beat.sourceStart)?.time ?? duration;
|
|
2141
|
-
} else {
|
|
2142
|
-
const before = script.tokens.filter((token) => token.charOffset < beat.sourceStart).length;
|
|
2143
|
-
time = (narration?.startTime ?? 0) + before / Math.max(1, script.tokens.length) * (narration?.duration ?? duration);
|
|
2144
|
-
}
|
|
2145
|
-
previous = Math.min(duration, Math.max(previous, time));
|
|
2270
|
+
previous = Math.min(duration, Math.max(previous, timeAt(beat.sourceStart)));
|
|
2146
2271
|
return previous;
|
|
2147
2272
|
});
|
|
2148
2273
|
const authoredBlocks = flattenRenderableBlocks(doc.blocks);
|
|
@@ -2229,8 +2354,16 @@ function compilePresentationPlan(doc, plan) {
|
|
|
2229
2354
|
});
|
|
2230
2355
|
continue;
|
|
2231
2356
|
}
|
|
2232
|
-
const
|
|
2233
|
-
|
|
2357
|
+
const table = beat.layout !== "image" && (beat.media ?? []).some((ref) => media.get(ref).kind === "table");
|
|
2358
|
+
const block = table ? tableFeatureBlock(beat, beat.layout, base, contents) : visualBlock(beat, beat.layout, base, index);
|
|
2359
|
+
const concept = conceptTemplate(block.template);
|
|
2360
|
+
blocks.push({
|
|
2361
|
+
...block,
|
|
2362
|
+
...contents.length ? { summaryMode: "brief" } : {},
|
|
2363
|
+
// A concept slide reveals its ideas one at a time, each as the narration
|
|
2364
|
+
// reaches the sentence it summarizes (`conceptReveal`).
|
|
2365
|
+
...concept && !build ? conceptReveal(concept, beat, start, end, script.sourceText, timeAt) : {}
|
|
2366
|
+
});
|
|
2234
2367
|
}
|
|
2235
2368
|
return {
|
|
2236
2369
|
...doc,
|
|
@@ -2242,6 +2375,65 @@ function compilePresentationPlan(doc, plan) {
|
|
|
2242
2375
|
audio: doc.audio.segments.some((segment) => segment.src) ? doc.audio : { segments: [{ src: "", name: "presentation-clock", startTime: 0, duration }] }
|
|
2243
2376
|
};
|
|
2244
2377
|
}
|
|
2378
|
+
var CONCEPT_TEMPLATES = ["cards", "process", "cycle", "layers"];
|
|
2379
|
+
function conceptTemplate(template) {
|
|
2380
|
+
return CONCEPT_TEMPLATES.find((name) => name === template);
|
|
2381
|
+
}
|
|
2382
|
+
function stems(text3) {
|
|
2383
|
+
return (text3.toLowerCase().match(/[\p{L}\p{N}][\p{L}\p{N}'-]*/gu) ?? []).filter((word) => word.length >= 4).map((word) => word.slice(0, 5));
|
|
2384
|
+
}
|
|
2385
|
+
function conceptReveal(template, beat, start, end, sourceText, timeAt) {
|
|
2386
|
+
const build = { mode: "items", effect: "fadeInUp", previous: "keep" };
|
|
2387
|
+
const steps = conceptBuildStepTexts(template, beat.points);
|
|
2388
|
+
const passage = sourceText.slice(beat.sourceStart, beat.sourceEnd);
|
|
2389
|
+
const sentences = [...passage.matchAll(/[^.!?\n]+[.!?]*/gu)].map((match) => ({
|
|
2390
|
+
offset: beat.sourceStart + match.index,
|
|
2391
|
+
words: new Set(stems(match[0]))
|
|
2392
|
+
}));
|
|
2393
|
+
let last = -1;
|
|
2394
|
+
const offsets = [];
|
|
2395
|
+
for (const point of beat.points.slice(0, steps.length)) {
|
|
2396
|
+
const words = stems(point);
|
|
2397
|
+
let best;
|
|
2398
|
+
for (const sentence of sentences) {
|
|
2399
|
+
const shared = words.filter((word) => sentence.words.has(word)).length;
|
|
2400
|
+
if (shared > (best?.shared ?? 1)) best = { offset: sentence.offset, shared };
|
|
2401
|
+
}
|
|
2402
|
+
if (!best || best.offset < last) return { build };
|
|
2403
|
+
offsets.push(last = best.offset);
|
|
2404
|
+
}
|
|
2405
|
+
if (offsets.length !== steps.length) return { build };
|
|
2406
|
+
const lead = Math.min(0.5, (end - start) / 8);
|
|
2407
|
+
const starts = offsets.map(
|
|
2408
|
+
(offset) => Math.min(Math.max(0, end - start - 0.1), Math.max(lead, timeAt(offset) - start))
|
|
2409
|
+
);
|
|
2410
|
+
starts[0] = Math.min(starts[0], Math.max(lead, Math.min(1, (end - start) / 6)));
|
|
2411
|
+
return {
|
|
2412
|
+
build,
|
|
2413
|
+
buildTiming: {
|
|
2414
|
+
sourceText: buildSourceText(steps.map((text3, index) => ({ id: `build-${index}`, text: text3 }))),
|
|
2415
|
+
starts
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
function tableFeatureBlock(beat, layout, base, features) {
|
|
2420
|
+
const paragraph = (text3) => ({
|
|
2421
|
+
type: "paragraph",
|
|
2422
|
+
children: [{ type: "text", value: text3 }]
|
|
2423
|
+
});
|
|
2424
|
+
const words = ["list", "steps", "comparison"].includes(layout) && beat.points.length > 1 ? [
|
|
2425
|
+
{
|
|
2426
|
+
type: "list",
|
|
2427
|
+
ordered: false,
|
|
2428
|
+
children: beat.points.map((point) => ({
|
|
2429
|
+
type: "listItem",
|
|
2430
|
+
children: [paragraph(point)]
|
|
2431
|
+
}))
|
|
2432
|
+
}
|
|
2433
|
+
] : beat.points.map(paragraph);
|
|
2434
|
+
const block = { ...base, template: "bottomFeature", title: beat.headline };
|
|
2435
|
+
return Object.assign(block, { contents: [...words, ...features] });
|
|
2436
|
+
}
|
|
2245
2437
|
function visualBlock(beat, layout, base, index) {
|
|
2246
2438
|
const colorScheme = ["blue", "green", "orange"][index % 3];
|
|
2247
2439
|
switch (layout) {
|
|
@@ -2250,52 +2442,40 @@ function visualBlock(beat, layout, base, index) {
|
|
|
2250
2442
|
case "divider":
|
|
2251
2443
|
return { ...base, template: "sectionHeader", title: beat.headline, colorScheme };
|
|
2252
2444
|
case "emphasis":
|
|
2253
|
-
return { ...base, template: "
|
|
2445
|
+
return { ...base, template: "cards", items: [beat.headline] };
|
|
2254
2446
|
case "quote":
|
|
2255
2447
|
return { ...base, template: "fullBleedQuote", text: beat.headline, colorScheme };
|
|
2256
2448
|
case "stat": {
|
|
2257
2449
|
const { match, line, index: at } = presentationStat(beat.headline, beat.points);
|
|
2258
|
-
const
|
|
2450
|
+
const others = beat.points.filter((_, i) => i !== at);
|
|
2451
|
+
const [description, detail] = at < 0 ? others : [line, others[0]];
|
|
2259
2452
|
return {
|
|
2260
2453
|
...base,
|
|
2261
2454
|
template: "statHighlight",
|
|
2455
|
+
headline: beat.headline,
|
|
2262
2456
|
stat: match.value,
|
|
2263
|
-
description:
|
|
2264
|
-
...detail ? { detail } : {}
|
|
2265
|
-
colorScheme
|
|
2457
|
+
description: description ?? "",
|
|
2458
|
+
...detail ? { detail } : {}
|
|
2266
2459
|
};
|
|
2267
2460
|
}
|
|
2268
2461
|
case "definition":
|
|
2269
2462
|
return {
|
|
2270
2463
|
...base,
|
|
2271
|
-
template: "
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
colorScheme
|
|
2464
|
+
template: "cards",
|
|
2465
|
+
title: beat.headline,
|
|
2466
|
+
items: beat.points.length ? [beat.points.map((point) => point.trim().replace(/([^.!?])$/u, "$1.")).join(" ")] : []
|
|
2275
2467
|
};
|
|
2276
2468
|
case "statement":
|
|
2277
|
-
return {
|
|
2278
|
-
...base,
|
|
2279
|
-
template: "factCard",
|
|
2280
|
-
fact: beat.headline,
|
|
2281
|
-
explanation: beat.points.join("\n\n")
|
|
2282
|
-
};
|
|
2469
|
+
return beat.points.length ? { ...base, template: "cards", title: beat.headline, items: beat.points } : { ...base, template: "factCard", fact: beat.headline, explanation: "" };
|
|
2283
2470
|
case "list":
|
|
2471
|
+
return { ...base, template: "cards", title: beat.headline, items: beat.points };
|
|
2472
|
+
case "comparison":
|
|
2284
2473
|
return {
|
|
2285
2474
|
...base,
|
|
2286
|
-
template: "
|
|
2475
|
+
template: "cards",
|
|
2287
2476
|
title: beat.headline,
|
|
2288
2477
|
items: beat.points,
|
|
2289
|
-
|
|
2290
|
-
colorScheme
|
|
2291
|
-
};
|
|
2292
|
-
case "comparison":
|
|
2293
|
-
return {
|
|
2294
|
-
...base,
|
|
2295
|
-
template: "twoColumn",
|
|
2296
|
-
header: beat.headline,
|
|
2297
|
-
left: { label: beat.points[0] },
|
|
2298
|
-
right: { label: beat.points[1] }
|
|
2478
|
+
...beat.points.length === 2 ? { versus: true } : {}
|
|
2299
2479
|
};
|
|
2300
2480
|
case "image":
|
|
2301
2481
|
return {
|
|
@@ -2308,22 +2488,14 @@ function visualBlock(beat, layout, base, index) {
|
|
|
2308
2488
|
ambientMotion: "zoomIn"
|
|
2309
2489
|
};
|
|
2310
2490
|
case "steps":
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
label: point,
|
|
2320
|
-
x: i * 320,
|
|
2321
|
-
y: 0,
|
|
2322
|
-
w: 260,
|
|
2323
|
-
h: 180
|
|
2324
|
-
})),
|
|
2325
|
-
edges: beat.points.slice(1).map((_, i) => ({ source: `step-${i}`, target: `step-${i + 1}`, directed: true }))
|
|
2326
|
-
};
|
|
2491
|
+
case "process":
|
|
2492
|
+
return { ...base, template: "process", title: beat.headline, items: beat.points };
|
|
2493
|
+
case "cards":
|
|
2494
|
+
return { ...base, template: "cards", title: beat.headline, items: beat.points };
|
|
2495
|
+
case "cycle":
|
|
2496
|
+
return { ...base, template: "cycle", title: beat.headline, items: beat.points };
|
|
2497
|
+
case "layers":
|
|
2498
|
+
return { ...base, template: "layers", title: beat.headline, items: beat.points };
|
|
2327
2499
|
}
|
|
2328
2500
|
}
|
|
2329
2501
|
function applySavedPresentation(doc) {
|
|
@@ -2371,13 +2543,13 @@ export {
|
|
|
2371
2543
|
serializePresentationHints,
|
|
2372
2544
|
presentationSections,
|
|
2373
2545
|
usesDynamicPresentation,
|
|
2374
|
-
extractDocImages,
|
|
2375
|
-
analyzeBlocks,
|
|
2376
2546
|
DEFAULT_TRANSFORM_STYLE_ID,
|
|
2377
2547
|
createTransformStyleRegistry,
|
|
2378
2548
|
resolveTransformStyle,
|
|
2379
2549
|
getTransformStyleIds,
|
|
2380
2550
|
getTransformStyleSummaries,
|
|
2551
|
+
extractDocImages,
|
|
2552
|
+
analyzeBlocks,
|
|
2381
2553
|
allocateTiming,
|
|
2382
2554
|
applyTransform,
|
|
2383
2555
|
transformNarratedSegment,
|