@bendyline/squisq 2.12.0 → 2.12.2
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-DIdmgDsV.d.ts → Doc-BJSPtsQD.d.ts} +27 -2
- package/dist/{ImageEditDoc-DXQMA6qH.d.ts → ImageEditDoc-QIzn1qK5.d.ts} +1 -1
- package/dist/{chunk-W45PAVYE.js → chunk-5VFC5T4N.js} +88 -1057
- package/dist/{chunk-RPPUCFRW.js → chunk-DTAYI7AV.js} +20 -20
- package/dist/{chunk-WAX4QYHA.js → chunk-E2NFHYCB.js} +14 -3
- package/dist/{chunk-H54A35NZ.js → chunk-EX75I5HX.js} +3 -3
- package/dist/{chunk-3H7SYCZS.js → chunk-FTN6SGW5.js} +1 -1
- package/dist/{chunk-QHNO67JS.js → chunk-GX3VSKYS.js} +1 -1
- package/dist/{chunk-I4L4GYDN.js → chunk-HYJ63N6V.js} +1 -1
- package/dist/{chunk-B3AFOXMA.js → chunk-MPEGJKFJ.js} +1031 -3
- package/dist/chunk-MWSGNEH6.js +2376 -0
- package/dist/{chunk-M6YXFNGO.js → chunk-P53Z4RYC.js} +10 -0
- package/dist/{chunk-K2PZMWQ5.js → chunk-TFMDVOUQ.js} +10 -9
- package/dist/chunk-Y25EJEZN.js +178 -0
- package/dist/{chunk-3Z2NGHLI.js → chunk-YZ66VMBB.js} +1 -1
- package/dist/{chunk-VTG7PNOL.js → chunk-ZKDJHO6Y.js} +3 -8
- package/dist/doc/index.d.ts +18 -10
- package/dist/doc/index.js +41 -39
- package/dist/fence/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/imageEdit/index.d.ts +3 -3
- package/dist/index.d.ts +6 -6
- package/dist/index.js +68 -48
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/jsonForm/index.js +4 -4
- package/dist/markdown/index.d.ts +143 -1
- package/dist/markdown/index.js +29 -10
- package/dist/{materializePageSection-C33MgcDg.d.ts → materializePageSection-DYnmh8X1.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 +8 -8
- package/dist/recommend/index.js +3 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/index.js +3 -3
- package/dist/{themeLibrary-BaDfxgzt.d.ts → themeLibrary-IHAirzT-.d.ts} +1 -1
- package/dist/timing/index.d.ts +1 -1
- package/dist/transform/index.d.ts +2 -2
- package/dist/transform/index.js +8 -8
- package/package.json +1 -1
- package/dist/chunk-NNJA7ICB.js +0 -1088
- package/dist/chunk-ZYWYSM6W.js +0 -657
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
import {
|
|
2
|
+
asciiDiagramToTemplateData,
|
|
3
|
+
asciiTimelineToTemplateData,
|
|
4
|
+
autoTemplatePreservesContent,
|
|
5
|
+
computeDiagramLayout,
|
|
6
|
+
deriveTemplateInputs,
|
|
2
7
|
detectAsciiDiagram,
|
|
3
8
|
detectAsciiTimeline,
|
|
4
9
|
detectTree,
|
|
10
|
+
extractEmbeddedVideos,
|
|
11
|
+
extractHostedVideos,
|
|
12
|
+
extractImages,
|
|
13
|
+
extractRichListItems,
|
|
14
|
+
extractTableFromContents,
|
|
5
15
|
isEligibleAsciiFenceLang,
|
|
6
16
|
isEligibleAsciiTimelineFenceLang,
|
|
7
17
|
isEligibleTreeFenceLang,
|
|
8
18
|
isExplicitDiagramLang,
|
|
9
19
|
isExplicitTimelineLang,
|
|
10
20
|
isExplicitTreeLang,
|
|
11
|
-
|
|
21
|
+
isMaterializedMediaNode,
|
|
22
|
+
isPictureFence,
|
|
23
|
+
markdownBlockSeparatorLines,
|
|
12
24
|
parseAsciiDiagram,
|
|
13
25
|
parseAsciiTimeline,
|
|
14
26
|
pickAutoTemplate,
|
|
15
|
-
profileBlockContents
|
|
16
|
-
|
|
27
|
+
profileBlockContents,
|
|
28
|
+
renderMarkdownBlocksHtml,
|
|
29
|
+
treeToTemplateData
|
|
30
|
+
} from "./chunk-MPEGJKFJ.js";
|
|
17
31
|
import {
|
|
18
32
|
anchorPoint,
|
|
19
33
|
base64ToUtf8,
|
|
@@ -43,37 +57,37 @@ import {
|
|
|
43
57
|
validateCustomTemplateDefinition,
|
|
44
58
|
validateTheme,
|
|
45
59
|
withAlpha
|
|
46
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-HYJ63N6V.js";
|
|
47
61
|
import {
|
|
48
62
|
mediaEditValuesFromAttributes,
|
|
49
63
|
mediaEditValuesFromParams,
|
|
50
64
|
parseMediaEdits
|
|
51
65
|
} from "./chunk-L7QZORLB.js";
|
|
52
66
|
import {
|
|
53
|
-
KNOWN_BLOCK_META_KEYS,
|
|
54
67
|
TABLE_FED_TEMPLATES,
|
|
55
|
-
coerceAnnotationValues,
|
|
56
68
|
isContainerTemplate,
|
|
57
69
|
matchTrailingTemplateAnnotation,
|
|
58
|
-
parseNumber,
|
|
59
|
-
parseTimeSeconds,
|
|
60
70
|
resolveTemplateName,
|
|
61
|
-
sanitizeUrl,
|
|
62
71
|
splitKeyValueToken,
|
|
63
72
|
tokenizeAttrTokens
|
|
64
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-Y25EJEZN.js";
|
|
65
74
|
import {
|
|
75
|
+
KNOWN_BLOCK_META_KEYS,
|
|
76
|
+
coerceAnnotationValues,
|
|
66
77
|
extractPlainText,
|
|
67
|
-
|
|
78
|
+
isSvgIslandLang,
|
|
68
79
|
isVideoEmbedUrl,
|
|
69
80
|
mediaKindForUrl,
|
|
81
|
+
parseNumber,
|
|
82
|
+
parseSvgIsland,
|
|
83
|
+
parseTimeSeconds,
|
|
70
84
|
parseVideoEmbedUrl,
|
|
71
85
|
readFrontmatterThemeId,
|
|
72
86
|
walkMarkdownTree
|
|
73
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-MWSGNEH6.js";
|
|
74
88
|
import {
|
|
75
89
|
resolveBlockTransition
|
|
76
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-P53Z4RYC.js";
|
|
77
91
|
import {
|
|
78
92
|
estimateReadingTime,
|
|
79
93
|
estimateSpokenWordCount
|
|
@@ -1281,6 +1295,11 @@ function sectionHeader(input, context) {
|
|
|
1281
1295
|
var BIG_TEXT_LINE_HEIGHT = 1.05;
|
|
1282
1296
|
var BIG_TEXT_WIDTH_FRACTION = 0.92;
|
|
1283
1297
|
var BIG_TEXT_HEIGHT_FRACTION = 0.86;
|
|
1298
|
+
var BIG_TEXT_BANDS = {
|
|
1299
|
+
center: { y: 0.5, height: BIG_TEXT_HEIGHT_FRACTION },
|
|
1300
|
+
upper: { y: 0.3, height: 0.44 },
|
|
1301
|
+
lower: { y: 0.7, height: 0.44 }
|
|
1302
|
+
};
|
|
1284
1303
|
var BIG_TEXT_CHAR_WIDTH = 0.62;
|
|
1285
1304
|
var BIG_TEXT_MIN_PX = 48;
|
|
1286
1305
|
function wrappedLineCount(wordLengths, charsPerLine) {
|
|
@@ -1297,9 +1316,9 @@ function wrappedLineCount(wordLengths, charsPerLine) {
|
|
|
1297
1316
|
}
|
|
1298
1317
|
return lines;
|
|
1299
1318
|
}
|
|
1300
|
-
function fitBigTextSize(title, viewport) {
|
|
1319
|
+
function fitBigTextSize(title, viewport, heightFraction = BIG_TEXT_HEIGHT_FRACTION) {
|
|
1301
1320
|
const availWidth = viewport.width * BIG_TEXT_WIDTH_FRACTION;
|
|
1302
|
-
const availHeight = viewport.height *
|
|
1321
|
+
const availHeight = viewport.height * heightFraction;
|
|
1303
1322
|
const wordLengths = title.trim().split(/\s+/).filter(Boolean).map((word) => word.length);
|
|
1304
1323
|
if (wordLengths.length === 0) return Math.floor(availHeight / BIG_TEXT_LINE_HEIGHT);
|
|
1305
1324
|
const longestWord = Math.max(...wordLengths);
|
|
@@ -1321,15 +1340,16 @@ function fitBigTextSize(title, viewport) {
|
|
|
1321
1340
|
}
|
|
1322
1341
|
return Math.floor(low);
|
|
1323
1342
|
}
|
|
1324
|
-
function bigTextBloom(background) {
|
|
1325
|
-
return `radial-gradient(ellipse at 50%
|
|
1343
|
+
function bigTextBloom(background, centerY = 0.5) {
|
|
1344
|
+
return `radial-gradient(ellipse at 50% ${Math.round(centerY * 100)}%, ${withAlpha(background, 0.92)} 0%, ${withAlpha(background, 0.78)} 38%, ${withAlpha(background, 0.32)} 66%, ${withAlpha(background, 0)} 88%)`;
|
|
1326
1345
|
}
|
|
1327
1346
|
function bigText(input, context) {
|
|
1328
1347
|
const { title = "", imageSrc, imageAlt, ambientMotion } = input;
|
|
1329
1348
|
const { theme, viewport } = context;
|
|
1349
|
+
const band = BIG_TEXT_BANDS[input.placement ?? "center"] ?? BIG_TEXT_BANDS.center;
|
|
1330
1350
|
const treatment = themedImageTreatment(context, input.imageTreatment);
|
|
1331
1351
|
const displayTitle = title.toUpperCase();
|
|
1332
|
-
const titleFontSize = fitBigTextSize(displayTitle, viewport);
|
|
1352
|
+
const titleFontSize = fitBigTextSize(displayTitle, viewport, band.height);
|
|
1333
1353
|
const layers = [];
|
|
1334
1354
|
if (imageSrc) {
|
|
1335
1355
|
layers.push(
|
|
@@ -1354,7 +1374,7 @@ function bigText(input, context) {
|
|
|
1354
1374
|
id: "bigtext-bloom",
|
|
1355
1375
|
content: {
|
|
1356
1376
|
shape: "rect",
|
|
1357
|
-
fill: bigTextBloom(theme.colors.background)
|
|
1377
|
+
fill: bigTextBloom(theme.colors.background, band.y)
|
|
1358
1378
|
},
|
|
1359
1379
|
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
1360
1380
|
}
|
|
@@ -1375,7 +1395,7 @@ function bigText(input, context) {
|
|
|
1375
1395
|
id: "bigtext-tint",
|
|
1376
1396
|
content: {
|
|
1377
1397
|
shape: "rect",
|
|
1378
|
-
fill: `radial-gradient(ellipse at 50%
|
|
1398
|
+
fill: `radial-gradient(ellipse at 50% ${Math.round(band.y * 100)}%, ${withAlpha(theme.colors.primary, 0.38)} 0%, ${withAlpha(theme.colors.primary, 0.14)} 48%, ${withAlpha(theme.colors.primary, 0)} 78%)`
|
|
1379
1399
|
},
|
|
1380
1400
|
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
1381
1401
|
}
|
|
@@ -1399,1008 +1419,16 @@ function bigText(input, context) {
|
|
|
1399
1419
|
},
|
|
1400
1420
|
position: {
|
|
1401
1421
|
x: "50%",
|
|
1402
|
-
y:
|
|
1422
|
+
y: `${band.y * 100}%`,
|
|
1403
1423
|
anchor: "center",
|
|
1404
1424
|
width: `${BIG_TEXT_WIDTH_FRACTION * 100}%`,
|
|
1405
|
-
height: `${
|
|
1425
|
+
height: `${band.height * 100}%`
|
|
1406
1426
|
},
|
|
1407
1427
|
animation: motionEntrance(context, { type: "fadeIn", duration: 1 })
|
|
1408
1428
|
});
|
|
1409
1429
|
return layers;
|
|
1410
1430
|
}
|
|
1411
1431
|
|
|
1412
|
-
// src/doc/templates/diagramLayout.ts
|
|
1413
|
-
var DEFAULT_OPTS = {
|
|
1414
|
-
gapX: 260,
|
|
1415
|
-
gapY: 140,
|
|
1416
|
-
gridPad: 80,
|
|
1417
|
-
gridOriginX: 80,
|
|
1418
|
-
gridOriginY: 80
|
|
1419
|
-
};
|
|
1420
|
-
function computeDiagramLayout(children, options = {}) {
|
|
1421
|
-
const opts = { ...DEFAULT_OPTS, ...options };
|
|
1422
|
-
const warnings = [];
|
|
1423
|
-
const entries = [];
|
|
1424
|
-
const unpositionedIdx = [];
|
|
1425
|
-
for (let i = 0; i < children.length; i++) {
|
|
1426
|
-
const c = children[i];
|
|
1427
|
-
const hasX = typeof c.x === "number";
|
|
1428
|
-
const hasY = typeof c.y === "number";
|
|
1429
|
-
if (hasX && hasY) {
|
|
1430
|
-
entries.push({ child: c, pinned: true, x: c.x, y: c.y });
|
|
1431
|
-
} else {
|
|
1432
|
-
entries.push({ child: c, pinned: false, x: 0, y: 0 });
|
|
1433
|
-
unpositionedIdx.push(i);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
if (unpositionedIdx.length > 0) {
|
|
1437
|
-
const cols = Math.max(1, Math.ceil(Math.sqrt(unpositionedIdx.length)));
|
|
1438
|
-
let baseX = opts.gridOriginX;
|
|
1439
|
-
let baseY = opts.gridOriginY;
|
|
1440
|
-
const pinned = entries.filter((e) => e.pinned);
|
|
1441
|
-
if (pinned.length > 0) {
|
|
1442
|
-
const minX = Math.min(...pinned.map((e) => e.x));
|
|
1443
|
-
const maxY = Math.max(...pinned.map((e) => e.y));
|
|
1444
|
-
baseX = minX;
|
|
1445
|
-
baseY = maxY + opts.gridPad + opts.gapY;
|
|
1446
|
-
}
|
|
1447
|
-
unpositionedIdx.forEach((entryIdx, k) => {
|
|
1448
|
-
const col = k % cols;
|
|
1449
|
-
const row = Math.floor(k / cols);
|
|
1450
|
-
entries[entryIdx].x = baseX + col * opts.gapX;
|
|
1451
|
-
entries[entryIdx].y = baseY + row * opts.gapY;
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
const nodes = entries.map((e) => ({
|
|
1455
|
-
id: e.child.id,
|
|
1456
|
-
label: e.child.title ?? e.child.id,
|
|
1457
|
-
x: e.x,
|
|
1458
|
-
y: e.y,
|
|
1459
|
-
pinned: e.pinned,
|
|
1460
|
-
...e.child.classes && e.child.classes.length > 0 ? { classes: e.child.classes } : {}
|
|
1461
|
-
}));
|
|
1462
|
-
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
1463
|
-
const edges = [];
|
|
1464
|
-
const seenEdgeIds = /* @__PURE__ */ new Set();
|
|
1465
|
-
for (const child of children) {
|
|
1466
|
-
if (!child.connectsTo) continue;
|
|
1467
|
-
for (const conn of child.connectsTo) {
|
|
1468
|
-
if (!nodeIds.has(conn.target)) {
|
|
1469
|
-
warnings.push(`Edge from "${child.id}" to "${conn.target}" dropped: no such sibling node`);
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
const edgeId = makeEdgeId(child.id, conn);
|
|
1473
|
-
if (seenEdgeIds.has(edgeId)) continue;
|
|
1474
|
-
seenEdgeIds.add(edgeId);
|
|
1475
|
-
edges.push({
|
|
1476
|
-
id: edgeId,
|
|
1477
|
-
source: child.id,
|
|
1478
|
-
target: conn.target,
|
|
1479
|
-
...conn.type ? { type: conn.type } : {}
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
return { nodes, edges, warnings };
|
|
1484
|
-
}
|
|
1485
|
-
function makeEdgeId(source, conn) {
|
|
1486
|
-
return conn.type ? `${source}->${conn.target}:${conn.type}` : `${source}->${conn.target}`;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// src/doc/asciiDiagram/types.ts
|
|
1490
|
-
var ASCII_CHAR_W = 14;
|
|
1491
|
-
var ASCII_CHAR_H = 28;
|
|
1492
|
-
|
|
1493
|
-
// src/doc/asciiDiagram/mapping.ts
|
|
1494
|
-
function asciiCellToCanvas(col, row) {
|
|
1495
|
-
return { x: col * ASCII_CHAR_W, y: row * ASCII_CHAR_H };
|
|
1496
|
-
}
|
|
1497
|
-
function canvasToAsciiCell(x, y) {
|
|
1498
|
-
return {
|
|
1499
|
-
col: Math.max(0, Math.round(x / ASCII_CHAR_W)),
|
|
1500
|
-
row: Math.max(0, Math.round(y / ASCII_CHAR_H))
|
|
1501
|
-
};
|
|
1502
|
-
}
|
|
1503
|
-
function asciiDiagramToTemplateData(diagram) {
|
|
1504
|
-
const nodes = diagram.nodes.map((n) => ({
|
|
1505
|
-
id: n.id,
|
|
1506
|
-
label: n.label,
|
|
1507
|
-
...asciiCellToCanvas(n.col, n.row),
|
|
1508
|
-
w: n.wCols * ASCII_CHAR_W,
|
|
1509
|
-
h: n.hRows * ASCII_CHAR_H,
|
|
1510
|
-
...n.containerId ? { container: n.containerId } : {}
|
|
1511
|
-
}));
|
|
1512
|
-
const anchors = inferEdgeAnchors(nodes, diagram.edges);
|
|
1513
|
-
const edges = diagram.edges.map((e, index) => ({
|
|
1514
|
-
source: e.source,
|
|
1515
|
-
target: e.target,
|
|
1516
|
-
...e.label ? { label: e.label } : {},
|
|
1517
|
-
...e.directed ? {} : { directed: false },
|
|
1518
|
-
...anchors[index],
|
|
1519
|
-
routing: "orthogonal"
|
|
1520
|
-
}));
|
|
1521
|
-
return { nodes, edges };
|
|
1522
|
-
}
|
|
1523
|
-
function inferEdgeAnchors(nodes, edges) {
|
|
1524
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1525
|
-
const pairs = edges.map((edge) => {
|
|
1526
|
-
const source = nodeById.get(edge.source);
|
|
1527
|
-
const target = nodeById.get(edge.target);
|
|
1528
|
-
if (!source || !target) {
|
|
1529
|
-
return {
|
|
1530
|
-
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1531
|
-
targetAnchor: { side: "left", offset: 0.5 }
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1535
|
-
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1536
|
-
const targetW = target.w ?? ASCII_CHAR_W * 12;
|
|
1537
|
-
const targetH = target.h ?? ASCII_CHAR_H * 3;
|
|
1538
|
-
const dx = target.x + targetW / 2 - (source.x + sourceW / 2);
|
|
1539
|
-
const dy = target.y + targetH / 2 - (source.y + sourceH / 2);
|
|
1540
|
-
const horizontalDistance = Math.abs(dx) / Math.max(1, (sourceW + targetW) / 2);
|
|
1541
|
-
const verticalDistance = Math.abs(dy) / Math.max(1, (sourceH + targetH) / 2);
|
|
1542
|
-
if (verticalDistance >= horizontalDistance) {
|
|
1543
|
-
return dy >= 0 ? {
|
|
1544
|
-
sourceAnchor: { side: "bottom", offset: 0.5 },
|
|
1545
|
-
targetAnchor: { side: "top", offset: 0.5 }
|
|
1546
|
-
} : {
|
|
1547
|
-
sourceAnchor: { side: "top", offset: 0.5 },
|
|
1548
|
-
targetAnchor: { side: "bottom", offset: 0.5 }
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
return dx >= 0 ? {
|
|
1552
|
-
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1553
|
-
targetAnchor: { side: "left", offset: 0.5 }
|
|
1554
|
-
} : {
|
|
1555
|
-
sourceAnchor: { side: "left", offset: 0.5 },
|
|
1556
|
-
targetAnchor: { side: "right", offset: 0.5 }
|
|
1557
|
-
};
|
|
1558
|
-
});
|
|
1559
|
-
distributeOffsets(pairs, edges, nodes, "source");
|
|
1560
|
-
distributeOffsets(pairs, edges, nodes, "target");
|
|
1561
|
-
coalesceAlignedFanOutAnchors(pairs, edges, nodes);
|
|
1562
|
-
return pairs;
|
|
1563
|
-
}
|
|
1564
|
-
function coalesceAlignedFanOutAnchors(pairs, edges, nodes) {
|
|
1565
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1566
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1567
|
-
edges.forEach((edge, index) => {
|
|
1568
|
-
const group = groups.get(edge.source) ?? [];
|
|
1569
|
-
group.push(index);
|
|
1570
|
-
groups.set(edge.source, group);
|
|
1571
|
-
});
|
|
1572
|
-
for (const [sourceId, indexes] of groups) {
|
|
1573
|
-
if (indexes.length < 2) continue;
|
|
1574
|
-
const source = nodeById.get(sourceId);
|
|
1575
|
-
const targets = indexes.map((index) => nodeById.get(edges[index].target)).filter((node) => node !== void 0);
|
|
1576
|
-
if (!source || targets.length !== indexes.length) continue;
|
|
1577
|
-
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1578
|
-
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1579
|
-
const sourceRight = source.x + sourceW;
|
|
1580
|
-
const sourceBottom = source.y + sourceH;
|
|
1581
|
-
const targetRight = (target) => target.x + (target.w ?? ASCII_CHAR_W * 12);
|
|
1582
|
-
const targetBottom = (target) => target.y + (target.h ?? ASCII_CHAR_H * 3);
|
|
1583
|
-
let sourceSide;
|
|
1584
|
-
let targetSide;
|
|
1585
|
-
if (targets.every((target) => target.y >= sourceBottom)) {
|
|
1586
|
-
sourceSide = "bottom";
|
|
1587
|
-
targetSide = "top";
|
|
1588
|
-
} else if (targets.every((target) => targetBottom(target) <= source.y)) {
|
|
1589
|
-
sourceSide = "top";
|
|
1590
|
-
targetSide = "bottom";
|
|
1591
|
-
} else if (targets.every((target) => target.x >= sourceRight)) {
|
|
1592
|
-
sourceSide = "right";
|
|
1593
|
-
targetSide = "left";
|
|
1594
|
-
} else if (targets.every((target) => targetRight(target) <= source.x)) {
|
|
1595
|
-
sourceSide = "left";
|
|
1596
|
-
targetSide = "right";
|
|
1597
|
-
}
|
|
1598
|
-
if (!sourceSide || !targetSide) continue;
|
|
1599
|
-
for (const index of indexes) {
|
|
1600
|
-
pairs[index] = {
|
|
1601
|
-
sourceAnchor: { side: sourceSide, offset: 0.5 },
|
|
1602
|
-
targetAnchor: { side: targetSide, offset: 0.5 }
|
|
1603
|
-
};
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
function distributeOffsets(pairs, edges, nodes, endpoint) {
|
|
1608
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1609
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1610
|
-
for (let index = 0; index < edges.length; index++) {
|
|
1611
|
-
const edge = edges[index];
|
|
1612
|
-
const anchor = endpoint === "source" ? pairs[index].sourceAnchor : pairs[index].targetAnchor;
|
|
1613
|
-
const nodeId = endpoint === "source" ? edge.source : edge.target;
|
|
1614
|
-
const key = `${nodeId}:${anchor.side}`;
|
|
1615
|
-
const group = groups.get(key) ?? [];
|
|
1616
|
-
group.push(index);
|
|
1617
|
-
groups.set(key, group);
|
|
1618
|
-
}
|
|
1619
|
-
for (const indexes of groups.values()) {
|
|
1620
|
-
if (indexes.length < 2) continue;
|
|
1621
|
-
const side = endpoint === "source" ? pairs[indexes[0]].sourceAnchor.side : pairs[indexes[0]].targetAnchor.side;
|
|
1622
|
-
indexes.sort((a, b) => {
|
|
1623
|
-
const oppositeA = nodeById.get(endpoint === "source" ? edges[a].target : edges[a].source);
|
|
1624
|
-
const oppositeB = nodeById.get(endpoint === "source" ? edges[b].target : edges[b].source);
|
|
1625
|
-
const axisA = side === "top" || side === "bottom" ? oppositeA?.x ?? 0 : oppositeA?.y ?? 0;
|
|
1626
|
-
const axisB = side === "top" || side === "bottom" ? oppositeB?.x ?? 0 : oppositeB?.y ?? 0;
|
|
1627
|
-
return axisA - axisB || a - b;
|
|
1628
|
-
});
|
|
1629
|
-
indexes.forEach((edgeIndex, rank) => {
|
|
1630
|
-
const anchor = endpoint === "source" ? pairs[edgeIndex].sourceAnchor : pairs[edgeIndex].targetAnchor;
|
|
1631
|
-
anchor.offset = (rank + 1) / (indexes.length + 1);
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
function asciiDiagramFromTemplateData(nodes, edges, options = {}) {
|
|
1636
|
-
const ids = new Set(nodes.map((n) => n.id));
|
|
1637
|
-
const outNodes = nodes.map((n) => {
|
|
1638
|
-
const { col, row } = canvasToAsciiCell(n.x, n.y);
|
|
1639
|
-
const labelLines = n.label.length > 0 ? n.label.split("\n") : [];
|
|
1640
|
-
const innerW = labelLines.reduce((w, l) => Math.max(w, l.length), 0);
|
|
1641
|
-
const wCols = typeof n.w === "number" ? Math.max(3, Math.round(n.w / ASCII_CHAR_W)) : innerW + 4;
|
|
1642
|
-
const hRows = typeof n.h === "number" ? Math.max(3, Math.round(n.h / ASCII_CHAR_H)) : labelLines.length + 2;
|
|
1643
|
-
return {
|
|
1644
|
-
id: n.id,
|
|
1645
|
-
label: n.label,
|
|
1646
|
-
col,
|
|
1647
|
-
row,
|
|
1648
|
-
wCols: Math.max(wCols, 3),
|
|
1649
|
-
hRows: Math.max(hRows, 3),
|
|
1650
|
-
...n.container && ids.has(n.container) && n.container !== n.id ? { containerId: n.container } : {}
|
|
1651
|
-
};
|
|
1652
|
-
});
|
|
1653
|
-
return {
|
|
1654
|
-
nodes: outNodes,
|
|
1655
|
-
edges: edges.filter((e) => ids.has(e.source) && ids.has(e.target)).map((e) => ({
|
|
1656
|
-
source: e.source,
|
|
1657
|
-
target: e.target,
|
|
1658
|
-
...e.label ? { label: e.label } : {},
|
|
1659
|
-
directed: e.directed !== false
|
|
1660
|
-
})),
|
|
1661
|
-
style: options.style ?? "unicode",
|
|
1662
|
-
warnings: []
|
|
1663
|
-
};
|
|
1664
|
-
}
|
|
1665
|
-
function asciiDiagramFromBlocks(children, options = {}) {
|
|
1666
|
-
const layout = computeDiagramLayout(children);
|
|
1667
|
-
const nodes = layout.nodes.map((n) => ({
|
|
1668
|
-
id: n.id,
|
|
1669
|
-
label: n.label,
|
|
1670
|
-
x: n.x,
|
|
1671
|
-
y: n.y
|
|
1672
|
-
}));
|
|
1673
|
-
const edges = layout.edges.map((e) => ({
|
|
1674
|
-
source: e.source,
|
|
1675
|
-
target: e.target,
|
|
1676
|
-
...e.type ? { label: e.type } : {}
|
|
1677
|
-
}));
|
|
1678
|
-
const diagram = asciiDiagramFromTemplateData(nodes, edges, options);
|
|
1679
|
-
diagram.warnings.push(...layout.warnings);
|
|
1680
|
-
return diagram;
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
// src/doc/asciiTimeline/mapping.ts
|
|
1684
|
-
function asciiTimelineToTemplateData(timeline) {
|
|
1685
|
-
const globalStart = timeline.tracks.length > 0 ? Math.min(...timeline.tracks.map((track) => track.startColumn)) : 0;
|
|
1686
|
-
const globalEnd = timeline.tracks.length > 0 ? Math.max(...timeline.tracks.map((track) => track.endColumn)) : 1;
|
|
1687
|
-
const span = Math.max(1, globalEnd - globalStart);
|
|
1688
|
-
const tracks = timeline.tracks.map((track) => ({
|
|
1689
|
-
id: track.id,
|
|
1690
|
-
...track.label ? { label: track.label } : {},
|
|
1691
|
-
...track.endLabel ? { endLabel: track.endLabel } : {},
|
|
1692
|
-
events: track.events.map((event) => ({
|
|
1693
|
-
id: event.id,
|
|
1694
|
-
label: event.label,
|
|
1695
|
-
...event.description ? { description: event.description } : {},
|
|
1696
|
-
position: clamp01((event.column - globalStart) / span),
|
|
1697
|
-
...event.side ? { side: event.side } : {},
|
|
1698
|
-
...event.descriptionSide ? { descriptionSide: event.descriptionSide } : {},
|
|
1699
|
-
...event.callout === false ? { callout: false } : {},
|
|
1700
|
-
...event.marker ? { marker: event.marker } : {}
|
|
1701
|
-
}))
|
|
1702
|
-
}));
|
|
1703
|
-
const links = timeline.links.map((link) => ({ ...link }));
|
|
1704
|
-
return { tracks, links };
|
|
1705
|
-
}
|
|
1706
|
-
function clamp01(value) {
|
|
1707
|
-
return Math.max(0, Math.min(1, value));
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
// src/doc/treeview/mapping.ts
|
|
1711
|
-
function treeToTemplateData(tree) {
|
|
1712
|
-
return { items: tree.roots.map(toItem) };
|
|
1713
|
-
}
|
|
1714
|
-
function toItem(node) {
|
|
1715
|
-
return {
|
|
1716
|
-
id: node.id,
|
|
1717
|
-
label: node.label,
|
|
1718
|
-
...node.isDir ? { isDir: true } : {},
|
|
1719
|
-
...node.comment ? { comment: node.comment } : {},
|
|
1720
|
-
children: node.children.map(toItem)
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
function treeFromTemplateData(items, options = {}) {
|
|
1724
|
-
const roots = items.map(fromItem);
|
|
1725
|
-
return { roots, style: options.style ?? "unicode", warnings: [] };
|
|
1726
|
-
}
|
|
1727
|
-
function fromItem(item) {
|
|
1728
|
-
const children = Array.isArray(item.children) ? item.children.map(fromItem) : [];
|
|
1729
|
-
return {
|
|
1730
|
-
id: typeof item.id === "string" && item.id ? item.id : "node",
|
|
1731
|
-
label: typeof item.label === "string" ? item.label : "",
|
|
1732
|
-
children,
|
|
1733
|
-
isDir: item.isDir === true || children.length > 0,
|
|
1734
|
-
...item.comment ? { comment: item.comment } : {}
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
function treeFromMarkdownList(list) {
|
|
1738
|
-
const usedIds = /* @__PURE__ */ new Map();
|
|
1739
|
-
const slugify2 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "node";
|
|
1740
|
-
const idFor = (label) => {
|
|
1741
|
-
const base = slugify2(label.replace(/\/$/, ""));
|
|
1742
|
-
const count = usedIds.get(base) ?? 0;
|
|
1743
|
-
usedIds.set(base, count + 1);
|
|
1744
|
-
return count === 0 ? base : `${base}-${count + 1}`;
|
|
1745
|
-
};
|
|
1746
|
-
const itemToNode = (item) => {
|
|
1747
|
-
const labelNode = item.children.find((c) => c.type !== "list");
|
|
1748
|
-
const label = labelNode ? extractPlainText(labelNode).trim() : "";
|
|
1749
|
-
const childLists = item.children.filter((c) => c.type === "list");
|
|
1750
|
-
const children = [];
|
|
1751
|
-
for (const cl of childLists) for (const ci of cl.children) children.push(itemToNode(ci));
|
|
1752
|
-
return {
|
|
1753
|
-
id: idFor(label),
|
|
1754
|
-
label,
|
|
1755
|
-
children,
|
|
1756
|
-
isDir: label.endsWith("/") || children.length > 0
|
|
1757
|
-
};
|
|
1758
|
-
};
|
|
1759
|
-
return { roots: list.children.map(itemToNode), style: "unicode", warnings: [] };
|
|
1760
|
-
}
|
|
1761
|
-
function findFirstList(contents) {
|
|
1762
|
-
return contents?.find((n) => n.type === "list");
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
// src/doc/templateInputs.ts
|
|
1766
|
-
var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
|
|
1767
|
-
function extractHostedVideos(contents, limit = Infinity) {
|
|
1768
|
-
const found = [];
|
|
1769
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1770
|
-
for (const node of contents ?? []) {
|
|
1771
|
-
if (found.length >= limit) break;
|
|
1772
|
-
const video = findBlockVideoEmbed(node);
|
|
1773
|
-
if (!video || seen.has(video.embed.watchUrl)) continue;
|
|
1774
|
-
seen.add(video.embed.watchUrl);
|
|
1775
|
-
found.push({
|
|
1776
|
-
url: video.embed.watchUrl,
|
|
1777
|
-
title: video.title,
|
|
1778
|
-
aspectRatio: video.embed.aspectRatio
|
|
1779
|
-
});
|
|
1780
|
-
}
|
|
1781
|
-
return found;
|
|
1782
|
-
}
|
|
1783
|
-
function isMaterializedMediaNode(node) {
|
|
1784
|
-
if (node.type === "code") return node.lang?.trim().toLowerCase() === "mermaid";
|
|
1785
|
-
return findBlockVideoEmbed(node) !== null;
|
|
1786
|
-
}
|
|
1787
|
-
function extractBodyPlainText(contents) {
|
|
1788
|
-
if (!contents || contents.length === 0) return "";
|
|
1789
|
-
return contents.filter((node) => !isMaterializedMediaNode(node)).map((n) => extractPlainText(n)).join("\n").trim();
|
|
1790
|
-
}
|
|
1791
|
-
function extractListItems(contents) {
|
|
1792
|
-
return extractRichListItems(contents).map((item) => item.text);
|
|
1793
|
-
}
|
|
1794
|
-
function escapeInlineHtml(value) {
|
|
1795
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1796
|
-
}
|
|
1797
|
-
function renderInlineHtml(nodes) {
|
|
1798
|
-
return nodes.map((node) => {
|
|
1799
|
-
switch (node.type) {
|
|
1800
|
-
case "text":
|
|
1801
|
-
return escapeInlineHtml(node.value);
|
|
1802
|
-
case "emphasis":
|
|
1803
|
-
return `<em>${renderInlineHtml(node.children)}</em>`;
|
|
1804
|
-
case "strong":
|
|
1805
|
-
return `<strong>${renderInlineHtml(node.children)}</strong>`;
|
|
1806
|
-
case "delete":
|
|
1807
|
-
return `<del>${renderInlineHtml(node.children)}</del>`;
|
|
1808
|
-
case "superscript":
|
|
1809
|
-
return `<sup>${renderInlineHtml(node.children)}</sup>`;
|
|
1810
|
-
case "subscript":
|
|
1811
|
-
return `<sub>${renderInlineHtml(node.children)}</sub>`;
|
|
1812
|
-
case "inlineCode":
|
|
1813
|
-
return `<code>${escapeInlineHtml(node.value)}</code>`;
|
|
1814
|
-
case "link": {
|
|
1815
|
-
const label = renderInlineHtml(node.children);
|
|
1816
|
-
const href = sanitizeUrl(node.url, "link");
|
|
1817
|
-
if (!href) return label;
|
|
1818
|
-
const title = node.title ? ` title="${escapeInlineHtml(node.title)}"` : "";
|
|
1819
|
-
return `<a href="${escapeInlineHtml(href)}"${title}>${label}</a>`;
|
|
1820
|
-
}
|
|
1821
|
-
case "break":
|
|
1822
|
-
return "<br>";
|
|
1823
|
-
case "image":
|
|
1824
|
-
return escapeInlineHtml(node.alt ?? "");
|
|
1825
|
-
case "inlineMath":
|
|
1826
|
-
return escapeInlineHtml(`$${node.value}$`);
|
|
1827
|
-
case "footnoteReference":
|
|
1828
|
-
return escapeInlineHtml(`[^${node.label ?? node.identifier}]`);
|
|
1829
|
-
case "linkReference":
|
|
1830
|
-
case "textDirective":
|
|
1831
|
-
return renderInlineHtml(node.children);
|
|
1832
|
-
case "imageReference":
|
|
1833
|
-
return escapeInlineHtml(node.alt ?? "");
|
|
1834
|
-
case "mention":
|
|
1835
|
-
return escapeInlineHtml(`@${node.displayName}`);
|
|
1836
|
-
case "inlineIcon":
|
|
1837
|
-
return `<i class="fa-${node.family} fa-${node.name}" aria-hidden="true"></i>`;
|
|
1838
|
-
case "htmlInline":
|
|
1839
|
-
return escapeInlineHtml(node.rawHtml);
|
|
1840
|
-
}
|
|
1841
|
-
}).join("");
|
|
1842
|
-
}
|
|
1843
|
-
function renderMarkdownBlocksHtml(nodes, builds) {
|
|
1844
|
-
const renderBlocks = (blocks) => blocks.map((node, index) => {
|
|
1845
|
-
const previous = blocks[index - 1];
|
|
1846
|
-
const extraBlankLines = previous ? Math.max(0, markdownBlockSeparatorLines(previous, node) - 2) : 0;
|
|
1847
|
-
const sourceGap = '<div data-squisq-source-gap aria-hidden="true"><br></div>'.repeat(
|
|
1848
|
-
extraBlankLines
|
|
1849
|
-
);
|
|
1850
|
-
const html = renderBlock(node);
|
|
1851
|
-
const step = builds?.targets.get(node);
|
|
1852
|
-
return `${sourceGap}${step === void 0 ? html : `<div data-squisq-build-step="${step}">${html}</div>`}`;
|
|
1853
|
-
}).join("");
|
|
1854
|
-
const renderList = (node) => {
|
|
1855
|
-
const tag = node.ordered ? "ol" : "ul";
|
|
1856
|
-
const start = node.ordered && node.start != null ? ` start="${node.start}"` : "";
|
|
1857
|
-
const items = node.children.map((item, index) => {
|
|
1858
|
-
const taskMarker = item.checked == null ? "" : `<span>${item.checked ? "[x]" : "[ ]"} </span>`;
|
|
1859
|
-
const step = builds?.targets.get(item);
|
|
1860
|
-
if (step === void 0) return `<li>${taskMarker}${renderBlocks(item.children)}</li>`;
|
|
1861
|
-
const marker = item.checked == null ? node.ordered ? `${(node.start ?? 1) + index}.` : "\u2022" : item.checked ? "[x]" : "[ ]";
|
|
1862
|
-
let first = true;
|
|
1863
|
-
let own = [];
|
|
1864
|
-
const fragments = [];
|
|
1865
|
-
const flush = () => {
|
|
1866
|
-
if (!first && !own.length) return;
|
|
1867
|
-
fragments.push(
|
|
1868
|
-
`<div data-squisq-build-step="${step}" data-squisq-build-row><span aria-hidden="true">${first ? marker : ""}</span><div>${renderBlocks(own)}</div></div>`
|
|
1869
|
-
);
|
|
1870
|
-
first = false;
|
|
1871
|
-
own = [];
|
|
1872
|
-
};
|
|
1873
|
-
for (const child of item.children) {
|
|
1874
|
-
if (child.type === "list") {
|
|
1875
|
-
flush();
|
|
1876
|
-
fragments.push(renderList(child));
|
|
1877
|
-
} else own.push(child);
|
|
1878
|
-
}
|
|
1879
|
-
flush();
|
|
1880
|
-
return `<li data-squisq-build-item>${fragments.join("")}</li>`;
|
|
1881
|
-
}).join("");
|
|
1882
|
-
return `<${tag}${start}>${items}</${tag}>`;
|
|
1883
|
-
};
|
|
1884
|
-
const renderBlock = (node) => {
|
|
1885
|
-
switch (node.type) {
|
|
1886
|
-
case "paragraph":
|
|
1887
|
-
return `<p>${renderInlineHtml(node.children)}</p>`;
|
|
1888
|
-
case "heading":
|
|
1889
|
-
return `<h${node.depth}>${renderInlineHtml(node.children)}</h${node.depth}>`;
|
|
1890
|
-
case "blockquote":
|
|
1891
|
-
return `<blockquote>${renderBlocks(node.children)}</blockquote>`;
|
|
1892
|
-
case "list":
|
|
1893
|
-
return renderList(node);
|
|
1894
|
-
case "code":
|
|
1895
|
-
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
1896
|
-
case "thematicBreak":
|
|
1897
|
-
return "<hr>";
|
|
1898
|
-
case "table": {
|
|
1899
|
-
const [head, ...body] = node.children;
|
|
1900
|
-
const row = (cells, header) => `<tr>${cells.map((cell, columnIndex) => {
|
|
1901
|
-
const tag = header ? "th" : "td";
|
|
1902
|
-
const alignment = node.align?.[columnIndex];
|
|
1903
|
-
const alignAttribute = alignment ? ` align="${alignment}"` : "";
|
|
1904
|
-
return `<${tag}${alignAttribute}>${renderInlineHtml(cell.children)}</${tag}>`;
|
|
1905
|
-
}).join("")}</tr>`;
|
|
1906
|
-
const thead = head ? `<thead>${row(head.children, true)}</thead>` : "";
|
|
1907
|
-
const tbody = body.length ? `<tbody>${body.map((item) => row(item.children, false)).join("")}</tbody>` : "";
|
|
1908
|
-
return `<div data-squisq-table-scroll role="region" aria-label="Scrollable data table"><table>${thead}${tbody}</table></div>`;
|
|
1909
|
-
}
|
|
1910
|
-
case "math":
|
|
1911
|
-
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
1912
|
-
case "definition":
|
|
1913
|
-
return "";
|
|
1914
|
-
default: {
|
|
1915
|
-
const text = extractPlainText(node).trim();
|
|
1916
|
-
return text ? `<p>${escapeInlineHtml(text)}</p>` : "";
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
};
|
|
1920
|
-
return renderBlocks(nodes);
|
|
1921
|
-
}
|
|
1922
|
-
function markdownBlockSeparatorLines(previous, next) {
|
|
1923
|
-
const previousEndLine = previous.position?.end.line;
|
|
1924
|
-
const nextStartLine = next.position?.start.line;
|
|
1925
|
-
if (previousEndLine == null || nextStartLine == null || nextStartLine <= previousEndLine) {
|
|
1926
|
-
return 2;
|
|
1927
|
-
}
|
|
1928
|
-
return Math.max(2, nextStartLine - previousEndLine);
|
|
1929
|
-
}
|
|
1930
|
-
function renderListItemHtml(markdown) {
|
|
1931
|
-
return markdown.map(
|
|
1932
|
-
(node) => node.type === "paragraph" || node.type === "heading" ? renderInlineHtml(node.children) : escapeInlineHtml(extractPlainText(node))
|
|
1933
|
-
).join("<br>");
|
|
1934
|
-
}
|
|
1935
|
-
function extractRichListItems(contents) {
|
|
1936
|
-
if (!contents) return [];
|
|
1937
|
-
const items = [];
|
|
1938
|
-
for (const node of contents) {
|
|
1939
|
-
if (node.type !== "list") continue;
|
|
1940
|
-
for (const item of node.children) {
|
|
1941
|
-
const text = extractPlainText(item).trim();
|
|
1942
|
-
if (!text) continue;
|
|
1943
|
-
const html = renderListItemHtml(item.children);
|
|
1944
|
-
const plainHtml = escapeInlineHtml(text).replace(/\r?\n/g, "<br>");
|
|
1945
|
-
items.push({
|
|
1946
|
-
text,
|
|
1947
|
-
markdown: item.children,
|
|
1948
|
-
...html && html !== plainHtml ? { html } : {}
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
return items;
|
|
1953
|
-
}
|
|
1954
|
-
function parseDim(raw) {
|
|
1955
|
-
if (raw === void 0) return void 0;
|
|
1956
|
-
const n = parseFloat(raw);
|
|
1957
|
-
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
1958
|
-
}
|
|
1959
|
-
function extractImages(contents, limit = Infinity) {
|
|
1960
|
-
if (!contents || contents.length === 0) return [];
|
|
1961
|
-
const found = [];
|
|
1962
|
-
function fromHtml(nodes) {
|
|
1963
|
-
for (const node of nodes) {
|
|
1964
|
-
if (found.length >= limit) return;
|
|
1965
|
-
if (!node || typeof node !== "object") continue;
|
|
1966
|
-
const n = node;
|
|
1967
|
-
if (n.type === "htmlElement" && n.tagName === "img") {
|
|
1968
|
-
const attrs = n.attributes;
|
|
1969
|
-
if (attrs && typeof attrs.src === "string" && attrs.src) {
|
|
1970
|
-
found.push({
|
|
1971
|
-
src: attrs.src,
|
|
1972
|
-
alt: typeof attrs.alt === "string" ? attrs.alt : "",
|
|
1973
|
-
width: parseDim(attrs.width),
|
|
1974
|
-
height: parseDim(attrs.height)
|
|
1975
|
-
});
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
if (Array.isArray(n.children)) fromHtml(n.children);
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
function walk(node) {
|
|
1982
|
-
if (found.length >= limit) return;
|
|
1983
|
-
if (!node || typeof node !== "object") return;
|
|
1984
|
-
const n = node;
|
|
1985
|
-
if (n.type === "image" && typeof n.url === "string" && n.url) {
|
|
1986
|
-
if (!mediaKindForUrl(n.url)) {
|
|
1987
|
-
found.push({ src: n.url, alt: typeof n.alt === "string" ? n.alt : "" });
|
|
1988
|
-
}
|
|
1989
|
-
return;
|
|
1990
|
-
}
|
|
1991
|
-
if ((n.type === "htmlBlock" || n.type === "htmlInline") && Array.isArray(n.htmlChildren)) {
|
|
1992
|
-
fromHtml(n.htmlChildren);
|
|
1993
|
-
}
|
|
1994
|
-
if (Array.isArray(n.children)) {
|
|
1995
|
-
for (const child of n.children) walk(child);
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
for (const node of contents) {
|
|
1999
|
-
if (found.length >= limit) break;
|
|
2000
|
-
walk(node);
|
|
2001
|
-
}
|
|
2002
|
-
return found;
|
|
2003
|
-
}
|
|
2004
|
-
function extractFirstImage(contents) {
|
|
2005
|
-
return extractImages(contents, 1)[0] ?? null;
|
|
2006
|
-
}
|
|
2007
|
-
function extractEmbeddedVideos(contents, limit = Infinity) {
|
|
2008
|
-
if (!contents || contents.length === 0) return [];
|
|
2009
|
-
const found = [];
|
|
2010
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2011
|
-
const add = (video) => {
|
|
2012
|
-
if (!video.src || found.length >= limit || seen.has(video.src)) return;
|
|
2013
|
-
seen.add(video.src);
|
|
2014
|
-
found.push(video);
|
|
2015
|
-
};
|
|
2016
|
-
function nestedSource(children) {
|
|
2017
|
-
if (!Array.isArray(children)) return void 0;
|
|
2018
|
-
for (const child of children) {
|
|
2019
|
-
if (!child || typeof child !== "object") continue;
|
|
2020
|
-
const node = child;
|
|
2021
|
-
if (node.tagName === "source") {
|
|
2022
|
-
const attrs = node.attributes;
|
|
2023
|
-
if (typeof attrs?.src === "string" && attrs.src) return attrs.src;
|
|
2024
|
-
}
|
|
2025
|
-
const nested = nestedSource(node.children);
|
|
2026
|
-
if (nested) return nested;
|
|
2027
|
-
}
|
|
2028
|
-
return void 0;
|
|
2029
|
-
}
|
|
2030
|
-
function walk(node) {
|
|
2031
|
-
if (found.length >= limit || !node || typeof node !== "object") return;
|
|
2032
|
-
const n = node;
|
|
2033
|
-
if (n.type === "htmlElement" && n.tagName === "video") {
|
|
2034
|
-
const attrs = n.attributes;
|
|
2035
|
-
const src = attrs?.src || nestedSource(n.children);
|
|
2036
|
-
if (src) {
|
|
2037
|
-
const startAt = attrs?.["data-squisq-video-start-at"] ? parseTimeSeconds(attrs["data-squisq-video-start-at"]) : null;
|
|
2038
|
-
const clipStart = attrs?.["data-squisq-video-clip-start"] ? parseTimeSeconds(attrs["data-squisq-video-clip-start"]) : null;
|
|
2039
|
-
const clipEnd = attrs?.["data-squisq-video-clip-end"] ? parseTimeSeconds(attrs["data-squisq-video-clip-end"]) : null;
|
|
2040
|
-
add({
|
|
2041
|
-
src,
|
|
2042
|
-
...attrs?.poster ? { posterSrc: attrs.poster } : {},
|
|
2043
|
-
alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || "",
|
|
2044
|
-
...startAt != null ? { startAt } : {},
|
|
2045
|
-
...clipStart != null ? { clipStart } : {},
|
|
2046
|
-
...clipEnd != null ? { clipEnd } : {}
|
|
2047
|
-
});
|
|
2048
|
-
}
|
|
2049
|
-
} else if ((n.type === "link" || n.type === "image") && typeof n.url === "string" && VIDEO_FILE_RE.test(n.url)) {
|
|
2050
|
-
add({
|
|
2051
|
-
src: n.url,
|
|
2052
|
-
alt: typeof n.alt === "string" ? n.alt : extractPlainText(n).trim()
|
|
2053
|
-
});
|
|
2054
|
-
}
|
|
2055
|
-
if (Array.isArray(n.children)) n.children.forEach(walk);
|
|
2056
|
-
if (Array.isArray(n.htmlChildren)) n.htmlChildren.forEach(walk);
|
|
2057
|
-
}
|
|
2058
|
-
contents.forEach(walk);
|
|
2059
|
-
return found;
|
|
2060
|
-
}
|
|
2061
|
-
function extractFirstEmbeddedVideo(contents) {
|
|
2062
|
-
return extractEmbeddedVideos(contents, 1)[0] ?? null;
|
|
2063
|
-
}
|
|
2064
|
-
function extractTableFromContents(contents) {
|
|
2065
|
-
if (!contents) return null;
|
|
2066
|
-
for (const node of contents) {
|
|
2067
|
-
if (node.type === "table") {
|
|
2068
|
-
const table = node;
|
|
2069
|
-
const [headerRow, ...bodyRows] = table.children;
|
|
2070
|
-
if (!headerRow) return null;
|
|
2071
|
-
const headers = headerRow.children.map((cell) => extractPlainText(cell).trim());
|
|
2072
|
-
const rows = bodyRows.map((row) => row.children.map((cell) => extractPlainText(cell).trim()));
|
|
2073
|
-
return { headers, rows, align: table.align };
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
return null;
|
|
2077
|
-
}
|
|
2078
|
-
function extractBlockquoteText(contents) {
|
|
2079
|
-
if (!contents) return "";
|
|
2080
|
-
for (const node of contents) {
|
|
2081
|
-
if (node.type === "blockquote") return extractPlainText(node).trim();
|
|
2082
|
-
}
|
|
2083
|
-
return "";
|
|
2084
|
-
}
|
|
2085
|
-
function firstParagraph(contents) {
|
|
2086
|
-
if (!contents) return null;
|
|
2087
|
-
const index = contents.findIndex((node) => node.type === "paragraph");
|
|
2088
|
-
if (index < 0) return null;
|
|
2089
|
-
return {
|
|
2090
|
-
node: contents[index],
|
|
2091
|
-
index
|
|
2092
|
-
};
|
|
2093
|
-
}
|
|
2094
|
-
function leadingStrongStat(paragraph) {
|
|
2095
|
-
for (const child of paragraph.children) {
|
|
2096
|
-
if (child.type === "text" && child.value.trim() === "") continue;
|
|
2097
|
-
if (child.type !== "strong") return null;
|
|
2098
|
-
const text = extractPlainText(child).trim();
|
|
2099
|
-
const match = text.match(/^(?:[$€£¥]\s*)?[+-]?\d+(?:[.,]\d+)*(?:\s?(?:[%‰x×]|[A-Za-z]+))?/);
|
|
2100
|
-
return match?.[0].trim() || null;
|
|
2101
|
-
}
|
|
2102
|
-
return null;
|
|
2103
|
-
}
|
|
2104
|
-
function removeStatPhrase(text, start, end) {
|
|
2105
|
-
const before = text.slice(0, start).trim();
|
|
2106
|
-
let after = text.slice(end);
|
|
2107
|
-
if (!before) after = after.replace(/^\s*(?:[-–—:]\s*)?/, "");
|
|
2108
|
-
else after = after.trim();
|
|
2109
|
-
return [before, after].filter(Boolean).join(" ").trim();
|
|
2110
|
-
}
|
|
2111
|
-
function bodyWithoutParagraphStat(contents, paragraphIndex, paragraphText, start, end) {
|
|
2112
|
-
if (!contents) return "";
|
|
2113
|
-
return contents.map(
|
|
2114
|
-
(node, index) => index === paragraphIndex ? removeStatPhrase(paragraphText, start, end) : extractPlainText(node).trim()
|
|
2115
|
-
).filter(Boolean).join("\n").trim();
|
|
2116
|
-
}
|
|
2117
|
-
function deriveStatHighlightInputs(headingText, contents, bodyText3) {
|
|
2118
|
-
const paragraph = firstParagraph(contents);
|
|
2119
|
-
if (paragraph) {
|
|
2120
|
-
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
2121
|
-
const strongStat = leadingStrongStat(paragraph.node);
|
|
2122
|
-
if (strongStat) {
|
|
2123
|
-
const start = paragraphText.indexOf(strongStat);
|
|
2124
|
-
const description = bodyWithoutParagraphStat(
|
|
2125
|
-
contents,
|
|
2126
|
-
paragraph.index,
|
|
2127
|
-
paragraphText,
|
|
2128
|
-
start,
|
|
2129
|
-
start + strongStat.length
|
|
2130
|
-
);
|
|
2131
|
-
return {
|
|
2132
|
-
stat: strongStat,
|
|
2133
|
-
description: description || headingText || strongStat
|
|
2134
|
-
};
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
const trimmedHeading = headingText.trim();
|
|
2138
|
-
const headingStat = matchNumberHighlight(trimmedHeading);
|
|
2139
|
-
if (headingStat && headingStat.index === 0 && headingStat.end === trimmedHeading.length) {
|
|
2140
|
-
return { stat: headingText, description: bodyText3 || headingText };
|
|
2141
|
-
}
|
|
2142
|
-
if (paragraph) {
|
|
2143
|
-
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
2144
|
-
const bodyStat = matchNumberHighlight(paragraphText);
|
|
2145
|
-
if (bodyStat) {
|
|
2146
|
-
const description = bodyWithoutParagraphStat(
|
|
2147
|
-
contents,
|
|
2148
|
-
paragraph.index,
|
|
2149
|
-
paragraphText,
|
|
2150
|
-
bodyStat.index,
|
|
2151
|
-
bodyStat.end
|
|
2152
|
-
);
|
|
2153
|
-
return {
|
|
2154
|
-
stat: bodyStat.value,
|
|
2155
|
-
description: description || headingText || bodyStat.value
|
|
2156
|
-
};
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
return { stat: headingText, description: bodyText3 || headingText };
|
|
2160
|
-
}
|
|
2161
|
-
function normalizeCoverageText(value) {
|
|
2162
|
-
return value.replace(/\s+/g, " ").trim();
|
|
2163
|
-
}
|
|
2164
|
-
function autoTemplatePreservesContent(templateName, contents) {
|
|
2165
|
-
const nodes = contents ?? [];
|
|
2166
|
-
switch (templateName) {
|
|
2167
|
-
case "quote":
|
|
2168
|
-
return nodes.length === 1 && nodes[0]?.type === "blockquote";
|
|
2169
|
-
case "dataTable":
|
|
2170
|
-
return nodes.length === 1 && nodes[0]?.type === "table";
|
|
2171
|
-
case "list":
|
|
2172
|
-
return nodes.length === 1 && nodes[0]?.type === "list";
|
|
2173
|
-
case "diagram":
|
|
2174
|
-
case "timeline":
|
|
2175
|
-
return nodes.length === 1 && nodes[0]?.type === "code";
|
|
2176
|
-
case "tree":
|
|
2177
|
-
return nodes.length === 1 && (nodes[0]?.type === "code" || nodes[0]?.type === "list");
|
|
2178
|
-
case "photoGrid": {
|
|
2179
|
-
const images = extractImages(nodes);
|
|
2180
|
-
if (images.length < 2 || images.length > 4) return false;
|
|
2181
|
-
const bodyText3 = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
2182
|
-
const representedText = normalizeCoverageText(
|
|
2183
|
-
images.map((image) => image.alt).filter(Boolean).join(" ")
|
|
2184
|
-
);
|
|
2185
|
-
return bodyText3 === "" || bodyText3 === representedText;
|
|
2186
|
-
}
|
|
2187
|
-
case "videoWithCaption": {
|
|
2188
|
-
const videos = extractEmbeddedVideos(nodes);
|
|
2189
|
-
if (videos.length + extractHostedVideos(nodes).length !== 1) return false;
|
|
2190
|
-
const bodyText3 = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
2191
|
-
return bodyText3 === normalizeCoverageText(videos[0]?.alt ?? "");
|
|
2192
|
-
}
|
|
2193
|
-
case "leftFeature":
|
|
2194
|
-
case "rightFeature":
|
|
2195
|
-
case "statHighlight":
|
|
2196
|
-
return nodes.length > 0 && nodes.every((node) => node.type === "paragraph");
|
|
2197
|
-
default:
|
|
2198
|
-
return false;
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
function deriveTemplateInputs(templateName, headingText, contents, options = {}) {
|
|
2202
|
-
const placeholders = options.placeholders === true;
|
|
2203
|
-
const bodyText3 = extractBodyPlainText(contents);
|
|
2204
|
-
switch (templateName) {
|
|
2205
|
-
case "statHighlight": {
|
|
2206
|
-
const inputs = deriveStatHighlightInputs(headingText, contents, bodyText3);
|
|
2207
|
-
if (options.preserveSourceHeading && headingText && inputs.stat !== headingText && inputs.description !== headingText) {
|
|
2208
|
-
return {
|
|
2209
|
-
...inputs,
|
|
2210
|
-
description: headingText,
|
|
2211
|
-
...inputs.description ? { detail: inputs.description } : {}
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
return inputs;
|
|
2215
|
-
}
|
|
2216
|
-
case "transcript": {
|
|
2217
|
-
const text = extractBlockquoteText(contents) || bodyText3;
|
|
2218
|
-
if (!text && !headingText) {
|
|
2219
|
-
return placeholders ? { speaker: "Speaker", text: "Message" } : null;
|
|
2220
|
-
}
|
|
2221
|
-
return {
|
|
2222
|
-
speaker: headingText || (placeholders ? "Speaker" : ""),
|
|
2223
|
-
text: text || (placeholders ? "Message" : "")
|
|
2224
|
-
};
|
|
2225
|
-
}
|
|
2226
|
-
case "quote": {
|
|
2227
|
-
const quote = extractBlockquoteText(contents) || bodyText3;
|
|
2228
|
-
if (quote) return { quote, ...headingText ? { title: headingText } : {} };
|
|
2229
|
-
return { quote: headingText };
|
|
2230
|
-
}
|
|
2231
|
-
case "fullBleedQuote": {
|
|
2232
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2233
|
-
return { text };
|
|
2234
|
-
}
|
|
2235
|
-
case "pullQuote": {
|
|
2236
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2237
|
-
const img = extractFirstImage(contents);
|
|
2238
|
-
return {
|
|
2239
|
-
text,
|
|
2240
|
-
...img ? { backgroundImage: { src: img.src, alt: img.alt || headingText } } : {}
|
|
2241
|
-
};
|
|
2242
|
-
}
|
|
2243
|
-
case "videoPullQuote": {
|
|
2244
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2245
|
-
const video = extractFirstEmbeddedVideo(contents);
|
|
2246
|
-
return {
|
|
2247
|
-
text,
|
|
2248
|
-
...video ? {
|
|
2249
|
-
backgroundVideo: {
|
|
2250
|
-
src: video.src,
|
|
2251
|
-
...video.posterSrc ? { posterSrc: video.posterSrc } : {},
|
|
2252
|
-
alt: video.alt || headingText,
|
|
2253
|
-
...video.clipStart != null ? { clipStart: video.clipStart } : {},
|
|
2254
|
-
...video.clipEnd != null ? { clipEnd: video.clipEnd } : {}
|
|
2255
|
-
}
|
|
2256
|
-
} : {}
|
|
2257
|
-
};
|
|
2258
|
-
}
|
|
2259
|
-
case "factCard":
|
|
2260
|
-
return { fact: headingText, explanation: bodyText3 };
|
|
2261
|
-
case "twoColumn":
|
|
2262
|
-
return headingText ? { header: headingText } : placeholders ? {} : null;
|
|
2263
|
-
case "comparisonBar":
|
|
2264
|
-
return placeholders ? { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 } : null;
|
|
2265
|
-
case "list": {
|
|
2266
|
-
const items = extractListItems(contents);
|
|
2267
|
-
const firstList = (contents ?? []).find((n) => n.type === "list");
|
|
2268
|
-
if (items.length > 0) {
|
|
2269
|
-
return {
|
|
2270
|
-
items,
|
|
2271
|
-
// Bullets stay bullets: only a numbered source list renders `1.` markers.
|
|
2272
|
-
ordered: firstList?.ordered === true,
|
|
2273
|
-
...headingText ? { title: headingText } : {}
|
|
2274
|
-
};
|
|
2275
|
-
}
|
|
2276
|
-
return placeholders ? { items: ["Item 1", "Item 2", "Item 3"] } : null;
|
|
2277
|
-
}
|
|
2278
|
-
case "definitionCard":
|
|
2279
|
-
return { term: headingText, definition: bodyText3 };
|
|
2280
|
-
case "dateEvent":
|
|
2281
|
-
return { date: headingText, description: bodyText3 };
|
|
2282
|
-
case "dataTable": {
|
|
2283
|
-
const tableData = extractTableFromContents(contents);
|
|
2284
|
-
if (tableData) return { ...tableData, ...headingText ? { title: headingText } : {} };
|
|
2285
|
-
return placeholders ? { headers: ["Column"], rows: [["Data"]] } : null;
|
|
2286
|
-
}
|
|
2287
|
-
case "barChart":
|
|
2288
|
-
case "columnChart":
|
|
2289
|
-
case "pieChart":
|
|
2290
|
-
case "donutChart":
|
|
2291
|
-
case "lineChart":
|
|
2292
|
-
case "areaChart":
|
|
2293
|
-
case "scatterChart": {
|
|
2294
|
-
const tableData = extractTableFromContents(contents);
|
|
2295
|
-
if (tableData) {
|
|
2296
|
-
return {
|
|
2297
|
-
headers: tableData.headers,
|
|
2298
|
-
rows: tableData.rows,
|
|
2299
|
-
...headingText ? { title: headingText } : {}
|
|
2300
|
-
};
|
|
2301
|
-
}
|
|
2302
|
-
return placeholders && !bodyText3.trim() ? {
|
|
2303
|
-
headers: ["Quarter", "Revenue", "Costs"],
|
|
2304
|
-
rows: [
|
|
2305
|
-
["Q1", "40", "28"],
|
|
2306
|
-
["Q2", "55", "31"],
|
|
2307
|
-
["Q3", "48", "30"],
|
|
2308
|
-
["Q4", "62", "35"]
|
|
2309
|
-
],
|
|
2310
|
-
...headingText ? { title: headingText } : {}
|
|
2311
|
-
} : null;
|
|
2312
|
-
}
|
|
2313
|
-
case "imageWithCaption": {
|
|
2314
|
-
const img = extractFirstImage(contents);
|
|
2315
|
-
if (!img) return placeholders ? { caption: headingText } : null;
|
|
2316
|
-
return { imageSrc: img.src, imageAlt: img.alt || headingText, caption: headingText };
|
|
2317
|
-
}
|
|
2318
|
-
case "bigText": {
|
|
2319
|
-
const img = extractFirstImage(contents);
|
|
2320
|
-
return {
|
|
2321
|
-
title: headingText,
|
|
2322
|
-
...img ? { imageSrc: img.src, imageAlt: img.alt || headingText } : {}
|
|
2323
|
-
};
|
|
2324
|
-
}
|
|
2325
|
-
case "photoGrid": {
|
|
2326
|
-
const images = extractImages(contents, 4);
|
|
2327
|
-
if (images.length < 2) return placeholders ? {} : null;
|
|
2328
|
-
return {
|
|
2329
|
-
images: images.map((i) => ({ src: i.src, alt: i.alt })),
|
|
2330
|
-
caption: headingText
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
case "videoWithCaption": {
|
|
2334
|
-
const video = extractFirstEmbeddedVideo(contents);
|
|
2335
|
-
if (!video) {
|
|
2336
|
-
const hosted = extractHostedVideos(contents, 1)[0];
|
|
2337
|
-
if (hosted) {
|
|
2338
|
-
return {
|
|
2339
|
-
videoSrc: hosted.url,
|
|
2340
|
-
videoAlt: hosted.title || headingText,
|
|
2341
|
-
caption: headingText
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
return placeholders ? { caption: headingText } : null;
|
|
2345
|
-
}
|
|
2346
|
-
return {
|
|
2347
|
-
videoSrc: video.src,
|
|
2348
|
-
posterSrc: video.posterSrc,
|
|
2349
|
-
videoAlt: video.alt || headingText,
|
|
2350
|
-
caption: headingText
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
case "leftFeature":
|
|
2354
|
-
case "rightFeature": {
|
|
2355
|
-
const img = extractFirstImage(contents);
|
|
2356
|
-
if (!img && !placeholders) return null;
|
|
2357
|
-
return {
|
|
2358
|
-
imageSrc: img?.src ?? "",
|
|
2359
|
-
imageAlt: img?.alt || headingText,
|
|
2360
|
-
imageWidth: img?.width,
|
|
2361
|
-
imageHeight: img?.height,
|
|
2362
|
-
title: headingText,
|
|
2363
|
-
body: bodyText3
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
case "diagram": {
|
|
2367
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2368
|
-
const fence = fences.length === 1 && isEligibleAsciiFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2369
|
-
const detection = fence ? detectAsciiDiagram(fence.value, { explicit: isExplicitDiagramLang(fence.lang) }) : void 0;
|
|
2370
|
-
if (!detection?.isDiagram || !detection.diagram) return placeholders ? {} : null;
|
|
2371
|
-
const { nodes, edges } = asciiDiagramToTemplateData(detection.diagram);
|
|
2372
|
-
return { nodes, edges, ...headingText ? { title: headingText } : {} };
|
|
2373
|
-
}
|
|
2374
|
-
case "timeline": {
|
|
2375
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2376
|
-
const fence = fences.length === 1 && isEligibleAsciiTimelineFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2377
|
-
const detection = fence ? detectAsciiTimeline(fence.value, { explicit: true }) : void 0;
|
|
2378
|
-
if (!detection?.isTimeline || !detection.timeline) return placeholders ? {} : null;
|
|
2379
|
-
const { tracks, links } = asciiTimelineToTemplateData(detection.timeline);
|
|
2380
|
-
return { tracks, links, ...headingText ? { title: headingText } : {} };
|
|
2381
|
-
}
|
|
2382
|
-
case "tree": {
|
|
2383
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2384
|
-
const fence = fences.length === 1 && isEligibleTreeFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2385
|
-
if (fence) {
|
|
2386
|
-
const detection = detectTree(fence.value, { explicit: isExplicitTreeLang(fence.lang) });
|
|
2387
|
-
if (detection.isTree && detection.tree) {
|
|
2388
|
-
const { items } = treeToTemplateData(detection.tree);
|
|
2389
|
-
return { items, ...headingText ? { title: headingText } : {} };
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
const list = findFirstList(contents);
|
|
2393
|
-
if (list) {
|
|
2394
|
-
const { items } = treeToTemplateData(treeFromMarkdownList(list));
|
|
2395
|
-
if (items.length > 0) return { items, ...headingText ? { title: headingText } : {} };
|
|
2396
|
-
}
|
|
2397
|
-
return placeholders ? {} : null;
|
|
2398
|
-
}
|
|
2399
|
-
default:
|
|
2400
|
-
return placeholders ? {} : null;
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
1432
|
// src/doc/templates/tokens/resolveTokens.ts
|
|
2405
1433
|
var TOKEN_RE = /\{(\w+)(?::([^}|]+))?(?:\|([^}]*))?\}/g;
|
|
2406
1434
|
var ESCAPE_RE = /\{\{([^{}]*)\}\}/g;
|
|
@@ -2440,7 +1468,7 @@ function collectListItems(block) {
|
|
|
2440
1468
|
}
|
|
2441
1469
|
function getBlockBodyText(block) {
|
|
2442
1470
|
if (!block.contents) return "";
|
|
2443
|
-
return block.contents.map((node) => extractPlainText(node).trim()).filter((s) => s.length > 0).join(" ");
|
|
1471
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node).trim()).filter((s) => s.length > 0).join(" ");
|
|
2444
1472
|
}
|
|
2445
1473
|
function getChildrenSummary(block) {
|
|
2446
1474
|
if (!block.children || block.children.length === 0) return "";
|
|
@@ -7174,7 +6202,7 @@ function resolveSupplementalMediaLayout(layers, template, viewport, mediaCount,
|
|
|
7174
6202
|
const policy = getBlockMediaLayoutPolicy(template);
|
|
7175
6203
|
const shape = mediaShape(mediaCount, aspectRatios);
|
|
7176
6204
|
const alreadyOwnsMedia = layers.some(
|
|
7177
|
-
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "videoEmbed" || layer.type === "map" || layer.type === "mermaid"
|
|
6205
|
+
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "videoEmbed" || layer.type === "map" || layer.type === "mermaid" || layer.type === "svg"
|
|
7178
6206
|
);
|
|
7179
6207
|
if (!alreadyOwnsMedia && (policy?.nativeLayout === "feature-left" || policy?.nativeLayout === "feature-right")) {
|
|
7180
6208
|
const { position } = featureMediaSlot(
|
|
@@ -7301,15 +6329,19 @@ function unconsumedMedia(block) {
|
|
|
7301
6329
|
seen.add(image.src);
|
|
7302
6330
|
return true;
|
|
7303
6331
|
});
|
|
7304
|
-
const
|
|
7305
|
-
(node) => node.type === "code" && node
|
|
6332
|
+
const pictures = contents.filter(
|
|
6333
|
+
(node) => node.type === "code" && isPictureFence(node) && node.value.trim().length > 0
|
|
7306
6334
|
);
|
|
7307
6335
|
const aspectRatios = [
|
|
7308
6336
|
...images.map(
|
|
7309
6337
|
(image) => image.width && image.height ? image.width / image.height : void 0
|
|
7310
6338
|
),
|
|
7311
6339
|
...videos.map(() => 16 / 9),
|
|
7312
|
-
|
|
6340
|
+
// An SVG island knows its shape; a Mermaid diagram does not until drawn.
|
|
6341
|
+
...pictures.map((fence) => {
|
|
6342
|
+
const island = isSvgIslandLang(fence.lang) ? parseSvgIsland(fence.value) : null;
|
|
6343
|
+
return island?.ok ? island.island.aspectRatio : 16 / 9;
|
|
6344
|
+
})
|
|
7313
6345
|
];
|
|
7314
6346
|
return { count: aspectRatios.length, aspectRatios };
|
|
7315
6347
|
}
|
|
@@ -7854,7 +6886,7 @@ function estimateTextWidth(text, fontSize, charWidthEm = AVERAGE_CHAR_WIDTH_EM)
|
|
|
7854
6886
|
}
|
|
7855
6887
|
|
|
7856
6888
|
// src/doc/templates/timelineBlock.ts
|
|
7857
|
-
var
|
|
6889
|
+
var clamp01 = (value) => Math.max(0, Math.min(1, value));
|
|
7858
6890
|
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
7859
6891
|
function safeId(value) {
|
|
7860
6892
|
return value.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "item";
|
|
@@ -7881,7 +6913,7 @@ function coerceTracks(raw) {
|
|
|
7881
6913
|
events.push({
|
|
7882
6914
|
id: eventId,
|
|
7883
6915
|
label: event.label,
|
|
7884
|
-
position:
|
|
6916
|
+
position: clamp01(position),
|
|
7885
6917
|
...typeof event.description === "string" && event.description.trim() ? { description: event.description.trim() } : {},
|
|
7886
6918
|
...side ? { side } : {},
|
|
7887
6919
|
...descriptionSide ? { descriptionSide } : {},
|
|
@@ -8027,7 +7059,7 @@ function timelineBlock(input, context) {
|
|
|
8027
7059
|
const eventFontSize = Math.min(baseEventFontSize, 28 * minViewportScale);
|
|
8028
7060
|
const descriptionFontSize = Math.min(baseDescriptionFontSize, 20 * minViewportScale);
|
|
8029
7061
|
const trackMarkerRadii = tracks.map((track) => {
|
|
8030
|
-
const positions = track.events.map((event) =>
|
|
7062
|
+
const positions = track.events.map((event) => clamp01(event.position)).sort((a, b) => a - b);
|
|
8031
7063
|
let minGapPx = Number.POSITIVE_INFINITY;
|
|
8032
7064
|
for (let index = 1; index < positions.length; index++) {
|
|
8033
7065
|
minGapPx = Math.min(minGapPx, (positions[index] - positions[index - 1]) * axisWidth);
|
|
@@ -8106,7 +7138,7 @@ function timelineBlock(input, context) {
|
|
|
8106
7138
|
event,
|
|
8107
7139
|
trackIndex,
|
|
8108
7140
|
eventIndex,
|
|
8109
|
-
x: axisStartX + axisWidth *
|
|
7141
|
+
x: axisStartX + axisWidth * clamp01(event.position),
|
|
8110
7142
|
y
|
|
8111
7143
|
};
|
|
8112
7144
|
eventPoints.push(point);
|
|
@@ -8461,7 +7493,7 @@ function normalizeShapeKind(name) {
|
|
|
8461
7493
|
function isShapeName(name) {
|
|
8462
7494
|
return normalizeShapeKind(name) != null;
|
|
8463
7495
|
}
|
|
8464
|
-
var
|
|
7496
|
+
var DEFAULT_OPTS = {
|
|
8465
7497
|
gapX: 180,
|
|
8466
7498
|
gapY: 140,
|
|
8467
7499
|
gridPad: 80,
|
|
@@ -8481,7 +7513,7 @@ function defaultSize(kind) {
|
|
|
8481
7513
|
return DEFAULT_SIZE[kind] ?? FALLBACK_SIZE;
|
|
8482
7514
|
}
|
|
8483
7515
|
function computeDrawingLayout(children, options = {}) {
|
|
8484
|
-
const opts = { ...
|
|
7516
|
+
const opts = { ...DEFAULT_OPTS, ...options };
|
|
8485
7517
|
const warnings = [];
|
|
8486
7518
|
const entries = [];
|
|
8487
7519
|
const unpositioned = [];
|
|
@@ -8658,7 +7690,7 @@ function connectorSemantics(params, kind) {
|
|
|
8658
7690
|
}
|
|
8659
7691
|
function bodyText(block) {
|
|
8660
7692
|
if (!block.contents || block.contents.length === 0) return "";
|
|
8661
|
-
return block.contents.map((node) => extractPlainText(node)).join(" ").trim();
|
|
7693
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join(" ").trim();
|
|
8662
7694
|
}
|
|
8663
7695
|
|
|
8664
7696
|
// src/doc/templates/drawingBlock.ts
|
|
@@ -9041,7 +8073,7 @@ function numParam2(params, key) {
|
|
|
9041
8073
|
}
|
|
9042
8074
|
function bodyText2(block) {
|
|
9043
8075
|
if (!block.contents || block.contents.length === 0) return "";
|
|
9044
|
-
return block.contents.map((node) => extractPlainText(node)).join("\n").trim();
|
|
8076
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
9045
8077
|
}
|
|
9046
8078
|
function round3(n) {
|
|
9047
8079
|
return Math.round(n * 100) / 100;
|
|
@@ -9996,7 +9028,7 @@ function fallbackBlockLayers(block, context, notice) {
|
|
|
9996
9028
|
}
|
|
9997
9029
|
function extractBodyText(block) {
|
|
9998
9030
|
if (!block.contents || block.contents.length === 0) return "";
|
|
9999
|
-
const text = block.contents.map((node) => extractPlainText(node)).join("\n").trim();
|
|
9031
|
+
const text = block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
10000
9032
|
if (text.length <= MAX_BODY_CHARS) return text;
|
|
10001
9033
|
return `${text.slice(0, MAX_BODY_CHARS).trimEnd()}\u2026`;
|
|
10002
9034
|
}
|
|
@@ -10119,6 +9151,13 @@ function mermaidSources(block) {
|
|
|
10119
9151
|
(node) => node.type === "code" && node.lang?.trim().toLowerCase() === "mermaid"
|
|
10120
9152
|
).map((node) => node.value).filter((source) => source.trim().length > 0);
|
|
10121
9153
|
}
|
|
9154
|
+
function svgIslandSources(block) {
|
|
9155
|
+
return (block.contents ?? []).filter((node) => node.type === "code" && isSvgIslandLang(node.lang)).map((node) => node.value).filter((source) => source.trim().length > 0);
|
|
9156
|
+
}
|
|
9157
|
+
function svgIslandAspectRatio(source) {
|
|
9158
|
+
const result = parseSvgIsland(source);
|
|
9159
|
+
return result.ok ? result.island.aspectRatio : 16 / 9;
|
|
9160
|
+
}
|
|
10122
9161
|
function embeddedSpatialMedia(block) {
|
|
10123
9162
|
const canonicalTemplate = block.template ? resolveTemplateName(block.template) : void 0;
|
|
10124
9163
|
const fences = (block.contents ?? []).filter(
|
|
@@ -10178,6 +9217,9 @@ function collectRichMediaItems(layers, block) {
|
|
|
10178
9217
|
const existingMermaid = new Set(
|
|
10179
9218
|
layers.filter((layer) => layer.type === "mermaid").map((layer) => layer.content.source)
|
|
10180
9219
|
);
|
|
9220
|
+
const existingSvg = new Set(
|
|
9221
|
+
layers.filter((layer) => layer.type === "svg").map((layer) => layer.content.source)
|
|
9222
|
+
);
|
|
10181
9223
|
const existingEmbeds = new Set(
|
|
10182
9224
|
layers.filter((layer) => layer.type === "videoEmbed").map((layer) => parseVideoEmbedUrl(layer.content.url)?.watchUrl ?? layer.content.url)
|
|
10183
9225
|
);
|
|
@@ -10191,6 +9233,7 @@ function collectRichMediaItems(layers, block) {
|
|
|
10191
9233
|
return true;
|
|
10192
9234
|
});
|
|
10193
9235
|
const sources = mermaidSources(block).filter((source) => !existingMermaid.has(source));
|
|
9236
|
+
const islands = svgIslandSources(block).filter((source) => !existingSvg.has(source));
|
|
10194
9237
|
const hosted = extractHostedVideos(block.contents).filter(
|
|
10195
9238
|
(video) => !existingEmbeds.has(video.url)
|
|
10196
9239
|
);
|
|
@@ -10218,6 +9261,11 @@ function collectRichMediaItems(layers, block) {
|
|
|
10218
9261
|
aspectRatio: video.aspectRatio
|
|
10219
9262
|
})),
|
|
10220
9263
|
...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 })),
|
|
9264
|
+
...islands.map((source) => ({
|
|
9265
|
+
kind: "svg",
|
|
9266
|
+
source,
|
|
9267
|
+
aspectRatio: svgIslandAspectRatio(source)
|
|
9268
|
+
})),
|
|
10221
9269
|
...embeddedSpatialMedia(block)
|
|
10222
9270
|
];
|
|
10223
9271
|
}
|
|
@@ -10314,6 +9362,16 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
|
|
|
10314
9362
|
}
|
|
10315
9363
|
];
|
|
10316
9364
|
}
|
|
9365
|
+
if (item.kind === "svg") {
|
|
9366
|
+
return [
|
|
9367
|
+
{
|
|
9368
|
+
id: `${block.id}-svg-${kindIndex}`,
|
|
9369
|
+
type: "svg",
|
|
9370
|
+
position,
|
|
9371
|
+
content: { source: item.source, color: theme.colors.text }
|
|
9372
|
+
}
|
|
9373
|
+
];
|
|
9374
|
+
}
|
|
10317
9375
|
const localViewport = {
|
|
10318
9376
|
width: position.width,
|
|
10319
9377
|
height: position.height,
|
|
@@ -10385,6 +9443,7 @@ function appendRichContentLayers(layers, block, theme, viewport, mediaAspectRati
|
|
|
10385
9443
|
video: 0,
|
|
10386
9444
|
videoEmbed: 0,
|
|
10387
9445
|
mermaid: 0,
|
|
9446
|
+
svg: 0,
|
|
10388
9447
|
spatial: 0
|
|
10389
9448
|
};
|
|
10390
9449
|
const mediaLayers = items.flatMap((item, index) => {
|
|
@@ -12403,7 +11462,7 @@ function getBlockBodyText2(block) {
|
|
|
12403
11462
|
if (!block.contents || block.contents.length === 0) return "";
|
|
12404
11463
|
const tmpl = resolveTemplateName(block.template ?? "");
|
|
12405
11464
|
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;
|
|
12406
|
-
return block.contents.filter((node) => !(consumedFence && node.type === "code")).filter((node) =>
|
|
11465
|
+
return block.contents.filter((node) => !(consumedFence && node.type === "code")).filter((node) => !isMaterializedMediaNode(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
12407
11466
|
}
|
|
12408
11467
|
var MAX_PHRASE_WORDS = 12;
|
|
12409
11468
|
function splitIntoSentences(text) {
|
|
@@ -13000,34 +12059,6 @@ export {
|
|
|
13000
12059
|
sectionHeader,
|
|
13001
12060
|
fitBigTextSize,
|
|
13002
12061
|
bigText,
|
|
13003
|
-
computeDiagramLayout,
|
|
13004
|
-
ASCII_CHAR_W,
|
|
13005
|
-
ASCII_CHAR_H,
|
|
13006
|
-
asciiCellToCanvas,
|
|
13007
|
-
canvasToAsciiCell,
|
|
13008
|
-
asciiDiagramToTemplateData,
|
|
13009
|
-
asciiDiagramFromTemplateData,
|
|
13010
|
-
asciiDiagramFromBlocks,
|
|
13011
|
-
asciiTimelineToTemplateData,
|
|
13012
|
-
treeToTemplateData,
|
|
13013
|
-
treeFromTemplateData,
|
|
13014
|
-
treeFromMarkdownList,
|
|
13015
|
-
findFirstList,
|
|
13016
|
-
extractHostedVideos,
|
|
13017
|
-
isMaterializedMediaNode,
|
|
13018
|
-
extractBodyPlainText,
|
|
13019
|
-
extractListItems,
|
|
13020
|
-
renderMarkdownBlocksHtml,
|
|
13021
|
-
markdownBlockSeparatorLines,
|
|
13022
|
-
extractRichListItems,
|
|
13023
|
-
extractImages,
|
|
13024
|
-
extractFirstImage,
|
|
13025
|
-
extractEmbeddedVideos,
|
|
13026
|
-
extractFirstEmbeddedVideo,
|
|
13027
|
-
extractTableFromContents,
|
|
13028
|
-
extractBlockquoteText,
|
|
13029
|
-
autoTemplatePreservesContent,
|
|
13030
|
-
deriveTemplateInputs,
|
|
13031
12062
|
contentBlock,
|
|
13032
12063
|
parseStatNumber,
|
|
13033
12064
|
countUpSpecFor,
|