@bendyline/squisq 2.11.16 → 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-Bd2kdzuy.d.ts → Doc-B6TYHYGc.d.ts} +41 -2
- package/dist/{ImageEditDoc-C3HOltpW.d.ts → ImageEditDoc-CN_3Ysj5.d.ts} +1 -1
- package/dist/chunk-3XEABCT3.js +114 -0
- package/dist/{chunk-WMXQDP2C.js → chunk-7IGKCHDP.js} +352 -17
- package/dist/{chunk-C3NAY5YP.js → chunk-A5QJJZ5S.js} +8 -5
- package/dist/{chunk-HVJ6VFDX.js → chunk-GB7OFYUK.js} +4 -0
- package/dist/{chunk-NC6LD6D3.js → chunk-J4ACZBO4.js} +50 -8
- package/dist/{chunk-ANH2OZ2M.js → chunk-LJZHC6GN.js} +146 -11
- package/dist/{chunk-IFJPPI63.js → chunk-NM747LUO.js} +829 -298
- package/dist/{chunk-GDHXT5Q7.js → chunk-PGWYC3S2.js} +2 -2
- package/dist/{chunk-X7BNDUNI.js → chunk-Q6ELOAOQ.js} +383 -1
- package/dist/{chunk-IRVXZUVU.js → chunk-RYO4QW6R.js} +2 -4
- package/dist/{chunk-TMOHAFYE.js → chunk-WSFXV5BT.js} +17 -0
- package/dist/doc/index.d.ts +35 -24
- package/dist/doc/index.js +13 -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 +7 -7
- package/dist/index.js +88 -34
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/jsonForm/index.js +1 -1
- package/dist/markdown/index.d.ts +170 -4
- package/dist/markdown/index.js +24 -4
- package/dist/{materializePageSection-FRv9whYy.d.ts → materializePageSection-IYXU3tDN.d.ts} +6 -1
- package/dist/mediaEdit/index.d.ts +2 -2
- package/dist/narration/index.d.ts +1 -1
- package/dist/narration/index.js +10 -9
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/index.js +1 -1
- package/dist/{themeLibrary-BbF3EKWM.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 +38 -8
- package/package.json +1 -1
- package/dist/chunk-5W7F46OO.js +0 -337
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
parseAsciiTimeline,
|
|
14
14
|
pickAutoTemplate,
|
|
15
15
|
profileBlockContents
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-A5QJJZ5S.js";
|
|
17
17
|
import {
|
|
18
18
|
anchorPoint,
|
|
19
19
|
base64ToUtf8,
|
|
@@ -61,17 +61,22 @@ import {
|
|
|
61
61
|
sanitizeUrl,
|
|
62
62
|
splitKeyValueToken,
|
|
63
63
|
tokenizeAttrTokens
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-WSFXV5BT.js";
|
|
65
65
|
import {
|
|
66
66
|
extractPlainText,
|
|
67
|
+
findBlockVideoEmbed,
|
|
68
|
+
isVideoEmbedUrl,
|
|
69
|
+
mediaKindForUrl,
|
|
70
|
+
parseVideoEmbedUrl,
|
|
67
71
|
readFrontmatterThemeId,
|
|
68
72
|
walkMarkdownTree
|
|
69
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-Q6ELOAOQ.js";
|
|
70
74
|
import {
|
|
71
75
|
resolveBlockTransition
|
|
72
76
|
} from "./chunk-4VOD55SX.js";
|
|
73
77
|
import {
|
|
74
|
-
estimateReadingTime
|
|
78
|
+
estimateReadingTime,
|
|
79
|
+
estimateSpokenWordCount
|
|
75
80
|
} from "./chunk-GHOPHGOK.js";
|
|
76
81
|
import {
|
|
77
82
|
SeededRandom,
|
|
@@ -1574,9 +1579,29 @@ function findFirstList(contents) {
|
|
|
1574
1579
|
|
|
1575
1580
|
// src/doc/templateInputs.ts
|
|
1576
1581
|
var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
|
|
1582
|
+
function extractHostedVideos(contents, limit = Infinity) {
|
|
1583
|
+
const found = [];
|
|
1584
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1585
|
+
for (const node of contents ?? []) {
|
|
1586
|
+
if (found.length >= limit) break;
|
|
1587
|
+
const video = findBlockVideoEmbed(node);
|
|
1588
|
+
if (!video || seen.has(video.embed.watchUrl)) continue;
|
|
1589
|
+
seen.add(video.embed.watchUrl);
|
|
1590
|
+
found.push({
|
|
1591
|
+
url: video.embed.watchUrl,
|
|
1592
|
+
title: video.title,
|
|
1593
|
+
aspectRatio: video.embed.aspectRatio
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
return found;
|
|
1597
|
+
}
|
|
1598
|
+
function isMaterializedMediaNode(node) {
|
|
1599
|
+
if (node.type === "code") return node.lang?.trim().toLowerCase() === "mermaid";
|
|
1600
|
+
return findBlockVideoEmbed(node) !== null;
|
|
1601
|
+
}
|
|
1577
1602
|
function extractBodyPlainText(contents) {
|
|
1578
1603
|
if (!contents || contents.length === 0) return "";
|
|
1579
|
-
return contents.filter((node) => !(node
|
|
1604
|
+
return contents.filter((node) => !isMaterializedMediaNode(node)).map((n) => extractPlainText(n)).join("\n").trim();
|
|
1580
1605
|
}
|
|
1581
1606
|
function extractListItems(contents) {
|
|
1582
1607
|
return extractRichListItems(contents).map((item) => item.text);
|
|
@@ -1750,7 +1775,9 @@ function extractImages(contents, limit = Infinity) {
|
|
|
1750
1775
|
if (!node || typeof node !== "object") return;
|
|
1751
1776
|
const n = node;
|
|
1752
1777
|
if (n.type === "image" && typeof n.url === "string" && n.url) {
|
|
1753
|
-
|
|
1778
|
+
if (!mediaKindForUrl(n.url)) {
|
|
1779
|
+
found.push({ src: n.url, alt: typeof n.alt === "string" ? n.alt : "" });
|
|
1780
|
+
}
|
|
1754
1781
|
return;
|
|
1755
1782
|
}
|
|
1756
1783
|
if ((n.type === "htmlBlock" || n.type === "htmlInline") && Array.isArray(n.htmlChildren)) {
|
|
@@ -1951,7 +1978,7 @@ function autoTemplatePreservesContent(templateName, contents) {
|
|
|
1951
1978
|
}
|
|
1952
1979
|
case "videoWithCaption": {
|
|
1953
1980
|
const videos = extractEmbeddedVideos(nodes);
|
|
1954
|
-
if (videos.length !== 1) return false;
|
|
1981
|
+
if (videos.length + extractHostedVideos(nodes).length !== 1) return false;
|
|
1955
1982
|
const bodyText3 = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
1956
1983
|
return bodyText3 === normalizeCoverageText(videos[0]?.alt ?? "");
|
|
1957
1984
|
}
|
|
@@ -2097,7 +2124,17 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
2097
2124
|
}
|
|
2098
2125
|
case "videoWithCaption": {
|
|
2099
2126
|
const video = extractFirstEmbeddedVideo(contents);
|
|
2100
|
-
if (!video)
|
|
2127
|
+
if (!video) {
|
|
2128
|
+
const hosted = extractHostedVideos(contents, 1)[0];
|
|
2129
|
+
if (hosted) {
|
|
2130
|
+
return {
|
|
2131
|
+
videoSrc: hosted.url,
|
|
2132
|
+
videoAlt: hosted.title || headingText,
|
|
2133
|
+
caption: headingText
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
return placeholders ? { caption: headingText } : null;
|
|
2137
|
+
}
|
|
2101
2138
|
return {
|
|
2102
2139
|
videoSrc: video.src,
|
|
2103
2140
|
posterSrc: video.posterSrc,
|
|
@@ -2160,10 +2197,10 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
2160
2197
|
function bodyNodes(context) {
|
|
2161
2198
|
if (context.block?.summaryMode === "headings-and-features") return [];
|
|
2162
2199
|
return (context.block?.contents ?? []).filter(
|
|
2163
|
-
// Mermaid source
|
|
2164
|
-
//
|
|
2165
|
-
// preserving any additional
|
|
2166
|
-
(node) => !(node
|
|
2200
|
+
// Mermaid source and hosted-video links are already materialized as
|
|
2201
|
+
// visual layers (a diagram, a player). Repeating them as prose makes the
|
|
2202
|
+
// loss-averse default noisy without preserving any additional meaning.
|
|
2203
|
+
(node) => !isMaterializedMediaNode(node)
|
|
2167
2204
|
);
|
|
2168
2205
|
}
|
|
2169
2206
|
function plainListText(list, depth = 0) {
|
|
@@ -4579,6 +4616,37 @@ function videoWithCaption(input, context) {
|
|
|
4579
4616
|
const resolvedClipEnd = Math.max(clipStart, clipEnd ?? clipStart + Math.max(0, input.duration));
|
|
4580
4617
|
const captionFontSize = themedFontSize(30, context, false);
|
|
4581
4618
|
const creditFontSize = themedFontSize(16, context, false);
|
|
4619
|
+
if (isVideoEmbedUrl(videoSrc)) {
|
|
4620
|
+
const layers2 = [
|
|
4621
|
+
createBackgroundLayer("bg", theme.colors.background),
|
|
4622
|
+
{
|
|
4623
|
+
type: "videoEmbed",
|
|
4624
|
+
id: "bg-video",
|
|
4625
|
+
content: { url: videoSrc, ...videoAlt ? { title: videoAlt } : {} },
|
|
4626
|
+
position: caption ? { x: "4%", y: "4%", width: "92%", height: "78%" } : { x: "4%", y: "4%", width: "92%", height: "92%" }
|
|
4627
|
+
}
|
|
4628
|
+
];
|
|
4629
|
+
if (caption) {
|
|
4630
|
+
layers2.push({
|
|
4631
|
+
type: "text",
|
|
4632
|
+
id: "caption",
|
|
4633
|
+
content: {
|
|
4634
|
+
text: caption,
|
|
4635
|
+
style: {
|
|
4636
|
+
fontSize: captionFontSize,
|
|
4637
|
+
fontFamily: getThemeFont(context, "body"),
|
|
4638
|
+
color: theme.colors.text,
|
|
4639
|
+
textAlign: "center",
|
|
4640
|
+
lineHeight: 1.18,
|
|
4641
|
+
maxLines: 2
|
|
4642
|
+
}
|
|
4643
|
+
},
|
|
4644
|
+
position: { x: "50%", y: "90%", anchor: "center", width: "84%" },
|
|
4645
|
+
animation: { type: "fadeIn", duration: 1.5, delay: 0.5 }
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
return layers2;
|
|
4649
|
+
}
|
|
4582
4650
|
const layers = [
|
|
4583
4651
|
// Background video clip
|
|
4584
4652
|
{
|
|
@@ -5668,7 +5736,7 @@ function resolveSupplementalMediaLayout(layers, template, viewport, mediaCount,
|
|
|
5668
5736
|
const policy = getBlockMediaLayoutPolicy(template);
|
|
5669
5737
|
const shape = mediaShape(mediaCount, aspectRatios);
|
|
5670
5738
|
const alreadyOwnsMedia = layers.some(
|
|
5671
|
-
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "map" || layer.type === "mermaid"
|
|
5739
|
+
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "videoEmbed" || layer.type === "map" || layer.type === "mermaid"
|
|
5672
5740
|
);
|
|
5673
5741
|
if (!alreadyOwnsMedia && (policy?.nativeLayout === "feature-left" || policy?.nativeLayout === "feature-right")) {
|
|
5674
5742
|
const { position } = featureMediaSlot(
|
|
@@ -5981,9 +6049,11 @@ function nodesFromTemplateData(input) {
|
|
|
5981
6049
|
function diagramBlock(input, context) {
|
|
5982
6050
|
const { theme, viewport, children = [] } = context;
|
|
5983
6051
|
const colors = input.colorScheme ? resolveColorScheme(context, input.colorScheme) : void 0;
|
|
6052
|
+
const background = input.background === "surface" ? [createBackgroundLayer("diagram-bg", themedSurfaceGradient(context, 170))] : [];
|
|
5984
6053
|
const resolved = children.length > 0 ? nodesFromChildren(context) : input.nodes && input.nodes.length > 0 ? nodesFromTemplateData(input) : { nodes: [], edges: [] };
|
|
5985
6054
|
if (resolved.nodes.length === 0) {
|
|
5986
6055
|
return [
|
|
6056
|
+
...background,
|
|
5987
6057
|
{
|
|
5988
6058
|
type: "text",
|
|
5989
6059
|
id: "diagram-empty",
|
|
@@ -6024,7 +6094,7 @@ function diagramBlock(input, context) {
|
|
|
6024
6094
|
w: n.w * scale,
|
|
6025
6095
|
h: n.h * scale
|
|
6026
6096
|
});
|
|
6027
|
-
const layers = [];
|
|
6097
|
+
const layers = [...background];
|
|
6028
6098
|
if (input.title) {
|
|
6029
6099
|
const titleLayer = canvasTitleLayer("diagram-title", input.title, frame, placement, context);
|
|
6030
6100
|
if (titleLayer) layers.push(titleLayer);
|
|
@@ -9921,6 +9991,9 @@ function collectRichMediaItems(layers, block) {
|
|
|
9921
9991
|
const existingMermaid = new Set(
|
|
9922
9992
|
layers.filter((layer) => layer.type === "mermaid").map((layer) => layer.content.source)
|
|
9923
9993
|
);
|
|
9994
|
+
const existingEmbeds = new Set(
|
|
9995
|
+
layers.filter((layer) => layer.type === "videoEmbed").map((layer) => parseVideoEmbedUrl(layer.content.url)?.watchUrl ?? layer.content.url)
|
|
9996
|
+
);
|
|
9924
9997
|
const embeddedVideos = extractEmbeddedVideos(block.contents);
|
|
9925
9998
|
const videos = embeddedVideos.filter((video) => !existingVideos.has(video.src));
|
|
9926
9999
|
const videoSources = new Set(embeddedVideos.map((video) => video.src));
|
|
@@ -9931,6 +10004,9 @@ function collectRichMediaItems(layers, block) {
|
|
|
9931
10004
|
return true;
|
|
9932
10005
|
});
|
|
9933
10006
|
const sources = mermaidSources(block).filter((source) => !existingMermaid.has(source));
|
|
10007
|
+
const hosted = extractHostedVideos(block.contents).filter(
|
|
10008
|
+
(video) => !existingEmbeds.has(video.url)
|
|
10009
|
+
);
|
|
9934
10010
|
return [
|
|
9935
10011
|
...images.map(
|
|
9936
10012
|
(image) => ({
|
|
@@ -9952,6 +10028,14 @@ function collectRichMediaItems(layers, block) {
|
|
|
9952
10028
|
...video.clipEnd != null ? { clipEnd: video.clipEnd } : {}
|
|
9953
10029
|
})
|
|
9954
10030
|
),
|
|
10031
|
+
...hosted.map(
|
|
10032
|
+
(video) => ({
|
|
10033
|
+
kind: "videoEmbed",
|
|
10034
|
+
url: video.url,
|
|
10035
|
+
title: video.title,
|
|
10036
|
+
aspectRatio: video.aspectRatio
|
|
10037
|
+
})
|
|
10038
|
+
),
|
|
9955
10039
|
...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 })),
|
|
9956
10040
|
...embeddedSpatialMedia(block)
|
|
9957
10041
|
];
|
|
@@ -10023,6 +10107,17 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
|
|
|
10023
10107
|
}
|
|
10024
10108
|
];
|
|
10025
10109
|
}
|
|
10110
|
+
if (item.kind === "videoEmbed") {
|
|
10111
|
+
const title = item.title || block.title;
|
|
10112
|
+
return [
|
|
10113
|
+
{
|
|
10114
|
+
id: `${block.id}-video-embed-${kindIndex}`,
|
|
10115
|
+
type: "videoEmbed",
|
|
10116
|
+
position,
|
|
10117
|
+
content: { url: item.url, ...title ? { title } : {} }
|
|
10118
|
+
}
|
|
10119
|
+
];
|
|
10120
|
+
}
|
|
10026
10121
|
if (item.kind === "mermaid") {
|
|
10027
10122
|
return [
|
|
10028
10123
|
{
|
|
@@ -10107,6 +10202,7 @@ function appendRichContentLayers(layers, block, theme, viewport, mediaAspectRati
|
|
|
10107
10202
|
const counters = {
|
|
10108
10203
|
image: 0,
|
|
10109
10204
|
video: 0,
|
|
10205
|
+
videoEmbed: 0,
|
|
10110
10206
|
mermaid: 0,
|
|
10111
10207
|
spatial: 0
|
|
10112
10208
|
};
|
|
@@ -10912,6 +11008,28 @@ function expandDocBlocks(blocks, options = {}) {
|
|
|
10912
11008
|
const splitParts = /* @__PURE__ */ new Map();
|
|
10913
11009
|
for (const [segmentIndex, segmentBlocks] of blocksBySegment) {
|
|
10914
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
|
+
}
|
|
10915
11033
|
if (!audioSegment) {
|
|
10916
11034
|
let offsetTime = 0;
|
|
10917
11035
|
for (const { block, originalIndex } of segmentBlocks) {
|
|
@@ -12272,7 +12390,7 @@ function getBlockBodyText2(block) {
|
|
|
12272
12390
|
if (!block.contents || block.contents.length === 0) return "";
|
|
12273
12391
|
const tmpl = resolveTemplateName(block.template ?? "");
|
|
12274
12392
|
const consumedFence = tmpl === "diagram" && block.templateData !== void 0 && "nodes" in block.templateData || tmpl === "timeline" && block.templateData !== void 0 && "tracks" in block.templateData || tmpl === "tree" && block.templateData !== void 0 && "items" in block.templateData;
|
|
12275
|
-
return block.contents.filter((node) => !(consumedFence && node.type === "code")).map((node) => extractPlainText(node)).join("\n").trim();
|
|
12393
|
+
return block.contents.filter((node) => !(consumedFence && node.type === "code")).filter((node) => findBlockVideoEmbed(node) === null).map((node) => extractPlainText(node)).join("\n").trim();
|
|
12276
12394
|
}
|
|
12277
12395
|
var MAX_PHRASE_WORDS = 12;
|
|
12278
12396
|
function splitIntoSentences(text) {
|
|
@@ -12334,7 +12452,7 @@ function findFirstHtmlImage(nodes) {
|
|
|
12334
12452
|
function findFirstImage(node) {
|
|
12335
12453
|
if (node.type === "image") {
|
|
12336
12454
|
const img = node;
|
|
12337
|
-
return { url: img.url, alt: img.alt };
|
|
12455
|
+
return mediaKindForUrl(img.url) ? void 0 : { url: img.url, alt: img.alt };
|
|
12338
12456
|
}
|
|
12339
12457
|
if (node.type === "htmlBlock" || node.type === "htmlInline") {
|
|
12340
12458
|
const html = node;
|
|
@@ -12392,6 +12510,215 @@ function buildStartBlock(markdownDoc, rootBlocks, fileName) {
|
|
|
12392
12510
|
return config;
|
|
12393
12511
|
}
|
|
12394
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
|
+
|
|
12395
12722
|
export {
|
|
12396
12723
|
expandPersistentLayers,
|
|
12397
12724
|
getPersistentLayersFromTheme,
|
|
@@ -12434,6 +12761,8 @@ export {
|
|
|
12434
12761
|
treeFromTemplateData,
|
|
12435
12762
|
treeFromMarkdownList,
|
|
12436
12763
|
findFirstList,
|
|
12764
|
+
extractHostedVideos,
|
|
12765
|
+
isMaterializedMediaNode,
|
|
12437
12766
|
extractBodyPlainText,
|
|
12438
12767
|
extractListItems,
|
|
12439
12768
|
renderMarkdownBlocksHtml,
|
|
@@ -12527,5 +12856,11 @@ export {
|
|
|
12527
12856
|
flattenRenderableBlocks,
|
|
12528
12857
|
countBlocks,
|
|
12529
12858
|
getBlockDepth,
|
|
12530
|
-
getBlockBodyText2 as getBlockBodyText
|
|
12859
|
+
getBlockBodyText2 as getBlockBodyText,
|
|
12860
|
+
estimateSyllables,
|
|
12861
|
+
buildNarrationScript,
|
|
12862
|
+
expectedSyllablesAt,
|
|
12863
|
+
wordPosAtExpectedSyllables,
|
|
12864
|
+
wordIndexAtChar,
|
|
12865
|
+
wordIndexAtTime
|
|
12531
12866
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractPlainText,
|
|
3
3
|
findNodesByType,
|
|
4
|
+
isVideoEmbedUrl,
|
|
5
|
+
mediaKindForUrl,
|
|
4
6
|
walkMarkdownTree
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-Q6ELOAOQ.js";
|
|
6
8
|
|
|
7
9
|
// src/doc/asciiDiagram/chars.ts
|
|
8
10
|
function toCells(s) {
|
|
@@ -2483,7 +2485,6 @@ var DATE_PATTERNS = [
|
|
|
2483
2485
|
/\b\d{4}s\b/,
|
|
2484
2486
|
/\b\d{1,2}(st|nd|rd|th)\s+century\b/i
|
|
2485
2487
|
];
|
|
2486
|
-
var VIDEO_HOST_RE = /(youtube\.com|youtu\.be|vimeo\.com|wistia\.|loom\.com)/i;
|
|
2487
2488
|
function htmlElementsByTag(root, tagNames) {
|
|
2488
2489
|
const blocks = findNodesByType(root, "htmlBlock");
|
|
2489
2490
|
const inline = findNodesByType(root, "htmlInline");
|
|
@@ -2508,7 +2509,9 @@ function profileBlockContents(nodes) {
|
|
|
2508
2509
|
let hasTable = false;
|
|
2509
2510
|
const plainParts = [];
|
|
2510
2511
|
for (const node of nodes) {
|
|
2511
|
-
imageCount += findNodesByType(node, "image").
|
|
2512
|
+
imageCount += findNodesByType(node, "image").filter(
|
|
2513
|
+
(img) => !mediaKindForUrl(img.url ?? "")
|
|
2514
|
+
).length;
|
|
2512
2515
|
imageCount += findNodesByType(node, "imageReference").length;
|
|
2513
2516
|
if (findNodesByType(node, "blockquote").length > 0) hasBlockquote = true;
|
|
2514
2517
|
if (findNodesByType(node, "list").length > 0) hasList = true;
|
|
@@ -2526,7 +2529,7 @@ function profileBlockContents(nodes) {
|
|
|
2526
2529
|
break;
|
|
2527
2530
|
}
|
|
2528
2531
|
const src = el.attributes.src || el.attributes.href || "";
|
|
2529
|
-
if (
|
|
2532
|
+
if (isVideoEmbedUrl(src)) {
|
|
2530
2533
|
hasVideo = true;
|
|
2531
2534
|
break;
|
|
2532
2535
|
}
|
|
@@ -2534,7 +2537,7 @@ function profileBlockContents(nodes) {
|
|
|
2534
2537
|
if (!hasVideo) {
|
|
2535
2538
|
const links = findNodesByType(root, "link");
|
|
2536
2539
|
for (const link of links) {
|
|
2537
|
-
if (
|
|
2540
|
+
if (isVideoEmbedUrl(link.url)) {
|
|
2538
2541
|
hasVideo = true;
|
|
2539
2542
|
break;
|
|
2540
2543
|
}
|
|
@@ -57,6 +57,7 @@ var LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
|
57
57
|
"path",
|
|
58
58
|
"map",
|
|
59
59
|
"video",
|
|
60
|
+
"videoEmbed",
|
|
60
61
|
"table",
|
|
61
62
|
"tree",
|
|
62
63
|
"mermaid"
|
|
@@ -199,6 +200,9 @@ function validateLayerContent(type, content, path, issues) {
|
|
|
199
200
|
requiredFiniteNumber(content, "clipStart", `${path}.clipStart`, issues, { min: 0 });
|
|
200
201
|
requiredFiniteNumber(content, "clipEnd", `${path}.clipEnd`, issues, { min: 0 });
|
|
201
202
|
break;
|
|
203
|
+
case "videoEmbed":
|
|
204
|
+
requiredString(content, "url", `${path}.url`, issues);
|
|
205
|
+
break;
|
|
202
206
|
case "table":
|
|
203
207
|
requiredStringArray(content, "headers", `${path}.headers`, issues);
|
|
204
208
|
validateStringRows(content.rows, `${path}.rows`, issues);
|