@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,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractPlainText,
|
|
3
|
+
findBlockVideoEmbed,
|
|
3
4
|
findNodesByType,
|
|
5
|
+
isSvgIslandLang,
|
|
4
6
|
isVideoEmbedUrl,
|
|
5
7
|
mediaKindForUrl,
|
|
8
|
+
parseTimeSeconds,
|
|
9
|
+
sanitizeUrl,
|
|
6
10
|
walkMarkdownTree
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MWSGNEH6.js";
|
|
8
12
|
|
|
9
13
|
// src/doc/asciiDiagram/chars.ts
|
|
10
14
|
function toCells(s) {
|
|
@@ -1207,6 +1211,277 @@ function isLattice(diagram) {
|
|
|
1207
1211
|
);
|
|
1208
1212
|
}
|
|
1209
1213
|
|
|
1214
|
+
// src/doc/templates/diagramLayout.ts
|
|
1215
|
+
var DEFAULT_OPTS = {
|
|
1216
|
+
gapX: 260,
|
|
1217
|
+
gapY: 140,
|
|
1218
|
+
gridPad: 80,
|
|
1219
|
+
gridOriginX: 80,
|
|
1220
|
+
gridOriginY: 80
|
|
1221
|
+
};
|
|
1222
|
+
function computeDiagramLayout(children, options = {}) {
|
|
1223
|
+
const opts = { ...DEFAULT_OPTS, ...options };
|
|
1224
|
+
const warnings = [];
|
|
1225
|
+
const entries = [];
|
|
1226
|
+
const unpositionedIdx = [];
|
|
1227
|
+
for (let i = 0; i < children.length; i++) {
|
|
1228
|
+
const c = children[i];
|
|
1229
|
+
const hasX = typeof c.x === "number";
|
|
1230
|
+
const hasY = typeof c.y === "number";
|
|
1231
|
+
if (hasX && hasY) {
|
|
1232
|
+
entries.push({ child: c, pinned: true, x: c.x, y: c.y });
|
|
1233
|
+
} else {
|
|
1234
|
+
entries.push({ child: c, pinned: false, x: 0, y: 0 });
|
|
1235
|
+
unpositionedIdx.push(i);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (unpositionedIdx.length > 0) {
|
|
1239
|
+
const cols = Math.max(1, Math.ceil(Math.sqrt(unpositionedIdx.length)));
|
|
1240
|
+
let baseX = opts.gridOriginX;
|
|
1241
|
+
let baseY = opts.gridOriginY;
|
|
1242
|
+
const pinned = entries.filter((e) => e.pinned);
|
|
1243
|
+
if (pinned.length > 0) {
|
|
1244
|
+
const minX = Math.min(...pinned.map((e) => e.x));
|
|
1245
|
+
const maxY = Math.max(...pinned.map((e) => e.y));
|
|
1246
|
+
baseX = minX;
|
|
1247
|
+
baseY = maxY + opts.gridPad + opts.gapY;
|
|
1248
|
+
}
|
|
1249
|
+
unpositionedIdx.forEach((entryIdx, k) => {
|
|
1250
|
+
const col = k % cols;
|
|
1251
|
+
const row = Math.floor(k / cols);
|
|
1252
|
+
entries[entryIdx].x = baseX + col * opts.gapX;
|
|
1253
|
+
entries[entryIdx].y = baseY + row * opts.gapY;
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
const nodes = entries.map((e) => ({
|
|
1257
|
+
id: e.child.id,
|
|
1258
|
+
label: e.child.title ?? e.child.id,
|
|
1259
|
+
x: e.x,
|
|
1260
|
+
y: e.y,
|
|
1261
|
+
pinned: e.pinned,
|
|
1262
|
+
...e.child.classes && e.child.classes.length > 0 ? { classes: e.child.classes } : {}
|
|
1263
|
+
}));
|
|
1264
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
1265
|
+
const edges = [];
|
|
1266
|
+
const seenEdgeIds = /* @__PURE__ */ new Set();
|
|
1267
|
+
for (const child of children) {
|
|
1268
|
+
if (!child.connectsTo) continue;
|
|
1269
|
+
for (const conn of child.connectsTo) {
|
|
1270
|
+
if (!nodeIds.has(conn.target)) {
|
|
1271
|
+
warnings.push(`Edge from "${child.id}" to "${conn.target}" dropped: no such sibling node`);
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
const edgeId = makeEdgeId(child.id, conn);
|
|
1275
|
+
if (seenEdgeIds.has(edgeId)) continue;
|
|
1276
|
+
seenEdgeIds.add(edgeId);
|
|
1277
|
+
edges.push({
|
|
1278
|
+
id: edgeId,
|
|
1279
|
+
source: child.id,
|
|
1280
|
+
target: conn.target,
|
|
1281
|
+
...conn.type ? { type: conn.type } : {}
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return { nodes, edges, warnings };
|
|
1286
|
+
}
|
|
1287
|
+
function makeEdgeId(source, conn) {
|
|
1288
|
+
return conn.type ? `${source}->${conn.target}:${conn.type}` : `${source}->${conn.target}`;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// src/doc/asciiDiagram/types.ts
|
|
1292
|
+
var ASCII_CHAR_W = 14;
|
|
1293
|
+
var ASCII_CHAR_H = 28;
|
|
1294
|
+
|
|
1295
|
+
// src/doc/asciiDiagram/mapping.ts
|
|
1296
|
+
function asciiCellToCanvas(col, row) {
|
|
1297
|
+
return { x: col * ASCII_CHAR_W, y: row * ASCII_CHAR_H };
|
|
1298
|
+
}
|
|
1299
|
+
function canvasToAsciiCell(x, y) {
|
|
1300
|
+
return {
|
|
1301
|
+
col: Math.max(0, Math.round(x / ASCII_CHAR_W)),
|
|
1302
|
+
row: Math.max(0, Math.round(y / ASCII_CHAR_H))
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function asciiDiagramToTemplateData(diagram) {
|
|
1306
|
+
const nodes = diagram.nodes.map((n) => ({
|
|
1307
|
+
id: n.id,
|
|
1308
|
+
label: n.label,
|
|
1309
|
+
...asciiCellToCanvas(n.col, n.row),
|
|
1310
|
+
w: n.wCols * ASCII_CHAR_W,
|
|
1311
|
+
h: n.hRows * ASCII_CHAR_H,
|
|
1312
|
+
...n.containerId ? { container: n.containerId } : {}
|
|
1313
|
+
}));
|
|
1314
|
+
const anchors = inferEdgeAnchors(nodes, diagram.edges);
|
|
1315
|
+
const edges = diagram.edges.map((e, index) => ({
|
|
1316
|
+
source: e.source,
|
|
1317
|
+
target: e.target,
|
|
1318
|
+
...e.label ? { label: e.label } : {},
|
|
1319
|
+
...e.directed ? {} : { directed: false },
|
|
1320
|
+
...anchors[index],
|
|
1321
|
+
routing: "orthogonal"
|
|
1322
|
+
}));
|
|
1323
|
+
return { nodes, edges };
|
|
1324
|
+
}
|
|
1325
|
+
function inferEdgeAnchors(nodes, edges) {
|
|
1326
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1327
|
+
const pairs = edges.map((edge) => {
|
|
1328
|
+
const source = nodeById.get(edge.source);
|
|
1329
|
+
const target = nodeById.get(edge.target);
|
|
1330
|
+
if (!source || !target) {
|
|
1331
|
+
return {
|
|
1332
|
+
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1333
|
+
targetAnchor: { side: "left", offset: 0.5 }
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1337
|
+
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1338
|
+
const targetW = target.w ?? ASCII_CHAR_W * 12;
|
|
1339
|
+
const targetH = target.h ?? ASCII_CHAR_H * 3;
|
|
1340
|
+
const dx = target.x + targetW / 2 - (source.x + sourceW / 2);
|
|
1341
|
+
const dy = target.y + targetH / 2 - (source.y + sourceH / 2);
|
|
1342
|
+
const horizontalDistance = Math.abs(dx) / Math.max(1, (sourceW + targetW) / 2);
|
|
1343
|
+
const verticalDistance = Math.abs(dy) / Math.max(1, (sourceH + targetH) / 2);
|
|
1344
|
+
if (verticalDistance >= horizontalDistance) {
|
|
1345
|
+
return dy >= 0 ? {
|
|
1346
|
+
sourceAnchor: { side: "bottom", offset: 0.5 },
|
|
1347
|
+
targetAnchor: { side: "top", offset: 0.5 }
|
|
1348
|
+
} : {
|
|
1349
|
+
sourceAnchor: { side: "top", offset: 0.5 },
|
|
1350
|
+
targetAnchor: { side: "bottom", offset: 0.5 }
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
return dx >= 0 ? {
|
|
1354
|
+
sourceAnchor: { side: "right", offset: 0.5 },
|
|
1355
|
+
targetAnchor: { side: "left", offset: 0.5 }
|
|
1356
|
+
} : {
|
|
1357
|
+
sourceAnchor: { side: "left", offset: 0.5 },
|
|
1358
|
+
targetAnchor: { side: "right", offset: 0.5 }
|
|
1359
|
+
};
|
|
1360
|
+
});
|
|
1361
|
+
distributeOffsets(pairs, edges, nodes, "source");
|
|
1362
|
+
distributeOffsets(pairs, edges, nodes, "target");
|
|
1363
|
+
coalesceAlignedFanOutAnchors(pairs, edges, nodes);
|
|
1364
|
+
return pairs;
|
|
1365
|
+
}
|
|
1366
|
+
function coalesceAlignedFanOutAnchors(pairs, edges, nodes) {
|
|
1367
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1368
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1369
|
+
edges.forEach((edge, index) => {
|
|
1370
|
+
const group = groups.get(edge.source) ?? [];
|
|
1371
|
+
group.push(index);
|
|
1372
|
+
groups.set(edge.source, group);
|
|
1373
|
+
});
|
|
1374
|
+
for (const [sourceId, indexes] of groups) {
|
|
1375
|
+
if (indexes.length < 2) continue;
|
|
1376
|
+
const source = nodeById.get(sourceId);
|
|
1377
|
+
const targets = indexes.map((index) => nodeById.get(edges[index].target)).filter((node) => node !== void 0);
|
|
1378
|
+
if (!source || targets.length !== indexes.length) continue;
|
|
1379
|
+
const sourceW = source.w ?? ASCII_CHAR_W * 12;
|
|
1380
|
+
const sourceH = source.h ?? ASCII_CHAR_H * 3;
|
|
1381
|
+
const sourceRight = source.x + sourceW;
|
|
1382
|
+
const sourceBottom = source.y + sourceH;
|
|
1383
|
+
const targetRight = (target) => target.x + (target.w ?? ASCII_CHAR_W * 12);
|
|
1384
|
+
const targetBottom = (target) => target.y + (target.h ?? ASCII_CHAR_H * 3);
|
|
1385
|
+
let sourceSide;
|
|
1386
|
+
let targetSide;
|
|
1387
|
+
if (targets.every((target) => target.y >= sourceBottom)) {
|
|
1388
|
+
sourceSide = "bottom";
|
|
1389
|
+
targetSide = "top";
|
|
1390
|
+
} else if (targets.every((target) => targetBottom(target) <= source.y)) {
|
|
1391
|
+
sourceSide = "top";
|
|
1392
|
+
targetSide = "bottom";
|
|
1393
|
+
} else if (targets.every((target) => target.x >= sourceRight)) {
|
|
1394
|
+
sourceSide = "right";
|
|
1395
|
+
targetSide = "left";
|
|
1396
|
+
} else if (targets.every((target) => targetRight(target) <= source.x)) {
|
|
1397
|
+
sourceSide = "left";
|
|
1398
|
+
targetSide = "right";
|
|
1399
|
+
}
|
|
1400
|
+
if (!sourceSide || !targetSide) continue;
|
|
1401
|
+
for (const index of indexes) {
|
|
1402
|
+
pairs[index] = {
|
|
1403
|
+
sourceAnchor: { side: sourceSide, offset: 0.5 },
|
|
1404
|
+
targetAnchor: { side: targetSide, offset: 0.5 }
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function distributeOffsets(pairs, edges, nodes, endpoint) {
|
|
1410
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
1411
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1412
|
+
for (let index = 0; index < edges.length; index++) {
|
|
1413
|
+
const edge = edges[index];
|
|
1414
|
+
const anchor = endpoint === "source" ? pairs[index].sourceAnchor : pairs[index].targetAnchor;
|
|
1415
|
+
const nodeId = endpoint === "source" ? edge.source : edge.target;
|
|
1416
|
+
const key = `${nodeId}:${anchor.side}`;
|
|
1417
|
+
const group = groups.get(key) ?? [];
|
|
1418
|
+
group.push(index);
|
|
1419
|
+
groups.set(key, group);
|
|
1420
|
+
}
|
|
1421
|
+
for (const indexes of groups.values()) {
|
|
1422
|
+
if (indexes.length < 2) continue;
|
|
1423
|
+
const side = endpoint === "source" ? pairs[indexes[0]].sourceAnchor.side : pairs[indexes[0]].targetAnchor.side;
|
|
1424
|
+
indexes.sort((a, b) => {
|
|
1425
|
+
const oppositeA = nodeById.get(endpoint === "source" ? edges[a].target : edges[a].source);
|
|
1426
|
+
const oppositeB = nodeById.get(endpoint === "source" ? edges[b].target : edges[b].source);
|
|
1427
|
+
const axisA = side === "top" || side === "bottom" ? oppositeA?.x ?? 0 : oppositeA?.y ?? 0;
|
|
1428
|
+
const axisB = side === "top" || side === "bottom" ? oppositeB?.x ?? 0 : oppositeB?.y ?? 0;
|
|
1429
|
+
return axisA - axisB || a - b;
|
|
1430
|
+
});
|
|
1431
|
+
indexes.forEach((edgeIndex, rank) => {
|
|
1432
|
+
const anchor = endpoint === "source" ? pairs[edgeIndex].sourceAnchor : pairs[edgeIndex].targetAnchor;
|
|
1433
|
+
anchor.offset = (rank + 1) / (indexes.length + 1);
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
function asciiDiagramFromTemplateData(nodes, edges, options = {}) {
|
|
1438
|
+
const ids = new Set(nodes.map((n) => n.id));
|
|
1439
|
+
const outNodes = nodes.map((n) => {
|
|
1440
|
+
const { col, row } = canvasToAsciiCell(n.x, n.y);
|
|
1441
|
+
const labelLines = n.label.length > 0 ? n.label.split("\n") : [];
|
|
1442
|
+
const innerW = labelLines.reduce((w, l) => Math.max(w, l.length), 0);
|
|
1443
|
+
const wCols = typeof n.w === "number" ? Math.max(3, Math.round(n.w / ASCII_CHAR_W)) : innerW + 4;
|
|
1444
|
+
const hRows = typeof n.h === "number" ? Math.max(3, Math.round(n.h / ASCII_CHAR_H)) : labelLines.length + 2;
|
|
1445
|
+
return {
|
|
1446
|
+
id: n.id,
|
|
1447
|
+
label: n.label,
|
|
1448
|
+
col,
|
|
1449
|
+
row,
|
|
1450
|
+
wCols: Math.max(wCols, 3),
|
|
1451
|
+
hRows: Math.max(hRows, 3),
|
|
1452
|
+
...n.container && ids.has(n.container) && n.container !== n.id ? { containerId: n.container } : {}
|
|
1453
|
+
};
|
|
1454
|
+
});
|
|
1455
|
+
return {
|
|
1456
|
+
nodes: outNodes,
|
|
1457
|
+
edges: edges.filter((e) => ids.has(e.source) && ids.has(e.target)).map((e) => ({
|
|
1458
|
+
source: e.source,
|
|
1459
|
+
target: e.target,
|
|
1460
|
+
...e.label ? { label: e.label } : {},
|
|
1461
|
+
directed: e.directed !== false
|
|
1462
|
+
})),
|
|
1463
|
+
style: options.style ?? "unicode",
|
|
1464
|
+
warnings: []
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function asciiDiagramFromBlocks(children, options = {}) {
|
|
1468
|
+
const layout = computeDiagramLayout(children);
|
|
1469
|
+
const nodes = layout.nodes.map((n) => ({
|
|
1470
|
+
id: n.id,
|
|
1471
|
+
label: n.label,
|
|
1472
|
+
x: n.x,
|
|
1473
|
+
y: n.y
|
|
1474
|
+
}));
|
|
1475
|
+
const edges = layout.edges.map((e) => ({
|
|
1476
|
+
source: e.source,
|
|
1477
|
+
target: e.target,
|
|
1478
|
+
...e.type ? { label: e.type } : {}
|
|
1479
|
+
}));
|
|
1480
|
+
const diagram = asciiDiagramFromTemplateData(nodes, edges, options);
|
|
1481
|
+
diagram.warnings.push(...layout.warnings);
|
|
1482
|
+
return diagram;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1210
1485
|
// src/doc/asciiTimeline/wrappedFlow.ts
|
|
1211
1486
|
var UNDERLINE_RE = /[─━═-]{6,}/gu;
|
|
1212
1487
|
var FLOW_CHAR_RE = /[─━═│|►◄→←┌┐└┘├┤]/u;
|
|
@@ -2213,6 +2488,33 @@ function isAsciiTimelineFence(node) {
|
|
|
2213
2488
|
return detectAsciiTimeline(node.value, { explicit: isExplicitTimelineLang(node.lang) }).isTimeline;
|
|
2214
2489
|
}
|
|
2215
2490
|
|
|
2491
|
+
// src/doc/asciiTimeline/mapping.ts
|
|
2492
|
+
function asciiTimelineToTemplateData(timeline) {
|
|
2493
|
+
const globalStart = timeline.tracks.length > 0 ? Math.min(...timeline.tracks.map((track) => track.startColumn)) : 0;
|
|
2494
|
+
const globalEnd = timeline.tracks.length > 0 ? Math.max(...timeline.tracks.map((track) => track.endColumn)) : 1;
|
|
2495
|
+
const span = Math.max(1, globalEnd - globalStart);
|
|
2496
|
+
const tracks = timeline.tracks.map((track) => ({
|
|
2497
|
+
id: track.id,
|
|
2498
|
+
...track.label ? { label: track.label } : {},
|
|
2499
|
+
...track.endLabel ? { endLabel: track.endLabel } : {},
|
|
2500
|
+
events: track.events.map((event) => ({
|
|
2501
|
+
id: event.id,
|
|
2502
|
+
label: event.label,
|
|
2503
|
+
...event.description ? { description: event.description } : {},
|
|
2504
|
+
position: clamp01((event.column - globalStart) / span),
|
|
2505
|
+
...event.side ? { side: event.side } : {},
|
|
2506
|
+
...event.descriptionSide ? { descriptionSide: event.descriptionSide } : {},
|
|
2507
|
+
...event.callout === false ? { callout: false } : {},
|
|
2508
|
+
...event.marker ? { marker: event.marker } : {}
|
|
2509
|
+
}))
|
|
2510
|
+
}));
|
|
2511
|
+
const links = timeline.links.map((link) => ({ ...link }));
|
|
2512
|
+
return { tracks, links };
|
|
2513
|
+
}
|
|
2514
|
+
function clamp01(value) {
|
|
2515
|
+
return Math.max(0, Math.min(1, value));
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2216
2518
|
// src/doc/treeview/chars.ts
|
|
2217
2519
|
var RAIL_CHARS = /* @__PURE__ */ new Set(["\u2502", "\u2503", "\u254E", "\u254F", "\u2551", "|"]);
|
|
2218
2520
|
var BRANCH_TEE = /* @__PURE__ */ new Set(["\u251C", "\u2523", "\u2520", "\u251D", "\u2560", "\u255E"]);
|
|
@@ -2450,6 +2752,61 @@ function countNodes(nodes) {
|
|
|
2450
2752
|
return n;
|
|
2451
2753
|
}
|
|
2452
2754
|
|
|
2755
|
+
// src/doc/treeview/mapping.ts
|
|
2756
|
+
function treeToTemplateData(tree) {
|
|
2757
|
+
return { items: tree.roots.map(toItem) };
|
|
2758
|
+
}
|
|
2759
|
+
function toItem(node) {
|
|
2760
|
+
return {
|
|
2761
|
+
id: node.id,
|
|
2762
|
+
label: node.label,
|
|
2763
|
+
...node.isDir ? { isDir: true } : {},
|
|
2764
|
+
...node.comment ? { comment: node.comment } : {},
|
|
2765
|
+
children: node.children.map(toItem)
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
function treeFromTemplateData(items, options = {}) {
|
|
2769
|
+
const roots = items.map(fromItem);
|
|
2770
|
+
return { roots, style: options.style ?? "unicode", warnings: [] };
|
|
2771
|
+
}
|
|
2772
|
+
function fromItem(item) {
|
|
2773
|
+
const children = Array.isArray(item.children) ? item.children.map(fromItem) : [];
|
|
2774
|
+
return {
|
|
2775
|
+
id: typeof item.id === "string" && item.id ? item.id : "node",
|
|
2776
|
+
label: typeof item.label === "string" ? item.label : "",
|
|
2777
|
+
children,
|
|
2778
|
+
isDir: item.isDir === true || children.length > 0,
|
|
2779
|
+
...item.comment ? { comment: item.comment } : {}
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
function treeFromMarkdownList(list) {
|
|
2783
|
+
const usedIds = /* @__PURE__ */ new Map();
|
|
2784
|
+
const slugify3 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "node";
|
|
2785
|
+
const idFor = (label) => {
|
|
2786
|
+
const base = slugify3(label.replace(/\/$/, ""));
|
|
2787
|
+
const count = usedIds.get(base) ?? 0;
|
|
2788
|
+
usedIds.set(base, count + 1);
|
|
2789
|
+
return count === 0 ? base : `${base}-${count + 1}`;
|
|
2790
|
+
};
|
|
2791
|
+
const itemToNode = (item) => {
|
|
2792
|
+
const labelNode = item.children.find((c) => c.type !== "list");
|
|
2793
|
+
const label = labelNode ? extractPlainText(labelNode).trim() : "";
|
|
2794
|
+
const childLists = item.children.filter((c) => c.type === "list");
|
|
2795
|
+
const children = [];
|
|
2796
|
+
for (const cl of childLists) for (const ci of cl.children) children.push(itemToNode(ci));
|
|
2797
|
+
return {
|
|
2798
|
+
id: idFor(label),
|
|
2799
|
+
label,
|
|
2800
|
+
children,
|
|
2801
|
+
isDir: label.endsWith("/") || children.length > 0
|
|
2802
|
+
};
|
|
2803
|
+
};
|
|
2804
|
+
return { roots: list.children.map(itemToNode), style: "unicode", warnings: [] };
|
|
2805
|
+
}
|
|
2806
|
+
function findFirstList(contents) {
|
|
2807
|
+
return contents?.find((n) => n.type === "list");
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2453
2810
|
// src/recommend/numberHighlight.ts
|
|
2454
2811
|
var NUMBER_HIGHLIGHT_RE = /(?:[$€£¥]\s?\d+(?:[.,]\d+)*(?:\s?(?:[MBK]|million|billion|thousand))?|\d+(?:[.,]\d+)*\s?(?:%|‰|x|×|[MBK]|million|billion|thousand|percent|years?|days?|hours?)|\d{3,}(?:[.,]\d+)*)/i;
|
|
2455
2812
|
function matchNumberHighlight(text) {
|
|
@@ -2462,6 +2819,649 @@ function matchNumberHighlight(text) {
|
|
|
2462
2819
|
};
|
|
2463
2820
|
}
|
|
2464
2821
|
|
|
2822
|
+
// src/doc/templateInputs.ts
|
|
2823
|
+
var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
|
|
2824
|
+
function extractHostedVideos(contents, limit = Infinity) {
|
|
2825
|
+
const found = [];
|
|
2826
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2827
|
+
for (const node of contents ?? []) {
|
|
2828
|
+
if (found.length >= limit) break;
|
|
2829
|
+
const video = findBlockVideoEmbed(node);
|
|
2830
|
+
if (!video || seen.has(video.embed.watchUrl)) continue;
|
|
2831
|
+
seen.add(video.embed.watchUrl);
|
|
2832
|
+
found.push({
|
|
2833
|
+
url: video.embed.watchUrl,
|
|
2834
|
+
title: video.title,
|
|
2835
|
+
aspectRatio: video.embed.aspectRatio
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
return found;
|
|
2839
|
+
}
|
|
2840
|
+
function isPictureFence(node) {
|
|
2841
|
+
if (node.type !== "code") return false;
|
|
2842
|
+
return node.lang?.trim().toLowerCase() === "mermaid" || isSvgIslandLang(node.lang);
|
|
2843
|
+
}
|
|
2844
|
+
function isMaterializedMediaNode(node) {
|
|
2845
|
+
if (node.type === "code") return isPictureFence(node);
|
|
2846
|
+
return findBlockVideoEmbed(node) !== null;
|
|
2847
|
+
}
|
|
2848
|
+
function extractBodyPlainText(contents) {
|
|
2849
|
+
if (!contents || contents.length === 0) return "";
|
|
2850
|
+
return contents.filter((node) => !isMaterializedMediaNode(node)).map((n) => extractPlainText(n)).join("\n").trim();
|
|
2851
|
+
}
|
|
2852
|
+
function extractListItems(contents) {
|
|
2853
|
+
return extractRichListItems(contents).map((item) => item.text);
|
|
2854
|
+
}
|
|
2855
|
+
function escapeInlineHtml(value) {
|
|
2856
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2857
|
+
}
|
|
2858
|
+
function renderInlineHtml(nodes) {
|
|
2859
|
+
return nodes.map((node) => {
|
|
2860
|
+
switch (node.type) {
|
|
2861
|
+
case "text":
|
|
2862
|
+
return escapeInlineHtml(node.value);
|
|
2863
|
+
case "emphasis":
|
|
2864
|
+
return `<em>${renderInlineHtml(node.children)}</em>`;
|
|
2865
|
+
case "strong":
|
|
2866
|
+
return `<strong>${renderInlineHtml(node.children)}</strong>`;
|
|
2867
|
+
case "delete":
|
|
2868
|
+
return `<del>${renderInlineHtml(node.children)}</del>`;
|
|
2869
|
+
case "superscript":
|
|
2870
|
+
return `<sup>${renderInlineHtml(node.children)}</sup>`;
|
|
2871
|
+
case "subscript":
|
|
2872
|
+
return `<sub>${renderInlineHtml(node.children)}</sub>`;
|
|
2873
|
+
case "inlineCode":
|
|
2874
|
+
return `<code>${escapeInlineHtml(node.value)}</code>`;
|
|
2875
|
+
case "link": {
|
|
2876
|
+
const label = renderInlineHtml(node.children);
|
|
2877
|
+
const href = sanitizeUrl(node.url, "link");
|
|
2878
|
+
if (!href) return label;
|
|
2879
|
+
const title = node.title ? ` title="${escapeInlineHtml(node.title)}"` : "";
|
|
2880
|
+
return `<a href="${escapeInlineHtml(href)}"${title}>${label}</a>`;
|
|
2881
|
+
}
|
|
2882
|
+
case "break":
|
|
2883
|
+
return "<br>";
|
|
2884
|
+
case "image":
|
|
2885
|
+
return escapeInlineHtml(node.alt ?? "");
|
|
2886
|
+
case "inlineMath":
|
|
2887
|
+
return escapeInlineHtml(`$${node.value}$`);
|
|
2888
|
+
case "footnoteReference":
|
|
2889
|
+
return escapeInlineHtml(`[^${node.label ?? node.identifier}]`);
|
|
2890
|
+
case "linkReference":
|
|
2891
|
+
case "textDirective":
|
|
2892
|
+
return renderInlineHtml(node.children);
|
|
2893
|
+
case "imageReference":
|
|
2894
|
+
return escapeInlineHtml(node.alt ?? "");
|
|
2895
|
+
case "mention":
|
|
2896
|
+
return escapeInlineHtml(`@${node.displayName}`);
|
|
2897
|
+
case "inlineIcon":
|
|
2898
|
+
return `<i class="fa-${node.family} fa-${node.name}" aria-hidden="true"></i>`;
|
|
2899
|
+
case "htmlInline":
|
|
2900
|
+
return escapeInlineHtml(node.rawHtml);
|
|
2901
|
+
}
|
|
2902
|
+
}).join("");
|
|
2903
|
+
}
|
|
2904
|
+
function renderMarkdownBlocksHtml(nodes, builds) {
|
|
2905
|
+
const renderBlocks = (blocks) => blocks.map((node, index) => {
|
|
2906
|
+
const previous = blocks[index - 1];
|
|
2907
|
+
const extraBlankLines = previous ? Math.max(0, markdownBlockSeparatorLines(previous, node) - 2) : 0;
|
|
2908
|
+
const sourceGap = '<div data-squisq-source-gap aria-hidden="true"><br></div>'.repeat(
|
|
2909
|
+
extraBlankLines
|
|
2910
|
+
);
|
|
2911
|
+
const html = renderBlock(node);
|
|
2912
|
+
const step = builds?.targets.get(node);
|
|
2913
|
+
return `${sourceGap}${step === void 0 ? html : `<div data-squisq-build-step="${step}">${html}</div>`}`;
|
|
2914
|
+
}).join("");
|
|
2915
|
+
const renderList = (node) => {
|
|
2916
|
+
const tag = node.ordered ? "ol" : "ul";
|
|
2917
|
+
const start = node.ordered && node.start != null ? ` start="${node.start}"` : "";
|
|
2918
|
+
const items = node.children.map((item, index) => {
|
|
2919
|
+
const taskMarker = item.checked == null ? "" : `<span>${item.checked ? "[x]" : "[ ]"} </span>`;
|
|
2920
|
+
const step = builds?.targets.get(item);
|
|
2921
|
+
if (step === void 0) return `<li>${taskMarker}${renderBlocks(item.children)}</li>`;
|
|
2922
|
+
const marker = item.checked == null ? node.ordered ? `${(node.start ?? 1) + index}.` : "\u2022" : item.checked ? "[x]" : "[ ]";
|
|
2923
|
+
let first = true;
|
|
2924
|
+
let own = [];
|
|
2925
|
+
const fragments = [];
|
|
2926
|
+
const flush = () => {
|
|
2927
|
+
if (!first && !own.length) return;
|
|
2928
|
+
fragments.push(
|
|
2929
|
+
`<div data-squisq-build-step="${step}" data-squisq-build-row><span aria-hidden="true">${first ? marker : ""}</span><div>${renderBlocks(own)}</div></div>`
|
|
2930
|
+
);
|
|
2931
|
+
first = false;
|
|
2932
|
+
own = [];
|
|
2933
|
+
};
|
|
2934
|
+
for (const child of item.children) {
|
|
2935
|
+
if (child.type === "list") {
|
|
2936
|
+
flush();
|
|
2937
|
+
fragments.push(renderList(child));
|
|
2938
|
+
} else own.push(child);
|
|
2939
|
+
}
|
|
2940
|
+
flush();
|
|
2941
|
+
return `<li data-squisq-build-item>${fragments.join("")}</li>`;
|
|
2942
|
+
}).join("");
|
|
2943
|
+
return `<${tag}${start}>${items}</${tag}>`;
|
|
2944
|
+
};
|
|
2945
|
+
const renderBlock = (node) => {
|
|
2946
|
+
switch (node.type) {
|
|
2947
|
+
case "paragraph":
|
|
2948
|
+
return `<p>${renderInlineHtml(node.children)}</p>`;
|
|
2949
|
+
case "heading":
|
|
2950
|
+
return `<h${node.depth}>${renderInlineHtml(node.children)}</h${node.depth}>`;
|
|
2951
|
+
case "blockquote":
|
|
2952
|
+
return `<blockquote>${renderBlocks(node.children)}</blockquote>`;
|
|
2953
|
+
case "list":
|
|
2954
|
+
return renderList(node);
|
|
2955
|
+
case "code":
|
|
2956
|
+
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
2957
|
+
case "thematicBreak":
|
|
2958
|
+
return "<hr>";
|
|
2959
|
+
case "table": {
|
|
2960
|
+
const [head, ...body] = node.children;
|
|
2961
|
+
const row = (cells, header) => `<tr>${cells.map((cell, columnIndex) => {
|
|
2962
|
+
const tag = header ? "th" : "td";
|
|
2963
|
+
const alignment = node.align?.[columnIndex];
|
|
2964
|
+
const alignAttribute = alignment ? ` align="${alignment}"` : "";
|
|
2965
|
+
return `<${tag}${alignAttribute}>${renderInlineHtml(cell.children)}</${tag}>`;
|
|
2966
|
+
}).join("")}</tr>`;
|
|
2967
|
+
const thead = head ? `<thead>${row(head.children, true)}</thead>` : "";
|
|
2968
|
+
const tbody = body.length ? `<tbody>${body.map((item) => row(item.children, false)).join("")}</tbody>` : "";
|
|
2969
|
+
return `<div data-squisq-table-scroll role="region" aria-label="Scrollable data table"><table>${thead}${tbody}</table></div>`;
|
|
2970
|
+
}
|
|
2971
|
+
case "math":
|
|
2972
|
+
return `<pre><code>${escapeInlineHtml(node.value)}</code></pre>`;
|
|
2973
|
+
case "definition":
|
|
2974
|
+
return "";
|
|
2975
|
+
default: {
|
|
2976
|
+
const text = extractPlainText(node).trim();
|
|
2977
|
+
return text ? `<p>${escapeInlineHtml(text)}</p>` : "";
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
};
|
|
2981
|
+
return renderBlocks(nodes);
|
|
2982
|
+
}
|
|
2983
|
+
function markdownBlockSeparatorLines(previous, next) {
|
|
2984
|
+
const previousEndLine = previous.position?.end.line;
|
|
2985
|
+
const nextStartLine = next.position?.start.line;
|
|
2986
|
+
if (previousEndLine == null || nextStartLine == null || nextStartLine <= previousEndLine) {
|
|
2987
|
+
return 2;
|
|
2988
|
+
}
|
|
2989
|
+
return Math.max(2, nextStartLine - previousEndLine);
|
|
2990
|
+
}
|
|
2991
|
+
function renderListItemHtml(markdown) {
|
|
2992
|
+
return markdown.map(
|
|
2993
|
+
(node) => node.type === "paragraph" || node.type === "heading" ? renderInlineHtml(node.children) : escapeInlineHtml(extractPlainText(node))
|
|
2994
|
+
).join("<br>");
|
|
2995
|
+
}
|
|
2996
|
+
function extractRichListItems(contents) {
|
|
2997
|
+
if (!contents) return [];
|
|
2998
|
+
const items = [];
|
|
2999
|
+
for (const node of contents) {
|
|
3000
|
+
if (node.type !== "list") continue;
|
|
3001
|
+
for (const item of node.children) {
|
|
3002
|
+
const text = extractPlainText(item).trim();
|
|
3003
|
+
if (!text) continue;
|
|
3004
|
+
const html = renderListItemHtml(item.children);
|
|
3005
|
+
const plainHtml = escapeInlineHtml(text).replace(/\r?\n/g, "<br>");
|
|
3006
|
+
items.push({
|
|
3007
|
+
text,
|
|
3008
|
+
markdown: item.children,
|
|
3009
|
+
...html && html !== plainHtml ? { html } : {}
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
return items;
|
|
3014
|
+
}
|
|
3015
|
+
function parseDim(raw) {
|
|
3016
|
+
if (raw === void 0) return void 0;
|
|
3017
|
+
const n = parseFloat(raw);
|
|
3018
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
3019
|
+
}
|
|
3020
|
+
function extractImages(contents, limit = Infinity) {
|
|
3021
|
+
if (!contents || contents.length === 0) return [];
|
|
3022
|
+
const found = [];
|
|
3023
|
+
function fromHtml(nodes) {
|
|
3024
|
+
for (const node of nodes) {
|
|
3025
|
+
if (found.length >= limit) return;
|
|
3026
|
+
if (!node || typeof node !== "object") continue;
|
|
3027
|
+
const n = node;
|
|
3028
|
+
if (n.type === "htmlElement" && n.tagName === "img") {
|
|
3029
|
+
const attrs = n.attributes;
|
|
3030
|
+
if (attrs && typeof attrs.src === "string" && attrs.src) {
|
|
3031
|
+
found.push({
|
|
3032
|
+
src: attrs.src,
|
|
3033
|
+
alt: typeof attrs.alt === "string" ? attrs.alt : "",
|
|
3034
|
+
width: parseDim(attrs.width),
|
|
3035
|
+
height: parseDim(attrs.height)
|
|
3036
|
+
});
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
if (Array.isArray(n.children)) fromHtml(n.children);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
function walk(node) {
|
|
3043
|
+
if (found.length >= limit) return;
|
|
3044
|
+
if (!node || typeof node !== "object") return;
|
|
3045
|
+
const n = node;
|
|
3046
|
+
if (n.type === "image" && typeof n.url === "string" && n.url) {
|
|
3047
|
+
if (!mediaKindForUrl(n.url)) {
|
|
3048
|
+
found.push({ src: n.url, alt: typeof n.alt === "string" ? n.alt : "" });
|
|
3049
|
+
}
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
if ((n.type === "htmlBlock" || n.type === "htmlInline") && Array.isArray(n.htmlChildren)) {
|
|
3053
|
+
fromHtml(n.htmlChildren);
|
|
3054
|
+
}
|
|
3055
|
+
if (Array.isArray(n.children)) {
|
|
3056
|
+
for (const child of n.children) walk(child);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
for (const node of contents) {
|
|
3060
|
+
if (found.length >= limit) break;
|
|
3061
|
+
walk(node);
|
|
3062
|
+
}
|
|
3063
|
+
return found;
|
|
3064
|
+
}
|
|
3065
|
+
function extractFirstImage(contents) {
|
|
3066
|
+
return extractImages(contents, 1)[0] ?? null;
|
|
3067
|
+
}
|
|
3068
|
+
function extractEmbeddedVideos(contents, limit = Infinity) {
|
|
3069
|
+
if (!contents || contents.length === 0) return [];
|
|
3070
|
+
const found = [];
|
|
3071
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3072
|
+
const add = (video) => {
|
|
3073
|
+
if (!video.src || found.length >= limit || seen.has(video.src)) return;
|
|
3074
|
+
seen.add(video.src);
|
|
3075
|
+
found.push(video);
|
|
3076
|
+
};
|
|
3077
|
+
function nestedSource(children) {
|
|
3078
|
+
if (!Array.isArray(children)) return void 0;
|
|
3079
|
+
for (const child of children) {
|
|
3080
|
+
if (!child || typeof child !== "object") continue;
|
|
3081
|
+
const node = child;
|
|
3082
|
+
if (node.tagName === "source") {
|
|
3083
|
+
const attrs = node.attributes;
|
|
3084
|
+
if (typeof attrs?.src === "string" && attrs.src) return attrs.src;
|
|
3085
|
+
}
|
|
3086
|
+
const nested = nestedSource(node.children);
|
|
3087
|
+
if (nested) return nested;
|
|
3088
|
+
}
|
|
3089
|
+
return void 0;
|
|
3090
|
+
}
|
|
3091
|
+
function walk(node) {
|
|
3092
|
+
if (found.length >= limit || !node || typeof node !== "object") return;
|
|
3093
|
+
const n = node;
|
|
3094
|
+
if (n.type === "htmlElement" && n.tagName === "video") {
|
|
3095
|
+
const attrs = n.attributes;
|
|
3096
|
+
const src = attrs?.src || nestedSource(n.children);
|
|
3097
|
+
if (src) {
|
|
3098
|
+
const startAt = attrs?.["data-squisq-video-start-at"] ? parseTimeSeconds(attrs["data-squisq-video-start-at"]) : null;
|
|
3099
|
+
const clipStart = attrs?.["data-squisq-video-clip-start"] ? parseTimeSeconds(attrs["data-squisq-video-clip-start"]) : null;
|
|
3100
|
+
const clipEnd = attrs?.["data-squisq-video-clip-end"] ? parseTimeSeconds(attrs["data-squisq-video-clip-end"]) : null;
|
|
3101
|
+
add({
|
|
3102
|
+
src,
|
|
3103
|
+
...attrs?.poster ? { posterSrc: attrs.poster } : {},
|
|
3104
|
+
alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || "",
|
|
3105
|
+
...startAt != null ? { startAt } : {},
|
|
3106
|
+
...clipStart != null ? { clipStart } : {},
|
|
3107
|
+
...clipEnd != null ? { clipEnd } : {}
|
|
3108
|
+
});
|
|
3109
|
+
}
|
|
3110
|
+
} else if ((n.type === "link" || n.type === "image") && typeof n.url === "string" && VIDEO_FILE_RE.test(n.url)) {
|
|
3111
|
+
add({
|
|
3112
|
+
src: n.url,
|
|
3113
|
+
alt: typeof n.alt === "string" ? n.alt : extractPlainText(n).trim()
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
if (Array.isArray(n.children)) n.children.forEach(walk);
|
|
3117
|
+
if (Array.isArray(n.htmlChildren)) n.htmlChildren.forEach(walk);
|
|
3118
|
+
}
|
|
3119
|
+
contents.forEach(walk);
|
|
3120
|
+
return found;
|
|
3121
|
+
}
|
|
3122
|
+
function extractFirstEmbeddedVideo(contents) {
|
|
3123
|
+
return extractEmbeddedVideos(contents, 1)[0] ?? null;
|
|
3124
|
+
}
|
|
3125
|
+
function extractTableFromContents(contents) {
|
|
3126
|
+
if (!contents) return null;
|
|
3127
|
+
for (const node of contents) {
|
|
3128
|
+
if (node.type === "table") {
|
|
3129
|
+
const table = node;
|
|
3130
|
+
const [headerRow, ...bodyRows] = table.children;
|
|
3131
|
+
if (!headerRow) return null;
|
|
3132
|
+
const headers = headerRow.children.map((cell) => extractPlainText(cell).trim());
|
|
3133
|
+
const rows = bodyRows.map((row) => row.children.map((cell) => extractPlainText(cell).trim()));
|
|
3134
|
+
return { headers, rows, align: table.align };
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
return null;
|
|
3138
|
+
}
|
|
3139
|
+
function extractBlockquoteText(contents) {
|
|
3140
|
+
if (!contents) return "";
|
|
3141
|
+
for (const node of contents) {
|
|
3142
|
+
if (node.type === "blockquote") return extractPlainText(node).trim();
|
|
3143
|
+
}
|
|
3144
|
+
return "";
|
|
3145
|
+
}
|
|
3146
|
+
function firstParagraph(contents) {
|
|
3147
|
+
if (!contents) return null;
|
|
3148
|
+
const index = contents.findIndex((node) => node.type === "paragraph");
|
|
3149
|
+
if (index < 0) return null;
|
|
3150
|
+
return {
|
|
3151
|
+
node: contents[index],
|
|
3152
|
+
index
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
function leadingStrongStat(paragraph) {
|
|
3156
|
+
for (const child of paragraph.children) {
|
|
3157
|
+
if (child.type === "text" && child.value.trim() === "") continue;
|
|
3158
|
+
if (child.type !== "strong") return null;
|
|
3159
|
+
const text = extractPlainText(child).trim();
|
|
3160
|
+
const match = text.match(/^(?:[$€£¥]\s*)?[+-]?\d+(?:[.,]\d+)*(?:\s?(?:[%‰x×]|[A-Za-z]+))?/);
|
|
3161
|
+
return match?.[0].trim() || null;
|
|
3162
|
+
}
|
|
3163
|
+
return null;
|
|
3164
|
+
}
|
|
3165
|
+
function removeStatPhrase(text, start, end) {
|
|
3166
|
+
const before = text.slice(0, start).trim();
|
|
3167
|
+
let after = text.slice(end);
|
|
3168
|
+
if (!before) after = after.replace(/^\s*(?:[-–—:]\s*)?/, "");
|
|
3169
|
+
else after = after.trim();
|
|
3170
|
+
return [before, after].filter(Boolean).join(" ").trim();
|
|
3171
|
+
}
|
|
3172
|
+
function bodyWithoutParagraphStat(contents, paragraphIndex, paragraphText, start, end) {
|
|
3173
|
+
if (!contents) return "";
|
|
3174
|
+
return contents.map(
|
|
3175
|
+
(node, index) => index === paragraphIndex ? removeStatPhrase(paragraphText, start, end) : extractPlainText(node).trim()
|
|
3176
|
+
).filter(Boolean).join("\n").trim();
|
|
3177
|
+
}
|
|
3178
|
+
function deriveStatHighlightInputs(headingText, contents, bodyText) {
|
|
3179
|
+
const paragraph = firstParagraph(contents);
|
|
3180
|
+
if (paragraph) {
|
|
3181
|
+
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
3182
|
+
const strongStat = leadingStrongStat(paragraph.node);
|
|
3183
|
+
if (strongStat) {
|
|
3184
|
+
const start = paragraphText.indexOf(strongStat);
|
|
3185
|
+
const description = bodyWithoutParagraphStat(
|
|
3186
|
+
contents,
|
|
3187
|
+
paragraph.index,
|
|
3188
|
+
paragraphText,
|
|
3189
|
+
start,
|
|
3190
|
+
start + strongStat.length
|
|
3191
|
+
);
|
|
3192
|
+
return {
|
|
3193
|
+
stat: strongStat,
|
|
3194
|
+
description: description || headingText || strongStat
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
const trimmedHeading = headingText.trim();
|
|
3199
|
+
const headingStat = matchNumberHighlight(trimmedHeading);
|
|
3200
|
+
if (headingStat && headingStat.index === 0 && headingStat.end === trimmedHeading.length) {
|
|
3201
|
+
return { stat: headingText, description: bodyText || headingText };
|
|
3202
|
+
}
|
|
3203
|
+
if (paragraph) {
|
|
3204
|
+
const paragraphText = extractPlainText(paragraph.node).trim();
|
|
3205
|
+
const bodyStat = matchNumberHighlight(paragraphText);
|
|
3206
|
+
if (bodyStat) {
|
|
3207
|
+
const description = bodyWithoutParagraphStat(
|
|
3208
|
+
contents,
|
|
3209
|
+
paragraph.index,
|
|
3210
|
+
paragraphText,
|
|
3211
|
+
bodyStat.index,
|
|
3212
|
+
bodyStat.end
|
|
3213
|
+
);
|
|
3214
|
+
return {
|
|
3215
|
+
stat: bodyStat.value,
|
|
3216
|
+
description: description || headingText || bodyStat.value
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
return { stat: headingText, description: bodyText || headingText };
|
|
3221
|
+
}
|
|
3222
|
+
function normalizeCoverageText(value) {
|
|
3223
|
+
return value.replace(/\s+/g, " ").trim();
|
|
3224
|
+
}
|
|
3225
|
+
function autoTemplatePreservesContent(templateName, contents) {
|
|
3226
|
+
const nodes = contents ?? [];
|
|
3227
|
+
switch (templateName) {
|
|
3228
|
+
case "quote":
|
|
3229
|
+
return nodes.length === 1 && nodes[0]?.type === "blockquote";
|
|
3230
|
+
case "dataTable":
|
|
3231
|
+
return nodes.length === 1 && nodes[0]?.type === "table";
|
|
3232
|
+
case "list":
|
|
3233
|
+
return nodes.length === 1 && nodes[0]?.type === "list";
|
|
3234
|
+
case "diagram":
|
|
3235
|
+
case "timeline":
|
|
3236
|
+
return nodes.length === 1 && nodes[0]?.type === "code";
|
|
3237
|
+
case "tree":
|
|
3238
|
+
return nodes.length === 1 && (nodes[0]?.type === "code" || nodes[0]?.type === "list");
|
|
3239
|
+
case "photoGrid": {
|
|
3240
|
+
const images = extractImages(nodes);
|
|
3241
|
+
if (images.length < 2 || images.length > 4) return false;
|
|
3242
|
+
const bodyText = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
3243
|
+
const representedText = normalizeCoverageText(
|
|
3244
|
+
images.map((image) => image.alt).filter(Boolean).join(" ")
|
|
3245
|
+
);
|
|
3246
|
+
return bodyText === "" || bodyText === representedText;
|
|
3247
|
+
}
|
|
3248
|
+
case "videoWithCaption": {
|
|
3249
|
+
const videos = extractEmbeddedVideos(nodes);
|
|
3250
|
+
if (videos.length + extractHostedVideos(nodes).length !== 1) return false;
|
|
3251
|
+
const bodyText = normalizeCoverageText(extractBodyPlainText(nodes));
|
|
3252
|
+
return bodyText === normalizeCoverageText(videos[0]?.alt ?? "");
|
|
3253
|
+
}
|
|
3254
|
+
case "leftFeature":
|
|
3255
|
+
case "rightFeature":
|
|
3256
|
+
case "statHighlight":
|
|
3257
|
+
return nodes.length > 0 && nodes.every((node) => node.type === "paragraph");
|
|
3258
|
+
default:
|
|
3259
|
+
return false;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
function deriveTemplateInputs(templateName, headingText, contents, options = {}) {
|
|
3263
|
+
const placeholders = options.placeholders === true;
|
|
3264
|
+
const bodyText = extractBodyPlainText(contents);
|
|
3265
|
+
switch (templateName) {
|
|
3266
|
+
case "statHighlight": {
|
|
3267
|
+
const inputs = deriveStatHighlightInputs(headingText, contents, bodyText);
|
|
3268
|
+
if (options.preserveSourceHeading && headingText && inputs.stat !== headingText && inputs.description !== headingText) {
|
|
3269
|
+
return {
|
|
3270
|
+
...inputs,
|
|
3271
|
+
description: headingText,
|
|
3272
|
+
...inputs.description ? { detail: inputs.description } : {}
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
return inputs;
|
|
3276
|
+
}
|
|
3277
|
+
case "transcript": {
|
|
3278
|
+
const text = extractBlockquoteText(contents) || bodyText;
|
|
3279
|
+
if (!text && !headingText) {
|
|
3280
|
+
return placeholders ? { speaker: "Speaker", text: "Message" } : null;
|
|
3281
|
+
}
|
|
3282
|
+
return {
|
|
3283
|
+
speaker: headingText || (placeholders ? "Speaker" : ""),
|
|
3284
|
+
text: text || (placeholders ? "Message" : "")
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
case "quote": {
|
|
3288
|
+
const quote = extractBlockquoteText(contents) || bodyText;
|
|
3289
|
+
if (quote) return { quote, ...headingText ? { title: headingText } : {} };
|
|
3290
|
+
return { quote: headingText };
|
|
3291
|
+
}
|
|
3292
|
+
case "fullBleedQuote": {
|
|
3293
|
+
const text = extractBlockquoteText(contents) || bodyText || headingText;
|
|
3294
|
+
return { text };
|
|
3295
|
+
}
|
|
3296
|
+
case "pullQuote": {
|
|
3297
|
+
const text = extractBlockquoteText(contents) || bodyText || headingText;
|
|
3298
|
+
const img = extractFirstImage(contents);
|
|
3299
|
+
return {
|
|
3300
|
+
text,
|
|
3301
|
+
...img ? { backgroundImage: { src: img.src, alt: img.alt || headingText } } : {}
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
case "videoPullQuote": {
|
|
3305
|
+
const text = extractBlockquoteText(contents) || bodyText || headingText;
|
|
3306
|
+
const video = extractFirstEmbeddedVideo(contents);
|
|
3307
|
+
return {
|
|
3308
|
+
text,
|
|
3309
|
+
...video ? {
|
|
3310
|
+
backgroundVideo: {
|
|
3311
|
+
src: video.src,
|
|
3312
|
+
...video.posterSrc ? { posterSrc: video.posterSrc } : {},
|
|
3313
|
+
alt: video.alt || headingText,
|
|
3314
|
+
...video.clipStart != null ? { clipStart: video.clipStart } : {},
|
|
3315
|
+
...video.clipEnd != null ? { clipEnd: video.clipEnd } : {}
|
|
3316
|
+
}
|
|
3317
|
+
} : {}
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
case "factCard":
|
|
3321
|
+
return { fact: headingText, explanation: bodyText };
|
|
3322
|
+
case "twoColumn":
|
|
3323
|
+
return headingText ? { header: headingText } : placeholders ? {} : null;
|
|
3324
|
+
case "comparisonBar":
|
|
3325
|
+
return placeholders ? { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 } : null;
|
|
3326
|
+
case "list": {
|
|
3327
|
+
const items = extractListItems(contents);
|
|
3328
|
+
const firstList = (contents ?? []).find((n) => n.type === "list");
|
|
3329
|
+
if (items.length > 0) {
|
|
3330
|
+
return {
|
|
3331
|
+
items,
|
|
3332
|
+
// Bullets stay bullets: only a numbered source list renders `1.` markers.
|
|
3333
|
+
ordered: firstList?.ordered === true,
|
|
3334
|
+
...headingText ? { title: headingText } : {}
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
return placeholders ? { items: ["Item 1", "Item 2", "Item 3"] } : null;
|
|
3338
|
+
}
|
|
3339
|
+
case "definitionCard":
|
|
3340
|
+
return { term: headingText, definition: bodyText };
|
|
3341
|
+
case "dateEvent":
|
|
3342
|
+
return { date: headingText, description: bodyText };
|
|
3343
|
+
case "dataTable": {
|
|
3344
|
+
const tableData = extractTableFromContents(contents);
|
|
3345
|
+
if (tableData) return { ...tableData, ...headingText ? { title: headingText } : {} };
|
|
3346
|
+
return placeholders ? { headers: ["Column"], rows: [["Data"]] } : null;
|
|
3347
|
+
}
|
|
3348
|
+
case "barChart":
|
|
3349
|
+
case "columnChart":
|
|
3350
|
+
case "pieChart":
|
|
3351
|
+
case "donutChart":
|
|
3352
|
+
case "lineChart":
|
|
3353
|
+
case "areaChart":
|
|
3354
|
+
case "scatterChart": {
|
|
3355
|
+
const tableData = extractTableFromContents(contents);
|
|
3356
|
+
if (tableData) {
|
|
3357
|
+
return {
|
|
3358
|
+
headers: tableData.headers,
|
|
3359
|
+
rows: tableData.rows,
|
|
3360
|
+
...headingText ? { title: headingText } : {}
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
return placeholders && !bodyText.trim() ? {
|
|
3364
|
+
headers: ["Quarter", "Revenue", "Costs"],
|
|
3365
|
+
rows: [
|
|
3366
|
+
["Q1", "40", "28"],
|
|
3367
|
+
["Q2", "55", "31"],
|
|
3368
|
+
["Q3", "48", "30"],
|
|
3369
|
+
["Q4", "62", "35"]
|
|
3370
|
+
],
|
|
3371
|
+
...headingText ? { title: headingText } : {}
|
|
3372
|
+
} : null;
|
|
3373
|
+
}
|
|
3374
|
+
case "imageWithCaption": {
|
|
3375
|
+
const img = extractFirstImage(contents);
|
|
3376
|
+
if (!img) return placeholders ? { caption: headingText } : null;
|
|
3377
|
+
return { imageSrc: img.src, imageAlt: img.alt || headingText, caption: headingText };
|
|
3378
|
+
}
|
|
3379
|
+
case "bigText": {
|
|
3380
|
+
const img = extractFirstImage(contents);
|
|
3381
|
+
return {
|
|
3382
|
+
title: headingText,
|
|
3383
|
+
...img ? { imageSrc: img.src, imageAlt: img.alt || headingText } : {}
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
3386
|
+
case "photoGrid": {
|
|
3387
|
+
const images = extractImages(contents, 4);
|
|
3388
|
+
if (images.length < 2) return placeholders ? {} : null;
|
|
3389
|
+
return {
|
|
3390
|
+
images: images.map((i) => ({ src: i.src, alt: i.alt })),
|
|
3391
|
+
caption: headingText
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
case "videoWithCaption": {
|
|
3395
|
+
const video = extractFirstEmbeddedVideo(contents);
|
|
3396
|
+
if (!video) {
|
|
3397
|
+
const hosted = extractHostedVideos(contents, 1)[0];
|
|
3398
|
+
if (hosted) {
|
|
3399
|
+
return {
|
|
3400
|
+
videoSrc: hosted.url,
|
|
3401
|
+
videoAlt: hosted.title || headingText,
|
|
3402
|
+
caption: headingText
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
return placeholders ? { caption: headingText } : null;
|
|
3406
|
+
}
|
|
3407
|
+
return {
|
|
3408
|
+
videoSrc: video.src,
|
|
3409
|
+
posterSrc: video.posterSrc,
|
|
3410
|
+
videoAlt: video.alt || headingText,
|
|
3411
|
+
caption: headingText
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
case "leftFeature":
|
|
3415
|
+
case "rightFeature": {
|
|
3416
|
+
const img = extractFirstImage(contents);
|
|
3417
|
+
if (!img && !placeholders) return null;
|
|
3418
|
+
return {
|
|
3419
|
+
imageSrc: img?.src ?? "",
|
|
3420
|
+
imageAlt: img?.alt || headingText,
|
|
3421
|
+
imageWidth: img?.width,
|
|
3422
|
+
imageHeight: img?.height,
|
|
3423
|
+
title: headingText,
|
|
3424
|
+
body: bodyText
|
|
3425
|
+
};
|
|
3426
|
+
}
|
|
3427
|
+
case "diagram": {
|
|
3428
|
+
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
3429
|
+
const fence = fences.length === 1 && isEligibleAsciiFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
3430
|
+
const detection = fence ? detectAsciiDiagram(fence.value, { explicit: isExplicitDiagramLang(fence.lang) }) : void 0;
|
|
3431
|
+
if (!detection?.isDiagram || !detection.diagram) return placeholders ? {} : null;
|
|
3432
|
+
const { nodes, edges } = asciiDiagramToTemplateData(detection.diagram);
|
|
3433
|
+
return { nodes, edges, ...headingText ? { title: headingText } : {} };
|
|
3434
|
+
}
|
|
3435
|
+
case "timeline": {
|
|
3436
|
+
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
3437
|
+
const fence = fences.length === 1 && isEligibleAsciiTimelineFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
3438
|
+
const detection = fence ? detectAsciiTimeline(fence.value, { explicit: true }) : void 0;
|
|
3439
|
+
if (!detection?.isTimeline || !detection.timeline) return placeholders ? {} : null;
|
|
3440
|
+
const { tracks, links } = asciiTimelineToTemplateData(detection.timeline);
|
|
3441
|
+
return { tracks, links, ...headingText ? { title: headingText } : {} };
|
|
3442
|
+
}
|
|
3443
|
+
case "tree": {
|
|
3444
|
+
const fences = (contents ?? []).filter((n) => n.type === "code");
|
|
3445
|
+
const fence = fences.length === 1 && isEligibleTreeFenceLang(fences[0].lang) ? fences[0] : void 0;
|
|
3446
|
+
if (fence) {
|
|
3447
|
+
const detection = detectTree(fence.value, { explicit: isExplicitTreeLang(fence.lang) });
|
|
3448
|
+
if (detection.isTree && detection.tree) {
|
|
3449
|
+
const { items } = treeToTemplateData(detection.tree);
|
|
3450
|
+
return { items, ...headingText ? { title: headingText } : {} };
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
const list = findFirstList(contents);
|
|
3454
|
+
if (list) {
|
|
3455
|
+
const { items } = treeToTemplateData(treeFromMarkdownList(list));
|
|
3456
|
+
if (items.length > 0) return { items, ...headingText ? { title: headingText } : {} };
|
|
3457
|
+
}
|
|
3458
|
+
return placeholders ? {} : null;
|
|
3459
|
+
}
|
|
3460
|
+
default:
|
|
3461
|
+
return placeholders ? {} : null;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
|
|
2465
3465
|
// src/recommend/templates.ts
|
|
2466
3466
|
var EMPTY_PROFILE = Object.freeze({
|
|
2467
3467
|
hasImage: false,
|
|
@@ -2516,7 +3516,7 @@ function profileBlockContents(nodes) {
|
|
|
2516
3516
|
if (findNodesByType(node, "blockquote").length > 0) hasBlockquote = true;
|
|
2517
3517
|
if (findNodesByType(node, "list").length > 0) hasList = true;
|
|
2518
3518
|
if (findNodesByType(node, "table").length > 0) hasTable = true;
|
|
2519
|
-
plainParts.push(extractPlainText(node));
|
|
3519
|
+
if (!isPictureFence(node)) plainParts.push(extractPlainText(node));
|
|
2520
3520
|
}
|
|
2521
3521
|
const root = { type: "document", children: nodes };
|
|
2522
3522
|
const imgTags = htmlElementsByTag(root, /* @__PURE__ */ new Set(["img"]));
|
|
@@ -2697,7 +3697,6 @@ function recommendTemplatesForBlock(profile, allNames) {
|
|
|
2697
3697
|
}
|
|
2698
3698
|
|
|
2699
3699
|
export {
|
|
2700
|
-
matchNumberHighlight,
|
|
2701
3700
|
toCells,
|
|
2702
3701
|
cellWidth,
|
|
2703
3702
|
isHoriz,
|
|
@@ -2716,6 +3715,14 @@ export {
|
|
|
2716
3715
|
isExplicitDiagramLang,
|
|
2717
3716
|
detectAsciiDiagram,
|
|
2718
3717
|
isAsciiDiagramFence,
|
|
3718
|
+
computeDiagramLayout,
|
|
3719
|
+
ASCII_CHAR_W,
|
|
3720
|
+
ASCII_CHAR_H,
|
|
3721
|
+
asciiCellToCanvas,
|
|
3722
|
+
canvasToAsciiCell,
|
|
3723
|
+
asciiDiagramToTemplateData,
|
|
3724
|
+
asciiDiagramFromTemplateData,
|
|
3725
|
+
asciiDiagramFromBlocks,
|
|
2719
3726
|
parseWrappedFlowTimeline,
|
|
2720
3727
|
isWrappedFlowTimelineSource,
|
|
2721
3728
|
parseAsciiTimeline,
|
|
@@ -2725,6 +3732,7 @@ export {
|
|
|
2725
3732
|
isExplicitTimelineLang,
|
|
2726
3733
|
detectAsciiTimeline,
|
|
2727
3734
|
isAsciiTimelineFence,
|
|
3735
|
+
asciiTimelineToTemplateData,
|
|
2728
3736
|
UNICODE_TREE_VOCAB,
|
|
2729
3737
|
ASCII_TREE_VOCAB,
|
|
2730
3738
|
parseTree,
|
|
@@ -2733,6 +3741,26 @@ export {
|
|
|
2733
3741
|
isExplicitTreeLang,
|
|
2734
3742
|
detectTree,
|
|
2735
3743
|
isTreeFence,
|
|
3744
|
+
treeToTemplateData,
|
|
3745
|
+
treeFromTemplateData,
|
|
3746
|
+
treeFromMarkdownList,
|
|
3747
|
+
findFirstList,
|
|
3748
|
+
extractHostedVideos,
|
|
3749
|
+
isPictureFence,
|
|
3750
|
+
isMaterializedMediaNode,
|
|
3751
|
+
extractBodyPlainText,
|
|
3752
|
+
extractListItems,
|
|
3753
|
+
renderMarkdownBlocksHtml,
|
|
3754
|
+
markdownBlockSeparatorLines,
|
|
3755
|
+
extractRichListItems,
|
|
3756
|
+
extractImages,
|
|
3757
|
+
extractFirstImage,
|
|
3758
|
+
extractEmbeddedVideos,
|
|
3759
|
+
extractFirstEmbeddedVideo,
|
|
3760
|
+
extractTableFromContents,
|
|
3761
|
+
extractBlockquoteText,
|
|
3762
|
+
autoTemplatePreservesContent,
|
|
3763
|
+
deriveTemplateInputs,
|
|
2736
3764
|
profileBlockContents,
|
|
2737
3765
|
pickAutoTemplate,
|
|
2738
3766
|
recommendTemplatesForBlock
|