@bendyline/squisq 2.13.14 → 2.13.16
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-BhvotZx_.d.ts → Doc-DLfTam0R.d.ts} +82 -6
- package/dist/{ImageEditDoc-D8hYio6b.d.ts → ImageEditDoc-DdSHb6sj.d.ts} +1 -1
- package/dist/{chunk-YQQWE4HG.js → chunk-33CAD575.js} +200 -262
- package/dist/{chunk-L7QZORLB.js → chunk-37RLFDIT.js} +11 -4
- package/dist/chunk-4R3YZTPX.js +42 -0
- package/dist/{chunk-DBVCSVNV.js → chunk-A4ZFYGU6.js} +13 -38
- package/dist/{chunk-CTT4XAGO.js → chunk-BG2OYBGC.js} +2534 -737
- package/dist/{chunk-2SRF5III.js → chunk-BVL3F64S.js} +1 -1
- package/dist/chunk-C7DKMO4H.js +88 -0
- package/dist/{chunk-SGNWO7OF.js → chunk-MA7XUCSQ.js} +517 -30
- package/dist/{chunk-JSGIWW35.js → chunk-MTIR5PWF.js} +1 -1
- package/dist/{chunk-C4SB3NN4.js → chunk-NBDF6SQ5.js} +22 -2
- package/dist/chunk-TOPEP5B7.js +515 -0
- package/dist/{chunk-OIQ2GO2O.js → chunk-TXN23SG2.js} +1 -1
- package/dist/{chunk-7MS26OXD.js → chunk-UDPPWYYM.js} +6 -0
- package/dist/{chunk-J2G6EOEJ.js → chunk-XY5Z23C4.js} +75 -16
- package/dist/{chunk-5DFFVW75.js → chunk-Z73DG6IL.js} +1 -1
- package/dist/doc/index.d.ts +412 -26
- package/dist/doc/index.js +66 -11
- 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 +9 -9
- package/dist/index.js +147 -29
- package/dist/jsonForm/index.d.ts +1 -1
- package/dist/jsonForm/index.js +5 -4
- package/dist/markdown/index.js +3 -3
- package/dist/{materializePageSection-CWxou4mX.d.ts → materializePageSection-HIoRYiZu.d.ts} +121 -8
- package/dist/mediaEdit/index.d.ts +154 -3
- package/dist/mediaEdit/index.js +241 -2
- package/dist/narration/index.d.ts +1 -1
- package/dist/narration/index.js +7 -5
- package/dist/recommend/index.d.ts +129 -2
- package/dist/recommend/index.js +13 -4
- package/dist/schemas/index.d.ts +26 -7
- package/dist/schemas/index.js +14 -8
- package/dist/{themeLibrary-BIYE6aLw.d.ts → themeLibrary-B2K-aAOy.d.ts} +1 -1
- package/dist/timing/index.d.ts +66 -2
- package/dist/timing/index.js +12 -0
- package/dist/transform/index.d.ts +173 -16
- package/dist/transform/index.js +58 -17
- package/package.json +1 -1
- package/dist/chunk-FCB6JLWB.js +0 -125
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
taskMarker
|
|
3
|
+
} from "./chunk-4R3YZTPX.js";
|
|
1
4
|
import {
|
|
2
5
|
mediaEditValuesFromAttributes,
|
|
3
6
|
mediaEditValuesFromParams,
|
|
4
7
|
parseMediaEdits
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-37RLFDIT.js";
|
|
6
9
|
import {
|
|
7
10
|
extractPlainText,
|
|
8
11
|
findBlockVideoEmbed,
|
|
@@ -209,6 +212,109 @@ function extractMediaFromContents(contents, makeId, blockId) {
|
|
|
209
212
|
return { media, documentMedia, remaining };
|
|
210
213
|
}
|
|
211
214
|
|
|
215
|
+
// src/doc/templates/conceptItems.ts
|
|
216
|
+
var LABEL_WORDS = 7;
|
|
217
|
+
function splitConceptItem(text) {
|
|
218
|
+
const clean = text.replace(/\s+/gu, " ").trim();
|
|
219
|
+
const match = clean.match(/^(.{1,64}?)(:\s|\s[—–-]\s)(.+)$/u);
|
|
220
|
+
const range = !!match && !match[2].startsWith(":") && /\d$/u.test(match[1]);
|
|
221
|
+
if (match && !range && !/[.!?]\s/u.test(match[1]) && match[1].split(" ").length <= LABEL_WORDS)
|
|
222
|
+
return { label: match[1].trim(), detail: match[3].trim() };
|
|
223
|
+
return { label: "", detail: clean };
|
|
224
|
+
}
|
|
225
|
+
var FIGURE = /^[~≈<>±+−-]?\s?[$€£¥]?\s?\d[\d.,]*(?:\s?(?:%|‰|°[CF]?|[x×](?![a-z])|[KMB](?![a-z])|(?:thousand|million|billion|trillion|percent)\b)|\s(?:in|of|out of)\s\d[\d.,]*)?/iu;
|
|
226
|
+
function isDottedIdentifier(figure) {
|
|
227
|
+
const digits = figure.match(/\d[\d.,]*/u)?.[0] ?? "";
|
|
228
|
+
return /\d\.\d+\.\d/u.test(digits) && !/^\d{1,3}(?:\.\d{3})+(?:,\d+)?$/u.test(digits);
|
|
229
|
+
}
|
|
230
|
+
function isFigureText(text) {
|
|
231
|
+
const clean = text.trim();
|
|
232
|
+
if (!clean || clean.length > 18) return false;
|
|
233
|
+
const match = clean.match(FIGURE);
|
|
234
|
+
if (!match || !match[0].trim() || isDottedIdentifier(match[0])) return false;
|
|
235
|
+
const rest = clean.slice(match[0].length).trim();
|
|
236
|
+
return rest === "" || /^[\p{L}°/%²³.]{1,10}(?:\s[\p{L}/²³.]{1,8})?$/u.test(rest);
|
|
237
|
+
}
|
|
238
|
+
function splitStatItem(text) {
|
|
239
|
+
const { label, detail } = splitConceptItem(text);
|
|
240
|
+
if (label && isFigureText(label)) return { figure: label, caption: detail };
|
|
241
|
+
if (label && isFigureText(detail)) return { figure: detail, caption: label };
|
|
242
|
+
const clean = label ? `${label}: ${detail}` : detail;
|
|
243
|
+
const lead = clean.match(FIGURE)?.[0] ?? "";
|
|
244
|
+
const after = clean.slice(lead.length);
|
|
245
|
+
const figure = /^(?:\s|,\s|$)/u.test(after) && !isDottedIdentifier(lead) ? lead.trim() : "";
|
|
246
|
+
const rest = figure ? after.replace(/^[\s,;:—–-]+/u, "") : "";
|
|
247
|
+
if (figure && rest) return { figure: figure.replace(/[.,]+$/u, ""), caption: rest };
|
|
248
|
+
return { figure: "", caption: clean };
|
|
249
|
+
}
|
|
250
|
+
var OVERLAP_LABEL = /^(?:both|shared?|overlap|in common|common|common ground|together|all|all three|each|either)$/iu;
|
|
251
|
+
function vennOverlapIndex(items) {
|
|
252
|
+
return items.findIndex((item) => OVERLAP_LABEL.test(item.label.trim()));
|
|
253
|
+
}
|
|
254
|
+
var MONTH = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\\.?\\s+";
|
|
255
|
+
var LEADING_DATE = new RegExp(
|
|
256
|
+
`^((?:${MONTH}(?:\\d{1,2},?\\s+)?)?(?:\\d{4}s?|\\d{1,4}\\s?(?:BCE?|AD|CE))(?![.,]?\\d))[\\s,:;\u2014\u2013-]*(.*)$`,
|
|
257
|
+
"u"
|
|
258
|
+
);
|
|
259
|
+
function timelineItems(items) {
|
|
260
|
+
if (items.length < 2) return void 0;
|
|
261
|
+
let last = -Infinity;
|
|
262
|
+
const dated = [];
|
|
263
|
+
for (const item of items) {
|
|
264
|
+
const match = (item.label || item.detail).match(LEADING_DATE);
|
|
265
|
+
if (!match) return void 0;
|
|
266
|
+
const when = match[1];
|
|
267
|
+
const year = Number(when.match(/\d{1,4}(?=s?\s?(?:BCE?|AD|CE)?$)/u)?.[0] ?? NaN);
|
|
268
|
+
const signed = /BCE?$/u.test(when) ? -year : year;
|
|
269
|
+
if (!Number.isFinite(signed) || signed < last) return void 0;
|
|
270
|
+
last = signed;
|
|
271
|
+
const rest = match[2].trim();
|
|
272
|
+
dated.push(
|
|
273
|
+
item.label ? { when, label: rest, detail: item.detail } : { when, label: "", detail: rest }
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
return dated;
|
|
277
|
+
}
|
|
278
|
+
var LEVEL = /\b(low|little|small|less|minimal|few|high|a lot of|lots of|much|more|big|large|great|major|significant)\s+([\p{L}][\p{L}-]*)/giu;
|
|
279
|
+
var LOW_LEVELS = /* @__PURE__ */ new Set([
|
|
280
|
+
"low",
|
|
281
|
+
"little",
|
|
282
|
+
"small",
|
|
283
|
+
"less",
|
|
284
|
+
"minimal",
|
|
285
|
+
"few"
|
|
286
|
+
]);
|
|
287
|
+
function readMatrixItems(items) {
|
|
288
|
+
if (items.length !== 4) return void 0;
|
|
289
|
+
const read = items.map((item) => {
|
|
290
|
+
const levels = /* @__PURE__ */ new Map();
|
|
291
|
+
for (const match of item.matchAll(LEVEL))
|
|
292
|
+
levels.set(match[2].toLowerCase(), !LOW_LEVELS.has(match[1].toLowerCase()));
|
|
293
|
+
return levels;
|
|
294
|
+
});
|
|
295
|
+
const measures = [...read[0].keys()].filter(
|
|
296
|
+
(measure) => read.every((levels) => levels.has(measure))
|
|
297
|
+
);
|
|
298
|
+
if (measures.length !== 2) return void 0;
|
|
299
|
+
const name = (measure) => `${measure[0].toUpperCase()}${measure.slice(1)}`;
|
|
300
|
+
const corner = (levels, x, y) => (levels.get(y) ? 0 : 2) + (levels.get(x) ? 1 : 0);
|
|
301
|
+
const [first, second] = measures;
|
|
302
|
+
for (const [x, y] of [
|
|
303
|
+
[first, second],
|
|
304
|
+
[second, first]
|
|
305
|
+
]) {
|
|
306
|
+
const corners = read.map((levels) => corner(levels, x, y));
|
|
307
|
+
if (new Set(corners).size !== 4) return void 0;
|
|
308
|
+
if (corners.every((at, index) => at === index))
|
|
309
|
+
return { xAxis: name(x), yAxis: name(y), items: [...items], reordered: false };
|
|
310
|
+
}
|
|
311
|
+
const placed = [];
|
|
312
|
+
read.forEach((levels, index) => {
|
|
313
|
+
placed[corner(levels, first, second)] = items[index];
|
|
314
|
+
});
|
|
315
|
+
return { xAxis: name(first), yAxis: name(second), items: placed, reordered: true };
|
|
316
|
+
}
|
|
317
|
+
|
|
212
318
|
// src/doc/asciiDiagram/chars.ts
|
|
213
319
|
function toCells(s) {
|
|
214
320
|
return Array.from(s);
|
|
@@ -1561,7 +1667,7 @@ function layeredPlacement(children, unpositionedIdx, opts) {
|
|
|
1561
1667
|
}
|
|
1562
1668
|
if (edges.length === 0) return null;
|
|
1563
1669
|
const across = opts.direction === "right";
|
|
1564
|
-
const
|
|
1670
|
+
const result2 = layoutGraph(
|
|
1565
1671
|
{
|
|
1566
1672
|
nodes: unpositionedIdx.map((i) => ({
|
|
1567
1673
|
id: children[i].id,
|
|
@@ -1582,7 +1688,7 @@ function layeredPlacement(children, unpositionedIdx, opts) {
|
|
|
1582
1688
|
);
|
|
1583
1689
|
const out = /* @__PURE__ */ new Map();
|
|
1584
1690
|
unpositionedIdx.forEach((entryIdx, k) => {
|
|
1585
|
-
const n =
|
|
1691
|
+
const n = result2.nodes[k];
|
|
1586
1692
|
out.set(entryIdx, { x: Math.round(n.x), y: Math.round(n.y) });
|
|
1587
1693
|
});
|
|
1588
1694
|
return out;
|
|
@@ -3145,8 +3251,19 @@ function isPictureFence(node) {
|
|
|
3145
3251
|
if (node.type !== "code") return false;
|
|
3146
3252
|
return node.lang?.trim().toLowerCase() === "mermaid" || isSvgIslandLang(node.lang);
|
|
3147
3253
|
}
|
|
3254
|
+
function isSpatialPictureFence(node) {
|
|
3255
|
+
if (node.type !== "code") return false;
|
|
3256
|
+
if (isExplicitDiagramLang(node.lang)) {
|
|
3257
|
+
return detectAsciiDiagram(node.value, { explicit: true }).isDiagram;
|
|
3258
|
+
}
|
|
3259
|
+
if (isExplicitTreeLang(node.lang)) return detectTree(node.value, { explicit: true }).isTree;
|
|
3260
|
+
if (isExplicitTimelineLang(node.lang)) {
|
|
3261
|
+
return detectAsciiTimeline(node.value, { explicit: true }).isTimeline;
|
|
3262
|
+
}
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3148
3265
|
function isMaterializedMediaNode(node) {
|
|
3149
|
-
if (node.type === "code") return isPictureFence(node);
|
|
3266
|
+
if (node.type === "code") return isPictureFence(node) || isSpatialPictureFence(node);
|
|
3150
3267
|
return findBlockVideoEmbed(node) !== null || isStandaloneHtmlVideo(node);
|
|
3151
3268
|
}
|
|
3152
3269
|
function isPictureParagraph(node) {
|
|
@@ -3175,6 +3292,37 @@ function extractBodyPlainText(contents) {
|
|
|
3175
3292
|
function extractListItems(contents) {
|
|
3176
3293
|
return extractRichListItems(contents).map((item) => item.text);
|
|
3177
3294
|
}
|
|
3295
|
+
function extractConceptItems(contents) {
|
|
3296
|
+
const flat = (node) => extractPlainText(node).replace(/\s+/g, " ").trim();
|
|
3297
|
+
const items = [];
|
|
3298
|
+
for (const node of contents ?? []) {
|
|
3299
|
+
if (node.type !== "list") continue;
|
|
3300
|
+
for (const item of node.children) {
|
|
3301
|
+
const own = item.children.filter((child) => child.type !== "list").map(flat).filter(Boolean).join(" ");
|
|
3302
|
+
const nested = item.children.filter((child) => child.type === "list").flatMap((list) => list.children.map(flat)).filter(Boolean);
|
|
3303
|
+
if (!own && !nested.length) continue;
|
|
3304
|
+
if (!nested.length) items.push(own);
|
|
3305
|
+
else if (!own) items.push(nested.join("; "));
|
|
3306
|
+
else
|
|
3307
|
+
items.push(
|
|
3308
|
+
`${own.replace(/[:;,.]$/, "")}${/:\s/.test(own) ? ";" : ":"} ${nested.join("; ")}`
|
|
3309
|
+
);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
return items;
|
|
3313
|
+
}
|
|
3314
|
+
var COLUMN_LABEL_CHARACTERS = 40;
|
|
3315
|
+
function twoColumnsFromList(contents) {
|
|
3316
|
+
const items = extractConceptItems(contents);
|
|
3317
|
+
if (items.length !== 2) return null;
|
|
3318
|
+
const sides = items.map((item) => {
|
|
3319
|
+
const { label, detail } = splitConceptItem(item);
|
|
3320
|
+
if (label) return { label, sublabel: detail };
|
|
3321
|
+
return detail.length <= COLUMN_LABEL_CHARACTERS ? { label: detail } : null;
|
|
3322
|
+
});
|
|
3323
|
+
const [left, right] = sides;
|
|
3324
|
+
return left && right ? { left, right } : null;
|
|
3325
|
+
}
|
|
3178
3326
|
function escapeInlineHtml(value) {
|
|
3179
3327
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3180
3328
|
}
|
|
@@ -3240,10 +3388,12 @@ function renderMarkdownBlocksHtml(nodes, builds) {
|
|
|
3240
3388
|
const tag = node.ordered ? "ol" : "ul";
|
|
3241
3389
|
const start = node.ordered && node.start != null ? ` start="${node.start}"` : "";
|
|
3242
3390
|
const items = node.children.map((item, index) => {
|
|
3243
|
-
const
|
|
3391
|
+
const task = item.checked == null ? null : taskMarker(item.checked);
|
|
3244
3392
|
const step = builds?.targets.get(item);
|
|
3245
|
-
if (step === void 0)
|
|
3246
|
-
|
|
3393
|
+
if (step === void 0) {
|
|
3394
|
+
return task === null ? `<li>${renderBlocks(item.children)}</li>` : `<li data-squisq-task><span aria-hidden="true">${task} </span>${renderBlocks(item.children)}</li>`;
|
|
3395
|
+
}
|
|
3396
|
+
const marker = task ?? (node.ordered ? `${(node.start ?? 1) + index}.` : "\u2022");
|
|
3247
3397
|
let first = true;
|
|
3248
3398
|
let own = [];
|
|
3249
3399
|
const fragments = [];
|
|
@@ -3428,13 +3578,17 @@ function extractEmbeddedVideos(contents, limit = Infinity) {
|
|
|
3428
3578
|
const startAt = attrs?.["data-squisq-video-start-at"] ? parseTimeSeconds(attrs["data-squisq-video-start-at"]) : null;
|
|
3429
3579
|
const clipStart = attrs?.["data-squisq-video-clip-start"] ? parseTimeSeconds(attrs["data-squisq-video-clip-start"]) : null;
|
|
3430
3580
|
const clipEnd = attrs?.["data-squisq-video-clip-end"] ? parseTimeSeconds(attrs["data-squisq-video-clip-end"]) : null;
|
|
3581
|
+
const edits = parseMediaEdits(mediaEditValuesFromAttributes("video", attrs ?? {}));
|
|
3582
|
+
const cuts = edits?.cuts;
|
|
3431
3583
|
add({
|
|
3432
3584
|
src,
|
|
3433
3585
|
...attrs?.poster ? { posterSrc: attrs.poster } : {},
|
|
3434
3586
|
alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || "",
|
|
3435
3587
|
...startAt != null ? { startAt } : {},
|
|
3436
3588
|
...clipStart != null ? { clipStart } : {},
|
|
3437
|
-
...clipEnd != null ? { clipEnd } : {}
|
|
3589
|
+
...clipEnd != null ? { clipEnd } : {},
|
|
3590
|
+
...cuts?.length ? { cuts } : {},
|
|
3591
|
+
...edits ? { edits } : {}
|
|
3438
3592
|
});
|
|
3439
3593
|
}
|
|
3440
3594
|
} else if ((n.type === "link" || n.type === "image") && typeof n.url === "string" && VIDEO_FILE_RE.test(n.url)) {
|
|
@@ -3466,12 +3620,26 @@ function extractTableFromContents(contents) {
|
|
|
3466
3620
|
}
|
|
3467
3621
|
return null;
|
|
3468
3622
|
}
|
|
3623
|
+
function blockquoteLines(contents) {
|
|
3624
|
+
const quote = (contents ?? []).find((node) => node.type === "blockquote");
|
|
3625
|
+
if (!quote) return [];
|
|
3626
|
+
const children = quote.children ?? [];
|
|
3627
|
+
return children.flatMap((child) => extractPlainText(child).split(/\r?\n/)).map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
3628
|
+
}
|
|
3469
3629
|
function extractBlockquoteText(contents) {
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3630
|
+
return blockquoteLines(contents).join(" ");
|
|
3631
|
+
}
|
|
3632
|
+
var ATTRIBUTION_LINE = /^(?:[—–―]|-{2,})\s*(\S.*)$/u;
|
|
3633
|
+
var ATTRIBUTION_TAIL = /^(.*[.!?…]["”’')\]]?)\s+(?:[—–―]|-{2,})\s*([^.!?…]{2,90})$/su;
|
|
3634
|
+
function extractBlockquote(contents) {
|
|
3635
|
+
const lines = blockquoteLines(contents);
|
|
3636
|
+
if (lines.length > 1) {
|
|
3637
|
+
const last = lines[lines.length - 1].match(ATTRIBUTION_LINE);
|
|
3638
|
+
if (last) return { text: lines.slice(0, -1).join(" "), attribution: last[1].trim() };
|
|
3473
3639
|
}
|
|
3474
|
-
|
|
3640
|
+
const text = lines.join(" ");
|
|
3641
|
+
const tail = text.match(ATTRIBUTION_TAIL);
|
|
3642
|
+
return tail ? { text: tail[1].trim(), attribution: tail[2].trim() } : { text };
|
|
3475
3643
|
}
|
|
3476
3644
|
function firstParagraph(contents) {
|
|
3477
3645
|
if (!contents) return null;
|
|
@@ -3552,6 +3720,61 @@ function deriveStatHighlightInputs(headingText, contents, bodyText) {
|
|
|
3552
3720
|
function normalizeCoverageText(value) {
|
|
3553
3721
|
return value.replace(/\s+/g, " ").trim();
|
|
3554
3722
|
}
|
|
3723
|
+
var RICH_INLINE_TYPES = /* @__PURE__ */ new Set([
|
|
3724
|
+
"link",
|
|
3725
|
+
"linkReference",
|
|
3726
|
+
"image",
|
|
3727
|
+
"imageReference",
|
|
3728
|
+
"inlineCode",
|
|
3729
|
+
"inlineMath",
|
|
3730
|
+
"htmlInline",
|
|
3731
|
+
"icon",
|
|
3732
|
+
"footnoteReference",
|
|
3733
|
+
"mention"
|
|
3734
|
+
]);
|
|
3735
|
+
function conceptListIsPlain(list) {
|
|
3736
|
+
const items = list.children;
|
|
3737
|
+
if (items.length < 2 || items.length > 6) return false;
|
|
3738
|
+
let plain = true;
|
|
3739
|
+
const visit = (node, depth) => {
|
|
3740
|
+
if (!plain) return;
|
|
3741
|
+
if (RICH_INLINE_TYPES.has(node.type) || node.type === "code" || node.type === "table") {
|
|
3742
|
+
plain = false;
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3745
|
+
const task = node.type === "listItem" && typeof node.checked === "boolean";
|
|
3746
|
+
if (task || node.type === "list" && depth >= 2) {
|
|
3747
|
+
plain = false;
|
|
3748
|
+
return;
|
|
3749
|
+
}
|
|
3750
|
+
const children = node.children ?? [];
|
|
3751
|
+
for (const child of children) visit(child, node.type === "list" ? depth + 1 : depth);
|
|
3752
|
+
};
|
|
3753
|
+
visit(list, 0);
|
|
3754
|
+
return plain;
|
|
3755
|
+
}
|
|
3756
|
+
var CONCEPT_PLACEHOLDERS = {
|
|
3757
|
+
stats: ["3\xD7: faster to make", "92%: finish a first draft", "14 days: saved each quarter"],
|
|
3758
|
+
pyramid: [
|
|
3759
|
+
"Vision: where it is going",
|
|
3760
|
+
"Strategy: how it gets there",
|
|
3761
|
+
"Daily work: what makes it happen"
|
|
3762
|
+
],
|
|
3763
|
+
funnel: ["Everyone: who hears of it", "Interested: who looks closer", "Committed: who stays"],
|
|
3764
|
+
hub: [
|
|
3765
|
+
"People: who does the work",
|
|
3766
|
+
"Process: how it gets done",
|
|
3767
|
+
"Tools: what they use",
|
|
3768
|
+
"Goals: what it is for"
|
|
3769
|
+
],
|
|
3770
|
+
venn: ["This: what only it has", "That: what only it has", "Both: what they share"],
|
|
3771
|
+
matrix: [
|
|
3772
|
+
"Do first: urgent and important",
|
|
3773
|
+
"Plan: important, not urgent",
|
|
3774
|
+
"Delegate: urgent, not important",
|
|
3775
|
+
"Drop: neither"
|
|
3776
|
+
]
|
|
3777
|
+
};
|
|
3555
3778
|
function autoTemplatePreservesContent(templateName, contents) {
|
|
3556
3779
|
const nodes = contents ?? [];
|
|
3557
3780
|
switch (templateName) {
|
|
@@ -3561,6 +3784,17 @@ function autoTemplatePreservesContent(templateName, contents) {
|
|
|
3561
3784
|
return nodes.length === 1 && nodes[0]?.type === "table";
|
|
3562
3785
|
case "list":
|
|
3563
3786
|
return nodes.length === 1 && nodes[0]?.type === "list";
|
|
3787
|
+
case "cards":
|
|
3788
|
+
case "process":
|
|
3789
|
+
case "cycle":
|
|
3790
|
+
case "layers":
|
|
3791
|
+
case "stats":
|
|
3792
|
+
case "pyramid":
|
|
3793
|
+
case "funnel":
|
|
3794
|
+
case "hub":
|
|
3795
|
+
case "venn":
|
|
3796
|
+
case "matrix":
|
|
3797
|
+
return nodes.length === 1 && nodes[0]?.type === "list" && conceptListIsPlain(nodes[0]);
|
|
3564
3798
|
case "diagram":
|
|
3565
3799
|
case "timeline":
|
|
3566
3800
|
return nodes.length === 1 && nodes[0]?.type === "code";
|
|
@@ -3615,8 +3849,14 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3615
3849
|
};
|
|
3616
3850
|
}
|
|
3617
3851
|
case "quote": {
|
|
3618
|
-
const
|
|
3619
|
-
|
|
3852
|
+
const quoted = extractBlockquote(contents);
|
|
3853
|
+
const quote = quoted.text || bodyText;
|
|
3854
|
+
if (quote)
|
|
3855
|
+
return {
|
|
3856
|
+
quote,
|
|
3857
|
+
...quoted.attribution ? { attribution: quoted.attribution } : {},
|
|
3858
|
+
...headingText ? { title: headingText } : {}
|
|
3859
|
+
};
|
|
3620
3860
|
return { quote: headingText };
|
|
3621
3861
|
}
|
|
3622
3862
|
case "fullBleedQuote": {
|
|
@@ -3624,18 +3864,22 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3624
3864
|
return { text };
|
|
3625
3865
|
}
|
|
3626
3866
|
case "pullQuote": {
|
|
3627
|
-
const
|
|
3867
|
+
const quoted = extractBlockquote(contents);
|
|
3868
|
+
const text = quoted.text || bodyText || headingText;
|
|
3628
3869
|
const img = extractFirstImage(contents);
|
|
3629
3870
|
return {
|
|
3630
3871
|
text,
|
|
3872
|
+
...quoted.attribution ? { attribution: quoted.attribution } : {},
|
|
3631
3873
|
...img ? { backgroundImage: { src: img.src, alt: img.alt || headingText } } : {}
|
|
3632
3874
|
};
|
|
3633
3875
|
}
|
|
3634
3876
|
case "videoPullQuote": {
|
|
3635
|
-
const
|
|
3877
|
+
const quoted = extractBlockquote(contents);
|
|
3878
|
+
const text = quoted.text || bodyText || headingText;
|
|
3636
3879
|
const video = extractFirstEmbeddedVideo(contents);
|
|
3637
3880
|
return {
|
|
3638
3881
|
text,
|
|
3882
|
+
...quoted.attribution ? { attribution: quoted.attribution } : {},
|
|
3639
3883
|
...video ? {
|
|
3640
3884
|
backgroundVideo: {
|
|
3641
3885
|
src: video.src,
|
|
@@ -3649,10 +3893,13 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3649
3893
|
}
|
|
3650
3894
|
case "factCard":
|
|
3651
3895
|
return { fact: headingText, explanation: bodyText };
|
|
3652
|
-
case "twoColumn":
|
|
3653
|
-
|
|
3896
|
+
case "twoColumn": {
|
|
3897
|
+
const columns = twoColumnsFromList(contents);
|
|
3898
|
+
if (!headingText && !columns) return placeholders ? {} : null;
|
|
3899
|
+
return { ...headingText ? { header: headingText } : {}, ...columns };
|
|
3900
|
+
}
|
|
3654
3901
|
case "comparisonBar":
|
|
3655
|
-
return placeholders ? { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 } : null;
|
|
3902
|
+
return placeholders && !contents?.length ? { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 } : null;
|
|
3656
3903
|
case "list": {
|
|
3657
3904
|
const items = extractListItems(contents);
|
|
3658
3905
|
const firstList = (contents ?? []).find((n) => n.type === "list");
|
|
@@ -3664,17 +3911,30 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3664
3911
|
...headingText ? { title: headingText } : {}
|
|
3665
3912
|
};
|
|
3666
3913
|
}
|
|
3667
|
-
return placeholders ? { items: ["Item 1", "Item 2", "Item 3"] } : null;
|
|
3914
|
+
return placeholders && !contents?.length ? { items: ["Item 1", "Item 2", "Item 3"] } : null;
|
|
3668
3915
|
}
|
|
3669
3916
|
case "cards":
|
|
3670
3917
|
case "process":
|
|
3671
3918
|
case "cycle":
|
|
3672
|
-
case "layers":
|
|
3673
|
-
|
|
3919
|
+
case "layers":
|
|
3920
|
+
case "stats":
|
|
3921
|
+
case "pyramid":
|
|
3922
|
+
case "funnel":
|
|
3923
|
+
case "hub":
|
|
3924
|
+
case "venn":
|
|
3925
|
+
case "matrix": {
|
|
3926
|
+
const listed = extractConceptItems(contents);
|
|
3674
3927
|
const items = listed.length ? listed : (contents ?? []).filter((node) => node.type === "paragraph" && !isPictureParagraph(node)).map((node) => extractPlainText(node).trim()).filter(Boolean);
|
|
3675
|
-
if (items.length > 0)
|
|
3676
|
-
|
|
3677
|
-
|
|
3928
|
+
if (items.length > 0) {
|
|
3929
|
+
const matrix = templateName === "matrix" ? readMatrixItems(items) : void 0;
|
|
3930
|
+
return {
|
|
3931
|
+
items,
|
|
3932
|
+
...matrix && !matrix.reordered ? { xAxis: matrix.xAxis, yAxis: matrix.yAxis } : {},
|
|
3933
|
+
...headingText ? { title: headingText } : {}
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
return placeholders && !contents?.length ? {
|
|
3937
|
+
items: CONCEPT_PLACEHOLDERS[templateName] ?? (templateName === "cards" ? [
|
|
3678
3938
|
"First idea: what makes it matter",
|
|
3679
3939
|
"Second idea: how it differs",
|
|
3680
3940
|
"Third idea: why it helps"
|
|
@@ -3682,7 +3942,11 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3682
3942
|
"Core: the innermost part",
|
|
3683
3943
|
"Middle: what surrounds it",
|
|
3684
3944
|
"Outer: the outermost layer"
|
|
3685
|
-
] : [
|
|
3945
|
+
] : [
|
|
3946
|
+
"Begin: the first stage",
|
|
3947
|
+
"Grow: what happens next",
|
|
3948
|
+
"Finish: where it leads"
|
|
3949
|
+
])
|
|
3686
3950
|
} : null;
|
|
3687
3951
|
}
|
|
3688
3952
|
case "definitionCard":
|
|
@@ -3811,6 +4075,203 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
|
|
|
3811
4075
|
}
|
|
3812
4076
|
}
|
|
3813
4077
|
|
|
4078
|
+
// src/recommend/listShape.ts
|
|
4079
|
+
var MAX_IDEAS = 6;
|
|
4080
|
+
var MAX_IDEA_CHARACTERS = 170;
|
|
4081
|
+
var CYCLE_CUE = /\b(?:cycles?(?!\s+(?:tracks?|lanes?|paths?|routes?|hire|shops?|parking|helmets?))|cyclical|loops?|recurring|rinse and repeat|virtuous circle|vicious circle)\b/iu;
|
|
4082
|
+
var CYCLE_INTRO = /\b(?:repeats? itself|(?:cycle|process|sequence|pattern|it|this|which) (?:then |all )?repeats?|over and over|again and again|feedback loop|comes? full circle|round and round)\b/iu;
|
|
4083
|
+
var CYCLE_CLOSE = /\b(?:repeats?|again|back to|returns? to|starts? over|begins? again|the cycle|full circle)\b/iu;
|
|
4084
|
+
var NESTED_CUE = /\b(?:layers?|anatomy|cross[- ]section|strata|shells?|nested|concentric|zones?|inside (?:the|an?)\b)/iu;
|
|
4085
|
+
var INNERMOST = /\b(?:inner(?:most)?|core|cent(?:er|re)|nucleus|heart|kernel)\b/iu;
|
|
4086
|
+
var OUTERMOST = /\b(?:outer(?:most)?|crust|surface|skin|shell|exosphere|atmosphere|bark|rind|periphery|edge)\b/iu;
|
|
4087
|
+
var HIERARCHY_CUE = /\b(?:hierarch(?:y|ies)|pyramids?|tiers?|levels? of|ranks?|chain of command|food chain|pecking order|priorit(?:y|ies)|maslow)\b/iu;
|
|
4088
|
+
var TOP_FIRST_CUE = /\b(?:chain of command|pecking order|priorit(?:y|ies)|ranks?|food chain)\b/iu;
|
|
4089
|
+
var APEX = /\b(?:top|highest|peak|apex|summit|chief|executive|president|king|queen|ceo)\b/iu;
|
|
4090
|
+
var BASE = /\b(?:base|bottom|foundation(?:al)?|basic|lowest|ground|bedrock|physiological)\b/iu;
|
|
4091
|
+
var FUNNEL_CUE = /\b(?:funnels?|conversion|drop[- ]?off|narrow(?:s|ing|ed)? down|winnow(?:s|ing|ed)?|whittl(?:e|es|ing|ed) down|filter(?:s|ing|ed)?(?: down)?|sieve)\b/iu;
|
|
4092
|
+
var SEQUENCE_CUE = /\b(?:steps?|how to|process(?:es)?|stages?|phases?|procedure|workflow|instructions?|journey|roadmap|pathway|sequence|walkthrough)\b/iu;
|
|
4093
|
+
var ORDINAL_OPENER = /^(?:(?:first|second|third|fourth|fifth)(?:ly)?|then|next|after(?:wards| that)?|finally|lastly|step \d+)\b/iu;
|
|
4094
|
+
var CONTRAST_CUE = /\b(?:vs\.?|versus|compared (?:to|with)|comparison|differences? between|contrast)\b|\bor\b[^?]*\?/iu;
|
|
4095
|
+
var OPPOSITES = [
|
|
4096
|
+
[/^before$/iu, /^after$/iu],
|
|
4097
|
+
[/^then$/iu, /^now$/iu],
|
|
4098
|
+
[/^pros?$/iu, /^cons?$/iu],
|
|
4099
|
+
[/^advantages?$/iu, /^disadvantages?$/iu],
|
|
4100
|
+
[/^strengths?$/iu, /^weakness(?:es)?$/iu],
|
|
4101
|
+
[/^benefits?$/iu, /^(?:risks?|costs?|drawbacks?)$/iu],
|
|
4102
|
+
[/^(?:risks?|costs?)$/iu, /^(?:benefits?|rewards?)$/iu],
|
|
4103
|
+
[/^old$/iu, /^new$/iu],
|
|
4104
|
+
[/^myth$/iu, /^(?:fact|reality|truth)$/iu],
|
|
4105
|
+
[/^expectations?$/iu, /^reality$/iu],
|
|
4106
|
+
[/^problem$/iu, /^solution$/iu],
|
|
4107
|
+
[/^causes?$/iu, /^effects?$/iu],
|
|
4108
|
+
[/^do$/iu, /^don[’']?t$/iu],
|
|
4109
|
+
[/^with$/iu, /^without$/iu],
|
|
4110
|
+
[/^for$/iu, /^against$/iu]
|
|
4111
|
+
];
|
|
4112
|
+
var OVERLAP_CUE = /\b(?:overlap(?:s|ping)?|in common|venn|intersection|common ground)\b/iu;
|
|
4113
|
+
var MATRIX_CUE = /\b(?:matrix|quadrants?|2\s?[x×]\s?2|two[- ]by[- ]two|four[- ]box|eisenhower|swot)\b/iu;
|
|
4114
|
+
var HUB_CUE = /\b(?:factors?|causes?|drivers?|pillars?|elements?|components?|ingredients?|principles?|dimensions?|aspects?|forces?|contributors?|building blocks|parts of|sources of|reasons?)\b/iu;
|
|
4115
|
+
var YEAR_LIKE = /^(?:1\d{3}|20\d{2})s?$/u;
|
|
4116
|
+
function figureValue(figure) {
|
|
4117
|
+
const match = figure.replace(/,/gu, "").match(/\d+(?:\.\d+)?/u);
|
|
4118
|
+
if (!match) return NaN;
|
|
4119
|
+
const scale = /\b(?:trillion)\b/iu.test(figure) ? 1e12 : /\b(?:billion)\b|\dB\b/u.test(figure) ? 1e9 : /\b(?:million)\b|\dM\b/u.test(figure) ? 1e6 : /\b(?:thousand)\b|\d[Kk]\b/u.test(figure) ? 1e3 : 1;
|
|
4120
|
+
return Number(match[0]) * scale;
|
|
4121
|
+
}
|
|
4122
|
+
function result(shape, template, confidence, reason, inputs) {
|
|
4123
|
+
return { shape, template, confidence, reason, ...inputs ? { inputs } : {} };
|
|
4124
|
+
}
|
|
4125
|
+
var PLAIN = (reason) => result("plain", "list", "strong", reason);
|
|
4126
|
+
function opposites(a, b) {
|
|
4127
|
+
const [x, y] = [a.trim(), b.trim()];
|
|
4128
|
+
return OPPOSITES.some(
|
|
4129
|
+
([left, right]) => left.test(x) && right.test(y) || left.test(y) && right.test(x)
|
|
4130
|
+
);
|
|
4131
|
+
}
|
|
4132
|
+
function classifyListShape(input) {
|
|
4133
|
+
const texts = input.items.map((item) => item.replace(/\s+/gu, " ").trim()).filter(Boolean);
|
|
4134
|
+
if (texts.length < 2) return PLAIN("fewer than two items");
|
|
4135
|
+
if (texts.length > MAX_IDEAS) return PLAIN(`more than ${MAX_IDEAS} items`);
|
|
4136
|
+
if (texts.some((text) => text.length > MAX_IDEA_CHARACTERS)) return PLAIN("items run long");
|
|
4137
|
+
const items = texts.map(splitConceptItem);
|
|
4138
|
+
const n = items.length;
|
|
4139
|
+
const heading = (input.heading ?? "").replace(/\s+/gu, " ").trim();
|
|
4140
|
+
const context = `${heading} ${input.intro ?? ""}`;
|
|
4141
|
+
const labelled = items.filter((item) => item.label).length;
|
|
4142
|
+
const first = `${items[0].label} ${items[0].detail}`;
|
|
4143
|
+
const last = `${items[n - 1].label} ${items[n - 1].detail}`;
|
|
4144
|
+
if (timelineItems(items))
|
|
4145
|
+
return result("timeline", "process", "strong", "items open with dates in order");
|
|
4146
|
+
const stats = texts.map(splitStatItem);
|
|
4147
|
+
const figures = stats.map((stat) => stat.figure);
|
|
4148
|
+
const allFigures = figures.every(Boolean) && !figures.every((figure) => YEAR_LIKE.test(figure));
|
|
4149
|
+
if (allFigures) {
|
|
4150
|
+
const values = figures.map(figureValue);
|
|
4151
|
+
const narrowing = n >= 3 && values.every((value, index) => index === 0 || value < values[index - 1]);
|
|
4152
|
+
if (narrowing && (FUNNEL_CUE.test(context) || SEQUENCE_CUE.test(context) || input.ordered))
|
|
4153
|
+
return result("funnel", "funnel", "strong", "figures that shrink stage by stage");
|
|
4154
|
+
const explicit = texts.every((text) => {
|
|
4155
|
+
const { label, detail } = splitConceptItem(text);
|
|
4156
|
+
return !!label && (isFigureText(label) || isFigureText(detail));
|
|
4157
|
+
});
|
|
4158
|
+
const scaled = figures.every(
|
|
4159
|
+
(figure) => /[%‰$€£¥×,]|\d\s?(?:[xKMB]|million|billion|trillion|thousand|percent)\b|\s(?:in|of)\s\d/iu.test(
|
|
4160
|
+
figure
|
|
4161
|
+
)
|
|
4162
|
+
);
|
|
4163
|
+
if (n <= 6 && (explicit || scaled))
|
|
4164
|
+
return result("figures", "stats", "strong", "every item is a figure and what it measures");
|
|
4165
|
+
}
|
|
4166
|
+
if (vennOverlapIndex(items) >= 0 && n === 3)
|
|
4167
|
+
return result("overlap", "venn", "strong", "two sets and an item naming what they share");
|
|
4168
|
+
if (n === 2 && items.every((item) => item.label) && opposites(items[0].label, items[1].label))
|
|
4169
|
+
return result("contrast", "cards", "strong", "two items labelled as opposites", {
|
|
4170
|
+
versus: true
|
|
4171
|
+
});
|
|
4172
|
+
if (n >= 3 && (CYCLE_CUE.test(heading) || CYCLE_INTRO.test(input.intro ?? "") || CYCLE_CLOSE.test(last) && (input.ordered || SEQUENCE_CUE.test(context))))
|
|
4173
|
+
return result(
|
|
4174
|
+
"cycle",
|
|
4175
|
+
"cycle",
|
|
4176
|
+
"strong",
|
|
4177
|
+
"the heading or the last item says it comes round again"
|
|
4178
|
+
);
|
|
4179
|
+
if (n === 4 && MATRIX_CUE.test(context))
|
|
4180
|
+
return result("matrix", "matrix", "strong", "four items under a heading that names a matrix");
|
|
4181
|
+
if (n >= 3 && FUNNEL_CUE.test(context))
|
|
4182
|
+
return result("funnel", "funnel", "strong", "the heading says the stages narrow");
|
|
4183
|
+
if (n >= 3 && NESTED_CUE.test(context)) {
|
|
4184
|
+
if (INNERMOST.test(first) && !INNERMOST.test(last))
|
|
4185
|
+
return result("nested", "layers", "strong", "nested parts, listed from the inside out");
|
|
4186
|
+
if (INNERMOST.test(last) && (OUTERMOST.test(first) || !INNERMOST.test(first)))
|
|
4187
|
+
return result("nested", "layers", "strong", "nested parts, listed from the outside in", {
|
|
4188
|
+
items: [...texts].reverse()
|
|
4189
|
+
});
|
|
4190
|
+
return result("nested", "layers", "weak", "the heading suggests nested parts");
|
|
4191
|
+
}
|
|
4192
|
+
if (n >= 3 && HIERARCHY_CUE.test(context)) {
|
|
4193
|
+
if (BASE.test(first) && !BASE.test(last))
|
|
4194
|
+
return result("hierarchy", "pyramid", "strong", "a hierarchy, listed from the base up", {
|
|
4195
|
+
items: [...texts].reverse()
|
|
4196
|
+
});
|
|
4197
|
+
if (APEX.test(first) || BASE.test(last) || TOP_FIRST_CUE.test(context))
|
|
4198
|
+
return result("hierarchy", "pyramid", "strong", "a hierarchy, listed from the top down");
|
|
4199
|
+
return result("hierarchy", "pyramid", "weak", "the heading suggests a hierarchy");
|
|
4200
|
+
}
|
|
4201
|
+
if (n === 2 && CONTRAST_CUE.test(heading))
|
|
4202
|
+
return result("contrast", "cards", "strong", "two items under a heading that compares them", {
|
|
4203
|
+
versus: true
|
|
4204
|
+
});
|
|
4205
|
+
if (n === 2 && OVERLAP_CUE.test(context))
|
|
4206
|
+
return result("overlap", "venn", "weak", "the heading suggests two sets that overlap");
|
|
4207
|
+
const openers = texts.filter((text) => ORDINAL_OPENER.test(text)).length;
|
|
4208
|
+
if (input.ordered || openers >= 2 || SEQUENCE_CUE.test(heading))
|
|
4209
|
+
return result(
|
|
4210
|
+
"sequence",
|
|
4211
|
+
"process",
|
|
4212
|
+
"strong",
|
|
4213
|
+
input.ordered ? "a numbered list" : openers >= 2 ? "items open with ordering words" : "the heading names steps"
|
|
4214
|
+
);
|
|
4215
|
+
const brief = texts.every((text) => text.length <= 110);
|
|
4216
|
+
if (n >= 3 && brief && HUB_CUE.test(heading) && labelled * 2 >= n)
|
|
4217
|
+
return result(
|
|
4218
|
+
"hub",
|
|
4219
|
+
"hub",
|
|
4220
|
+
"strong",
|
|
4221
|
+
"labelled items under a heading that names what they share"
|
|
4222
|
+
);
|
|
4223
|
+
if (labelled * 3 >= n * 2)
|
|
4224
|
+
return result("parallel", "cards", "strong", "items written as a label and its detail");
|
|
4225
|
+
if (n >= 3 && brief && HUB_CUE.test(heading))
|
|
4226
|
+
return result("hub", "hub", "weak", "the heading names what the items share");
|
|
4227
|
+
if (n <= 4 && texts.every((text) => text.length <= 90))
|
|
4228
|
+
return result("parallel", "cards", "weak", "a few short items");
|
|
4229
|
+
return PLAIN("no relationship the items make clear");
|
|
4230
|
+
}
|
|
4231
|
+
var SHARED_CUE = /\b(?:both|shared?|shares|sharing|alike|similar(?:ly|ity|ities)?|in common)\b/iu;
|
|
4232
|
+
function shapeIsStated(template, evidence) {
|
|
4233
|
+
const heading = (evidence.heading ?? "").replace(/\s+/gu, " ").trim();
|
|
4234
|
+
const passage = (evidence.passage ?? "").replace(/\s+/gu, " ").trim();
|
|
4235
|
+
const texts = evidence.items.map((item) => item.replace(/\s+/gu, " ").trim()).filter(Boolean);
|
|
4236
|
+
const context = `${heading} ${passage}`;
|
|
4237
|
+
const everything = `${context} ${texts.join(" ")}`;
|
|
4238
|
+
switch (template) {
|
|
4239
|
+
case "cycle":
|
|
4240
|
+
return CYCLE_CUE.test(context) || CYCLE_INTRO.test(passage) || texts.length > 0 && CYCLE_CLOSE.test(texts[texts.length - 1]);
|
|
4241
|
+
case "layers":
|
|
4242
|
+
return NESTED_CUE.test(context) || INNERMOST.test(everything) && OUTERMOST.test(everything);
|
|
4243
|
+
case "pyramid":
|
|
4244
|
+
return HIERARCHY_CUE.test(context) || APEX.test(everything) && BASE.test(everything);
|
|
4245
|
+
case "funnel": {
|
|
4246
|
+
if (FUNNEL_CUE.test(context)) return true;
|
|
4247
|
+
const values = texts.map((text) => figureValue(splitStatItem(text).figure));
|
|
4248
|
+
return values.length >= 3 && values.every(
|
|
4249
|
+
(value, index) => Number.isFinite(value) && (!index || value < values[index - 1])
|
|
4250
|
+
);
|
|
4251
|
+
}
|
|
4252
|
+
case "venn":
|
|
4253
|
+
return OVERLAP_CUE.test(context) || SHARED_CUE.test(passage);
|
|
4254
|
+
case "matrix":
|
|
4255
|
+
return MATRIX_CUE.test(context) || !!evidence.axes?.[0]?.trim() && !!evidence.axes[1]?.trim();
|
|
4256
|
+
default:
|
|
4257
|
+
return true;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
function unstatedShapeFallback(template) {
|
|
4261
|
+
switch (template) {
|
|
4262
|
+
case "cycle":
|
|
4263
|
+
case "funnel":
|
|
4264
|
+
return "process";
|
|
4265
|
+
case "layers":
|
|
4266
|
+
case "pyramid":
|
|
4267
|
+
case "venn":
|
|
4268
|
+
case "matrix":
|
|
4269
|
+
return "cards";
|
|
4270
|
+
default:
|
|
4271
|
+
return void 0;
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
|
|
3814
4275
|
// src/recommend/templates.ts
|
|
3815
4276
|
var EMPTY_PROFILE = Object.freeze({
|
|
3816
4277
|
hasImage: false,
|
|
@@ -3850,7 +4311,7 @@ function htmlElementsByTag(root, tagNames) {
|
|
|
3850
4311
|
}
|
|
3851
4312
|
return out;
|
|
3852
4313
|
}
|
|
3853
|
-
function profileBlockContents(nodes) {
|
|
4314
|
+
function profileBlockContents(nodes, options = {}) {
|
|
3854
4315
|
if (!nodes || nodes.length === 0) return { ...EMPTY_PROFILE };
|
|
3855
4316
|
let imageCount = 0;
|
|
3856
4317
|
let hasBlockquote = false;
|
|
@@ -3904,6 +4365,7 @@ function profileBlockContents(nodes) {
|
|
|
3904
4365
|
let hasNumberHighlight = false;
|
|
3905
4366
|
walkMarkdownTree(root, (n) => {
|
|
3906
4367
|
if (hasNumberHighlight) return true;
|
|
4368
|
+
if (n.type === "list") return true;
|
|
3907
4369
|
if (n.type !== "paragraph") return;
|
|
3908
4370
|
const text = extractPlainText(n).trim();
|
|
3909
4371
|
if (!text) return true;
|
|
@@ -3939,6 +4401,12 @@ function profileBlockContents(nodes) {
|
|
|
3939
4401
|
hasTree = true;
|
|
3940
4402
|
}
|
|
3941
4403
|
}
|
|
4404
|
+
const onlyList = nodes.length === 1 && nodes[0].type === "list" ? nodes[0] : void 0;
|
|
4405
|
+
const listShape = onlyList ? classifyListShape({
|
|
4406
|
+
heading: options.heading,
|
|
4407
|
+
items: extractConceptItems([onlyList]),
|
|
4408
|
+
ordered: onlyList.ordered === true
|
|
4409
|
+
}) : void 0;
|
|
3942
4410
|
return {
|
|
3943
4411
|
hasImage: imageCount > 0,
|
|
3944
4412
|
imageCount,
|
|
@@ -3952,7 +4420,8 @@ function profileBlockContents(nodes) {
|
|
|
3952
4420
|
hasCompoundBody: nodes.length > 1 && nodes.some((node) => node.type !== "paragraph"),
|
|
3953
4421
|
hasAsciiDiagram,
|
|
3954
4422
|
hasTimeline,
|
|
3955
|
-
hasTree
|
|
4423
|
+
hasTree,
|
|
4424
|
+
...listShape ? { listShape } : {}
|
|
3956
4425
|
};
|
|
3957
4426
|
}
|
|
3958
4427
|
var UNIVERSAL_DEFAULTS = ["title", "sectionHeader", "content", "factCard", "twoColumn"];
|
|
@@ -4001,6 +4470,11 @@ function recommendedNamesForProfile(profile) {
|
|
|
4001
4470
|
if (profile.hasList) {
|
|
4002
4471
|
anyContentSignal = true;
|
|
4003
4472
|
names.add("list");
|
|
4473
|
+
if (profile.listShape && profile.listShape.shape !== "plain") {
|
|
4474
|
+
names.add(profile.listShape.template);
|
|
4475
|
+
names.add("cards");
|
|
4476
|
+
names.add("process");
|
|
4477
|
+
}
|
|
4004
4478
|
}
|
|
4005
4479
|
if (profile.hasTable) {
|
|
4006
4480
|
anyContentSignal = true;
|
|
@@ -4028,7 +4502,7 @@ function recommendedNamesForProfile(profile) {
|
|
|
4028
4502
|
}
|
|
4029
4503
|
return Array.from(names);
|
|
4030
4504
|
}
|
|
4031
|
-
function pickAutoTemplate(profile, blockIndex = 0) {
|
|
4505
|
+
function pickAutoTemplate(profile, blockIndex = 0, options = {}) {
|
|
4032
4506
|
if (profile.hasCompoundBody) return void 0;
|
|
4033
4507
|
if (profile.hasAsciiDiagram) return "diagram";
|
|
4034
4508
|
if (profile.hasTimeline) return "timeline";
|
|
@@ -4039,7 +4513,8 @@ function pickAutoTemplate(profile, blockIndex = 0) {
|
|
|
4039
4513
|
if (profile.hasImage) return blockIndex % 2 === 0 ? "leftFeature" : "rightFeature";
|
|
4040
4514
|
if (profile.hasBlockquote) return "quote";
|
|
4041
4515
|
if (profile.hasNumberHighlight) return "statHighlight";
|
|
4042
|
-
if (profile.hasList)
|
|
4516
|
+
if (profile.hasList)
|
|
4517
|
+
return options.concepts !== false && profile.listShape?.confidence === "strong" ? profile.listShape.template : "list";
|
|
4043
4518
|
return void 0;
|
|
4044
4519
|
}
|
|
4045
4520
|
function recommendTemplatesForBlock(profile, allNames) {
|
|
@@ -4060,6 +4535,12 @@ export {
|
|
|
4060
4535
|
isMediaAnnotationName,
|
|
4061
4536
|
isStandaloneHtmlVideo,
|
|
4062
4537
|
extractMediaFromContents,
|
|
4538
|
+
splitConceptItem,
|
|
4539
|
+
isFigureText,
|
|
4540
|
+
splitStatItem,
|
|
4541
|
+
vennOverlapIndex,
|
|
4542
|
+
timelineItems,
|
|
4543
|
+
readMatrixItems,
|
|
4063
4544
|
toCells,
|
|
4064
4545
|
cellWidth,
|
|
4065
4546
|
isHoriz,
|
|
@@ -4110,10 +4591,12 @@ export {
|
|
|
4110
4591
|
findFirstList,
|
|
4111
4592
|
extractHostedVideos,
|
|
4112
4593
|
isPictureFence,
|
|
4594
|
+
isSpatialPictureFence,
|
|
4113
4595
|
isMaterializedMediaNode,
|
|
4114
4596
|
isPictureParagraph,
|
|
4115
4597
|
extractBodyPlainText,
|
|
4116
4598
|
extractListItems,
|
|
4599
|
+
extractConceptItems,
|
|
4117
4600
|
renderMarkdownBlocksHtml,
|
|
4118
4601
|
markdownBlockSeparatorLines,
|
|
4119
4602
|
extractRichListItems,
|
|
@@ -4123,8 +4606,12 @@ export {
|
|
|
4123
4606
|
extractFirstEmbeddedVideo,
|
|
4124
4607
|
extractTableFromContents,
|
|
4125
4608
|
extractBlockquoteText,
|
|
4609
|
+
extractBlockquote,
|
|
4126
4610
|
autoTemplatePreservesContent,
|
|
4127
4611
|
deriveTemplateInputs,
|
|
4612
|
+
classifyListShape,
|
|
4613
|
+
shapeIsStated,
|
|
4614
|
+
unstatedShapeFallback,
|
|
4128
4615
|
profileBlockContents,
|
|
4129
4616
|
pickAutoTemplate,
|
|
4130
4617
|
recommendTemplatesForBlock
|