@bendyline/squisq 2.11.17 → 2.11.18
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-CXx1WL8G.d.ts → Doc-B6TYHYGc.d.ts} +14 -0
- package/dist/{ImageEditDoc-BVLXI-Zd.d.ts → ImageEditDoc-CN_3Ysj5.d.ts} +1 -1
- package/dist/chunk-3XEABCT3.js +114 -0
- package/dist/{chunk-L7IIFWWW.js → chunk-7IGKCHDP.js} +243 -3
- package/dist/{chunk-54RU7TBZ.js → chunk-J4ACZBO4.js} +18 -3
- package/dist/{chunk-KNC4ZBQC.js → chunk-NM747LUO.js} +825 -294
- package/dist/{chunk-MY46W5O5.js → chunk-RYO4QW6R.js} +2 -4
- package/dist/doc/index.d.ts +5 -22
- package/dist/doc/index.js +5 -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 +58 -28
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/{materializePageSection-BX1uOAlN.d.ts → materializePageSection-IYXU3tDN.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 +7 -6
- package/dist/schemas/index.d.ts +4 -4
- package/dist/{themeLibrary-Bnq_x-db.d.ts → themeLibrary-_46saKYO.d.ts} +1 -1
- package/dist/timing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +84 -4
- package/dist/transform/index.js +35 -5
- package/package.json +1 -1
- package/dist/chunk-PNXBDMNS.js +0 -337
|
@@ -703,6 +703,8 @@ interface AccentImage {
|
|
|
703
703
|
* Base properties shared by all template blocks.
|
|
704
704
|
*/
|
|
705
705
|
interface BaseTemplateBlock {
|
|
706
|
+
/** Compiled presentation timing is authoritative; pacing must not merge or split it. */
|
|
707
|
+
timelineLocked?: boolean;
|
|
706
708
|
/** Unique block ID */
|
|
707
709
|
id: string;
|
|
708
710
|
/** Block duration in seconds */
|
|
@@ -1310,6 +1312,8 @@ interface DiagramTemplateEdge {
|
|
|
1310
1312
|
*/
|
|
1311
1313
|
interface DiagramBlockInput extends BaseTemplateBlock {
|
|
1312
1314
|
template: 'diagram';
|
|
1315
|
+
/** Paint an opaque themed surface for standalone slides; omitted stays transparent. */
|
|
1316
|
+
background?: 'surface';
|
|
1313
1317
|
/** Optional diagram title displayed above the canvas. */
|
|
1314
1318
|
title?: string;
|
|
1315
1319
|
/** Color scheme for nodes and edges. */
|
|
@@ -2330,6 +2334,16 @@ interface DocDiagnostic {
|
|
|
2330
2334
|
* A complete visual doc for an article.
|
|
2331
2335
|
*/
|
|
2332
2336
|
interface Doc {
|
|
2337
|
+
/** Projection-only source/timing metadata from the document narration take. */
|
|
2338
|
+
presentationNarration?: {
|
|
2339
|
+
sourceText: string;
|
|
2340
|
+
src: string;
|
|
2341
|
+
startTime: number;
|
|
2342
|
+
duration: number;
|
|
2343
|
+
bookmarks: AudioBookmark[];
|
|
2344
|
+
};
|
|
2345
|
+
/** Projection-only guard: never compile an accepted plan twice. */
|
|
2346
|
+
presentationApplied?: boolean;
|
|
2333
2347
|
/** Article ID this doc belongs to */
|
|
2334
2348
|
articleId: string;
|
|
2335
2349
|
/** Total duration in seconds (sum of all audio segments) */
|
|
@@ -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-B6TYHYGc.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ImageEditDoc — JSON document describing a layered, editable image.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// src/narration/sidecar.ts
|
|
2
|
+
function resolveNarrationTimingGenerator(options) {
|
|
3
|
+
const baseWpm = options?.generator?.baseWpm ?? options?.baseWpm;
|
|
4
|
+
return {
|
|
5
|
+
name: options?.generator?.name ?? "squisq-teleprompter",
|
|
6
|
+
method: options?.generator?.method ?? "dsp-align",
|
|
7
|
+
...baseWpm !== void 0 ? { baseWpm } : {}
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function buildNarrationTimingJson(script, alignment, durationSec, options) {
|
|
11
|
+
const bookmarks = alignment.words.map((word) => {
|
|
12
|
+
const token = script.tokens[word.tokenIndex];
|
|
13
|
+
return {
|
|
14
|
+
id: `word-${word.tokenIndex}`,
|
|
15
|
+
time: word.tSec,
|
|
16
|
+
charOffset: token?.charOffset ?? 0,
|
|
17
|
+
textFragment: token?.text ?? ""
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
const blocks = alignment.blocks.map((range) => ({
|
|
21
|
+
...range.blockId ? { blockId: range.blockId } : {},
|
|
22
|
+
...range.heading !== void 0 ? { heading: range.heading } : {},
|
|
23
|
+
blockIndex: range.blockIndex,
|
|
24
|
+
charStart: range.charStart,
|
|
25
|
+
charEnd: range.charEnd,
|
|
26
|
+
startSec: range.startSec,
|
|
27
|
+
endSec: range.endSec
|
|
28
|
+
}));
|
|
29
|
+
return {
|
|
30
|
+
version: 3,
|
|
31
|
+
sourceText: script.sourceText,
|
|
32
|
+
duration: Number.isFinite(durationSec) && durationSec >= 0 ? durationSec : 0,
|
|
33
|
+
bookmarks,
|
|
34
|
+
blocks,
|
|
35
|
+
// Write only what `parseNarrationTimingJson` will read back, so the codec
|
|
36
|
+
// is a round-trip pair. A non-finite value would serialize to `null`
|
|
37
|
+
// (JSON has no NaN) and be dropped on parse anyway — omitting it here
|
|
38
|
+
// keeps the emitted sidecar honest rather than carrying a dead field.
|
|
39
|
+
...finiteNumber(options?.cameraOffsetSec) ? { cameraOffsetSec: options.cameraOffsetSec } : {},
|
|
40
|
+
generator: resolveNarrationTimingGenerator(options)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function finiteNonNegative(value) {
|
|
44
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
45
|
+
}
|
|
46
|
+
function finiteNumber(value) {
|
|
47
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
48
|
+
}
|
|
49
|
+
function parseNarrationTimingJson(data) {
|
|
50
|
+
try {
|
|
51
|
+
const text = typeof data === "string" ? data : new TextDecoder().decode(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
52
|
+
const parsed = JSON.parse(text);
|
|
53
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
54
|
+
const raw = parsed;
|
|
55
|
+
if (typeof raw.sourceText !== "string" || !finiteNonNegative(raw.duration)) return null;
|
|
56
|
+
const bookmarks = Array.isArray(raw.bookmarks) ? raw.bookmarks.flatMap((b) => {
|
|
57
|
+
if (typeof b !== "object" || b === null) return [];
|
|
58
|
+
const bookmark = b;
|
|
59
|
+
if (!finiteNonNegative(bookmark.time) || !finiteNonNegative(bookmark.charOffset)) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
id: typeof bookmark.id === "string" ? bookmark.id : `bookmark-${bookmark.charOffset}`,
|
|
65
|
+
time: bookmark.time,
|
|
66
|
+
charOffset: bookmark.charOffset,
|
|
67
|
+
...typeof bookmark.textFragment === "string" ? { textFragment: bookmark.textFragment } : {}
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
}) : [];
|
|
71
|
+
let blocks = Array.isArray(raw.blocks) ? raw.blocks.flatMap((b) => {
|
|
72
|
+
if (typeof b !== "object" || b === null) return [];
|
|
73
|
+
const block = b;
|
|
74
|
+
if (!finiteNonNegative(block.startSec) || !finiteNonNegative(block.endSec) || !finiteNonNegative(block.charStart) || !finiteNonNegative(block.charEnd)) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
...typeof block.blockId === "string" ? { blockId: block.blockId } : {},
|
|
80
|
+
...typeof block.heading === "string" ? { heading: block.heading } : {},
|
|
81
|
+
blockIndex: finiteNonNegative(block.blockIndex) ? block.blockIndex : 0,
|
|
82
|
+
charStart: block.charStart,
|
|
83
|
+
charEnd: block.charEnd,
|
|
84
|
+
startSec: block.startSec,
|
|
85
|
+
endSec: Math.max(block.startSec, block.endSec)
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
}) : [];
|
|
89
|
+
blocks = blocks.sort((a, b) => a.startSec - b.startSec);
|
|
90
|
+
let cursor = 0;
|
|
91
|
+
for (const block of blocks) {
|
|
92
|
+
if (block.startSec < cursor) block.startSec = cursor;
|
|
93
|
+
if (block.endSec < block.startSec) block.endSec = block.startSec;
|
|
94
|
+
cursor = block.startSec;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
version: 3,
|
|
98
|
+
sourceText: raw.sourceText,
|
|
99
|
+
duration: raw.duration,
|
|
100
|
+
bookmarks,
|
|
101
|
+
blocks,
|
|
102
|
+
...finiteNumber(raw.cameraOffsetSec) ? { cameraOffsetSec: raw.cameraOffsetSec } : {},
|
|
103
|
+
...typeof raw.generator === "object" && raw.generator !== null ? { generator: raw.generator } : {}
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
resolveNarrationTimingGenerator,
|
|
112
|
+
buildNarrationTimingJson,
|
|
113
|
+
parseNarrationTimingJson
|
|
114
|
+
};
|
|
@@ -75,7 +75,8 @@ import {
|
|
|
75
75
|
resolveBlockTransition
|
|
76
76
|
} from "./chunk-4VOD55SX.js";
|
|
77
77
|
import {
|
|
78
|
-
estimateReadingTime
|
|
78
|
+
estimateReadingTime,
|
|
79
|
+
estimateSpokenWordCount
|
|
79
80
|
} from "./chunk-GHOPHGOK.js";
|
|
80
81
|
import {
|
|
81
82
|
SeededRandom,
|
|
@@ -6048,9 +6049,11 @@ function nodesFromTemplateData(input) {
|
|
|
6048
6049
|
function diagramBlock(input, context) {
|
|
6049
6050
|
const { theme, viewport, children = [] } = context;
|
|
6050
6051
|
const colors = input.colorScheme ? resolveColorScheme(context, input.colorScheme) : void 0;
|
|
6052
|
+
const background = input.background === "surface" ? [createBackgroundLayer("diagram-bg", themedSurfaceGradient(context, 170))] : [];
|
|
6051
6053
|
const resolved = children.length > 0 ? nodesFromChildren(context) : input.nodes && input.nodes.length > 0 ? nodesFromTemplateData(input) : { nodes: [], edges: [] };
|
|
6052
6054
|
if (resolved.nodes.length === 0) {
|
|
6053
6055
|
return [
|
|
6056
|
+
...background,
|
|
6054
6057
|
{
|
|
6055
6058
|
type: "text",
|
|
6056
6059
|
id: "diagram-empty",
|
|
@@ -6091,7 +6094,7 @@ function diagramBlock(input, context) {
|
|
|
6091
6094
|
w: n.w * scale,
|
|
6092
6095
|
h: n.h * scale
|
|
6093
6096
|
});
|
|
6094
|
-
const layers = [];
|
|
6097
|
+
const layers = [...background];
|
|
6095
6098
|
if (input.title) {
|
|
6096
6099
|
const titleLayer = canvasTitleLayer("diagram-title", input.title, frame, placement, context);
|
|
6097
6100
|
if (titleLayer) layers.push(titleLayer);
|
|
@@ -11005,6 +11008,28 @@ function expandDocBlocks(blocks, options = {}) {
|
|
|
11005
11008
|
const splitParts = /* @__PURE__ */ new Map();
|
|
11006
11009
|
for (const [segmentIndex, segmentBlocks] of blocksBySegment) {
|
|
11007
11010
|
const audioSegment = audioSegments[segmentIndex];
|
|
11011
|
+
if (segmentBlocks.every(({ block }) => block.timelineLocked === true)) {
|
|
11012
|
+
for (const { block, originalIndex } of segmentBlocks) {
|
|
11013
|
+
const expanded = materializeScheduledBlock(
|
|
11014
|
+
block,
|
|
11015
|
+
originalIndex,
|
|
11016
|
+
totalBlocks,
|
|
11017
|
+
theme,
|
|
11018
|
+
viewport,
|
|
11019
|
+
effectivePersistentLayers,
|
|
11020
|
+
expandedPersistentLayers,
|
|
11021
|
+
registry,
|
|
11022
|
+
failureMode,
|
|
11023
|
+
onDiagnostic,
|
|
11024
|
+
motion,
|
|
11025
|
+
opts.mediaAspectRatios
|
|
11026
|
+
);
|
|
11027
|
+
expanded.startTime = block.sourceStartTime ?? 0;
|
|
11028
|
+
expanded.duration = block.duration;
|
|
11029
|
+
expandedBlocks[originalIndex] = expanded;
|
|
11030
|
+
}
|
|
11031
|
+
continue;
|
|
11032
|
+
}
|
|
11008
11033
|
if (!audioSegment) {
|
|
11009
11034
|
let offsetTime = 0;
|
|
11010
11035
|
for (const { block, originalIndex } of segmentBlocks) {
|
|
@@ -12485,6 +12510,215 @@ function buildStartBlock(markdownDoc, rootBlocks, fileName) {
|
|
|
12485
12510
|
return config;
|
|
12486
12511
|
}
|
|
12487
12512
|
|
|
12513
|
+
// src/narration/syllables.ts
|
|
12514
|
+
var BOUNDARY_PUNCT = /^[.,!?;:'"()[\]{}]+|[.,!?;:'"()[\]{}]+$/g;
|
|
12515
|
+
var SYLLABLES_PER_SPOKEN_WORD = 1.4;
|
|
12516
|
+
function estimateSyllables(token) {
|
|
12517
|
+
const stripped = token.replace(BOUNDARY_PUNCT, "");
|
|
12518
|
+
if (!stripped) return 1;
|
|
12519
|
+
if (/^[-+]?[\d.,]+$/.test(stripped)) {
|
|
12520
|
+
const num = parseFloat(stripped.replace(/,/g, ""));
|
|
12521
|
+
if (!Number.isNaN(num) && Number.isFinite(num)) {
|
|
12522
|
+
return Math.max(1, Math.round(estimateSpokenWordCount(stripped) * SYLLABLES_PER_SPOKEN_WORD));
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12525
|
+
if (/^[A-Z]{2,5}$/.test(stripped) && !/[AEIOUY]/.test(stripped)) {
|
|
12526
|
+
return stripped.length;
|
|
12527
|
+
}
|
|
12528
|
+
const parts = stripped.toLowerCase().split(/[^a-z']+/).filter((p) => p.length > 0);
|
|
12529
|
+
if (parts.length === 0) return 1;
|
|
12530
|
+
let total = 0;
|
|
12531
|
+
for (const part of parts) {
|
|
12532
|
+
total += countAlphaSyllables(part.replace(/'/g, ""));
|
|
12533
|
+
}
|
|
12534
|
+
return Math.max(1, total);
|
|
12535
|
+
}
|
|
12536
|
+
function countAlphaSyllables(word) {
|
|
12537
|
+
if (!word) return 0;
|
|
12538
|
+
const groups = word.match(/[aeiouy]+/g);
|
|
12539
|
+
let count = groups ? groups.length : 0;
|
|
12540
|
+
if (count === 0) return 1;
|
|
12541
|
+
if (count > 1 && /[^aeiouy]e$/.test(word)) {
|
|
12542
|
+
count -= 1;
|
|
12543
|
+
}
|
|
12544
|
+
if (count > 1 && /[^aeiouy]ed$/.test(word) && !/[td]ed$/.test(word)) {
|
|
12545
|
+
count -= 1;
|
|
12546
|
+
}
|
|
12547
|
+
if (count > 1 && /[^aeiouy]es$/.test(word) && !/(?:[sxz]|[cs]h)es$/.test(word)) {
|
|
12548
|
+
count -= 1;
|
|
12549
|
+
}
|
|
12550
|
+
if (/[^aeiouy]le$/.test(word)) {
|
|
12551
|
+
count += 1;
|
|
12552
|
+
}
|
|
12553
|
+
return Math.max(1, count);
|
|
12554
|
+
}
|
|
12555
|
+
|
|
12556
|
+
// src/narration/script.ts
|
|
12557
|
+
function blockSpokenText(block, includeTitles) {
|
|
12558
|
+
const title = includeTitles && block.title ? block.title.trim() : "";
|
|
12559
|
+
const body = getBlockBodyText2(block);
|
|
12560
|
+
if (title && body) return `${title}
|
|
12561
|
+
${body}`;
|
|
12562
|
+
return title || body;
|
|
12563
|
+
}
|
|
12564
|
+
function buildNarrationScript(doc, options) {
|
|
12565
|
+
const includeTitles = options?.includeTitles ?? true;
|
|
12566
|
+
const separator = options?.blockSeparator ?? "\n\n";
|
|
12567
|
+
const pieces = [];
|
|
12568
|
+
for (const block of flattenRenderableBlocks(doc.blocks)) {
|
|
12569
|
+
const text = blockSpokenText(block, includeTitles);
|
|
12570
|
+
if (!text) continue;
|
|
12571
|
+
pieces.push({
|
|
12572
|
+
blockId: block.id,
|
|
12573
|
+
heading: block.title || void 0,
|
|
12574
|
+
text
|
|
12575
|
+
});
|
|
12576
|
+
}
|
|
12577
|
+
let sourceText = "";
|
|
12578
|
+
const blocks = [];
|
|
12579
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
12580
|
+
if (i > 0) sourceText += separator;
|
|
12581
|
+
const charStart = sourceText.length;
|
|
12582
|
+
sourceText += pieces[i].text;
|
|
12583
|
+
blocks.push({
|
|
12584
|
+
blockId: pieces[i].blockId,
|
|
12585
|
+
...pieces[i].heading !== void 0 ? { heading: pieces[i].heading } : {},
|
|
12586
|
+
tokenStart: 0,
|
|
12587
|
+
tokenEnd: 0,
|
|
12588
|
+
charStart,
|
|
12589
|
+
charEnd: sourceText.length
|
|
12590
|
+
});
|
|
12591
|
+
}
|
|
12592
|
+
const tokens = [];
|
|
12593
|
+
const tokenRe = /\S+/g;
|
|
12594
|
+
let blockIndex = 0;
|
|
12595
|
+
let match;
|
|
12596
|
+
while ((match = tokenRe.exec(sourceText)) !== null) {
|
|
12597
|
+
const charOffset = match.index;
|
|
12598
|
+
const charEnd = charOffset + match[0].length;
|
|
12599
|
+
while (blockIndex < blocks.length - 1 && charOffset >= blocks[blockIndex].charEnd) {
|
|
12600
|
+
blockIndex++;
|
|
12601
|
+
}
|
|
12602
|
+
const spoken = hasSpokenContent(match[0]);
|
|
12603
|
+
tokens.push({
|
|
12604
|
+
text: match[0],
|
|
12605
|
+
charOffset,
|
|
12606
|
+
charEnd,
|
|
12607
|
+
blockId: blocks[blockIndex]?.blockId ?? "",
|
|
12608
|
+
blockIndex,
|
|
12609
|
+
syllables: spoken ? estimateSyllables(match[0]) : 0,
|
|
12610
|
+
spokenWordEquiv: spoken ? estimateSpokenWordCount(stripBoundaryPunct(match[0])) : 0,
|
|
12611
|
+
pauseAfter: 0,
|
|
12612
|
+
spoken
|
|
12613
|
+
});
|
|
12614
|
+
}
|
|
12615
|
+
for (const range of blocks) {
|
|
12616
|
+
range.tokenStart = tokens.length;
|
|
12617
|
+
range.tokenEnd = 0;
|
|
12618
|
+
}
|
|
12619
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12620
|
+
const range = blocks[tokens[i].blockIndex];
|
|
12621
|
+
if (range) {
|
|
12622
|
+
if (i < range.tokenStart) range.tokenStart = i;
|
|
12623
|
+
range.tokenEnd = i + 1;
|
|
12624
|
+
}
|
|
12625
|
+
}
|
|
12626
|
+
for (const range of blocks) {
|
|
12627
|
+
if (range.tokenStart > range.tokenEnd) {
|
|
12628
|
+
range.tokenStart = range.tokenEnd;
|
|
12629
|
+
}
|
|
12630
|
+
}
|
|
12631
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12632
|
+
const token = tokens[i];
|
|
12633
|
+
const next = tokens[i + 1];
|
|
12634
|
+
if (!next || next.blockIndex !== token.blockIndex) {
|
|
12635
|
+
token.pauseAfter = 3;
|
|
12636
|
+
} else if (sourceText.slice(token.charEnd, next.charOffset).includes("\n")) {
|
|
12637
|
+
token.pauseAfter = 2;
|
|
12638
|
+
} else if (endsWithClausePunctuation(token.text)) {
|
|
12639
|
+
token.pauseAfter = 1;
|
|
12640
|
+
}
|
|
12641
|
+
}
|
|
12642
|
+
const cumulativeSyllables = new Array(tokens.length + 1);
|
|
12643
|
+
cumulativeSyllables[0] = 0;
|
|
12644
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
12645
|
+
cumulativeSyllables[i + 1] = cumulativeSyllables[i] + tokens[i].syllables;
|
|
12646
|
+
}
|
|
12647
|
+
return {
|
|
12648
|
+
sourceText,
|
|
12649
|
+
tokens,
|
|
12650
|
+
blocks,
|
|
12651
|
+
totalSyllables: cumulativeSyllables[tokens.length],
|
|
12652
|
+
cumulativeSyllables
|
|
12653
|
+
};
|
|
12654
|
+
}
|
|
12655
|
+
var TRAILING_CLOSERS = /["')\]}»”’]+$/;
|
|
12656
|
+
function hasSpokenContent(token) {
|
|
12657
|
+
return /[\p{L}\p{N}]/u.test(token);
|
|
12658
|
+
}
|
|
12659
|
+
function stripBoundaryPunct(token) {
|
|
12660
|
+
return token.replace(/^[.,!?;:'"()[\]{}]+|[.,!?;:'"()[\]{}]+$/g, "");
|
|
12661
|
+
}
|
|
12662
|
+
function endsWithClausePunctuation(token) {
|
|
12663
|
+
const bare = token.replace(TRAILING_CLOSERS, "");
|
|
12664
|
+
const last = bare.charAt(bare.length - 1);
|
|
12665
|
+
if (!last) return false;
|
|
12666
|
+
if (last === ",") {
|
|
12667
|
+
return true;
|
|
12668
|
+
}
|
|
12669
|
+
return last === "." || last === ";" || last === ":" || last === "!" || last === "?";
|
|
12670
|
+
}
|
|
12671
|
+
function clamp2(value, min, max) {
|
|
12672
|
+
return value < min ? min : value > max ? max : value;
|
|
12673
|
+
}
|
|
12674
|
+
function expectedSyllablesAt(script, wordPos) {
|
|
12675
|
+
const n = script.tokens.length;
|
|
12676
|
+
if (n === 0) return 0;
|
|
12677
|
+
const pos = clamp2(wordPos, 0, n);
|
|
12678
|
+
const idx = Math.floor(pos);
|
|
12679
|
+
if (idx >= n) return script.totalSyllables;
|
|
12680
|
+
return script.cumulativeSyllables[idx] + (pos - idx) * script.tokens[idx].syllables;
|
|
12681
|
+
}
|
|
12682
|
+
function wordPosAtExpectedSyllables(script, syllables) {
|
|
12683
|
+
const n = script.tokens.length;
|
|
12684
|
+
if (n === 0) return 0;
|
|
12685
|
+
const target = clamp2(syllables, 0, script.totalSyllables);
|
|
12686
|
+
let lo = 0;
|
|
12687
|
+
let hi = n;
|
|
12688
|
+
while (lo < hi) {
|
|
12689
|
+
const mid = lo + hi + 1 >> 1;
|
|
12690
|
+
if (script.cumulativeSyllables[mid] <= target) lo = mid;
|
|
12691
|
+
else hi = mid - 1;
|
|
12692
|
+
}
|
|
12693
|
+
if (lo >= n) return n;
|
|
12694
|
+
const within = target - script.cumulativeSyllables[lo];
|
|
12695
|
+
const sylls = script.tokens[lo].syllables;
|
|
12696
|
+
return lo + (sylls > 0 ? within / sylls : 0);
|
|
12697
|
+
}
|
|
12698
|
+
function wordIndexAtChar(script, charOffset) {
|
|
12699
|
+
const tokens = script.tokens;
|
|
12700
|
+
if (tokens.length === 0) return 0;
|
|
12701
|
+
let lo = 0;
|
|
12702
|
+
let hi = tokens.length - 1;
|
|
12703
|
+
while (lo < hi) {
|
|
12704
|
+
const mid = lo + hi >> 1;
|
|
12705
|
+
if (tokens[mid].charEnd <= charOffset) lo = mid + 1;
|
|
12706
|
+
else hi = mid;
|
|
12707
|
+
}
|
|
12708
|
+
return lo;
|
|
12709
|
+
}
|
|
12710
|
+
function wordIndexAtTime(words, tSec) {
|
|
12711
|
+
if (words.length === 0) return 0;
|
|
12712
|
+
let lo = 0;
|
|
12713
|
+
let hi = words.length - 1;
|
|
12714
|
+
while (lo < hi) {
|
|
12715
|
+
const mid = lo + hi + 1 >> 1;
|
|
12716
|
+
if (words[mid].tSec <= tSec) lo = mid;
|
|
12717
|
+
else hi = mid - 1;
|
|
12718
|
+
}
|
|
12719
|
+
return words[lo].tSec <= tSec ? lo : 0;
|
|
12720
|
+
}
|
|
12721
|
+
|
|
12488
12722
|
export {
|
|
12489
12723
|
expandPersistentLayers,
|
|
12490
12724
|
getPersistentLayersFromTheme,
|
|
@@ -12622,5 +12856,11 @@ export {
|
|
|
12622
12856
|
flattenRenderableBlocks,
|
|
12623
12857
|
countBlocks,
|
|
12624
12858
|
getBlockDepth,
|
|
12625
|
-
getBlockBodyText2 as getBlockBodyText
|
|
12859
|
+
getBlockBodyText2 as getBlockBodyText,
|
|
12860
|
+
estimateSyllables,
|
|
12861
|
+
buildNarrationScript,
|
|
12862
|
+
expectedSyllablesAt,
|
|
12863
|
+
wordPosAtExpectedSyllables,
|
|
12864
|
+
wordIndexAtChar,
|
|
12865
|
+
wordIndexAtTime
|
|
12626
12866
|
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildNarrationScript,
|
|
3
2
|
parseNarrationTimingJson
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3XEABCT3.js";
|
|
4
|
+
import {
|
|
5
|
+
applySavedPresentation
|
|
6
|
+
} from "./chunk-NM747LUO.js";
|
|
5
7
|
import {
|
|
6
8
|
SHAPE_NAMES,
|
|
7
9
|
buildChartData,
|
|
10
|
+
buildNarrationScript,
|
|
8
11
|
buildRegistry,
|
|
9
12
|
coerceTemplateParams,
|
|
10
13
|
deriveTemplateInputs,
|
|
@@ -32,7 +35,7 @@ import {
|
|
|
32
35
|
templateRegistry,
|
|
33
36
|
writeCustomTemplatesToFrontmatter,
|
|
34
37
|
writeCustomThemesToFrontmatter
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-7IGKCHDP.js";
|
|
36
39
|
import {
|
|
37
40
|
ASCII_TREE_VOCAB,
|
|
38
41
|
ASCII_VOCAB,
|
|
@@ -1033,6 +1036,8 @@ function resolveDocumentTitle(doc, provided) {
|
|
|
1033
1036
|
return provided?.trim() ?? "";
|
|
1034
1037
|
}
|
|
1035
1038
|
function buildPreviewDoc(doc, options) {
|
|
1039
|
+
doc = applySavedPresentation(doc);
|
|
1040
|
+
if (doc.presentationApplied) options = { ...options, interleaveImages: false };
|
|
1036
1041
|
const flat = flattenRenderableBlocks(doc.blocks);
|
|
1037
1042
|
const allImages = collectAllDocImages(doc.blocks);
|
|
1038
1043
|
const usedImageSrcs = /* @__PURE__ */ new Set();
|
|
@@ -4418,6 +4423,16 @@ async function applyNarrationTiming(doc, container) {
|
|
|
4418
4423
|
...doc,
|
|
4419
4424
|
blocks,
|
|
4420
4425
|
duration,
|
|
4426
|
+
presentationNarration: {
|
|
4427
|
+
sourceText: timing.sourceText,
|
|
4428
|
+
src: clip.src,
|
|
4429
|
+
startTime: clip.startAt,
|
|
4430
|
+
duration: playedDuration,
|
|
4431
|
+
bookmarks: timing.bookmarks.flatMap((bookmark) => {
|
|
4432
|
+
const time = timeMap ? timeMap.sourceToPlayed(bookmark.time) : bookmark.time;
|
|
4433
|
+
return time !== null && time >= 0 && time <= playedDuration ? [{ ...bookmark, time: clip.startAt + time }] : [];
|
|
4434
|
+
})
|
|
4435
|
+
},
|
|
4421
4436
|
...captions ? { captions } : {},
|
|
4422
4437
|
...ctx.diagnostics.length > 0 ? { diagnostics: [...doc.diagnostics ?? [], ...ctx.diagnostics] } : {}
|
|
4423
4438
|
};
|