@bendyline/squisq 2.12.1 → 2.12.3
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-CHD4fxfm.d.ts → Doc-BkSiCJnM.d.ts} +28 -2
- package/dist/{ImageEditDoc-DSUM5MiA.d.ts → ImageEditDoc-UjQv8q3j.d.ts} +1 -1
- 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-ISH4HDH7.js → chunk-J6AGNXFO.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-B2BQFF5B.js → chunk-SK5TQSYN.js} +20 -20
- package/dist/{chunk-RKCARBWM.js → chunk-VTVBSZXB.js} +78 -1048
- package/dist/chunk-Y25EJEZN.js +178 -0
- package/dist/{chunk-LN72K54P.js → chunk-ZBPWNH4L.js} +10 -9
- package/dist/{chunk-VTG7PNOL.js → chunk-ZKDJHO6Y.js} +3 -8
- package/dist/doc/index.d.ts +17 -9
- 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-Dx1Sz0vK.d.ts → materializePageSection-DEXohgVt.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-DLmZ1Ct8.d.ts → themeLibrary-BrBc0mrP.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
|
|
@@ -710,6 +724,7 @@ function getTemplateHint(context, templateName, key, fallback) {
|
|
|
710
724
|
|
|
711
725
|
// src/doc/templates/captionUtils.ts
|
|
712
726
|
var TRAILING_PUNCT_RE = /[.;:!?]+$/;
|
|
727
|
+
var CAPTION_BASELINE_SHIFT_EM = 0.055;
|
|
713
728
|
function cleanCaption(text) {
|
|
714
729
|
return text.replace(TRAILING_PUNCT_RE, "").trim();
|
|
715
730
|
}
|
|
@@ -1415,998 +1430,6 @@ function bigText(input, context) {
|
|
|
1415
1430
|
return layers;
|
|
1416
1431
|
}
|
|
1417
1432
|
|
|
1418
|
-
// src/doc/templates/diagramLayout.ts
|
|
1419
|
-
var DEFAULT_OPTS = {
|
|
1420
|
-
gapX: 260,
|
|
1421
|
-
gapY: 140,
|
|
1422
|
-
gridPad: 80,
|
|
1423
|
-
gridOriginX: 80,
|
|
1424
|
-
gridOriginY: 80
|
|
1425
|
-
};
|
|
1426
|
-
function computeDiagramLayout(children, options = {}) {
|
|
1427
|
-
const opts = { ...DEFAULT_OPTS, ...options };
|
|
1428
|
-
const warnings = [];
|
|
1429
|
-
const entries = [];
|
|
1430
|
-
const unpositionedIdx = [];
|
|
1431
|
-
for (let i = 0; i < children.length; i++) {
|
|
1432
|
-
const c = children[i];
|
|
1433
|
-
const hasX = typeof c.x === "number";
|
|
1434
|
-
const hasY = typeof c.y === "number";
|
|
1435
|
-
if (hasX && hasY) {
|
|
1436
|
-
entries.push({ child: c, pinned: true, x: c.x, y: c.y });
|
|
1437
|
-
} else {
|
|
1438
|
-
entries.push({ child: c, pinned: false, x: 0, y: 0 });
|
|
1439
|
-
unpositionedIdx.push(i);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
if (unpositionedIdx.length > 0) {
|
|
1443
|
-
const cols = Math.max(1, Math.ceil(Math.sqrt(unpositionedIdx.length)));
|
|
1444
|
-
let baseX = opts.gridOriginX;
|
|
1445
|
-
let baseY = opts.gridOriginY;
|
|
1446
|
-
const pinned = entries.filter((e) => e.pinned);
|
|
1447
|
-
if (pinned.length > 0) {
|
|
1448
|
-
const minX = Math.min(...pinned.map((e) => e.x));
|
|
1449
|
-
const maxY = Math.max(...pinned.map((e) => e.y));
|
|
1450
|
-
baseX = minX;
|
|
1451
|
-
baseY = maxY + opts.gridPad + opts.gapY;
|
|
1452
|
-
}
|
|
1453
|
-
unpositionedIdx.forEach((entryIdx, k) => {
|
|
1454
|
-
const col = k % cols;
|
|
1455
|
-
const row = Math.floor(k / cols);
|
|
1456
|
-
entries[entryIdx].x = baseX + col * opts.gapX;
|
|
1457
|
-
entries[entryIdx].y = baseY + row * opts.gapY;
|
|
1458
|
-
});
|
|
1459
|
-
}
|
|
1460
|
-
const nodes = entries.map((e) => ({
|
|
1461
|
-
id: e.child.id,
|
|
1462
|
-
label: e.child.title ?? e.child.id,
|
|
1463
|
-
x: e.x,
|
|
1464
|
-
y: e.y,
|
|
1465
|
-
pinned: e.pinned,
|
|
1466
|
-
...e.child.classes && e.child.classes.length > 0 ? { classes: e.child.classes } : {}
|
|
1467
|
-
}));
|
|
1468
|
-
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
1469
|
-
const edges = [];
|
|
1470
|
-
const seenEdgeIds = /* @__PURE__ */ new Set();
|
|
1471
|
-
for (const child of children) {
|
|
1472
|
-
if (!child.connectsTo) continue;
|
|
1473
|
-
for (const conn of child.connectsTo) {
|
|
1474
|
-
if (!nodeIds.has(conn.target)) {
|
|
1475
|
-
warnings.push(`Edge from "${child.id}" to "${conn.target}" dropped: no such sibling node`);
|
|
1476
|
-
continue;
|
|
1477
|
-
}
|
|
1478
|
-
const edgeId = makeEdgeId(child.id, conn);
|
|
1479
|
-
if (seenEdgeIds.has(edgeId)) continue;
|
|
1480
|
-
seenEdgeIds.add(edgeId);
|
|
1481
|
-
edges.push({
|
|
1482
|
-
id: edgeId,
|
|
1483
|
-
source: child.id,
|
|
1484
|
-
target: conn.target,
|
|
1485
|
-
...conn.type ? { type: conn.type } : {}
|
|
1486
|
-
});
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
return { nodes, edges, warnings };
|
|
1490
|
-
}
|
|
1491
|
-
function makeEdgeId(source, conn) {
|
|
1492
|
-
return conn.type ? `${source}->${conn.target}:${conn.type}` : `${source}->${conn.target}`;
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
// src/doc/asciiDiagram/types.ts
|
|
1496
|
-
var ASCII_CHAR_W = 14;
|
|
1497
|
-
var ASCII_CHAR_H = 28;
|
|
1498
|
-
|
|
1499
|
-
// src/doc/asciiDiagram/mapping.ts
|
|
1500
|
-
function asciiCellToCanvas(col, row) {
|
|
1501
|
-
return { x: col * ASCII_CHAR_W, y: row * ASCII_CHAR_H };
|
|
1502
|
-
}
|
|
1503
|
-
function canvasToAsciiCell(x, y) {
|
|
1504
|
-
return {
|
|
1505
|
-
col: Math.max(0, Math.round(x / ASCII_CHAR_W)),
|
|
1506
|
-
row: Math.max(0, Math.round(y / ASCII_CHAR_H))
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
function asciiDiagramToTemplateData(diagram) {
|
|
1510
|
-
const nodes = diagram.nodes.map((n) => ({
|
|
1511
|
-
id: n.id,
|
|
1512
|
-
label: n.label,
|
|
1513
|
-
...asciiCellToCanvas(n.col, n.row),
|
|
1514
|
-
w: n.wCols * ASCII_CHAR_W,
|
|
1515
|
-
h: n.hRows * ASCII_CHAR_H,
|
|
1516
|
-
...n.containerId ? { container: n.containerId } : {}
|
|
1517
|
-
}));
|
|
1518
|
-
const anchors = inferEdgeAnchors(nodes, diagram.edges);
|
|
1519
|
-
const edges = diagram.edges.map((e, index) => ({
|
|
1520
|
-
source: e.source,
|
|
1521
|
-
target: e.target,
|
|
1522
|
-
...e.label ? { label: e.label } : {},
|
|
1523
|
-
...e.directed ? {} : { directed: false },
|
|
1524
|
-
...anchors[index],
|
|
1525
|
-
routing: "orthogonal"
|
|
1526
|
-
}));
|
|
1527
|
-
return { nodes, edges };
|
|
1528
|
-
}
|
|
1529
|
-
function inferEdgeAnchors(nodes, edges) {
|
|
1530
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1531
|
-
const pairs = edges.map((edge) => {
|
|
1532
|
-
const source = nodeById.get(edge.source);
|
|
1533
|
-
const target = nodeById.get(edge.target);
|
|
1534
|
-
if (!source || !target) {
|
|
1535
|
-
return {
|
|
1536
|
-
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1537
|
-
targetAnchor: { side: "left", offset: 0.5 }
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1541
|
-
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1542
|
-
const targetW = target.w ?? ASCII_CHAR_W * 12;
|
|
1543
|
-
const targetH = target.h ?? ASCII_CHAR_H * 3;
|
|
1544
|
-
const dx = target.x + targetW / 2 - (source.x + sourceW / 2);
|
|
1545
|
-
const dy = target.y + targetH / 2 - (source.y + sourceH / 2);
|
|
1546
|
-
const horizontalDistance = Math.abs(dx) / Math.max(1, (sourceW + targetW) / 2);
|
|
1547
|
-
const verticalDistance = Math.abs(dy) / Math.max(1, (sourceH + targetH) / 2);
|
|
1548
|
-
if (verticalDistance >= horizontalDistance) {
|
|
1549
|
-
return dy >= 0 ? {
|
|
1550
|
-
sourceAnchor: { side: "bottom", offset: 0.5 },
|
|
1551
|
-
targetAnchor: { side: "top", offset: 0.5 }
|
|
1552
|
-
} : {
|
|
1553
|
-
sourceAnchor: { side: "top", offset: 0.5 },
|
|
1554
|
-
targetAnchor: { side: "bottom", offset: 0.5 }
|
|
1555
|
-
};
|
|
1556
|
-
}
|
|
1557
|
-
return dx >= 0 ? {
|
|
1558
|
-
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1559
|
-
targetAnchor: { side: "left", offset: 0.5 }
|
|
1560
|
-
} : {
|
|
1561
|
-
sourceAnchor: { side: "left", offset: 0.5 },
|
|
1562
|
-
targetAnchor: { side: "right", offset: 0.5 }
|
|
1563
|
-
};
|
|
1564
|
-
});
|
|
1565
|
-
distributeOffsets(pairs, edges, nodes, "source");
|
|
1566
|
-
distributeOffsets(pairs, edges, nodes, "target");
|
|
1567
|
-
coalesceAlignedFanOutAnchors(pairs, edges, nodes);
|
|
1568
|
-
return pairs;
|
|
1569
|
-
}
|
|
1570
|
-
function coalesceAlignedFanOutAnchors(pairs, edges, nodes) {
|
|
1571
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1572
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1573
|
-
edges.forEach((edge, index) => {
|
|
1574
|
-
const group = groups.get(edge.source) ?? [];
|
|
1575
|
-
group.push(index);
|
|
1576
|
-
groups.set(edge.source, group);
|
|
1577
|
-
});
|
|
1578
|
-
for (const [sourceId, indexes] of groups) {
|
|
1579
|
-
if (indexes.length < 2) continue;
|
|
1580
|
-
const source = nodeById.get(sourceId);
|
|
1581
|
-
const targets = indexes.map((index) => nodeById.get(edges[index].target)).filter((node) => node !== void 0);
|
|
1582
|
-
if (!source || targets.length !== indexes.length) continue;
|
|
1583
|
-
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1584
|
-
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1585
|
-
const sourceRight = source.x + sourceW;
|
|
1586
|
-
const sourceBottom = source.y + sourceH;
|
|
1587
|
-
const targetRight = (target) => target.x + (target.w ?? ASCII_CHAR_W * 12);
|
|
1588
|
-
const targetBottom = (target) => target.y + (target.h ?? ASCII_CHAR_H * 3);
|
|
1589
|
-
let sourceSide;
|
|
1590
|
-
let targetSide;
|
|
1591
|
-
if (targets.every((target) => target.y >= sourceBottom)) {
|
|
1592
|
-
sourceSide = "bottom";
|
|
1593
|
-
targetSide = "top";
|
|
1594
|
-
} else if (targets.every((target) => targetBottom(target) <= source.y)) {
|
|
1595
|
-
sourceSide = "top";
|
|
1596
|
-
targetSide = "bottom";
|
|
1597
|
-
} else if (targets.every((target) => target.x >= sourceRight)) {
|
|
1598
|
-
sourceSide = "right";
|
|
1599
|
-
targetSide = "left";
|
|
1600
|
-
} else if (targets.every((target) => targetRight(target) <= source.x)) {
|
|
1601
|
-
sourceSide = "left";
|
|
1602
|
-
targetSide = "right";
|
|
1603
|
-
}
|
|
1604
|
-
if (!sourceSide || !targetSide) continue;
|
|
1605
|
-
for (const index of indexes) {
|
|
1606
|
-
pairs[index] = {
|
|
1607
|
-
sourceAnchor: { side: sourceSide, offset: 0.5 },
|
|
1608
|
-
targetAnchor: { side: targetSide, offset: 0.5 }
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
function distributeOffsets(pairs, edges, nodes, endpoint) {
|
|
1614
|
-
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1615
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1616
|
-
for (let index = 0; index < edges.length; index++) {
|
|
1617
|
-
const edge = edges[index];
|
|
1618
|
-
const anchor = endpoint === "source" ? pairs[index].sourceAnchor : pairs[index].targetAnchor;
|
|
1619
|
-
const nodeId = endpoint === "source" ? edge.source : edge.target;
|
|
1620
|
-
const key = `${nodeId}:${anchor.side}`;
|
|
1621
|
-
const group = groups.get(key) ?? [];
|
|
1622
|
-
group.push(index);
|
|
1623
|
-
groups.set(key, group);
|
|
1624
|
-
}
|
|
1625
|
-
for (const indexes of groups.values()) {
|
|
1626
|
-
if (indexes.length < 2) continue;
|
|
1627
|
-
const side = endpoint === "source" ? pairs[indexes[0]].sourceAnchor.side : pairs[indexes[0]].targetAnchor.side;
|
|
1628
|
-
indexes.sort((a, b) => {
|
|
1629
|
-
const oppositeA = nodeById.get(endpoint === "source" ? edges[a].target : edges[a].source);
|
|
1630
|
-
const oppositeB = nodeById.get(endpoint === "source" ? edges[b].target : edges[b].source);
|
|
1631
|
-
const axisA = side === "top" || side === "bottom" ? oppositeA?.x ?? 0 : oppositeA?.y ?? 0;
|
|
1632
|
-
const axisB = side === "top" || side === "bottom" ? oppositeB?.x ?? 0 : oppositeB?.y ?? 0;
|
|
1633
|
-
return axisA - axisB || a - b;
|
|
1634
|
-
});
|
|
1635
|
-
indexes.forEach((edgeIndex, rank) => {
|
|
1636
|
-
const anchor = endpoint === "source" ? pairs[edgeIndex].sourceAnchor : pairs[edgeIndex].targetAnchor;
|
|
1637
|
-
anchor.offset = (rank + 1) / (indexes.length + 1);
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
function asciiDiagramFromTemplateData(nodes, edges, options = {}) {
|
|
1642
|
-
const ids = new Set(nodes.map((n) => n.id));
|
|
1643
|
-
const outNodes = nodes.map((n) => {
|
|
1644
|
-
const { col, row } = canvasToAsciiCell(n.x, n.y);
|
|
1645
|
-
const labelLines = n.label.length > 0 ? n.label.split("\n") : [];
|
|
1646
|
-
const innerW = labelLines.reduce((w, l) => Math.max(w, l.length), 0);
|
|
1647
|
-
const wCols = typeof n.w === "number" ? Math.max(3, Math.round(n.w / ASCII_CHAR_W)) : innerW + 4;
|
|
1648
|
-
const hRows = typeof n.h === "number" ? Math.max(3, Math.round(n.h / ASCII_CHAR_H)) : labelLines.length + 2;
|
|
1649
|
-
return {
|
|
1650
|
-
id: n.id,
|
|
1651
|
-
label: n.label,
|
|
1652
|
-
col,
|
|
1653
|
-
row,
|
|
1654
|
-
wCols: Math.max(wCols, 3),
|
|
1655
|
-
hRows: Math.max(hRows, 3),
|
|
1656
|
-
...n.container && ids.has(n.container) && n.container !== n.id ? { containerId: n.container } : {}
|
|
1657
|
-
};
|
|
1658
|
-
});
|
|
1659
|
-
return {
|
|
1660
|
-
nodes: outNodes,
|
|
1661
|
-
edges: edges.filter((e) => ids.has(e.source) && ids.has(e.target)).map((e) => ({
|
|
1662
|
-
source: e.source,
|
|
1663
|
-
target: e.target,
|
|
1664
|
-
...e.label ? { label: e.label } : {},
|
|
1665
|
-
directed: e.directed !== false
|
|
1666
|
-
})),
|
|
1667
|
-
style: options.style ?? "unicode",
|
|
1668
|
-
warnings: []
|
|
1669
|
-
};
|
|
1670
|
-
}
|
|
1671
|
-
function asciiDiagramFromBlocks(children, options = {}) {
|
|
1672
|
-
const layout = computeDiagramLayout(children);
|
|
1673
|
-
const nodes = layout.nodes.map((n) => ({
|
|
1674
|
-
id: n.id,
|
|
1675
|
-
label: n.label,
|
|
1676
|
-
x: n.x,
|
|
1677
|
-
y: n.y
|
|
1678
|
-
}));
|
|
1679
|
-
const edges = layout.edges.map((e) => ({
|
|
1680
|
-
source: e.source,
|
|
1681
|
-
target: e.target,
|
|
1682
|
-
...e.type ? { label: e.type } : {}
|
|
1683
|
-
}));
|
|
1684
|
-
const diagram = asciiDiagramFromTemplateData(nodes, edges, options);
|
|
1685
|
-
diagram.warnings.push(...layout.warnings);
|
|
1686
|
-
return diagram;
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
// src/doc/asciiTimeline/mapping.ts
|
|
1690
|
-
function asciiTimelineToTemplateData(timeline) {
|
|
1691
|
-
const globalStart = timeline.tracks.length > 0 ? Math.min(...timeline.tracks.map((track) => track.startColumn)) : 0;
|
|
1692
|
-
const globalEnd = timeline.tracks.length > 0 ? Math.max(...timeline.tracks.map((track) => track.endColumn)) : 1;
|
|
1693
|
-
const span = Math.max(1, globalEnd - globalStart);
|
|
1694
|
-
const tracks = timeline.tracks.map((track) => ({
|
|
1695
|
-
id: track.id,
|
|
1696
|
-
...track.label ? { label: track.label } : {},
|
|
1697
|
-
...track.endLabel ? { endLabel: track.endLabel } : {},
|
|
1698
|
-
events: track.events.map((event) => ({
|
|
1699
|
-
id: event.id,
|
|
1700
|
-
label: event.label,
|
|
1701
|
-
...event.description ? { description: event.description } : {},
|
|
1702
|
-
position: clamp01((event.column - globalStart) / span),
|
|
1703
|
-
...event.side ? { side: event.side } : {},
|
|
1704
|
-
...event.descriptionSide ? { descriptionSide: event.descriptionSide } : {},
|
|
1705
|
-
...event.callout === false ? { callout: false } : {},
|
|
1706
|
-
...event.marker ? { marker: event.marker } : {}
|
|
1707
|
-
}))
|
|
1708
|
-
}));
|
|
1709
|
-
const links = timeline.links.map((link) => ({ ...link }));
|
|
1710
|
-
return { tracks, links };
|
|
1711
|
-
}
|
|
1712
|
-
function clamp01(value) {
|
|
1713
|
-
return Math.max(0, Math.min(1, value));
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
// src/doc/treeview/mapping.ts
|
|
1717
|
-
function treeToTemplateData(tree) {
|
|
1718
|
-
return { items: tree.roots.map(toItem) };
|
|
1719
|
-
}
|
|
1720
|
-
function toItem(node) {
|
|
1721
|
-
return {
|
|
1722
|
-
id: node.id,
|
|
1723
|
-
label: node.label,
|
|
1724
|
-
...node.isDir ? { isDir: true } : {},
|
|
1725
|
-
...node.comment ? { comment: node.comment } : {},
|
|
1726
|
-
children: node.children.map(toItem)
|
|
1727
|
-
};
|
|
1728
|
-
}
|
|
1729
|
-
function treeFromTemplateData(items, options = {}) {
|
|
1730
|
-
const roots = items.map(fromItem);
|
|
1731
|
-
return { roots, style: options.style ?? "unicode", warnings: [] };
|
|
1732
|
-
}
|
|
1733
|
-
function fromItem(item) {
|
|
1734
|
-
const children = Array.isArray(item.children) ? item.children.map(fromItem) : [];
|
|
1735
|
-
return {
|
|
1736
|
-
id: typeof item.id === "string" && item.id ? item.id : "node",
|
|
1737
|
-
label: typeof item.label === "string" ? item.label : "",
|
|
1738
|
-
children,
|
|
1739
|
-
isDir: item.isDir === true || children.length > 0,
|
|
1740
|
-
...item.comment ? { comment: item.comment } : {}
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
function treeFromMarkdownList(list) {
|
|
1744
|
-
const usedIds = /* @__PURE__ */ new Map();
|
|
1745
|
-
const slugify2 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "node";
|
|
1746
|
-
const idFor = (label) => {
|
|
1747
|
-
const base = slugify2(label.replace(/\/$/, ""));
|
|
1748
|
-
const count = usedIds.get(base) ?? 0;
|
|
1749
|
-
usedIds.set(base, count + 1);
|
|
1750
|
-
return count === 0 ? base : `${base}-${count + 1}`;
|
|
1751
|
-
};
|
|
1752
|
-
const itemToNode = (item) => {
|
|
1753
|
-
const labelNode = item.children.find((c) => c.type !== "list");
|
|
1754
|
-
const label = labelNode ? extractPlainText(labelNode).trim() : "";
|
|
1755
|
-
const childLists = item.children.filter((c) => c.type === "list");
|
|
1756
|
-
const children = [];
|
|
1757
|
-
for (const cl of childLists) for (const ci of cl.children) children.push(itemToNode(ci));
|
|
1758
|
-
return {
|
|
1759
|
-
id: idFor(label),
|
|
1760
|
-
label,
|
|
1761
|
-
children,
|
|
1762
|
-
isDir: label.endsWith("/") || children.length > 0
|
|
1763
|
-
};
|
|
1764
|
-
};
|
|
1765
|
-
return { roots: list.children.map(itemToNode), style: "unicode", warnings: [] };
|
|
1766
|
-
}
|
|
1767
|
-
function findFirstList(contents) {
|
|
1768
|
-
return contents?.find((n) => n.type === "list");
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
// src/doc/templateInputs.ts
|
|
1772
|
-
var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
|
|
1773
|
-
function extractHostedVideos(contents, limit = Infinity) {
|
|
1774
|
-
const found = [];
|
|
1775
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1776
|
-
for (const node of contents ?? []) {
|
|
1777
|
-
if (found.length >= limit) break;
|
|
1778
|
-
const video = findBlockVideoEmbed(node);
|
|
1779
|
-
if (!video || seen.has(video.embed.watchUrl)) continue;
|
|
1780
|
-
seen.add(video.embed.watchUrl);
|
|
1781
|
-
found.push({
|
|
1782
|
-
url: video.embed.watchUrl,
|
|
1783
|
-
title: video.title,
|
|
1784
|
-
aspectRatio: video.embed.aspectRatio
|
|
1785
|
-
});
|
|
1786
|
-
}
|
|
1787
|
-
return found;
|
|
1788
|
-
}
|
|
1789
|
-
function isMaterializedMediaNode(node) {
|
|
1790
|
-
if (node.type === "code") return node.lang?.trim().toLowerCase() === "mermaid";
|
|
1791
|
-
return findBlockVideoEmbed(node) !== null;
|
|
1792
|
-
}
|
|
1793
|
-
function extractBodyPlainText(contents) {
|
|
1794
|
-
if (!contents || contents.length === 0) return "";
|
|
1795
|
-
return contents.filter((node) => !isMaterializedMediaNode(node)).map((n) => extractPlainText(n)).join("\n").trim();
|
|
1796
|
-
}
|
|
1797
|
-
function extractListItems(contents) {
|
|
1798
|
-
return extractRichListItems(contents).map((item) => item.text);
|
|
1799
|
-
}
|
|
1800
|
-
function escapeInlineHtml(value) {
|
|
1801
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1802
|
-
}
|
|
1803
|
-
function renderInlineHtml(nodes) {
|
|
1804
|
-
return nodes.map((node) => {
|
|
1805
|
-
switch (node.type) {
|
|
1806
|
-
case "text":
|
|
1807
|
-
return escapeInlineHtml(node.value);
|
|
1808
|
-
case "emphasis":
|
|
1809
|
-
return `<em>${renderInlineHtml(node.children)}</em>`;
|
|
1810
|
-
case "strong":
|
|
1811
|
-
return `<strong>${renderInlineHtml(node.children)}</strong>`;
|
|
1812
|
-
case "delete":
|
|
1813
|
-
return `<del>${renderInlineHtml(node.children)}</del>`;
|
|
1814
|
-
case "superscript":
|
|
1815
|
-
return `<sup>${renderInlineHtml(node.children)}</sup>`;
|
|
1816
|
-
case "subscript":
|
|
1817
|
-
return `<sub>${renderInlineHtml(node.children)}</sub>`;
|
|
1818
|
-
case "inlineCode":
|
|
1819
|
-
return `<code>${escapeInlineHtml(node.value)}</code>`;
|
|
1820
|
-
case "link": {
|
|
1821
|
-
const label = renderInlineHtml(node.children);
|
|
1822
|
-
const href = sanitizeUrl(node.url, "link");
|
|
1823
|
-
if (!href) return label;
|
|
1824
|
-
const title = node.title ? ` title="${escapeInlineHtml(node.title)}"` : "";
|
|
1825
|
-
return `<a href="${escapeInlineHtml(href)}"${title}>${label}</a>`;
|
|
1826
|
-
}
|
|
1827
|
-
case "break":
|
|
1828
|
-
return "<br>";
|
|
1829
|
-
case "image":
|
|
1830
|
-
return escapeInlineHtml(node.alt ?? "");
|
|
1831
|
-
case "inlineMath":
|
|
1832
|
-
return escapeInlineHtml(`$${node.value}$`);
|
|
1833
|
-
case "footnoteReference":
|
|
1834
|
-
return escapeInlineHtml(`[^${node.label ?? node.identifier}]`);
|
|
1835
|
-
case "linkReference":
|
|
1836
|
-
case "textDirective":
|
|
1837
|
-
return renderInlineHtml(node.children);
|
|
1838
|
-
case "imageReference":
|
|
1839
|
-
return escapeInlineHtml(node.alt ?? "");
|
|
1840
|
-
case "mention":
|
|
1841
|
-
return escapeInlineHtml(`@${node.displayName}`);
|
|
1842
|
-
case "inlineIcon":
|
|
1843
|
-
return `<i class="fa-${node.family} fa-${node.name}" aria-hidden="true"></i>`;
|
|
1844
|
-
case "htmlInline":
|
|
1845
|
-
return escapeInlineHtml(node.rawHtml);
|
|
1846
|
-
}
|
|
1847
|
-
}).join("");
|
|
1848
|
-
}
|
|
1849
|
-
function renderMarkdownBlocksHtml(nodes, builds) {
|
|
1850
|
-
const renderBlocks = (blocks) => blocks.map((node, index) => {
|
|
1851
|
-
const previous = blocks[index - 1];
|
|
1852
|
-
const extraBlankLines = previous ? Math.max(0, markdownBlockSeparatorLines(previous, node) - 2) : 0;
|
|
1853
|
-
const sourceGap = '<div data-squisq-source-gap aria-hidden="true"><br></div>'.repeat(
|
|
1854
|
-
extraBlankLines
|
|
1855
|
-
);
|
|
1856
|
-
const html = renderBlock(node);
|
|
1857
|
-
const step = builds?.targets.get(node);
|
|
1858
|
-
return `${sourceGap}${step === void 0 ? html : `<div data-squisq-build-step="${step}">${html}</div>`}`;
|
|
1859
|
-
}).join("");
|
|
1860
|
-
const renderList = (node) => {
|
|
1861
|
-
const tag = node.ordered ? "ol" : "ul";
|
|
1862
|
-
const start = node.ordered && node.start != null ? ` start="${node.start}"` : "";
|
|
1863
|
-
const items = node.children.map((item, index) => {
|
|
1864
|
-
const taskMarker = item.checked == null ? "" : `<span>${item.checked ? "[x]" : "[ ]"} </span>`;
|
|
1865
|
-
const step = builds?.targets.get(item);
|
|
1866
|
-
if (step === void 0) return `<li>${taskMarker}${renderBlocks(item.children)}</li>`;
|
|
1867
|
-
const marker = item.checked == null ? node.ordered ? `${(node.start ?? 1) + index}.` : "\u2022" : item.checked ? "[x]" : "[ ]";
|
|
1868
|
-
let first = true;
|
|
1869
|
-
let own = [];
|
|
1870
|
-
const fragments = [];
|
|
1871
|
-
const flush = () => {
|
|
1872
|
-
if (!first && !own.length) return;
|
|
1873
|
-
fragments.push(
|
|
1874
|
-
`<div data-squisq-build-step="${step}" data-squisq-build-row><span aria-hidden="true">${first ? marker : ""}</span><div>${renderBlocks(own)}</div></div>`
|
|
1875
|
-
);
|
|
1876
|
-
first = false;
|
|
1877
|
-
own = [];
|
|
1878
|
-
};
|
|
1879
|
-
for (const child of item.children) {
|
|
1880
|
-
if (child.type === "list") {
|
|
1881
|
-
flush();
|
|
1882
|
-
fragments.push(renderList(child));
|
|
1883
|
-
} else own.push(child);
|
|
1884
|
-
}
|
|
1885
|
-
flush();
|
|
1886
|
-
return `<li data-squisq-build-item>${fragments.join("")}</li>`;
|
|
1887
|
-
}).join("");
|
|
1888
|
-
return `<${tag}${start}>${items}</${tag}>`;
|
|
1889
|
-
};
|
|
1890
|
-
const renderBlock = (node) => {
|
|
1891
|
-
switch (node.type) {
|
|
1892
|
-
case "paragraph":
|
|
1893
|
-
return `<p>${renderInlineHtml(node.children)}</p>`;
|
|
1894
|
-
case "heading":
|
|
1895
|
-
return `<h${node.depth}>${renderInlineHtml(node.children)}</h${node.depth}>`;
|
|
1896
|
-
case "blockquote":
|
|
1897
|
-
return `<blockquote>${renderBlocks(node.children)}</blockquote>`;
|
|
1898
|
-
case "list":
|
|
1899
|
-
return renderList(node);
|
|
1900
|
-
case "code":
|
|
1901
|
-
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
1902
|
-
case "thematicBreak":
|
|
1903
|
-
return "<hr>";
|
|
1904
|
-
case "table": {
|
|
1905
|
-
const [head, ...body] = node.children;
|
|
1906
|
-
const row = (cells, header) => `<tr>${cells.map((cell, columnIndex) => {
|
|
1907
|
-
const tag = header ? "th" : "td";
|
|
1908
|
-
const alignment = node.align?.[columnIndex];
|
|
1909
|
-
const alignAttribute = alignment ? ` align="${alignment}"` : "";
|
|
1910
|
-
return `<${tag}${alignAttribute}>${renderInlineHtml(cell.children)}</${tag}>`;
|
|
1911
|
-
}).join("")}</tr>`;
|
|
1912
|
-
const thead = head ? `<thead>${row(head.children, true)}</thead>` : "";
|
|
1913
|
-
const tbody = body.length ? `<tbody>${body.map((item) => row(item.children, false)).join("")}</tbody>` : "";
|
|
1914
|
-
return `<div data-squisq-table-scroll role="region" aria-label="Scrollable data table"><table>${thead}${tbody}</table></div>`;
|
|
1915
|
-
}
|
|
1916
|
-
case "math":
|
|
1917
|
-
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
1918
|
-
case "definition":
|
|
1919
|
-
return "";
|
|
1920
|
-
default: {
|
|
1921
|
-
const text = extractPlainText(node).trim();
|
|
1922
|
-
return text ? `<p>${escapeInlineHtml(text)}</p>` : "";
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
};
|
|
1926
|
-
return renderBlocks(nodes);
|
|
1927
|
-
}
|
|
1928
|
-
function markdownBlockSeparatorLines(previous, next) {
|
|
1929
|
-
const previousEndLine = previous.position?.end.line;
|
|
1930
|
-
const nextStartLine = next.position?.start.line;
|
|
1931
|
-
if (previousEndLine == null || nextStartLine == null || nextStartLine <= previousEndLine) {
|
|
1932
|
-
return 2;
|
|
1933
|
-
}
|
|
1934
|
-
return Math.max(2, nextStartLine - previousEndLine);
|
|
1935
|
-
}
|
|
1936
|
-
function renderListItemHtml(markdown) {
|
|
1937
|
-
return markdown.map(
|
|
1938
|
-
(node) => node.type === "paragraph" || node.type === "heading" ? renderInlineHtml(node.children) : escapeInlineHtml(extractPlainText(node))
|
|
1939
|
-
).join("<br>");
|
|
1940
|
-
}
|
|
1941
|
-
function extractRichListItems(contents) {
|
|
1942
|
-
if (!contents) return [];
|
|
1943
|
-
const items = [];
|
|
1944
|
-
for (const node of contents) {
|
|
1945
|
-
if (node.type !== "list") continue;
|
|
1946
|
-
for (const item of node.children) {
|
|
1947
|
-
const text = extractPlainText(item).trim();
|
|
1948
|
-
if (!text) continue;
|
|
1949
|
-
const html = renderListItemHtml(item.children);
|
|
1950
|
-
const plainHtml = escapeInlineHtml(text).replace(/\r?\n/g, "<br>");
|
|
1951
|
-
items.push({
|
|
1952
|
-
text,
|
|
1953
|
-
markdown: item.children,
|
|
1954
|
-
...html && html !== plainHtml ? { html } : {}
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
return items;
|
|
1959
|
-
}
|
|
1960
|
-
function parseDim(raw) {
|
|
1961
|
-
if (raw === void 0) return void 0;
|
|
1962
|
-
const n = parseFloat(raw);
|
|
1963
|
-
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
1964
|
-
}
|
|
1965
|
-
function extractImages(contents, limit = Infinity) {
|
|
1966
|
-
if (!contents || contents.length === 0) return [];
|
|
1967
|
-
const found = [];
|
|
1968
|
-
function fromHtml(nodes) {
|
|
1969
|
-
for (const node of nodes) {
|
|
1970
|
-
if (found.length >= limit) return;
|
|
1971
|
-
if (!node || typeof node !== "object") continue;
|
|
1972
|
-
const n = node;
|
|
1973
|
-
if (n.type === "htmlElement" && n.tagName === "img") {
|
|
1974
|
-
const attrs = n.attributes;
|
|
1975
|
-
if (attrs && typeof attrs.src === "string" && attrs.src) {
|
|
1976
|
-
found.push({
|
|
1977
|
-
src: attrs.src,
|
|
1978
|
-
alt: typeof attrs.alt === "string" ? attrs.alt : "",
|
|
1979
|
-
width: parseDim(attrs.width),
|
|
1980
|
-
height: parseDim(attrs.height)
|
|
1981
|
-
});
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
if (Array.isArray(n.children)) fromHtml(n.children);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
function walk(node) {
|
|
1988
|
-
if (found.length >= limit) return;
|
|
1989
|
-
if (!node || typeof node !== "object") return;
|
|
1990
|
-
const n = node;
|
|
1991
|
-
if (n.type === "image" && typeof n.url === "string" && n.url) {
|
|
1992
|
-
if (!mediaKindForUrl(n.url)) {
|
|
1993
|
-
found.push({ src: n.url, alt: typeof n.alt === "string" ? n.alt : "" });
|
|
1994
|
-
}
|
|
1995
|
-
return;
|
|
1996
|
-
}
|
|
1997
|
-
if ((n.type === "htmlBlock" || n.type === "htmlInline") && Array.isArray(n.htmlChildren)) {
|
|
1998
|
-
fromHtml(n.htmlChildren);
|
|
1999
|
-
}
|
|
2000
|
-
if (Array.isArray(n.children)) {
|
|
2001
|
-
for (const child of n.children) walk(child);
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
for (const node of contents) {
|
|
2005
|
-
if (found.length >= limit) break;
|
|
2006
|
-
walk(node);
|
|
2007
|
-
}
|
|
2008
|
-
return found;
|
|
2009
|
-
}
|
|
2010
|
-
function extractFirstImage(contents) {
|
|
2011
|
-
return extractImages(contents, 1)[0] ?? null;
|
|
2012
|
-
}
|
|
2013
|
-
function extractEmbeddedVideos(contents, limit = Infinity) {
|
|
2014
|
-
if (!contents || contents.length === 0) return [];
|
|
2015
|
-
const found = [];
|
|
2016
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2017
|
-
const add = (video) => {
|
|
2018
|
-
if (!video.src || found.length >= limit || seen.has(video.src)) return;
|
|
2019
|
-
seen.add(video.src);
|
|
2020
|
-
found.push(video);
|
|
2021
|
-
};
|
|
2022
|
-
function nestedSource(children) {
|
|
2023
|
-
if (!Array.isArray(children)) return void 0;
|
|
2024
|
-
for (const child of children) {
|
|
2025
|
-
if (!child || typeof child !== "object") continue;
|
|
2026
|
-
const node = child;
|
|
2027
|
-
if (node.tagName === "source") {
|
|
2028
|
-
const attrs = node.attributes;
|
|
2029
|
-
if (typeof attrs?.src === "string" && attrs.src) return attrs.src;
|
|
2030
|
-
}
|
|
2031
|
-
const nested = nestedSource(node.children);
|
|
2032
|
-
if (nested) return nested;
|
|
2033
|
-
}
|
|
2034
|
-
return void 0;
|
|
2035
|
-
}
|
|
2036
|
-
function walk(node) {
|
|
2037
|
-
if (found.length >= limit || !node || typeof node !== "object") return;
|
|
2038
|
-
const n = node;
|
|
2039
|
-
if (n.type === "htmlElement" && n.tagName === "video") {
|
|
2040
|
-
const attrs = n.attributes;
|
|
2041
|
-
const src = attrs?.src || nestedSource(n.children);
|
|
2042
|
-
if (src) {
|
|
2043
|
-
const startAt = attrs?.["data-squisq-video-start-at"] ? parseTimeSeconds(attrs["data-squisq-video-start-at"]) : null;
|
|
2044
|
-
const clipStart = attrs?.["data-squisq-video-clip-start"] ? parseTimeSeconds(attrs["data-squisq-video-clip-start"]) : null;
|
|
2045
|
-
const clipEnd = attrs?.["data-squisq-video-clip-end"] ? parseTimeSeconds(attrs["data-squisq-video-clip-end"]) : null;
|
|
2046
|
-
add({
|
|
2047
|
-
src,
|
|
2048
|
-
...attrs?.poster ? { posterSrc: attrs.poster } : {},
|
|
2049
|
-
alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || "",
|
|
2050
|
-
...startAt != null ? { startAt } : {},
|
|
2051
|
-
...clipStart != null ? { clipStart } : {},
|
|
2052
|
-
...clipEnd != null ? { clipEnd } : {}
|
|
2053
|
-
});
|
|
2054
|
-
}
|
|
2055
|
-
} else if ((n.type === "link" || n.type === "image") && typeof n.url === "string" && VIDEO_FILE_RE.test(n.url)) {
|
|
2056
|
-
add({
|
|
2057
|
-
src: n.url,
|
|
2058
|
-
alt: typeof n.alt === "string" ? n.alt : extractPlainText(n).trim()
|
|
2059
|
-
});
|
|
2060
|
-
}
|
|
2061
|
-
if (Array.isArray(n.children)) n.children.forEach(walk);
|
|
2062
|
-
if (Array.isArray(n.htmlChildren)) n.htmlChildren.forEach(walk);
|
|
2063
|
-
}
|
|
2064
|
-
contents.forEach(walk);
|
|
2065
|
-
return found;
|
|
2066
|
-
}
|
|
2067
|
-
function extractFirstEmbeddedVideo(contents) {
|
|
2068
|
-
return extractEmbeddedVideos(contents, 1)[0] ?? null;
|
|
2069
|
-
}
|
|
2070
|
-
function extractTableFromContents(contents) {
|
|
2071
|
-
if (!contents) return null;
|
|
2072
|
-
for (const node of contents) {
|
|
2073
|
-
if (node.type === "table") {
|
|
2074
|
-
const table = node;
|
|
2075
|
-
const [headerRow, ...bodyRows] = table.children;
|
|
2076
|
-
if (!headerRow) return null;
|
|
2077
|
-
const headers = headerRow.children.map((cell) => extractPlainText(cell).trim());
|
|
2078
|
-
const rows = bodyRows.map((row) => row.children.map((cell) => extractPlainText(cell).trim()));
|
|
2079
|
-
return { headers, rows, align: table.align };
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
return null;
|
|
2083
|
-
}
|
|
2084
|
-
function extractBlockquoteText(contents) {
|
|
2085
|
-
if (!contents) return "";
|
|
2086
|
-
for (const node of contents) {
|
|
2087
|
-
if (node.type === "blockquote") return extractPlainText(node).trim();
|
|
2088
|
-
}
|
|
2089
|
-
return "";
|
|
2090
|
-
}
|
|
2091
|
-
function firstParagraph(contents) {
|
|
2092
|
-
if (!contents) return null;
|
|
2093
|
-
const index = contents.findIndex((node) => node.type === "paragraph");
|
|
2094
|
-
if (index < 0) return null;
|
|
2095
|
-
return {
|
|
2096
|
-
node: contents[index],
|
|
2097
|
-
index
|
|
2098
|
-
};
|
|
2099
|
-
}
|
|
2100
|
-
function leadingStrongStat(paragraph) {
|
|
2101
|
-
for (const child of paragraph.children) {
|
|
2102
|
-
if (child.type === "text" && child.value.trim() === "") continue;
|
|
2103
|
-
if (child.type !== "strong") return null;
|
|
2104
|
-
const text = extractPlainText(child).trim();
|
|
2105
|
-
const match = text.match(/^(?:[$€£¥]\s*)?[+-]?\d+(?:[.,]\d+)*(?:\s?(?:[%‰x×]|[A-Za-z]+))?/);
|
|
2106
|
-
return match?.[0].trim() || null;
|
|
2107
|
-
}
|
|
2108
|
-
return null;
|
|
2109
|
-
}
|
|
2110
|
-
function removeStatPhrase(text, start, end) {
|
|
2111
|
-
const before = text.slice(0, start).trim();
|
|
2112
|
-
let after = text.slice(end);
|
|
2113
|
-
if (!before) after = after.replace(/^\s*(?:[-–—:]\s*)?/, "");
|
|
2114
|
-
else after = after.trim();
|
|
2115
|
-
return [before, after].filter(Boolean).join(" ").trim();
|
|
2116
|
-
}
|
|
2117
|
-
function bodyWithoutParagraphStat(contents, paragraphIndex, paragraphText, start, end) {
|
|
2118
|
-
if (!contents) return "";
|
|
2119
|
-
return contents.map(
|
|
2120
|
-
(node, index) => index === paragraphIndex ? removeStatPhrase(paragraphText, start, end) : extractPlainText(node).trim()
|
|
2121
|
-
).filter(Boolean).join("\n").trim();
|
|
2122
|
-
}
|
|
2123
|
-
function deriveStatHighlightInputs(headingText, contents, bodyText3) {
|
|
2124
|
-
const paragraph = firstParagraph(contents);
|
|
2125
|
-
if (paragraph) {
|
|
2126
|
-
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
2127
|
-
const strongStat = leadingStrongStat(paragraph.node);
|
|
2128
|
-
if (strongStat) {
|
|
2129
|
-
const start = paragraphText.indexOf(strongStat);
|
|
2130
|
-
const description = bodyWithoutParagraphStat(
|
|
2131
|
-
contents,
|
|
2132
|
-
paragraph.index,
|
|
2133
|
-
paragraphText,
|
|
2134
|
-
start,
|
|
2135
|
-
start + strongStat.length
|
|
2136
|
-
);
|
|
2137
|
-
return {
|
|
2138
|
-
stat: strongStat,
|
|
2139
|
-
description: description || headingText || strongStat
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
const trimmedHeading = headingText.trim();
|
|
2144
|
-
const headingStat = matchNumberHighlight(trimmedHeading);
|
|
2145
|
-
if (headingStat && headingStat.index === 0 && headingStat.end === trimmedHeading.length) {
|
|
2146
|
-
return { stat: headingText, description: bodyText3 || headingText };
|
|
2147
|
-
}
|
|
2148
|
-
if (paragraph) {
|
|
2149
|
-
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
2150
|
-
const bodyStat = matchNumberHighlight(paragraphText);
|
|
2151
|
-
if (bodyStat) {
|
|
2152
|
-
const description = bodyWithoutParagraphStat(
|
|
2153
|
-
contents,
|
|
2154
|
-
paragraph.index,
|
|
2155
|
-
paragraphText,
|
|
2156
|
-
bodyStat.index,
|
|
2157
|
-
bodyStat.end
|
|
2158
|
-
);
|
|
2159
|
-
return {
|
|
2160
|
-
stat: bodyStat.value,
|
|
2161
|
-
description: description || headingText || bodyStat.value
|
|
2162
|
-
};
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
return { stat: headingText, description: bodyText3 || headingText };
|
|
2166
|
-
}
|
|
2167
|
-
function normalizeCoverageText(value) {
|
|
2168
|
-
return value.replace(/\s+/g, " ").trim();
|
|
2169
|
-
}
|
|
2170
|
-
function autoTemplatePreservesContent(templateName, contents) {
|
|
2171
|
-
const nodes = contents ?? [];
|
|
2172
|
-
switch (templateName) {
|
|
2173
|
-
case "quote":
|
|
2174
|
-
return nodes.length === 1 && nodes[0]?.type === "blockquote";
|
|
2175
|
-
case "dataTable":
|
|
2176
|
-
return nodes.length === 1 && nodes[0]?.type === "table";
|
|
2177
|
-
case "list":
|
|
2178
|
-
return nodes.length === 1 && nodes[0]?.type === "list";
|
|
2179
|
-
case "diagram":
|
|
2180
|
-
case "timeline":
|
|
2181
|
-
return nodes.length === 1 && nodes[0]?.type === "code";
|
|
2182
|
-
case "tree":
|
|
2183
|
-
return nodes.length === 1 && (nodes[0]?.type === "code" || nodes[0]?.type === "list");
|
|
2184
|
-
case "photoGrid": {
|
|
2185
|
-
const images = extractImages(nodes);
|
|
2186
|
-
if (images.length < 2 || images.length > 4) return false;
|
|
2187
|
-
const bodyText3 = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
2188
|
-
const representedText = normalizeCoverageText(
|
|
2189
|
-
images.map((image) => image.alt).filter(Boolean).join(" ")
|
|
2190
|
-
);
|
|
2191
|
-
return bodyText3 === "" || bodyText3 === representedText;
|
|
2192
|
-
}
|
|
2193
|
-
case "videoWithCaption": {
|
|
2194
|
-
const videos = extractEmbeddedVideos(nodes);
|
|
2195
|
-
if (videos.length + extractHostedVideos(nodes).length !== 1) return false;
|
|
2196
|
-
const bodyText3 = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
2197
|
-
return bodyText3 === normalizeCoverageText(videos[0]?.alt ?? "");
|
|
2198
|
-
}
|
|
2199
|
-
case "leftFeature":
|
|
2200
|
-
case "rightFeature":
|
|
2201
|
-
case "statHighlight":
|
|
2202
|
-
return nodes.length > 0 && nodes.every((node) => node.type === "paragraph");
|
|
2203
|
-
default:
|
|
2204
|
-
return false;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
function deriveTemplateInputs(templateName, headingText, contents, options = {}) {
|
|
2208
|
-
const placeholders = options.placeholders === true;
|
|
2209
|
-
const bodyText3 = extractBodyPlainText(contents);
|
|
2210
|
-
switch (templateName) {
|
|
2211
|
-
case "statHighlight": {
|
|
2212
|
-
const inputs = deriveStatHighlightInputs(headingText, contents, bodyText3);
|
|
2213
|
-
if (options.preserveSourceHeading && headingText && inputs.stat !== headingText && inputs.description !== headingText) {
|
|
2214
|
-
return {
|
|
2215
|
-
...inputs,
|
|
2216
|
-
description: headingText,
|
|
2217
|
-
...inputs.description ? { detail: inputs.description } : {}
|
|
2218
|
-
};
|
|
2219
|
-
}
|
|
2220
|
-
return inputs;
|
|
2221
|
-
}
|
|
2222
|
-
case "transcript": {
|
|
2223
|
-
const text = extractBlockquoteText(contents) || bodyText3;
|
|
2224
|
-
if (!text && !headingText) {
|
|
2225
|
-
return placeholders ? { speaker: "Speaker", text: "Message" } : null;
|
|
2226
|
-
}
|
|
2227
|
-
return {
|
|
2228
|
-
speaker: headingText || (placeholders ? "Speaker" : ""),
|
|
2229
|
-
text: text || (placeholders ? "Message" : "")
|
|
2230
|
-
};
|
|
2231
|
-
}
|
|
2232
|
-
case "quote": {
|
|
2233
|
-
const quote = extractBlockquoteText(contents) || bodyText3;
|
|
2234
|
-
if (quote) return { quote, ...headingText ? { title: headingText } : {} };
|
|
2235
|
-
return { quote: headingText };
|
|
2236
|
-
}
|
|
2237
|
-
case "fullBleedQuote": {
|
|
2238
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2239
|
-
return { text };
|
|
2240
|
-
}
|
|
2241
|
-
case "pullQuote": {
|
|
2242
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2243
|
-
const img = extractFirstImage(contents);
|
|
2244
|
-
return {
|
|
2245
|
-
text,
|
|
2246
|
-
...img ? { backgroundImage: { src: img.src, alt: img.alt || headingText } } : {}
|
|
2247
|
-
};
|
|
2248
|
-
}
|
|
2249
|
-
case "videoPullQuote": {
|
|
2250
|
-
const text = extractBlockquoteText(contents) || bodyText3 || headingText;
|
|
2251
|
-
const video = extractFirstEmbeddedVideo(contents);
|
|
2252
|
-
return {
|
|
2253
|
-
text,
|
|
2254
|
-
...video ? {
|
|
2255
|
-
backgroundVideo: {
|
|
2256
|
-
src: video.src,
|
|
2257
|
-
...video.posterSrc ? { posterSrc: video.posterSrc } : {},
|
|
2258
|
-
alt: video.alt || headingText,
|
|
2259
|
-
...video.clipStart != null ? { clipStart: video.clipStart } : {},
|
|
2260
|
-
...video.clipEnd != null ? { clipEnd: video.clipEnd } : {}
|
|
2261
|
-
}
|
|
2262
|
-
} : {}
|
|
2263
|
-
};
|
|
2264
|
-
}
|
|
2265
|
-
case "factCard":
|
|
2266
|
-
return { fact: headingText, explanation: bodyText3 };
|
|
2267
|
-
case "twoColumn":
|
|
2268
|
-
return headingText ? { header: headingText } : placeholders ? {} : null;
|
|
2269
|
-
case "comparisonBar":
|
|
2270
|
-
return placeholders ? { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 } : null;
|
|
2271
|
-
case "list": {
|
|
2272
|
-
const items = extractListItems(contents);
|
|
2273
|
-
const firstList = (contents ?? []).find((n) => n.type === "list");
|
|
2274
|
-
if (items.length > 0) {
|
|
2275
|
-
return {
|
|
2276
|
-
items,
|
|
2277
|
-
// Bullets stay bullets: only a numbered source list renders `1.` markers.
|
|
2278
|
-
ordered: firstList?.ordered === true,
|
|
2279
|
-
...headingText ? { title: headingText } : {}
|
|
2280
|
-
};
|
|
2281
|
-
}
|
|
2282
|
-
return placeholders ? { items: ["Item 1", "Item 2", "Item 3"] } : null;
|
|
2283
|
-
}
|
|
2284
|
-
case "definitionCard":
|
|
2285
|
-
return { term: headingText, definition: bodyText3 };
|
|
2286
|
-
case "dateEvent":
|
|
2287
|
-
return { date: headingText, description: bodyText3 };
|
|
2288
|
-
case "dataTable": {
|
|
2289
|
-
const tableData = extractTableFromContents(contents);
|
|
2290
|
-
if (tableData) return { ...tableData, ...headingText ? { title: headingText } : {} };
|
|
2291
|
-
return placeholders ? { headers: ["Column"], rows: [["Data"]] } : null;
|
|
2292
|
-
}
|
|
2293
|
-
case "barChart":
|
|
2294
|
-
case "columnChart":
|
|
2295
|
-
case "pieChart":
|
|
2296
|
-
case "donutChart":
|
|
2297
|
-
case "lineChart":
|
|
2298
|
-
case "areaChart":
|
|
2299
|
-
case "scatterChart": {
|
|
2300
|
-
const tableData = extractTableFromContents(contents);
|
|
2301
|
-
if (tableData) {
|
|
2302
|
-
return {
|
|
2303
|
-
headers: tableData.headers,
|
|
2304
|
-
rows: tableData.rows,
|
|
2305
|
-
...headingText ? { title: headingText } : {}
|
|
2306
|
-
};
|
|
2307
|
-
}
|
|
2308
|
-
return placeholders && !bodyText3.trim() ? {
|
|
2309
|
-
headers: ["Quarter", "Revenue", "Costs"],
|
|
2310
|
-
rows: [
|
|
2311
|
-
["Q1", "40", "28"],
|
|
2312
|
-
["Q2", "55", "31"],
|
|
2313
|
-
["Q3", "48", "30"],
|
|
2314
|
-
["Q4", "62", "35"]
|
|
2315
|
-
],
|
|
2316
|
-
...headingText ? { title: headingText } : {}
|
|
2317
|
-
} : null;
|
|
2318
|
-
}
|
|
2319
|
-
case "imageWithCaption": {
|
|
2320
|
-
const img = extractFirstImage(contents);
|
|
2321
|
-
if (!img) return placeholders ? { caption: headingText } : null;
|
|
2322
|
-
return { imageSrc: img.src, imageAlt: img.alt || headingText, caption: headingText };
|
|
2323
|
-
}
|
|
2324
|
-
case "bigText": {
|
|
2325
|
-
const img = extractFirstImage(contents);
|
|
2326
|
-
return {
|
|
2327
|
-
title: headingText,
|
|
2328
|
-
...img ? { imageSrc: img.src, imageAlt: img.alt || headingText } : {}
|
|
2329
|
-
};
|
|
2330
|
-
}
|
|
2331
|
-
case "photoGrid": {
|
|
2332
|
-
const images = extractImages(contents, 4);
|
|
2333
|
-
if (images.length < 2) return placeholders ? {} : null;
|
|
2334
|
-
return {
|
|
2335
|
-
images: images.map((i) => ({ src: i.src, alt: i.alt })),
|
|
2336
|
-
caption: headingText
|
|
2337
|
-
};
|
|
2338
|
-
}
|
|
2339
|
-
case "videoWithCaption": {
|
|
2340
|
-
const video = extractFirstEmbeddedVideo(contents);
|
|
2341
|
-
if (!video) {
|
|
2342
|
-
const hosted = extractHostedVideos(contents, 1)[0];
|
|
2343
|
-
if (hosted) {
|
|
2344
|
-
return {
|
|
2345
|
-
videoSrc: hosted.url,
|
|
2346
|
-
videoAlt: hosted.title || headingText,
|
|
2347
|
-
caption: headingText
|
|
2348
|
-
};
|
|
2349
|
-
}
|
|
2350
|
-
return placeholders ? { caption: headingText } : null;
|
|
2351
|
-
}
|
|
2352
|
-
return {
|
|
2353
|
-
videoSrc: video.src,
|
|
2354
|
-
posterSrc: video.posterSrc,
|
|
2355
|
-
videoAlt: video.alt || headingText,
|
|
2356
|
-
caption: headingText
|
|
2357
|
-
};
|
|
2358
|
-
}
|
|
2359
|
-
case "leftFeature":
|
|
2360
|
-
case "rightFeature": {
|
|
2361
|
-
const img = extractFirstImage(contents);
|
|
2362
|
-
if (!img && !placeholders) return null;
|
|
2363
|
-
return {
|
|
2364
|
-
imageSrc: img?.src ?? "",
|
|
2365
|
-
imageAlt: img?.alt || headingText,
|
|
2366
|
-
imageWidth: img?.width,
|
|
2367
|
-
imageHeight: img?.height,
|
|
2368
|
-
title: headingText,
|
|
2369
|
-
body: bodyText3
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
case "diagram": {
|
|
2373
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2374
|
-
const fence = fences.length === 1 && isEligibleAsciiFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2375
|
-
const detection = fence ? detectAsciiDiagram(fence.value, { explicit: isExplicitDiagramLang(fence.lang) }) : void 0;
|
|
2376
|
-
if (!detection?.isDiagram || !detection.diagram) return placeholders ? {} : null;
|
|
2377
|
-
const { nodes, edges } = asciiDiagramToTemplateData(detection.diagram);
|
|
2378
|
-
return { nodes, edges, ...headingText ? { title: headingText } : {} };
|
|
2379
|
-
}
|
|
2380
|
-
case "timeline": {
|
|
2381
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2382
|
-
const fence = fences.length === 1 && isEligibleAsciiTimelineFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2383
|
-
const detection = fence ? detectAsciiTimeline(fence.value, { explicit: true }) : void 0;
|
|
2384
|
-
if (!detection?.isTimeline || !detection.timeline) return placeholders ? {} : null;
|
|
2385
|
-
const { tracks, links } = asciiTimelineToTemplateData(detection.timeline);
|
|
2386
|
-
return { tracks, links, ...headingText ? { title: headingText } : {} };
|
|
2387
|
-
}
|
|
2388
|
-
case "tree": {
|
|
2389
|
-
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
2390
|
-
const fence = fences.length === 1 && isEligibleTreeFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
2391
|
-
if (fence) {
|
|
2392
|
-
const detection = detectTree(fence.value, { explicit: isExplicitTreeLang(fence.lang) });
|
|
2393
|
-
if (detection.isTree && detection.tree) {
|
|
2394
|
-
const { items } = treeToTemplateData(detection.tree);
|
|
2395
|
-
return { items, ...headingText ? { title: headingText } : {} };
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
const list = findFirstList(contents);
|
|
2399
|
-
if (list) {
|
|
2400
|
-
const { items } = treeToTemplateData(treeFromMarkdownList(list));
|
|
2401
|
-
if (items.length > 0) return { items, ...headingText ? { title: headingText } : {} };
|
|
2402
|
-
}
|
|
2403
|
-
return placeholders ? {} : null;
|
|
2404
|
-
}
|
|
2405
|
-
default:
|
|
2406
|
-
return placeholders ? {} : null;
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
1433
|
// src/doc/templates/tokens/resolveTokens.ts
|
|
2411
1434
|
var TOKEN_RE = /\{(\w+)(?::([^}|]+))?(?:\|([^}]*))?\}/g;
|
|
2412
1435
|
var ESCAPE_RE = /\{\{([^{}]*)\}\}/g;
|
|
@@ -2446,7 +1469,7 @@ function collectListItems(block) {
|
|
|
2446
1469
|
}
|
|
2447
1470
|
function getBlockBodyText(block) {
|
|
2448
1471
|
if (!block.contents) return "";
|
|
2449
|
-
return block.contents.map((node) => extractPlainText(node).trim()).filter((s) => s.length > 0).join(" ");
|
|
1472
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node).trim()).filter((s) => s.length > 0).join(" ");
|
|
2450
1473
|
}
|
|
2451
1474
|
function getChildrenSummary(block) {
|
|
2452
1475
|
if (!block.children || block.children.length === 0) return "";
|
|
@@ -3706,6 +2729,8 @@ function imageWithCaption(input, context) {
|
|
|
3706
2729
|
textAlign: "center",
|
|
3707
2730
|
shadow: shouldUseShadow(context),
|
|
3708
2731
|
lineHeight: 1.18,
|
|
2732
|
+
// Capitals read as centred on the band (CAPTION_BASELINE_SHIFT_EM)
|
|
2733
|
+
baselineShift: CAPTION_BASELINE_SHIFT_EM,
|
|
3709
2734
|
maxLines: 2,
|
|
3710
2735
|
// A two-line caption centres on the band instead of hanging its
|
|
3711
2736
|
// second line below it (the block centres on the anchor).
|
|
@@ -5116,6 +4141,8 @@ function videoWithCaption(input, context) {
|
|
|
5116
4141
|
textAlign: "center",
|
|
5117
4142
|
shadow: shouldUseShadow(context),
|
|
5118
4143
|
lineHeight: 1.18,
|
|
4144
|
+
// Capitals read as centred on the band (CAPTION_BASELINE_SHIFT_EM)
|
|
4145
|
+
baselineShift: CAPTION_BASELINE_SHIFT_EM,
|
|
5119
4146
|
maxLines: 2
|
|
5120
4147
|
}
|
|
5121
4148
|
},
|
|
@@ -7180,7 +6207,7 @@ function resolveSupplementalMediaLayout(layers, template, viewport, mediaCount,
|
|
|
7180
6207
|
const policy = getBlockMediaLayoutPolicy(template);
|
|
7181
6208
|
const shape = mediaShape(mediaCount, aspectRatios);
|
|
7182
6209
|
const alreadyOwnsMedia = layers.some(
|
|
7183
|
-
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "videoEmbed" || layer.type === "map" || layer.type === "mermaid"
|
|
6210
|
+
(layer) => layer.type === "image" || layer.type === "video" || layer.type === "videoEmbed" || layer.type === "map" || layer.type === "mermaid" || layer.type === "svg"
|
|
7184
6211
|
);
|
|
7185
6212
|
if (!alreadyOwnsMedia && (policy?.nativeLayout === "feature-left" || policy?.nativeLayout === "feature-right")) {
|
|
7186
6213
|
const { position } = featureMediaSlot(
|
|
@@ -7307,15 +6334,19 @@ function unconsumedMedia(block) {
|
|
|
7307
6334
|
seen.add(image.src);
|
|
7308
6335
|
return true;
|
|
7309
6336
|
});
|
|
7310
|
-
const
|
|
7311
|
-
(node) => node.type === "code" && node
|
|
6337
|
+
const pictures = contents.filter(
|
|
6338
|
+
(node) => node.type === "code" && isPictureFence(node) && node.value.trim().length > 0
|
|
7312
6339
|
);
|
|
7313
6340
|
const aspectRatios = [
|
|
7314
6341
|
...images.map(
|
|
7315
6342
|
(image) => image.width && image.height ? image.width / image.height : void 0
|
|
7316
6343
|
),
|
|
7317
6344
|
...videos.map(() => 16 / 9),
|
|
7318
|
-
|
|
6345
|
+
// An SVG island knows its shape; a Mermaid diagram does not until drawn.
|
|
6346
|
+
...pictures.map((fence) => {
|
|
6347
|
+
const island = isSvgIslandLang(fence.lang) ? parseSvgIsland(fence.value) : null;
|
|
6348
|
+
return island?.ok ? island.island.aspectRatio : 16 / 9;
|
|
6349
|
+
})
|
|
7319
6350
|
];
|
|
7320
6351
|
return { count: aspectRatios.length, aspectRatios };
|
|
7321
6352
|
}
|
|
@@ -7860,7 +6891,7 @@ function estimateTextWidth(text, fontSize, charWidthEm = AVERAGE_CHAR_WIDTH_EM)
|
|
|
7860
6891
|
}
|
|
7861
6892
|
|
|
7862
6893
|
// src/doc/templates/timelineBlock.ts
|
|
7863
|
-
var
|
|
6894
|
+
var clamp01 = (value) => Math.max(0, Math.min(1, value));
|
|
7864
6895
|
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
7865
6896
|
function safeId(value) {
|
|
7866
6897
|
return value.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "item";
|
|
@@ -7887,7 +6918,7 @@ function coerceTracks(raw) {
|
|
|
7887
6918
|
events.push({
|
|
7888
6919
|
id: eventId,
|
|
7889
6920
|
label: event.label,
|
|
7890
|
-
position:
|
|
6921
|
+
position: clamp01(position),
|
|
7891
6922
|
...typeof event.description === "string" && event.description.trim() ? { description: event.description.trim() } : {},
|
|
7892
6923
|
...side ? { side } : {},
|
|
7893
6924
|
...descriptionSide ? { descriptionSide } : {},
|
|
@@ -8033,7 +7064,7 @@ function timelineBlock(input, context) {
|
|
|
8033
7064
|
const eventFontSize = Math.min(baseEventFontSize, 28 * minViewportScale);
|
|
8034
7065
|
const descriptionFontSize = Math.min(baseDescriptionFontSize, 20 * minViewportScale);
|
|
8035
7066
|
const trackMarkerRadii = tracks.map((track) => {
|
|
8036
|
-
const positions = track.events.map((event) =>
|
|
7067
|
+
const positions = track.events.map((event) => clamp01(event.position)).sort((a, b) => a - b);
|
|
8037
7068
|
let minGapPx = Number.POSITIVE_INFINITY;
|
|
8038
7069
|
for (let index = 1; index < positions.length; index++) {
|
|
8039
7070
|
minGapPx = Math.min(minGapPx, (positions[index] - positions[index - 1]) * axisWidth);
|
|
@@ -8112,7 +7143,7 @@ function timelineBlock(input, context) {
|
|
|
8112
7143
|
event,
|
|
8113
7144
|
trackIndex,
|
|
8114
7145
|
eventIndex,
|
|
8115
|
-
x: axisStartX + axisWidth *
|
|
7146
|
+
x: axisStartX + axisWidth * clamp01(event.position),
|
|
8116
7147
|
y
|
|
8117
7148
|
};
|
|
8118
7149
|
eventPoints.push(point);
|
|
@@ -8467,7 +7498,7 @@ function normalizeShapeKind(name) {
|
|
|
8467
7498
|
function isShapeName(name) {
|
|
8468
7499
|
return normalizeShapeKind(name) != null;
|
|
8469
7500
|
}
|
|
8470
|
-
var
|
|
7501
|
+
var DEFAULT_OPTS = {
|
|
8471
7502
|
gapX: 180,
|
|
8472
7503
|
gapY: 140,
|
|
8473
7504
|
gridPad: 80,
|
|
@@ -8487,7 +7518,7 @@ function defaultSize(kind) {
|
|
|
8487
7518
|
return DEFAULT_SIZE[kind] ?? FALLBACK_SIZE;
|
|
8488
7519
|
}
|
|
8489
7520
|
function computeDrawingLayout(children, options = {}) {
|
|
8490
|
-
const opts = { ...
|
|
7521
|
+
const opts = { ...DEFAULT_OPTS, ...options };
|
|
8491
7522
|
const warnings = [];
|
|
8492
7523
|
const entries = [];
|
|
8493
7524
|
const unpositioned = [];
|
|
@@ -8664,7 +7695,7 @@ function connectorSemantics(params, kind) {
|
|
|
8664
7695
|
}
|
|
8665
7696
|
function bodyText(block) {
|
|
8666
7697
|
if (!block.contents || block.contents.length === 0) return "";
|
|
8667
|
-
return block.contents.map((node) => extractPlainText(node)).join(" ").trim();
|
|
7698
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join(" ").trim();
|
|
8668
7699
|
}
|
|
8669
7700
|
|
|
8670
7701
|
// src/doc/templates/drawingBlock.ts
|
|
@@ -9047,7 +8078,7 @@ function numParam2(params, key) {
|
|
|
9047
8078
|
}
|
|
9048
8079
|
function bodyText2(block) {
|
|
9049
8080
|
if (!block.contents || block.contents.length === 0) return "";
|
|
9050
|
-
return block.contents.map((node) => extractPlainText(node)).join("\n").trim();
|
|
8081
|
+
return block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
9051
8082
|
}
|
|
9052
8083
|
function round3(n) {
|
|
9053
8084
|
return Math.round(n * 100) / 100;
|
|
@@ -10002,7 +9033,7 @@ function fallbackBlockLayers(block, context, notice) {
|
|
|
10002
9033
|
}
|
|
10003
9034
|
function extractBodyText(block) {
|
|
10004
9035
|
if (!block.contents || block.contents.length === 0) return "";
|
|
10005
|
-
const text = block.contents.map((node) => extractPlainText(node)).join("\n").trim();
|
|
9036
|
+
const text = block.contents.filter((node) => !isPictureFence(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
10006
9037
|
if (text.length <= MAX_BODY_CHARS) return text;
|
|
10007
9038
|
return `${text.slice(0, MAX_BODY_CHARS).trimEnd()}\u2026`;
|
|
10008
9039
|
}
|
|
@@ -10125,6 +9156,13 @@ function mermaidSources(block) {
|
|
|
10125
9156
|
(node) => node.type === "code" && node.lang?.trim().toLowerCase() === "mermaid"
|
|
10126
9157
|
).map((node) => node.value).filter((source) => source.trim().length > 0);
|
|
10127
9158
|
}
|
|
9159
|
+
function svgIslandSources(block) {
|
|
9160
|
+
return (block.contents ?? []).filter((node) => node.type === "code" && isSvgIslandLang(node.lang)).map((node) => node.value).filter((source) => source.trim().length > 0);
|
|
9161
|
+
}
|
|
9162
|
+
function svgIslandAspectRatio(source) {
|
|
9163
|
+
const result = parseSvgIsland(source);
|
|
9164
|
+
return result.ok ? result.island.aspectRatio : 16 / 9;
|
|
9165
|
+
}
|
|
10128
9166
|
function embeddedSpatialMedia(block) {
|
|
10129
9167
|
const canonicalTemplate = block.template ? resolveTemplateName(block.template) : void 0;
|
|
10130
9168
|
const fences = (block.contents ?? []).filter(
|
|
@@ -10184,6 +9222,9 @@ function collectRichMediaItems(layers, block) {
|
|
|
10184
9222
|
const existingMermaid = new Set(
|
|
10185
9223
|
layers.filter((layer) => layer.type === "mermaid").map((layer) => layer.content.source)
|
|
10186
9224
|
);
|
|
9225
|
+
const existingSvg = new Set(
|
|
9226
|
+
layers.filter((layer) => layer.type === "svg").map((layer) => layer.content.source)
|
|
9227
|
+
);
|
|
10187
9228
|
const existingEmbeds = new Set(
|
|
10188
9229
|
layers.filter((layer) => layer.type === "videoEmbed").map((layer) => parseVideoEmbedUrl(layer.content.url)?.watchUrl ?? layer.content.url)
|
|
10189
9230
|
);
|
|
@@ -10197,6 +9238,7 @@ function collectRichMediaItems(layers, block) {
|
|
|
10197
9238
|
return true;
|
|
10198
9239
|
});
|
|
10199
9240
|
const sources = mermaidSources(block).filter((source) => !existingMermaid.has(source));
|
|
9241
|
+
const islands = svgIslandSources(block).filter((source) => !existingSvg.has(source));
|
|
10200
9242
|
const hosted = extractHostedVideos(block.contents).filter(
|
|
10201
9243
|
(video) => !existingEmbeds.has(video.url)
|
|
10202
9244
|
);
|
|
@@ -10224,6 +9266,11 @@ function collectRichMediaItems(layers, block) {
|
|
|
10224
9266
|
aspectRatio: video.aspectRatio
|
|
10225
9267
|
})),
|
|
10226
9268
|
...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 })),
|
|
9269
|
+
...islands.map((source) => ({
|
|
9270
|
+
kind: "svg",
|
|
9271
|
+
source,
|
|
9272
|
+
aspectRatio: svgIslandAspectRatio(source)
|
|
9273
|
+
})),
|
|
10227
9274
|
...embeddedSpatialMedia(block)
|
|
10228
9275
|
];
|
|
10229
9276
|
}
|
|
@@ -10320,6 +9367,16 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
|
|
|
10320
9367
|
}
|
|
10321
9368
|
];
|
|
10322
9369
|
}
|
|
9370
|
+
if (item.kind === "svg") {
|
|
9371
|
+
return [
|
|
9372
|
+
{
|
|
9373
|
+
id: `${block.id}-svg-${kindIndex}`,
|
|
9374
|
+
type: "svg",
|
|
9375
|
+
position,
|
|
9376
|
+
content: { source: item.source, color: theme.colors.text }
|
|
9377
|
+
}
|
|
9378
|
+
];
|
|
9379
|
+
}
|
|
10323
9380
|
const localViewport = {
|
|
10324
9381
|
width: position.width,
|
|
10325
9382
|
height: position.height,
|
|
@@ -10391,6 +9448,7 @@ function appendRichContentLayers(layers, block, theme, viewport, mediaAspectRati
|
|
|
10391
9448
|
video: 0,
|
|
10392
9449
|
videoEmbed: 0,
|
|
10393
9450
|
mermaid: 0,
|
|
9451
|
+
svg: 0,
|
|
10394
9452
|
spatial: 0
|
|
10395
9453
|
};
|
|
10396
9454
|
const mediaLayers = items.flatMap((item, index) => {
|
|
@@ -12409,7 +11467,7 @@ function getBlockBodyText2(block) {
|
|
|
12409
11467
|
if (!block.contents || block.contents.length === 0) return "";
|
|
12410
11468
|
const tmpl = resolveTemplateName(block.template ?? "");
|
|
12411
11469
|
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;
|
|
12412
|
-
return block.contents.filter((node) => !(consumedFence && node.type === "code")).filter((node) =>
|
|
11470
|
+
return block.contents.filter((node) => !(consumedFence && node.type === "code")).filter((node) => !isMaterializedMediaNode(node)).map((node) => extractPlainText(node)).join("\n").trim();
|
|
12413
11471
|
}
|
|
12414
11472
|
var MAX_PHRASE_WORDS = 12;
|
|
12415
11473
|
function splitIntoSentences(text) {
|
|
@@ -13006,34 +12064,6 @@ export {
|
|
|
13006
12064
|
sectionHeader,
|
|
13007
12065
|
fitBigTextSize,
|
|
13008
12066
|
bigText,
|
|
13009
|
-
computeDiagramLayout,
|
|
13010
|
-
ASCII_CHAR_W,
|
|
13011
|
-
ASCII_CHAR_H,
|
|
13012
|
-
asciiCellToCanvas,
|
|
13013
|
-
canvasToAsciiCell,
|
|
13014
|
-
asciiDiagramToTemplateData,
|
|
13015
|
-
asciiDiagramFromTemplateData,
|
|
13016
|
-
asciiDiagramFromBlocks,
|
|
13017
|
-
asciiTimelineToTemplateData,
|
|
13018
|
-
treeToTemplateData,
|
|
13019
|
-
treeFromTemplateData,
|
|
13020
|
-
treeFromMarkdownList,
|
|
13021
|
-
findFirstList,
|
|
13022
|
-
extractHostedVideos,
|
|
13023
|
-
isMaterializedMediaNode,
|
|
13024
|
-
extractBodyPlainText,
|
|
13025
|
-
extractListItems,
|
|
13026
|
-
renderMarkdownBlocksHtml,
|
|
13027
|
-
markdownBlockSeparatorLines,
|
|
13028
|
-
extractRichListItems,
|
|
13029
|
-
extractImages,
|
|
13030
|
-
extractFirstImage,
|
|
13031
|
-
extractEmbeddedVideos,
|
|
13032
|
-
extractFirstEmbeddedVideo,
|
|
13033
|
-
extractTableFromContents,
|
|
13034
|
-
extractBlockquoteText,
|
|
13035
|
-
autoTemplatePreservesContent,
|
|
13036
|
-
deriveTemplateInputs,
|
|
13037
12067
|
contentBlock,
|
|
13038
12068
|
parseStatNumber,
|
|
13039
12069
|
countUpSpecFor,
|