@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
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
mapElementToBlock,
|
|
4
|
-
stripMarkdown
|
|
5
|
-
} from "./chunk-SVN7T6BK.js";
|
|
6
|
-
import {
|
|
2
|
+
buildNarrationScript,
|
|
7
3
|
coerceTemplateParams,
|
|
8
4
|
deriveTemplateInputs,
|
|
9
5
|
flattenRenderableBlocks,
|
|
6
|
+
getPinnedBlockMeta,
|
|
10
7
|
markdownToDoc
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7IGKCHDP.js";
|
|
12
9
|
import {
|
|
13
10
|
isAsciiDiagramFence,
|
|
14
11
|
isAsciiTimelineFence,
|
|
15
12
|
isTreeFence
|
|
16
13
|
} from "./chunk-A5QJJZ5S.js";
|
|
14
|
+
import {
|
|
15
|
+
extractContent,
|
|
16
|
+
mapElementToBlock,
|
|
17
|
+
stripMarkdown
|
|
18
|
+
} from "./chunk-SVN7T6BK.js";
|
|
17
19
|
import {
|
|
18
20
|
cloneAndFreezeData,
|
|
19
21
|
isTemplateBlock
|
|
@@ -37,6 +39,288 @@ import {
|
|
|
37
39
|
hashString
|
|
38
40
|
} from "./chunk-XRW6P7IF.js";
|
|
39
41
|
|
|
42
|
+
// src/transform/presentationPlan.ts
|
|
43
|
+
var PRESENTATION_KEY = "squisq-presentation";
|
|
44
|
+
var PRESENTATION_LAYOUTS = [
|
|
45
|
+
"title",
|
|
46
|
+
"statement",
|
|
47
|
+
"list",
|
|
48
|
+
"comparison",
|
|
49
|
+
"steps",
|
|
50
|
+
"image"
|
|
51
|
+
];
|
|
52
|
+
var PRESENTATION_LIMITS = {
|
|
53
|
+
sourceCharacters: 4e4,
|
|
54
|
+
beats: 80,
|
|
55
|
+
serializedCharacters: 16e4
|
|
56
|
+
};
|
|
57
|
+
function record(value) {
|
|
58
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
59
|
+
}
|
|
60
|
+
function keys(value, required, optional = []) {
|
|
61
|
+
return required.every((key) => Object.prototype.hasOwnProperty.call(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
62
|
+
}
|
|
63
|
+
function text(value, max) {
|
|
64
|
+
return typeof value === "string" && value.trim().length > 0 && value.length <= max && !Array.from(value).some((character) => {
|
|
65
|
+
const code = character.charCodeAt(0);
|
|
66
|
+
return code < 32 && code !== 9 && code !== 10 && code !== 13;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function parsePresentationPlan(input) {
|
|
70
|
+
let value = input;
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
if (value.length > PRESENTATION_LIMITS.serializedCharacters) return null;
|
|
73
|
+
try {
|
|
74
|
+
value = JSON.parse(value);
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!record(value) || !keys(value, ["version", "sourceText", "origin", "beats"]) || value.version !== 1 || !text(value.sourceText, PRESENTATION_LIMITS.sourceCharacters) || typeof value.origin !== "string" || !["automatic", "ai"].includes(value.origin) || !Array.isArray(value.beats) || !value.beats.length || value.beats.length > PRESENTATION_LIMITS.beats)
|
|
80
|
+
return null;
|
|
81
|
+
const beats = [];
|
|
82
|
+
const ids = /* @__PURE__ */ new Set();
|
|
83
|
+
let end = 0;
|
|
84
|
+
for (const beat of value.beats) {
|
|
85
|
+
if (!record(beat) || !keys(
|
|
86
|
+
beat,
|
|
87
|
+
["id", "sourceStart", "sourceEnd", "layout", "headline", "points"],
|
|
88
|
+
["imageSrc"]
|
|
89
|
+
) || !text(beat.id, 80) || ids.has(beat.id) || !Number.isInteger(beat.sourceStart) || !Number.isInteger(beat.sourceEnd) || typeof beat.sourceStart !== "number" || typeof beat.sourceEnd !== "number" || beat.sourceStart < end || beat.sourceEnd <= beat.sourceStart || beat.sourceEnd > value.sourceText.length || value.sourceText.slice(end, beat.sourceStart).trim() || !PRESENTATION_LAYOUTS.includes(beat.layout) || !text(beat.headline, 110) || !Array.isArray(beat.points) || beat.points.length > 4 || !beat.points.every((point) => text(point, 180)))
|
|
90
|
+
return null;
|
|
91
|
+
if (["comparison", "steps", "list"].includes(String(beat.layout)) && beat.points.length < 2)
|
|
92
|
+
return null;
|
|
93
|
+
if (beat.layout === "comparison" && beat.points.length !== 2) return null;
|
|
94
|
+
if (beat.layout === "image" ? !text(beat.imageSrc, 2048) : "imageSrc" in beat) return null;
|
|
95
|
+
ids.add(beat.id);
|
|
96
|
+
end = beat.sourceEnd;
|
|
97
|
+
beats.push({
|
|
98
|
+
id: beat.id,
|
|
99
|
+
sourceStart: beat.sourceStart,
|
|
100
|
+
sourceEnd: beat.sourceEnd,
|
|
101
|
+
layout: beat.layout,
|
|
102
|
+
headline: beat.headline,
|
|
103
|
+
points: [...beat.points],
|
|
104
|
+
...beat.layout === "image" ? { imageSrc: beat.imageSrc } : {}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (value.sourceText.slice(end).trim()) return null;
|
|
108
|
+
return {
|
|
109
|
+
version: 1,
|
|
110
|
+
sourceText: value.sourceText,
|
|
111
|
+
origin: value.origin,
|
|
112
|
+
beats
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function serializePresentationPlan(plan) {
|
|
116
|
+
const parsed = parsePresentationPlan(plan);
|
|
117
|
+
if (!parsed) throw new Error("The presentation plan is invalid.");
|
|
118
|
+
const result = JSON.stringify(parsed);
|
|
119
|
+
if (result.length > PRESENTATION_LIMITS.serializedCharacters)
|
|
120
|
+
throw new Error("The presentation plan is too large.");
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/transform/presentationHints.ts
|
|
125
|
+
var PRESENTATION_HINTS_KEY = "squisq-presentation-hints";
|
|
126
|
+
var DYNAMIC_PRESENTATION_STYLE = "dynamic-slides";
|
|
127
|
+
function defaultPresentationHints() {
|
|
128
|
+
return { version: 1, density: "balanced", inbetweens: true, blocks: [] };
|
|
129
|
+
}
|
|
130
|
+
function record2(value) {
|
|
131
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
132
|
+
}
|
|
133
|
+
function exact(value, required, optional = []) {
|
|
134
|
+
return required.every((key) => Object.prototype.hasOwnProperty.call(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
135
|
+
}
|
|
136
|
+
function text2(value, max) {
|
|
137
|
+
return typeof value === "string" && !!value.trim() && value.length <= max && [...value].every((char) => char.charCodeAt(0) >= 32 || ["\n", "\r", " "].includes(char));
|
|
138
|
+
}
|
|
139
|
+
function parsePresentationHints(input) {
|
|
140
|
+
let value = input;
|
|
141
|
+
if (typeof value === "string") {
|
|
142
|
+
if (value.length > 16e4) return null;
|
|
143
|
+
try {
|
|
144
|
+
value = JSON.parse(value);
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!record2(value) || !exact(value, ["version", "density", "inbetweens", "blocks"]) || value.version !== 1 || !["concise", "balanced", "detailed"].includes(String(value.density)) || typeof value.inbetweens !== "boolean" || !Array.isArray(value.blocks) || value.blocks.length > 80)
|
|
150
|
+
return null;
|
|
151
|
+
const ids = /* @__PURE__ */ new Set();
|
|
152
|
+
const blocks = [];
|
|
153
|
+
for (const entry of value.blocks) {
|
|
154
|
+
if (!record2(entry) || !exact(entry, ["blockId"], ["layout", "inbetweens", "wording", "ai"]) || !text2(entry.blockId, 1024) || ids.has(entry.blockId) || entry.layout !== void 0 && !PRESENTATION_LAYOUTS.includes(entry.layout) || entry.inbetweens !== void 0 && typeof entry.inbetweens !== "boolean")
|
|
155
|
+
return null;
|
|
156
|
+
const hint = { blockId: entry.blockId };
|
|
157
|
+
if (entry.layout !== void 0) hint.layout = entry.layout;
|
|
158
|
+
if (entry.inbetweens !== void 0) hint.inbetweens = entry.inbetweens;
|
|
159
|
+
if (entry.ai !== void 0) {
|
|
160
|
+
if (!Array.isArray(entry.ai) || entry.ai.length > 80) return null;
|
|
161
|
+
hint.ai = [];
|
|
162
|
+
for (const item of entry.ai) {
|
|
163
|
+
if (!record2(item) || !exact(item, ["source", "layout", "headline", "points"]) || !text2(item.source, 4e3) || !PRESENTATION_LAYOUTS.includes(item.layout) || !text2(item.headline, 110) || !Array.isArray(item.points) || item.points.length > 4 || !item.points.every((point) => text2(point, 180)) || ["list", "steps"].includes(String(item.layout)) && item.points.length < 2 || item.layout === "comparison" && item.points.length !== 2)
|
|
164
|
+
return null;
|
|
165
|
+
hint.ai.push({
|
|
166
|
+
source: item.source,
|
|
167
|
+
layout: item.layout,
|
|
168
|
+
headline: item.headline,
|
|
169
|
+
points: [...item.points]
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (entry.wording !== void 0) {
|
|
174
|
+
const wording = entry.wording;
|
|
175
|
+
if (!record2(wording) || !exact(wording, ["source"], ["headline", "points"]) || !text2(wording.source, 4e4) || wording.headline === void 0 && wording.points === void 0 || wording.headline !== void 0 && !text2(wording.headline, 110) || wording.points !== void 0 && (!Array.isArray(wording.points) || wording.points.length > 4 || !wording.points.every((point) => text2(point, 180))))
|
|
176
|
+
return null;
|
|
177
|
+
hint.wording = {
|
|
178
|
+
source: wording.source,
|
|
179
|
+
...wording.headline !== void 0 ? { headline: wording.headline } : {},
|
|
180
|
+
...wording.points !== void 0 ? { points: [...wording.points] } : {}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
ids.add(entry.blockId);
|
|
184
|
+
blocks.push(hint);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
version: 1,
|
|
188
|
+
density: value.density,
|
|
189
|
+
inbetweens: value.inbetweens,
|
|
190
|
+
blocks
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function serializePresentationHints(hints) {
|
|
194
|
+
const parsed = parsePresentationHints(hints);
|
|
195
|
+
if (!parsed) throw new Error("The slide hints are invalid.");
|
|
196
|
+
const json = JSON.stringify(parsed);
|
|
197
|
+
if (json.length > 16e4) throw new Error("The slide hints are too large.");
|
|
198
|
+
return json;
|
|
199
|
+
}
|
|
200
|
+
function presentationSections(doc) {
|
|
201
|
+
const script = buildNarrationScript(doc);
|
|
202
|
+
return script.blocks.map((block) => ({
|
|
203
|
+
blockId: block.blockId,
|
|
204
|
+
heading: block.heading,
|
|
205
|
+
sourceStart: block.charStart,
|
|
206
|
+
sourceEnd: block.charEnd,
|
|
207
|
+
text: script.sourceText.slice(block.charStart, block.charEnd)
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
function usesDynamicPresentation(doc) {
|
|
211
|
+
const fm = doc.frontmatter ?? {};
|
|
212
|
+
const style = Object.prototype.hasOwnProperty.call(fm, "squisq-transform") ? fm["squisq-transform"] : fm["transform-style"];
|
|
213
|
+
return typeof style === "string" && style.trim().toLowerCase().replace(/\s+/g, "-") === DYNAMIC_PRESENTATION_STYLE;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/transform/blockAnalyzer.ts
|
|
217
|
+
function contentsToPlainText(contents) {
|
|
218
|
+
return contents.map((node) => extractPlainText(node)).join("\n");
|
|
219
|
+
}
|
|
220
|
+
function collectImages(node) {
|
|
221
|
+
const results = [];
|
|
222
|
+
if ("type" in node && node.type === "image" && "url" in node) {
|
|
223
|
+
const img = node;
|
|
224
|
+
if (img.url) {
|
|
225
|
+
results.push({ src: img.url, alt: img.alt ?? "" });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const child of getChildren(node)) {
|
|
229
|
+
results.push(...collectImages(child));
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
function extractBlockImages(block, blockIndex) {
|
|
234
|
+
if (!block.contents || block.contents.length === 0) return [];
|
|
235
|
+
const images = [];
|
|
236
|
+
for (const node of block.contents) {
|
|
237
|
+
for (const img of collectImages(node)) {
|
|
238
|
+
images.push({ ...img, sourceBlockIndex: blockIndex });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return images;
|
|
242
|
+
}
|
|
243
|
+
function extractDocImages(blocks) {
|
|
244
|
+
const seen = /* @__PURE__ */ new Set();
|
|
245
|
+
const images = [];
|
|
246
|
+
function walk(blockList) {
|
|
247
|
+
for (const block of blockList) {
|
|
248
|
+
if (block.contents) {
|
|
249
|
+
for (const node of block.contents) {
|
|
250
|
+
for (const img of collectImages(node)) {
|
|
251
|
+
if (!seen.has(img.src)) {
|
|
252
|
+
seen.add(img.src);
|
|
253
|
+
images.push({ src: img.src, alt: img.alt });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (block.children) {
|
|
259
|
+
walk(block.children);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
walk(blocks);
|
|
264
|
+
return images;
|
|
265
|
+
}
|
|
266
|
+
function collectLeafBlocks(blocks) {
|
|
267
|
+
const result = [];
|
|
268
|
+
for (const block of blocks) {
|
|
269
|
+
if (block.children && block.children.length > 0) {
|
|
270
|
+
if (!block.contents || block.contents.length === 0) {
|
|
271
|
+
result.push(block);
|
|
272
|
+
}
|
|
273
|
+
result.push(...collectLeafBlocks(block.children));
|
|
274
|
+
} else {
|
|
275
|
+
result.push(block);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
function analyzeBlocks(blocks, options) {
|
|
281
|
+
return analyzeFlatBlocks(collectLeafBlocks(blocks), options);
|
|
282
|
+
}
|
|
283
|
+
function analyzeFlatBlocks(flat, options) {
|
|
284
|
+
const results = [];
|
|
285
|
+
for (const block of flat) {
|
|
286
|
+
let plainText = "";
|
|
287
|
+
if (block.contents && block.contents.length > 0) {
|
|
288
|
+
plainText = contentsToPlainText(block.contents);
|
|
289
|
+
} else if (block.title) {
|
|
290
|
+
plainText = block.title;
|
|
291
|
+
}
|
|
292
|
+
const stripped = stripMarkdown(plainText);
|
|
293
|
+
const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
|
|
294
|
+
const extraction = extractContent(stripped, options);
|
|
295
|
+
const images = extractBlockImages(block, results.length);
|
|
296
|
+
results.push({
|
|
297
|
+
block,
|
|
298
|
+
plainText: stripped,
|
|
299
|
+
extractions: extraction.elements,
|
|
300
|
+
hasChildren: !!(block.children && block.children.length > 0),
|
|
301
|
+
bodyWordCount,
|
|
302
|
+
images
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return results;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/transform/styles/dynamicSlides.ts
|
|
309
|
+
var dynamicSlidesStyle = {
|
|
310
|
+
id: "dynamic-slides",
|
|
311
|
+
name: "Dynamic slides",
|
|
312
|
+
description: "Advanced summarization: generate concise slides and diagrams from the current text and your hints",
|
|
313
|
+
contentMode: "presentation",
|
|
314
|
+
minConfidence: 0.25,
|
|
315
|
+
transformRatio: 1,
|
|
316
|
+
preferredTypes: [],
|
|
317
|
+
colorSchemes: ["blue", "green", "orange"],
|
|
318
|
+
insertSectionHeaders: false,
|
|
319
|
+
interleaveImages: false,
|
|
320
|
+
blocksPerSection: { max: 4 },
|
|
321
|
+
transitionStyle: "fade"
|
|
322
|
+
};
|
|
323
|
+
|
|
40
324
|
// src/transform/styles/documentary.ts
|
|
41
325
|
var documentaryStyle = {
|
|
42
326
|
id: "documentary",
|
|
@@ -144,6 +428,7 @@ var headingsAndFeaturesStyle = {
|
|
|
144
428
|
|
|
145
429
|
// src/transform/registry.ts
|
|
146
430
|
var TRANSFORM_STYLES = Object.freeze({
|
|
431
|
+
[dynamicSlidesStyle.id]: cloneAndFreezeData(dynamicSlidesStyle),
|
|
147
432
|
[documentaryStyle.id]: cloneAndFreezeData(documentaryStyle),
|
|
148
433
|
[magazineStyle.id]: cloneAndFreezeData(magazineStyle),
|
|
149
434
|
[dataDrivenStyle.id]: cloneAndFreezeData(dataDrivenStyle),
|
|
@@ -179,8 +464,8 @@ function validateTransformStyle(style) {
|
|
|
179
464
|
fail(`${key} must be a finite number between 0 and 1`);
|
|
180
465
|
}
|
|
181
466
|
}
|
|
182
|
-
if (candidate.contentMode !== void 0 && candidate.contentMode !== "brief" && candidate.contentMode !== "headings-and-features") {
|
|
183
|
-
fail("contentMode must be brief
|
|
467
|
+
if (candidate.contentMode !== void 0 && candidate.contentMode !== "brief" && candidate.contentMode !== "headings-and-features" && candidate.contentMode !== "presentation") {
|
|
468
|
+
fail("contentMode must be brief, headings-and-features, or presentation");
|
|
184
469
|
}
|
|
185
470
|
if (!Array.isArray(candidate.preferredTypes) || candidate.preferredTypes.some((type) => !EXTRACTION_TYPES.has(type))) {
|
|
186
471
|
fail("preferredTypes contains an unknown extraction type");
|
|
@@ -297,96 +582,202 @@ function getTransformStyleSummaries(registry) {
|
|
|
297
582
|
});
|
|
298
583
|
}
|
|
299
584
|
|
|
300
|
-
// src/transform/
|
|
301
|
-
|
|
302
|
-
|
|
585
|
+
// src/transform/timingAllocator.ts
|
|
586
|
+
var MIN_BLOCK_DURATION = 3;
|
|
587
|
+
var MAX_BLOCK_DURATION = 20;
|
|
588
|
+
var DEFAULT_BLOCK_DURATION = 6;
|
|
589
|
+
function allocateTiming(blocks, totalDuration) {
|
|
590
|
+
if (blocks.length === 0) return [];
|
|
591
|
+
const hasAnchors = blocks.some(
|
|
592
|
+
(b) => isTemplateBlock(b) && typeof b.sourceStartTime === "number"
|
|
593
|
+
);
|
|
594
|
+
return hasAnchors ? allocateAnchored(blocks, totalDuration) : allocateRescaled(blocks, totalDuration);
|
|
303
595
|
}
|
|
304
|
-
function
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
596
|
+
function allocateRescaled(blocks, totalDuration) {
|
|
597
|
+
const effectiveDuration = totalDuration > 0 ? totalDuration : estimateDefaultDuration(blocks);
|
|
598
|
+
const durations = blocks.map((block) => {
|
|
599
|
+
if (!isTemplateBlock(block) && block.duration > 0) {
|
|
600
|
+
return block.duration;
|
|
601
|
+
}
|
|
602
|
+
if (isTemplateBlock(block) && block.duration > 0) {
|
|
603
|
+
return block.duration;
|
|
310
604
|
}
|
|
605
|
+
return DEFAULT_BLOCK_DURATION;
|
|
606
|
+
});
|
|
607
|
+
const rawTotal = durations.reduce((sum, d) => sum + d, 0);
|
|
608
|
+
if (rawTotal > 0 && effectiveDuration > 0) {
|
|
609
|
+
fitDurations(durations, effectiveDuration);
|
|
311
610
|
}
|
|
312
|
-
|
|
313
|
-
|
|
611
|
+
let currentTime = 0;
|
|
612
|
+
const result = [];
|
|
613
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
614
|
+
result.push(toOutputBlock(blocks[i], currentTime, durations[i]));
|
|
615
|
+
currentTime += durations[i];
|
|
314
616
|
}
|
|
315
|
-
return
|
|
617
|
+
return result;
|
|
316
618
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
619
|
+
var MIN_FLOATING_DURATION = 1;
|
|
620
|
+
function allocateAnchored(blocks, totalDuration) {
|
|
621
|
+
const entries = blocks.map((block) => {
|
|
622
|
+
if (isTemplateBlock(block)) {
|
|
623
|
+
return {
|
|
624
|
+
block,
|
|
625
|
+
anchorStart: typeof block.sourceStartTime === "number" ? block.sourceStartTime : null,
|
|
626
|
+
rawDuration: block.duration > 0 ? block.duration : DEFAULT_BLOCK_DURATION
|
|
627
|
+
};
|
|
323
628
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
629
|
+
return {
|
|
630
|
+
block,
|
|
631
|
+
anchorStart: block.startTime,
|
|
632
|
+
rawDuration: block.duration > 0 ? block.duration : DEFAULT_BLOCK_DURATION
|
|
633
|
+
};
|
|
634
|
+
});
|
|
635
|
+
const starts = new Array(entries.length);
|
|
636
|
+
let cursor = 0;
|
|
637
|
+
let seenAnchor = false;
|
|
638
|
+
let i = 0;
|
|
639
|
+
while (i < entries.length) {
|
|
640
|
+
const entry = entries[i];
|
|
641
|
+
if (entry.anchorStart !== null) {
|
|
642
|
+
const start = Math.max(cursor, entry.anchorStart);
|
|
643
|
+
starts[i] = start;
|
|
644
|
+
const hint = isTemplateBlock(entry.block) ? entry.block.sourceDuration ?? entry.rawDuration : entry.rawDuration;
|
|
645
|
+
cursor = start + Math.max(MIN_FLOATING_DURATION, hint);
|
|
646
|
+
seenAnchor = true;
|
|
647
|
+
i++;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
const runStart = i;
|
|
651
|
+
while (i < entries.length && entries[i].anchorStart === null) i++;
|
|
652
|
+
const run = entries.slice(runStart, i);
|
|
653
|
+
const rawSum = run.reduce((sum, e) => sum + e.rawDuration, 0);
|
|
654
|
+
const nextAnchorStart = i < entries.length ? Math.max(entries[i].anchorStart ?? cursor, cursor) : null;
|
|
655
|
+
if (nextAnchorStart === null) {
|
|
656
|
+
let acc = cursor;
|
|
657
|
+
for (let k = 0; k < run.length; k++) {
|
|
658
|
+
starts[runStart + k] = acc;
|
|
659
|
+
acc += run[k].rawDuration;
|
|
341
660
|
}
|
|
342
|
-
|
|
343
|
-
|
|
661
|
+
cursor = acc;
|
|
662
|
+
} else if (!seenAnchor) {
|
|
663
|
+
const gap = nextAnchorStart - cursor;
|
|
664
|
+
let acc = cursor;
|
|
665
|
+
for (let k = 0; k < run.length; k++) {
|
|
666
|
+
starts[runStart + k] = acc;
|
|
667
|
+
acc += rawSum > 0 ? run[k].rawDuration / rawSum * gap : gap / run.length;
|
|
344
668
|
}
|
|
669
|
+
cursor = nextAnchorStart;
|
|
670
|
+
} else {
|
|
671
|
+
const gap = Math.max(0, nextAnchorStart - cursor);
|
|
672
|
+
const scale = rawSum > 0 ? Math.min(1, gap / rawSum) : 0;
|
|
673
|
+
let acc = Math.max(cursor, nextAnchorStart - rawSum * scale);
|
|
674
|
+
for (let k = 0; k < run.length; k++) {
|
|
675
|
+
starts[runStart + k] = acc;
|
|
676
|
+
acc += run[k].rawDuration * scale;
|
|
677
|
+
}
|
|
678
|
+
cursor = nextAnchorStart;
|
|
345
679
|
}
|
|
346
680
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
681
|
+
for (let k = 0; k < starts.length; k++) {
|
|
682
|
+
const floor = k > 0 ? starts[k - 1] + MIN_FLOATING_DURATION : 0;
|
|
683
|
+
if (starts[k] < floor) starts[k] = floor;
|
|
684
|
+
}
|
|
351
685
|
const result = [];
|
|
352
|
-
for (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
result.push(...collectLeafBlocks(block.children));
|
|
686
|
+
for (let k = 0; k < entries.length; k++) {
|
|
687
|
+
const entry = entries[k];
|
|
688
|
+
let duration;
|
|
689
|
+
if (k + 1 < entries.length) {
|
|
690
|
+
duration = Math.max(MIN_FLOATING_DURATION, starts[k + 1] - starts[k]);
|
|
358
691
|
} else {
|
|
359
|
-
|
|
692
|
+
const authored = isTemplateBlock(entry.block) ? entry.block.sourceDuration ?? entry.rawDuration : entry.rawDuration;
|
|
693
|
+
duration = Math.max(MIN_FLOATING_DURATION, authored, totalDuration - starts[k]);
|
|
360
694
|
}
|
|
695
|
+
result.push(toOutputBlock(entry.block, starts[k], duration));
|
|
361
696
|
}
|
|
362
697
|
return result;
|
|
363
698
|
}
|
|
364
|
-
function
|
|
365
|
-
|
|
699
|
+
function toOutputBlock(block, startTime, duration) {
|
|
700
|
+
if (isTemplateBlock(block)) {
|
|
701
|
+
return {
|
|
702
|
+
...block,
|
|
703
|
+
startTime,
|
|
704
|
+
duration,
|
|
705
|
+
title: getTemplateTitle(block)
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
return { ...block, startTime, duration };
|
|
366
709
|
}
|
|
367
|
-
function
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
710
|
+
function clampDuration(d) {
|
|
711
|
+
return Math.max(MIN_BLOCK_DURATION, Math.min(MAX_BLOCK_DURATION, d));
|
|
712
|
+
}
|
|
713
|
+
var SCALE_SEARCH_ITERATIONS = 60;
|
|
714
|
+
function totalAtScale(durations, scale) {
|
|
715
|
+
let sum = 0;
|
|
716
|
+
for (const d of durations) sum += clampDuration(d * scale);
|
|
717
|
+
return sum;
|
|
718
|
+
}
|
|
719
|
+
function fitDurations(durations, target) {
|
|
720
|
+
const rawTotal = durations.reduce((sum, d) => sum + d, 0);
|
|
721
|
+
if (rawTotal <= 0) return;
|
|
722
|
+
const plain = target / rawTotal;
|
|
723
|
+
const clampsNothing = durations.every((d) => {
|
|
724
|
+
const scaled = d * plain;
|
|
725
|
+
return scaled >= MIN_BLOCK_DURATION && scaled <= MAX_BLOCK_DURATION;
|
|
726
|
+
});
|
|
727
|
+
if (clampsNothing) {
|
|
728
|
+
for (let i = 0; i < durations.length; i++) durations[i] *= plain;
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
let lo = 0;
|
|
732
|
+
let hi = 1;
|
|
733
|
+
for (let i = 0; i < SCALE_SEARCH_ITERATIONS && totalAtScale(durations, hi) < target; i++) {
|
|
734
|
+
hi *= 2;
|
|
735
|
+
}
|
|
736
|
+
for (let i = 0; i < SCALE_SEARCH_ITERATIONS; i++) {
|
|
737
|
+
const mid = (lo + hi) / 2;
|
|
738
|
+
if (totalAtScale(durations, mid) < target) lo = mid;
|
|
739
|
+
else hi = mid;
|
|
740
|
+
}
|
|
741
|
+
const scale = (lo + hi) / 2;
|
|
742
|
+
let clampedTotal = 0;
|
|
743
|
+
let freeRaw = 0;
|
|
744
|
+
for (const d of durations) {
|
|
745
|
+
const scaled = d * scale;
|
|
746
|
+
if (scaled < MIN_BLOCK_DURATION) clampedTotal += MIN_BLOCK_DURATION;
|
|
747
|
+
else if (scaled > MAX_BLOCK_DURATION) clampedTotal += MAX_BLOCK_DURATION;
|
|
748
|
+
else freeRaw += d;
|
|
749
|
+
}
|
|
750
|
+
const exact2 = freeRaw > 0 ? (target - clampedTotal) / freeRaw : scale;
|
|
751
|
+
const finalScale = Number.isFinite(exact2) && exact2 > 0 ? exact2 : scale;
|
|
752
|
+
for (let i = 0; i < durations.length; i++) {
|
|
753
|
+
durations[i] = clampDuration(durations[i] * finalScale);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
function estimateDefaultDuration(blocks) {
|
|
757
|
+
let totalSeconds = 0;
|
|
758
|
+
for (const block of blocks) {
|
|
759
|
+
if (!isTemplateBlock(block) && block.duration > 0) {
|
|
760
|
+
totalSeconds += block.duration;
|
|
761
|
+
} else {
|
|
762
|
+
totalSeconds += DEFAULT_BLOCK_DURATION;
|
|
375
763
|
}
|
|
376
|
-
const stripped = stripMarkdown(plainText);
|
|
377
|
-
const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
|
|
378
|
-
const extraction = extractContent(stripped, options);
|
|
379
|
-
const images = extractBlockImages(block, results.length);
|
|
380
|
-
results.push({
|
|
381
|
-
block,
|
|
382
|
-
plainText: stripped,
|
|
383
|
-
extractions: extraction.elements,
|
|
384
|
-
hasChildren: !!(block.children && block.children.length > 0),
|
|
385
|
-
bodyWordCount,
|
|
386
|
-
images
|
|
387
|
-
});
|
|
388
764
|
}
|
|
389
|
-
return
|
|
765
|
+
return Math.max(totalSeconds, blocks.length * MIN_BLOCK_DURATION);
|
|
766
|
+
}
|
|
767
|
+
function getTemplateTitle(block) {
|
|
768
|
+
if ("title" in block && typeof block.title === "string") {
|
|
769
|
+
return block.title;
|
|
770
|
+
}
|
|
771
|
+
if ("stat" in block && typeof block.stat === "string") {
|
|
772
|
+
return block.stat;
|
|
773
|
+
}
|
|
774
|
+
if ("fact" in block && typeof block.fact === "string") {
|
|
775
|
+
return block.fact;
|
|
776
|
+
}
|
|
777
|
+
if ("quote" in block && typeof block.quote === "string") {
|
|
778
|
+
return block.quote.slice(0, 60);
|
|
779
|
+
}
|
|
780
|
+
return void 0;
|
|
390
781
|
}
|
|
391
782
|
|
|
392
783
|
// src/transform/contentSummary.ts
|
|
@@ -418,8 +809,8 @@ var PROSE_KEYS = /* @__PURE__ */ new Set([
|
|
|
418
809
|
"quote",
|
|
419
810
|
"text"
|
|
420
811
|
]);
|
|
421
|
-
function briefText(
|
|
422
|
-
const normalized =
|
|
812
|
+
function briefText(text3) {
|
|
813
|
+
const normalized = text3.replace(/\s+/g, " ").trim();
|
|
423
814
|
const sentence = normalized.match(/^.*?[.!?](?:["'”’])?(?=\s|$)/)?.[0] ?? normalized;
|
|
424
815
|
const words = sentence.split(/\s+/).filter(Boolean);
|
|
425
816
|
let brief = words.slice(0, 24).join(" ");
|
|
@@ -480,11 +871,11 @@ function extractFeatureContents(contents, keepData = false) {
|
|
|
480
871
|
for (const node of contents ?? []) visit(node);
|
|
481
872
|
return features;
|
|
482
873
|
}
|
|
483
|
-
function withoutProse(
|
|
484
|
-
return
|
|
874
|
+
function withoutProse(record3) {
|
|
875
|
+
return record3 && Object.fromEntries(Object.entries(record3).filter(([key]) => !PROSE_KEYS.has(key)));
|
|
485
876
|
}
|
|
486
877
|
function typedMediaContents(block) {
|
|
487
|
-
const
|
|
878
|
+
const record3 = {
|
|
488
879
|
...block,
|
|
489
880
|
...block.templateData,
|
|
490
881
|
...coerceTemplateParams(block.template ?? "", block.templateOverrides ?? {}).input
|
|
@@ -498,23 +889,23 @@ function typedMediaContents(block) {
|
|
|
498
889
|
});
|
|
499
890
|
}
|
|
500
891
|
};
|
|
501
|
-
addImage(
|
|
892
|
+
addImage(record3.imageSrc, record3.imageAlt);
|
|
502
893
|
for (const key of ["accentImage", "backgroundImage"]) {
|
|
503
|
-
const image =
|
|
894
|
+
const image = record3[key];
|
|
504
895
|
if (image && typeof image === "object") {
|
|
505
896
|
const data = image;
|
|
506
897
|
addImage(data.src, data.alt);
|
|
507
898
|
}
|
|
508
899
|
}
|
|
509
|
-
if (Array.isArray(
|
|
510
|
-
for (const image of
|
|
900
|
+
if (Array.isArray(record3.images)) {
|
|
901
|
+
for (const image of record3.images) {
|
|
511
902
|
if (image && typeof image === "object") {
|
|
512
903
|
const data = image;
|
|
513
904
|
addImage(data.src, data.alt);
|
|
514
905
|
}
|
|
515
906
|
}
|
|
516
907
|
}
|
|
517
|
-
if (typeof
|
|
908
|
+
if (typeof record3.videoSrc === "string" && record3.videoSrc) {
|
|
518
909
|
const htmlChildren = [
|
|
519
910
|
{
|
|
520
911
|
type: "htmlElement",
|
|
@@ -522,8 +913,8 @@ function typedMediaContents(block) {
|
|
|
522
913
|
selfClosing: false,
|
|
523
914
|
children: [],
|
|
524
915
|
attributes: {
|
|
525
|
-
src:
|
|
526
|
-
...typeof
|
|
916
|
+
src: record3.videoSrc,
|
|
917
|
+
...typeof record3.posterSrc === "string" ? { poster: record3.posterSrc } : {}
|
|
527
918
|
}
|
|
528
919
|
}
|
|
529
920
|
];
|
|
@@ -833,207 +1224,27 @@ function translateTemplateBlock(block, target) {
|
|
|
833
1224
|
return null;
|
|
834
1225
|
}
|
|
835
1226
|
|
|
836
|
-
// src/transform/
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
function allocateTiming(blocks, totalDuration) {
|
|
841
|
-
if (blocks.length === 0) return [];
|
|
842
|
-
const hasAnchors = blocks.some(
|
|
843
|
-
(b) => isTemplateBlock(b) && typeof b.sourceStartTime === "number"
|
|
844
|
-
);
|
|
845
|
-
return hasAnchors ? allocateAnchored(blocks, totalDuration) : allocateRescaled(blocks, totalDuration);
|
|
846
|
-
}
|
|
847
|
-
function allocateRescaled(blocks, totalDuration) {
|
|
848
|
-
const effectiveDuration = totalDuration > 0 ? totalDuration : estimateDefaultDuration(blocks);
|
|
849
|
-
const durations = blocks.map((block) => {
|
|
850
|
-
if (!isTemplateBlock(block) && block.duration > 0) {
|
|
851
|
-
return block.duration;
|
|
852
|
-
}
|
|
853
|
-
if (isTemplateBlock(block) && block.duration > 0) {
|
|
854
|
-
return block.duration;
|
|
855
|
-
}
|
|
856
|
-
return DEFAULT_BLOCK_DURATION;
|
|
857
|
-
});
|
|
858
|
-
const rawTotal = durations.reduce((sum, d) => sum + d, 0);
|
|
859
|
-
if (rawTotal > 0 && effectiveDuration > 0) {
|
|
860
|
-
fitDurations(durations, effectiveDuration);
|
|
861
|
-
}
|
|
862
|
-
let currentTime = 0;
|
|
863
|
-
const result = [];
|
|
864
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
865
|
-
result.push(toOutputBlock(blocks[i], currentTime, durations[i]));
|
|
866
|
-
currentTime += durations[i];
|
|
867
|
-
}
|
|
868
|
-
return result;
|
|
869
|
-
}
|
|
870
|
-
var MIN_FLOATING_DURATION = 1;
|
|
871
|
-
function allocateAnchored(blocks, totalDuration) {
|
|
872
|
-
const entries = blocks.map((block) => {
|
|
873
|
-
if (isTemplateBlock(block)) {
|
|
874
|
-
return {
|
|
875
|
-
block,
|
|
876
|
-
anchorStart: typeof block.sourceStartTime === "number" ? block.sourceStartTime : null,
|
|
877
|
-
rawDuration: block.duration > 0 ? block.duration : DEFAULT_BLOCK_DURATION
|
|
878
|
-
};
|
|
879
|
-
}
|
|
1227
|
+
// src/transform/applyTransform.ts
|
|
1228
|
+
function applyTransform(doc, style, options) {
|
|
1229
|
+
const baseConfig = resolveTransformStyle(style, options?.registry);
|
|
1230
|
+
if (baseConfig.contentMode === "presentation") {
|
|
880
1231
|
return {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1232
|
+
doc: {
|
|
1233
|
+
...doc,
|
|
1234
|
+
frontmatter: { ...doc.frontmatter, "squisq-transform": DYNAMIC_PRESENTATION_STYLE }
|
|
1235
|
+
},
|
|
1236
|
+
stats: { totalInputBlocks: doc.blocks.length, transformedBlocks: 0, insertedBlocks: 0 }
|
|
884
1237
|
};
|
|
885
|
-
});
|
|
886
|
-
const starts = new Array(entries.length);
|
|
887
|
-
let cursor = 0;
|
|
888
|
-
let seenAnchor = false;
|
|
889
|
-
let i = 0;
|
|
890
|
-
while (i < entries.length) {
|
|
891
|
-
const entry = entries[i];
|
|
892
|
-
if (entry.anchorStart !== null) {
|
|
893
|
-
const start = Math.max(cursor, entry.anchorStart);
|
|
894
|
-
starts[i] = start;
|
|
895
|
-
const hint = isTemplateBlock(entry.block) ? entry.block.sourceDuration ?? entry.rawDuration : entry.rawDuration;
|
|
896
|
-
cursor = start + Math.max(MIN_FLOATING_DURATION, hint);
|
|
897
|
-
seenAnchor = true;
|
|
898
|
-
i++;
|
|
899
|
-
continue;
|
|
900
|
-
}
|
|
901
|
-
const runStart = i;
|
|
902
|
-
while (i < entries.length && entries[i].anchorStart === null) i++;
|
|
903
|
-
const run = entries.slice(runStart, i);
|
|
904
|
-
const rawSum = run.reduce((sum, e) => sum + e.rawDuration, 0);
|
|
905
|
-
const nextAnchorStart = i < entries.length ? Math.max(entries[i].anchorStart ?? cursor, cursor) : null;
|
|
906
|
-
if (nextAnchorStart === null) {
|
|
907
|
-
let acc = cursor;
|
|
908
|
-
for (let k = 0; k < run.length; k++) {
|
|
909
|
-
starts[runStart + k] = acc;
|
|
910
|
-
acc += run[k].rawDuration;
|
|
911
|
-
}
|
|
912
|
-
cursor = acc;
|
|
913
|
-
} else if (!seenAnchor) {
|
|
914
|
-
const gap = nextAnchorStart - cursor;
|
|
915
|
-
let acc = cursor;
|
|
916
|
-
for (let k = 0; k < run.length; k++) {
|
|
917
|
-
starts[runStart + k] = acc;
|
|
918
|
-
acc += rawSum > 0 ? run[k].rawDuration / rawSum * gap : gap / run.length;
|
|
919
|
-
}
|
|
920
|
-
cursor = nextAnchorStart;
|
|
921
|
-
} else {
|
|
922
|
-
const gap = Math.max(0, nextAnchorStart - cursor);
|
|
923
|
-
const scale = rawSum > 0 ? Math.min(1, gap / rawSum) : 0;
|
|
924
|
-
let acc = Math.max(cursor, nextAnchorStart - rawSum * scale);
|
|
925
|
-
for (let k = 0; k < run.length; k++) {
|
|
926
|
-
starts[runStart + k] = acc;
|
|
927
|
-
acc += run[k].rawDuration * scale;
|
|
928
|
-
}
|
|
929
|
-
cursor = nextAnchorStart;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
for (let k = 0; k < starts.length; k++) {
|
|
933
|
-
const floor = k > 0 ? starts[k - 1] + MIN_FLOATING_DURATION : 0;
|
|
934
|
-
if (starts[k] < floor) starts[k] = floor;
|
|
935
|
-
}
|
|
936
|
-
const result = [];
|
|
937
|
-
for (let k = 0; k < entries.length; k++) {
|
|
938
|
-
const entry = entries[k];
|
|
939
|
-
let duration;
|
|
940
|
-
if (k + 1 < entries.length) {
|
|
941
|
-
duration = Math.max(MIN_FLOATING_DURATION, starts[k + 1] - starts[k]);
|
|
942
|
-
} else {
|
|
943
|
-
const authored = isTemplateBlock(entry.block) ? entry.block.sourceDuration ?? entry.rawDuration : entry.rawDuration;
|
|
944
|
-
duration = Math.max(MIN_FLOATING_DURATION, authored, totalDuration - starts[k]);
|
|
945
|
-
}
|
|
946
|
-
result.push(toOutputBlock(entry.block, starts[k], duration));
|
|
947
1238
|
}
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
function toOutputBlock(block, startTime, duration) {
|
|
951
|
-
if (isTemplateBlock(block)) {
|
|
1239
|
+
if (Object.prototype.hasOwnProperty.call(doc.frontmatter ?? {}, PRESENTATION_KEY) && !usesDynamicPresentation(doc)) {
|
|
952
1240
|
return {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
duration,
|
|
956
|
-
title: getTemplateTitle(block)
|
|
1241
|
+
doc,
|
|
1242
|
+
stats: { totalInputBlocks: doc.blocks.length, transformedBlocks: 0, insertedBlocks: 0 }
|
|
957
1243
|
};
|
|
958
1244
|
}
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
function clampDuration(d) {
|
|
962
|
-
return Math.max(MIN_BLOCK_DURATION, Math.min(MAX_BLOCK_DURATION, d));
|
|
963
|
-
}
|
|
964
|
-
var SCALE_SEARCH_ITERATIONS = 60;
|
|
965
|
-
function totalAtScale(durations, scale) {
|
|
966
|
-
let sum = 0;
|
|
967
|
-
for (const d of durations) sum += clampDuration(d * scale);
|
|
968
|
-
return sum;
|
|
969
|
-
}
|
|
970
|
-
function fitDurations(durations, target) {
|
|
971
|
-
const rawTotal = durations.reduce((sum, d) => sum + d, 0);
|
|
972
|
-
if (rawTotal <= 0) return;
|
|
973
|
-
const plain = target / rawTotal;
|
|
974
|
-
const clampsNothing = durations.every((d) => {
|
|
975
|
-
const scaled = d * plain;
|
|
976
|
-
return scaled >= MIN_BLOCK_DURATION && scaled <= MAX_BLOCK_DURATION;
|
|
977
|
-
});
|
|
978
|
-
if (clampsNothing) {
|
|
979
|
-
for (let i = 0; i < durations.length; i++) durations[i] *= plain;
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
let lo = 0;
|
|
983
|
-
let hi = 1;
|
|
984
|
-
for (let i = 0; i < SCALE_SEARCH_ITERATIONS && totalAtScale(durations, hi) < target; i++) {
|
|
985
|
-
hi *= 2;
|
|
986
|
-
}
|
|
987
|
-
for (let i = 0; i < SCALE_SEARCH_ITERATIONS; i++) {
|
|
988
|
-
const mid = (lo + hi) / 2;
|
|
989
|
-
if (totalAtScale(durations, mid) < target) lo = mid;
|
|
990
|
-
else hi = mid;
|
|
991
|
-
}
|
|
992
|
-
const scale = (lo + hi) / 2;
|
|
993
|
-
let clampedTotal = 0;
|
|
994
|
-
let freeRaw = 0;
|
|
995
|
-
for (const d of durations) {
|
|
996
|
-
const scaled = d * scale;
|
|
997
|
-
if (scaled < MIN_BLOCK_DURATION) clampedTotal += MIN_BLOCK_DURATION;
|
|
998
|
-
else if (scaled > MAX_BLOCK_DURATION) clampedTotal += MAX_BLOCK_DURATION;
|
|
999
|
-
else freeRaw += d;
|
|
1000
|
-
}
|
|
1001
|
-
const exact = freeRaw > 0 ? (target - clampedTotal) / freeRaw : scale;
|
|
1002
|
-
const finalScale = Number.isFinite(exact) && exact > 0 ? exact : scale;
|
|
1003
|
-
for (let i = 0; i < durations.length; i++) {
|
|
1004
|
-
durations[i] = clampDuration(durations[i] * finalScale);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
function estimateDefaultDuration(blocks) {
|
|
1008
|
-
let totalSeconds = 0;
|
|
1009
|
-
for (const block of blocks) {
|
|
1010
|
-
if (!isTemplateBlock(block) && block.duration > 0) {
|
|
1011
|
-
totalSeconds += block.duration;
|
|
1012
|
-
} else {
|
|
1013
|
-
totalSeconds += DEFAULT_BLOCK_DURATION;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
return Math.max(totalSeconds, blocks.length * MIN_BLOCK_DURATION);
|
|
1017
|
-
}
|
|
1018
|
-
function getTemplateTitle(block) {
|
|
1019
|
-
if ("title" in block && typeof block.title === "string") {
|
|
1020
|
-
return block.title;
|
|
1021
|
-
}
|
|
1022
|
-
if ("stat" in block && typeof block.stat === "string") {
|
|
1023
|
-
return block.stat;
|
|
1024
|
-
}
|
|
1025
|
-
if ("fact" in block && typeof block.fact === "string") {
|
|
1026
|
-
return block.fact;
|
|
1027
|
-
}
|
|
1028
|
-
if ("quote" in block && typeof block.quote === "string") {
|
|
1029
|
-
return block.quote.slice(0, 60);
|
|
1245
|
+
if (usesDynamicPresentation(doc)) {
|
|
1246
|
+
doc = { ...doc, frontmatter: { ...doc.frontmatter, "squisq-transform": baseConfig.id } };
|
|
1030
1247
|
}
|
|
1031
|
-
return void 0;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// src/transform/applyTransform.ts
|
|
1035
|
-
function applyTransform(doc, style, options) {
|
|
1036
|
-
const baseConfig = resolveTransformStyle(style, options?.registry);
|
|
1037
1248
|
const config = options?.overrides ? { ...baseConfig, ...options.overrides, id: baseConfig.id } : baseConfig;
|
|
1038
1249
|
const seed = options?.seed ?? hashString(doc.articleId || "transform");
|
|
1039
1250
|
const images = options?.images ?? extractDocImages(doc.blocks);
|
|
@@ -1334,21 +1545,21 @@ function standaloneImagePool(images, content) {
|
|
|
1334
1545
|
...available.filter((image) => accents.has(image.src))
|
|
1335
1546
|
];
|
|
1336
1547
|
}
|
|
1337
|
-
function chunkNarration(
|
|
1338
|
-
if (count <= 1) return [
|
|
1548
|
+
function chunkNarration(text3, count) {
|
|
1549
|
+
if (count <= 1) return [text3];
|
|
1339
1550
|
const sentences = [];
|
|
1340
1551
|
const boundary = /[.!?]["'’”)\]]*\s+/g;
|
|
1341
1552
|
let start = 0;
|
|
1342
1553
|
let match;
|
|
1343
|
-
while ((match = boundary.exec(
|
|
1554
|
+
while ((match = boundary.exec(text3)) !== null) {
|
|
1344
1555
|
const end = match.index + match[0].length;
|
|
1345
|
-
const sentence =
|
|
1556
|
+
const sentence = text3.slice(start, end).trim();
|
|
1346
1557
|
if (sentence) sentences.push(sentence);
|
|
1347
1558
|
start = end;
|
|
1348
1559
|
}
|
|
1349
|
-
const tail =
|
|
1560
|
+
const tail = text3.slice(start).trim();
|
|
1350
1561
|
if (tail) sentences.push(tail);
|
|
1351
|
-
if (sentences.length < 2) return [
|
|
1562
|
+
if (sentences.length < 2) return [text3];
|
|
1352
1563
|
const chunkCount = Math.min(count, sentences.length);
|
|
1353
1564
|
const words = sentences.map((sentence) => sentence.split(/\s+/).length);
|
|
1354
1565
|
const totalWords = words.reduce((sum, n) => sum + n, 0);
|
|
@@ -1419,15 +1630,335 @@ function videoBlock(video, index, segmentId, duration) {
|
|
|
1419
1630
|
};
|
|
1420
1631
|
}
|
|
1421
1632
|
|
|
1633
|
+
// src/transform/presentationPlanner.ts
|
|
1634
|
+
function clip(text3, words = 12) {
|
|
1635
|
+
const clean = text3.replace(/\s+/g, " ").trim();
|
|
1636
|
+
const parts = clean.split(" ");
|
|
1637
|
+
return (parts.length > words ? parts.slice(0, words).join(" ") + "\u2026" : clean).slice(
|
|
1638
|
+
0,
|
|
1639
|
+
words === 12 ? 110 : 180
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
function field(block, key) {
|
|
1643
|
+
if (!block || typeof block !== "object") return void 0;
|
|
1644
|
+
const value = block[key];
|
|
1645
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
1646
|
+
}
|
|
1647
|
+
function createPresentationPlan(doc, hints = defaultPresentationHints()) {
|
|
1648
|
+
if (!parsePresentationHints(hints)) throw new Error("The slide hints are invalid.");
|
|
1649
|
+
const script = buildNarrationScript(doc);
|
|
1650
|
+
if (!script.sourceText.trim()) throw new Error("Add some text before making a presentation.");
|
|
1651
|
+
if (script.sourceText.length > PRESENTATION_LIMITS.sourceCharacters)
|
|
1652
|
+
throw new Error(
|
|
1653
|
+
"This document is too long for one presentation. Split it into shorter documents."
|
|
1654
|
+
);
|
|
1655
|
+
const images = extractDocImages(doc.blocks);
|
|
1656
|
+
const beats = [];
|
|
1657
|
+
const supportingOpeners = /* @__PURE__ */ new Set();
|
|
1658
|
+
const wordLimit = { concise: 28, balanced: 42, detailed: 64 }[hints.density];
|
|
1659
|
+
for (const block of script.blocks) {
|
|
1660
|
+
const source = script.sourceText.slice(block.charStart, block.charEnd);
|
|
1661
|
+
const words = [...source.matchAll(/\S+/gu)];
|
|
1662
|
+
const hint = hints.blocks.find((item) => item.blockId === block.blockId);
|
|
1663
|
+
const wording = hint?.wording?.source === source ? hint.wording : void 0;
|
|
1664
|
+
const inbetweens = hint?.inbetweens ?? hints.inbetweens;
|
|
1665
|
+
let first = 0;
|
|
1666
|
+
while (first < words.length) {
|
|
1667
|
+
let last = Math.min(first + wordLimit, words.length);
|
|
1668
|
+
if (last < words.length) {
|
|
1669
|
+
for (let cursor = last; cursor > first + 18; cursor--) {
|
|
1670
|
+
if (/[.!?][”"')\]]?$/u.test(words[cursor - 1][0])) {
|
|
1671
|
+
last = cursor;
|
|
1672
|
+
break;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const start = block.charStart + words[first].index;
|
|
1677
|
+
const end = block.charStart + words[last - 1].index + words[last - 1][0].length;
|
|
1678
|
+
const passage = script.sourceText.slice(start, end);
|
|
1679
|
+
const index = beats.length;
|
|
1680
|
+
const suggested = transformNarratedSegment(
|
|
1681
|
+
{
|
|
1682
|
+
articleId: doc.articleId,
|
|
1683
|
+
segmentId: `presentation-${index}`,
|
|
1684
|
+
title: block.heading ?? "",
|
|
1685
|
+
text: passage,
|
|
1686
|
+
duration: Math.max(4, (last - first) / 2.5),
|
|
1687
|
+
audioSrc: "",
|
|
1688
|
+
includeTitleBlock: false
|
|
1689
|
+
},
|
|
1690
|
+
{ slidesPerMinute: 4, minConfidence: 0.25, style: "documentary" }
|
|
1691
|
+
).doc.blocks[0];
|
|
1692
|
+
const sentences = passage.split(/(?<=[.!?])\s+|\n+/u).map((part) => part.trim()).filter(Boolean);
|
|
1693
|
+
const bodySentences = sentences.filter((part) => part !== block.heading);
|
|
1694
|
+
let points = (bodySentences.length ? bodySentences : sentences).slice(0, 3).map((part) => clip(part, 22));
|
|
1695
|
+
let headline = clip(
|
|
1696
|
+
first === 0 && block.heading ? block.heading : field(suggested, "fact") ?? field(suggested, "description") ?? field(suggested, "quote") ?? sentences[0] ?? passage
|
|
1697
|
+
);
|
|
1698
|
+
if (!headline) headline = clip(passage);
|
|
1699
|
+
let layout = index === 0 ? "title" : points.length >= 2 && index % 3 === 1 ? "list" : "statement";
|
|
1700
|
+
if (points.length >= 2 && /\b(first|then|finally|steps?|next)\b/iu.test(passage))
|
|
1701
|
+
layout = "steps";
|
|
1702
|
+
if (points.length === 2 && /\b(versus|whereas|compared|instead)\b/iu.test(passage))
|
|
1703
|
+
layout = "comparison";
|
|
1704
|
+
const image = images.length && index > 0 && index % 3 === 2 ? images[Math.floor(index / 3) % images.length] : void 0;
|
|
1705
|
+
if (image) layout = "image";
|
|
1706
|
+
if (first === 0 && wording) {
|
|
1707
|
+
headline = wording.headline ?? headline;
|
|
1708
|
+
points = wording.points ?? points;
|
|
1709
|
+
}
|
|
1710
|
+
if (hint?.layout) {
|
|
1711
|
+
const eligible = hint.layout === "image" ? !!image : hint.layout === "comparison" ? points.length === 2 : ["steps", "list"].includes(hint.layout) ? points.length >= 2 : true;
|
|
1712
|
+
if (eligible) layout = hint.layout;
|
|
1713
|
+
}
|
|
1714
|
+
const beat = {
|
|
1715
|
+
id: `beat-${index + 1}`,
|
|
1716
|
+
sourceStart: start,
|
|
1717
|
+
sourceEnd: end,
|
|
1718
|
+
layout,
|
|
1719
|
+
headline,
|
|
1720
|
+
points,
|
|
1721
|
+
...layout === "image" && image ? { imageSrc: image.src } : {}
|
|
1722
|
+
};
|
|
1723
|
+
const explainer = inbetweens && last - first >= 24 && (layout === "steps" || layout === "comparison" || !!image);
|
|
1724
|
+
if (explainer) {
|
|
1725
|
+
const split = block.charStart + words[first + Math.floor((last - first) / 2)].index;
|
|
1726
|
+
const { imageSrc: _image, ...textBeat } = beat;
|
|
1727
|
+
supportingOpeners.add(`beat-${beats.length + 1}`);
|
|
1728
|
+
beats.push({
|
|
1729
|
+
...textBeat,
|
|
1730
|
+
id: `beat-${beats.length + 1}`,
|
|
1731
|
+
layout: "statement",
|
|
1732
|
+
sourceEnd: split
|
|
1733
|
+
});
|
|
1734
|
+
beats.push({ ...beat, id: `beat-${beats.length + 1}`, sourceStart: split });
|
|
1735
|
+
} else beats.push(beat);
|
|
1736
|
+
first = last;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (beats.length > PRESENTATION_LIMITS.beats)
|
|
1740
|
+
throw new Error(
|
|
1741
|
+
"This document has too many sections for one presentation. Split it into shorter documents."
|
|
1742
|
+
);
|
|
1743
|
+
const closing = beats[beats.length - 1];
|
|
1744
|
+
if (beats.length > 2 && closing?.layout === "statement" && !hints.blocks.some(
|
|
1745
|
+
(hint) => hint.layout && script.blocks.some(
|
|
1746
|
+
(block) => block.blockId === hint.blockId && closing.sourceStart >= block.charStart && closing.sourceStart < block.charEnd
|
|
1747
|
+
)
|
|
1748
|
+
))
|
|
1749
|
+
closing.layout = "title";
|
|
1750
|
+
let origin = "automatic";
|
|
1751
|
+
for (const beat of beats) {
|
|
1752
|
+
const section = script.blocks.find(
|
|
1753
|
+
(block) => beat.sourceStart >= block.charStart && beat.sourceStart < block.charEnd
|
|
1754
|
+
);
|
|
1755
|
+
const hint = hints.blocks.find((item) => item.blockId === section?.blockId);
|
|
1756
|
+
const cached = hint?.ai?.find(
|
|
1757
|
+
(item) => item.source === script.sourceText.slice(beat.sourceStart, beat.sourceEnd)
|
|
1758
|
+
);
|
|
1759
|
+
if (cached && (cached.layout !== "image" || beat.imageSrc)) {
|
|
1760
|
+
beat.headline = cached.headline;
|
|
1761
|
+
beat.points = [...cached.points];
|
|
1762
|
+
beat.layout = cached.layout;
|
|
1763
|
+
origin = "ai";
|
|
1764
|
+
}
|
|
1765
|
+
if (hint?.layout && !supportingOpeners.has(beat.id) && (hint.layout !== "image" || beat.imageSrc) && (hint.layout !== "comparison" || beat.points.length === 2) && (!["steps", "list"].includes(hint.layout) || beat.points.length >= 2))
|
|
1766
|
+
beat.layout = hint.layout;
|
|
1767
|
+
if (section && beat.sourceStart === section.charStart && hint?.wording?.source === script.sourceText.slice(section.charStart, section.charEnd)) {
|
|
1768
|
+
beat.headline = hint.wording.headline ?? beat.headline;
|
|
1769
|
+
beat.points = hint.wording.points ?? beat.points;
|
|
1770
|
+
}
|
|
1771
|
+
if (["steps", "list", "comparison"].includes(beat.layout) && (beat.points.length < 2 || beat.layout === "comparison" && beat.points.length !== 2))
|
|
1772
|
+
beat.layout = "statement";
|
|
1773
|
+
if (beat.layout !== "image") delete beat.imageSrc;
|
|
1774
|
+
}
|
|
1775
|
+
return { version: 1, sourceText: script.sourceText, origin, beats };
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/transform/presentationCompiler.ts
|
|
1779
|
+
function compilePresentationPlan(doc, plan) {
|
|
1780
|
+
const checked = parsePresentationPlan(plan);
|
|
1781
|
+
if (!checked) throw new Error("The saved presentation is invalid. Make the presentation again.");
|
|
1782
|
+
const script = buildNarrationScript(doc);
|
|
1783
|
+
if (script.sourceText !== checked.sourceText)
|
|
1784
|
+
throw new Error("The document text has changed. Make the presentation again to match it.");
|
|
1785
|
+
const narration = doc.presentationNarration;
|
|
1786
|
+
const hasNarration = (doc.documentMedia ?? []).some(
|
|
1787
|
+
(clip2) => clip2.anchor === "document" && clip2.src
|
|
1788
|
+
);
|
|
1789
|
+
if (hasNarration && !narration)
|
|
1790
|
+
throw new Error(
|
|
1791
|
+
"Narration timings are unavailable. Restore the timing file or generate narration again."
|
|
1792
|
+
);
|
|
1793
|
+
if (narration && narration.sourceText !== checked.sourceText)
|
|
1794
|
+
throw new Error(
|
|
1795
|
+
"The narration no longer matches the document. Generate narration again before presenting."
|
|
1796
|
+
);
|
|
1797
|
+
if (narration && flattenRenderableBlocks(doc.blocks).some((block) => {
|
|
1798
|
+
const pin = getPinnedBlockMeta(block);
|
|
1799
|
+
return pin.duration !== void 0 || pin.startTime !== void 0;
|
|
1800
|
+
}))
|
|
1801
|
+
throw new Error(
|
|
1802
|
+
"Remove manually pinned slide times before fitting a presentation to narration."
|
|
1803
|
+
);
|
|
1804
|
+
const images = new Set(extractDocImages(doc.blocks).map((image) => image.src));
|
|
1805
|
+
if (checked.beats.some((beat) => beat.imageSrc && !images.has(beat.imageSrc)))
|
|
1806
|
+
throw new Error(
|
|
1807
|
+
"A presentation image is no longer in the document. Make the presentation again."
|
|
1808
|
+
);
|
|
1809
|
+
const duration = narration ? narration.startTime + narration.duration : Math.max(4, script.tokens.length / 2.5);
|
|
1810
|
+
const bookmarks = (narration?.bookmarks ?? []).filter(
|
|
1811
|
+
(word) => Number.isFinite(word.time) && word.time >= 0 && word.time <= duration && Number.isInteger(word.charOffset) && word.charOffset >= 0 && word.charOffset < script.sourceText.length
|
|
1812
|
+
).sort((a, b) => a.charOffset - b.charOffset);
|
|
1813
|
+
let previous = 0;
|
|
1814
|
+
const starts = checked.beats.map((beat, index) => {
|
|
1815
|
+
if (!index) return 0;
|
|
1816
|
+
let time;
|
|
1817
|
+
if (narration && bookmarks.length) {
|
|
1818
|
+
time = bookmarks.find((word) => word.charOffset >= beat.sourceStart)?.time ?? duration;
|
|
1819
|
+
} else {
|
|
1820
|
+
const before = script.tokens.filter((token) => token.charOffset < beat.sourceStart).length;
|
|
1821
|
+
time = (narration?.startTime ?? 0) + before / Math.max(1, script.tokens.length) * (narration?.duration ?? duration);
|
|
1822
|
+
}
|
|
1823
|
+
previous = Math.min(duration, Math.max(previous, time));
|
|
1824
|
+
return previous;
|
|
1825
|
+
});
|
|
1826
|
+
const blocks = [];
|
|
1827
|
+
checked.beats.forEach((beat, index) => {
|
|
1828
|
+
const start = starts[index];
|
|
1829
|
+
const end = starts[index + 1] ?? duration;
|
|
1830
|
+
if (end <= start) return;
|
|
1831
|
+
const source = script.blocks.find(
|
|
1832
|
+
(block2) => beat.sourceStart >= block2.charStart && beat.sourceStart < block2.charEnd
|
|
1833
|
+
);
|
|
1834
|
+
const base = {
|
|
1835
|
+
id: beat.id,
|
|
1836
|
+
duration: end - start,
|
|
1837
|
+
startTime: start,
|
|
1838
|
+
audioSegment: 0,
|
|
1839
|
+
sourceStartTime: start,
|
|
1840
|
+
sourceDuration: end - start,
|
|
1841
|
+
timelineLocked: true,
|
|
1842
|
+
sourceBlockId: source?.blockId,
|
|
1843
|
+
sourceCharOffset: beat.sourceStart,
|
|
1844
|
+
transition: { type: "fade", duration: Math.min(0.3, (end - start) / 5) }
|
|
1845
|
+
};
|
|
1846
|
+
const block = visualBlock(beat, base, index);
|
|
1847
|
+
blocks.push(block);
|
|
1848
|
+
});
|
|
1849
|
+
return {
|
|
1850
|
+
...doc,
|
|
1851
|
+
blocks,
|
|
1852
|
+
duration,
|
|
1853
|
+
presentationApplied: true,
|
|
1854
|
+
// The plan supplies its own opening. Preserve an explicit author override.
|
|
1855
|
+
frontmatter: { "squisq-cover-slide": false, ...doc.frontmatter },
|
|
1856
|
+
audio: doc.audio.segments.some((segment) => segment.src) ? doc.audio : { segments: [{ src: "", name: "presentation-clock", startTime: 0, duration }] }
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function visualBlock(beat, base, index) {
|
|
1860
|
+
const colorScheme = ["blue", "green", "orange"][index % 3];
|
|
1861
|
+
switch (beat.layout) {
|
|
1862
|
+
case "title":
|
|
1863
|
+
return { ...base, template: "title", title: beat.headline, subtitle: beat.points[0] ?? "" };
|
|
1864
|
+
case "statement":
|
|
1865
|
+
return {
|
|
1866
|
+
...base,
|
|
1867
|
+
template: "factCard",
|
|
1868
|
+
fact: beat.headline,
|
|
1869
|
+
explanation: beat.points.join("\n\n")
|
|
1870
|
+
};
|
|
1871
|
+
case "list":
|
|
1872
|
+
return { ...base, template: "list", title: beat.headline, items: beat.points, colorScheme };
|
|
1873
|
+
case "comparison":
|
|
1874
|
+
return {
|
|
1875
|
+
...base,
|
|
1876
|
+
template: "twoColumn",
|
|
1877
|
+
header: beat.headline,
|
|
1878
|
+
left: { label: beat.points[0] },
|
|
1879
|
+
right: { label: beat.points[1] }
|
|
1880
|
+
};
|
|
1881
|
+
case "image":
|
|
1882
|
+
return {
|
|
1883
|
+
...base,
|
|
1884
|
+
template: "imageWithCaption",
|
|
1885
|
+
imageSrc: beat.imageSrc,
|
|
1886
|
+
imageAlt: beat.headline,
|
|
1887
|
+
caption: beat.headline,
|
|
1888
|
+
captionPosition: "bottom",
|
|
1889
|
+
ambientMotion: "zoomIn"
|
|
1890
|
+
};
|
|
1891
|
+
case "steps":
|
|
1892
|
+
return {
|
|
1893
|
+
...base,
|
|
1894
|
+
template: "diagram",
|
|
1895
|
+
background: "surface",
|
|
1896
|
+
title: beat.headline,
|
|
1897
|
+
colorScheme,
|
|
1898
|
+
nodes: beat.points.map((point, i) => ({
|
|
1899
|
+
id: `step-${i}`,
|
|
1900
|
+
label: point,
|
|
1901
|
+
x: i * 320,
|
|
1902
|
+
y: 0,
|
|
1903
|
+
w: 260,
|
|
1904
|
+
h: 180
|
|
1905
|
+
})),
|
|
1906
|
+
edges: beat.points.slice(1).map((_, i) => ({ source: `step-${i}`, target: `step-${i + 1}`, directed: true }))
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
function applySavedPresentation(doc) {
|
|
1911
|
+
if (doc.presentationApplied) return doc;
|
|
1912
|
+
const dynamic = usesDynamicPresentation(doc);
|
|
1913
|
+
if (!dynamic && !Object.prototype.hasOwnProperty.call(doc.frontmatter ?? {}, PRESENTATION_KEY))
|
|
1914
|
+
return doc;
|
|
1915
|
+
try {
|
|
1916
|
+
const rawHints = doc.frontmatter?.[PRESENTATION_HINTS_KEY];
|
|
1917
|
+
const hints = rawHints === void 0 ? defaultPresentationHints() : parsePresentationHints(rawHints);
|
|
1918
|
+
if (dynamic && !hints)
|
|
1919
|
+
throw new Error("The slide hints are invalid. Review the summarization settings.");
|
|
1920
|
+
const plan = dynamic ? createPresentationPlan(doc, hints) : parsePresentationPlan(doc.frontmatter?.[PRESENTATION_KEY]);
|
|
1921
|
+
if (!plan) throw new Error("The saved presentation is invalid. Make the presentation again.");
|
|
1922
|
+
return compilePresentationPlan(doc, plan);
|
|
1923
|
+
} catch (error) {
|
|
1924
|
+
return {
|
|
1925
|
+
...doc,
|
|
1926
|
+
diagnostics: [
|
|
1927
|
+
...doc.diagnostics ?? [],
|
|
1928
|
+
{
|
|
1929
|
+
severity: "error",
|
|
1930
|
+
code: "presentation-invalid",
|
|
1931
|
+
message: error instanceof Error ? error.message : "The presentation could not be loaded."
|
|
1932
|
+
}
|
|
1933
|
+
]
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1422
1938
|
export {
|
|
1939
|
+
PRESENTATION_KEY,
|
|
1940
|
+
PRESENTATION_LAYOUTS,
|
|
1941
|
+
PRESENTATION_LIMITS,
|
|
1942
|
+
parsePresentationPlan,
|
|
1943
|
+
serializePresentationPlan,
|
|
1944
|
+
PRESENTATION_HINTS_KEY,
|
|
1945
|
+
DYNAMIC_PRESENTATION_STYLE,
|
|
1946
|
+
defaultPresentationHints,
|
|
1947
|
+
parsePresentationHints,
|
|
1948
|
+
serializePresentationHints,
|
|
1949
|
+
presentationSections,
|
|
1950
|
+
usesDynamicPresentation,
|
|
1951
|
+
extractDocImages,
|
|
1952
|
+
analyzeBlocks,
|
|
1423
1953
|
DEFAULT_TRANSFORM_STYLE_ID,
|
|
1424
1954
|
createTransformStyleRegistry,
|
|
1425
1955
|
resolveTransformStyle,
|
|
1426
1956
|
getTransformStyleIds,
|
|
1427
1957
|
getTransformStyleSummaries,
|
|
1428
|
-
extractDocImages,
|
|
1429
|
-
analyzeBlocks,
|
|
1430
1958
|
allocateTiming,
|
|
1431
1959
|
applyTransform,
|
|
1432
|
-
transformNarratedSegment
|
|
1960
|
+
transformNarratedSegment,
|
|
1961
|
+
createPresentationPlan,
|
|
1962
|
+
compilePresentationPlan,
|
|
1963
|
+
applySavedPresentation
|
|
1433
1964
|
};
|