@bendyline/squisq 1.5.0 → 1.5.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/README.md +14 -1
- package/dist/{ContentContainer-DNx460y8.d.ts → ContentContainer-BXUlIf18.d.ts} +6 -2
- package/dist/{Doc-BsMkmzH8.d.ts → Doc-BBVGq1_9.d.ts} +94 -15
- package/dist/{ImageEditDoc-DvoeHMP9.d.ts → ImageEditDoc-FuyGtt6o.d.ts} +1 -1
- package/dist/{chunk-4ZFRZBU2.js → chunk-5TYS5V3G.js} +866 -192
- package/dist/chunk-5TYS5V3G.js.map +1 -0
- package/dist/{chunk-7MQGNR76.js → chunk-A4O7GIWE.js} +2 -27
- package/dist/chunk-A4O7GIWE.js.map +1 -0
- package/dist/{chunk-OH43RPTP.js → chunk-BEQODJWV.js} +20 -3
- package/dist/chunk-BEQODJWV.js.map +1 -0
- package/dist/chunk-ET53IIEP.js +1 -0
- package/dist/chunk-ET53IIEP.js.map +1 -0
- package/dist/{chunk-KZNXZ5SM.js → chunk-GPLTCSXO.js} +18 -7
- package/dist/chunk-GPLTCSXO.js.map +1 -0
- package/dist/{chunk-OKJOQXK4.js → chunk-HWVFJAAH.js} +38 -2
- package/dist/chunk-HWVFJAAH.js.map +1 -0
- package/dist/{chunk-2TMAWB4Y.js → chunk-IMSCRLLV.js} +1 -1
- package/dist/chunk-IMSCRLLV.js.map +1 -0
- package/dist/{chunk-4AKUQPEE.js → chunk-KKNUBQ6Y.js} +1 -1
- package/dist/chunk-KKNUBQ6Y.js.map +1 -0
- package/dist/{chunk-TVSGLUID.js → chunk-LDQ2HJIX.js} +3 -3
- package/dist/{chunk-RUDYOTA6.js → chunk-LTN6I7QW.js} +56 -1
- package/dist/chunk-LTN6I7QW.js.map +1 -0
- package/dist/{chunk-2OIBZYKP.js → chunk-P3K7NLHF.js} +2 -2
- package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
- package/dist/chunk-T5UK6YOB.js.map +1 -0
- package/dist/{chunk-FR2RBTKO.js → chunk-TQWLI6S2.js} +93 -27
- package/dist/chunk-TQWLI6S2.js.map +1 -0
- package/dist/{chunk-LRCV7N7F.js → chunk-UY7KGQ5R.js} +2 -2
- package/dist/{chunk-3E5F2XMR.js → chunk-VWUFZ6ZG.js} +29 -2
- package/dist/chunk-VWUFZ6ZG.js.map +1 -0
- package/dist/doc/index.d.ts +4 -4
- package/dist/doc/index.js +24 -9
- package/dist/generate/index.d.ts +2 -80
- package/dist/generate/index.js +2 -7
- package/dist/imageEdit/index.d.ts +4 -4
- package/dist/imageEdit/index.js +2 -2
- package/dist/index.d.ts +8 -8
- package/dist/index.js +41 -26
- package/dist/jsonForm/index.d.ts +42 -1
- package/dist/jsonForm/index.js +9 -1
- package/dist/markdown/index.d.ts +18 -2
- package/dist/markdown/index.js +5 -3
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/index.js +3 -5
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +1 -1
- package/dist/story/index.d.ts +151 -13
- package/dist/story/index.js +24 -9
- package/dist/{themeLibrary-DQQKuRMx.d.ts → themeLibrary-CehcJhzz.d.ts} +1 -1
- package/dist/timing/index.js +3 -5
- package/dist/transform/index.d.ts +2 -2
- package/dist/transform/index.js +3 -3
- package/dist/versions/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/__tests__/contentExtractor.test.ts +60 -0
- package/src/__tests__/coverBlock.test.ts +79 -3
- package/src/__tests__/customTemplateSample.test.ts +63 -2
- package/src/__tests__/customTemplatesFrontmatter.test.ts +31 -0
- package/src/__tests__/customThemesFrontmatter.test.ts +27 -0
- package/src/__tests__/inputDescriptors.test.ts +162 -0
- package/src/__tests__/jsonForm.tokens.test.ts +84 -0
- package/src/__tests__/markdown.test.ts +64 -0
- package/src/__tests__/markdownSanitize.test.ts +15 -0
- package/src/__tests__/markdownToDoc.test.ts +173 -2
- package/src/__tests__/mediaAnnotations.test.ts +27 -0
- package/src/__tests__/shapeGeometry.test.ts +37 -0
- package/src/__tests__/structuredData.test.ts +73 -3
- package/src/__tests__/templateMetadata.test.ts +11 -1
- package/src/__tests__/templates.test.ts +112 -0
- package/src/__tests__/validateDoc.test.ts +160 -0
- package/src/doc/annotationBlocks.ts +117 -0
- package/src/doc/customTemplatesFrontmatter.ts +7 -1
- package/src/doc/customThemesFrontmatter.ts +6 -1
- package/src/doc/docToMarkdown.ts +78 -29
- package/src/doc/getLayers.ts +11 -2
- package/src/doc/index.ts +12 -0
- package/src/doc/markdownToDoc.ts +80 -3
- package/src/doc/mediaAnnotations.ts +15 -33
- package/src/doc/standaloneAnnotation.ts +58 -0
- package/src/doc/structuredData.ts +92 -21
- package/src/doc/templates/__tests__/customTemplate.test.ts +21 -2
- package/src/doc/templates/comparisonBar.ts +8 -1
- package/src/doc/templates/coverBlock.ts +78 -15
- package/src/doc/templates/customTemplate.ts +10 -4
- package/src/doc/templates/diagramBlock.ts +2 -2
- package/src/doc/templates/drawingBlock.ts +2 -2
- package/src/doc/templates/factCard.ts +4 -2
- package/src/doc/templates/imageWithCaption.ts +10 -8
- package/src/doc/templates/index.ts +12 -2
- package/src/doc/templates/inputDescriptors.ts +432 -0
- package/src/doc/templates/photoGrid.ts +16 -39
- package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +267 -1
- package/src/doc/templates/tokens/resolveTokens.ts +0 -0
- package/src/doc/templates/videoWithCaption.ts +15 -14
- package/src/doc/utils/nearest.ts +59 -0
- package/src/doc/utils/shapeGeometry.ts +90 -2
- package/src/doc/validate.ts +153 -54
- package/src/generate/contentExtractor.ts +148 -35
- package/src/generate/index.ts +0 -3
- package/src/jsonForm/index.ts +2 -0
- package/src/jsonForm/tokens.ts +78 -0
- package/src/markdown/annotationCoercion.ts +1 -1
- package/src/markdown/attrTokens.ts +19 -0
- package/src/markdown/convert.ts +11 -1
- package/src/markdown/index.ts +2 -1
- package/src/markdown/sanitize.ts +22 -2
- package/src/markdown/stringify.ts +12 -3
- package/src/markdown/utils.ts +62 -1
- package/src/schemas/CustomTemplates.ts +56 -4
- package/src/schemas/Doc.ts +39 -41
- package/src/schemas/themeCompile.ts +1 -1
- package/src/storage/ContentContainer.ts +6 -2
- package/src/transform/types.ts +1 -1
- package/dist/chunk-2TMAWB4Y.js.map +0 -1
- package/dist/chunk-3E5F2XMR.js.map +0 -1
- package/dist/chunk-4AKUQPEE.js.map +0 -1
- package/dist/chunk-4X3JQXNM.js +0 -35
- package/dist/chunk-4X3JQXNM.js.map +0 -1
- package/dist/chunk-4ZFRZBU2.js.map +0 -1
- package/dist/chunk-7MQGNR76.js.map +0 -1
- package/dist/chunk-FBKP5CAO.js +0 -145
- package/dist/chunk-FBKP5CAO.js.map +0 -1
- package/dist/chunk-FR2RBTKO.js.map +0 -1
- package/dist/chunk-K32VJONL.js.map +0 -1
- package/dist/chunk-KZNXZ5SM.js.map +0 -1
- package/dist/chunk-OH43RPTP.js.map +0 -1
- package/dist/chunk-OKJOQXK4.js.map +0 -1
- package/dist/chunk-RUDYOTA6.js.map +0 -1
- package/src/__tests__/slideshowGenerator.test.ts +0 -128
- package/src/generate/slideshowGenerator.ts +0 -280
- /package/dist/{chunk-TVSGLUID.js.map → chunk-LDQ2HJIX.js.map} +0 -0
- /package/dist/{chunk-2OIBZYKP.js.map → chunk-P3K7NLHF.js.map} +0 -0
- /package/dist/{chunk-LRCV7N7F.js.map → chunk-UY7KGQ5R.js.map} +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
pickAutoTemplate,
|
|
3
3
|
profileBlockContents
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UY7KGQ5R.js";
|
|
5
5
|
import {
|
|
6
6
|
SeededRandom,
|
|
7
7
|
hashString
|
|
8
8
|
} from "./chunk-FVTQSNR7.js";
|
|
9
9
|
import {
|
|
10
|
-
clipEndpoints,
|
|
11
10
|
connectorPath,
|
|
12
11
|
lineStyleDasharray,
|
|
13
|
-
shapePath
|
|
14
|
-
|
|
12
|
+
shapePath,
|
|
13
|
+
snapEndpoints
|
|
14
|
+
} from "./chunk-LTN6I7QW.js";
|
|
15
15
|
import {
|
|
16
16
|
DEFAULT_THEME,
|
|
17
17
|
FRONTMATTER_CUSTOM_TEMPLATES_KEY,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
resolveTheme,
|
|
25
25
|
validateTheme,
|
|
26
26
|
withAlpha
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-KKNUBQ6Y.js";
|
|
28
28
|
import {
|
|
29
29
|
VIEWPORT_PRESETS,
|
|
30
30
|
createTemplateContext,
|
|
@@ -34,25 +34,28 @@ import {
|
|
|
34
34
|
scaledFontSize2 as scaledFontSize
|
|
35
35
|
} from "./chunk-DLXZMS5K.js";
|
|
36
36
|
import {
|
|
37
|
+
KNOWN_BLOCK_META_KEYS,
|
|
37
38
|
coerceAnnotationValues,
|
|
38
39
|
matchTrailingTemplateAnnotation,
|
|
39
40
|
parseMarkdown,
|
|
41
|
+
parseNumber,
|
|
40
42
|
parseTimeSeconds,
|
|
43
|
+
serializeAnnotation,
|
|
41
44
|
splitKeyValueToken,
|
|
42
45
|
tokenizeAttrTokens
|
|
43
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-BEQODJWV.js";
|
|
44
47
|
import {
|
|
45
48
|
extractPlainText,
|
|
46
49
|
getChildren,
|
|
47
50
|
readFrontmatterThemeId,
|
|
48
51
|
walkMarkdownTree
|
|
49
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-HWVFJAAH.js";
|
|
50
53
|
import {
|
|
51
54
|
resolveBlockTransition
|
|
52
55
|
} from "./chunk-QE7TWGVT.js";
|
|
53
56
|
import {
|
|
54
57
|
estimateReadingTime
|
|
55
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-VWUFZ6ZG.js";
|
|
56
59
|
|
|
57
60
|
// src/doc/templates/persistentLayers.ts
|
|
58
61
|
var GRADIENT_PRESETS = {
|
|
@@ -530,6 +533,10 @@ function applyRenderStyleToLayers(layers, block, theme) {
|
|
|
530
533
|
}
|
|
531
534
|
|
|
532
535
|
// src/doc/templates/tokens/resolveTokens.ts
|
|
536
|
+
var TOKEN_RE = /\{(\w+)(?::([^}|]+))?(?:\|([^}]*))?\}/g;
|
|
537
|
+
var ESCAPE_RE = /\{\{([^{}]*)\}\}/g;
|
|
538
|
+
var ESCAPED_OPEN = " ESC_OPEN ";
|
|
539
|
+
var ESCAPED_CLOSE = " ESC_CLOSE ";
|
|
533
540
|
function collectImages(block) {
|
|
534
541
|
if (!block.contents) return [];
|
|
535
542
|
const out = [];
|
|
@@ -547,6 +554,21 @@ function collectImages(block) {
|
|
|
547
554
|
}
|
|
548
555
|
return out;
|
|
549
556
|
}
|
|
557
|
+
function collectListItems(block) {
|
|
558
|
+
if (!block.contents) return [];
|
|
559
|
+
const out = [];
|
|
560
|
+
for (const node of block.contents) {
|
|
561
|
+
walkMarkdownTree(node, (n) => {
|
|
562
|
+
if (n.type === "listItem") {
|
|
563
|
+
const text = extractPlainText(n).replace(/\s+/g, " ").trim();
|
|
564
|
+
if (text) out.push(text);
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
return;
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return out;
|
|
571
|
+
}
|
|
550
572
|
function getBlockBodyText(block) {
|
|
551
573
|
if (!block.contents) return "";
|
|
552
574
|
return block.contents.map((node) => extractPlainText(node).trim()).filter((s) => s.length > 0).join(" ");
|
|
@@ -555,22 +577,26 @@ function getChildrenSummary(block) {
|
|
|
555
577
|
if (!block.children || block.children.length === 0) return "";
|
|
556
578
|
return block.children.map((c) => c.title?.trim()).filter((t) => Boolean(t)).join(", ");
|
|
557
579
|
}
|
|
558
|
-
function
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
580
|
+
function escapeBraces(text) {
|
|
581
|
+
return text.replace(ESCAPE_RE, (_match, inner) => ESCAPED_OPEN + inner + ESCAPED_CLOSE);
|
|
582
|
+
}
|
|
583
|
+
function unescapeBraces(text) {
|
|
584
|
+
return text.replace(new RegExp(ESCAPED_OPEN, "g"), "{").replace(new RegExp(ESCAPED_CLOSE, "g"), "}");
|
|
585
|
+
}
|
|
586
|
+
function substitute(text, resolve) {
|
|
587
|
+
const escaped = escapeBraces(text);
|
|
564
588
|
const tokenized = escaped.replace(
|
|
565
|
-
|
|
566
|
-
(match, name, arg) => {
|
|
567
|
-
const value =
|
|
568
|
-
|
|
589
|
+
TOKEN_RE,
|
|
590
|
+
(match, name, arg, def) => {
|
|
591
|
+
const value = resolve(name, arg);
|
|
592
|
+
if (value == null) return match;
|
|
593
|
+
if (value === "" && def !== void 0) return def;
|
|
594
|
+
return value;
|
|
569
595
|
}
|
|
570
596
|
);
|
|
571
|
-
return tokenized
|
|
597
|
+
return unescapeBraces(tokenized);
|
|
572
598
|
}
|
|
573
|
-
function resolveToken(name, arg, block, images) {
|
|
599
|
+
function resolveToken(name, arg, block, images, item) {
|
|
574
600
|
switch (name) {
|
|
575
601
|
case "title":
|
|
576
602
|
return block.title ?? "";
|
|
@@ -578,49 +604,148 @@ function resolveToken(name, arg, block, images) {
|
|
|
578
604
|
return getBlockBodyText(block);
|
|
579
605
|
case "children":
|
|
580
606
|
return getChildrenSummary(block);
|
|
607
|
+
case "attr": {
|
|
608
|
+
if (!arg) return "";
|
|
609
|
+
const raw = block.templateOverrides?.[arg] ?? block.templateData?.[arg] ?? block.metadata?.[arg];
|
|
610
|
+
return raw == null ? "" : String(raw);
|
|
611
|
+
}
|
|
581
612
|
case "image": {
|
|
582
613
|
const idx = arg ? parseInt(arg, 10) : 0;
|
|
583
614
|
if (!Number.isFinite(idx) || idx < 0 || idx >= images.length) return "";
|
|
584
615
|
return images[idx].alt;
|
|
585
616
|
}
|
|
617
|
+
case "item": {
|
|
618
|
+
if (!item) return null;
|
|
619
|
+
if (!arg) return item.text;
|
|
620
|
+
if (arg === "src") return item.src ?? "";
|
|
621
|
+
if (arg === "label") return item.label ?? "";
|
|
622
|
+
return "";
|
|
623
|
+
}
|
|
624
|
+
case "index":
|
|
625
|
+
return item ? String(item.index) : null;
|
|
586
626
|
default:
|
|
587
627
|
return null;
|
|
588
628
|
}
|
|
589
629
|
}
|
|
590
|
-
function
|
|
591
|
-
|
|
630
|
+
function substituteText(text, block, images, item) {
|
|
631
|
+
return substitute(text, (name, arg) => resolveToken(name, arg, block, images, item));
|
|
632
|
+
}
|
|
633
|
+
function resolveImageSrc(src, block, images, item) {
|
|
634
|
+
if (!src.includes("{")) return src;
|
|
592
635
|
let dropped = false;
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
636
|
+
const escaped = escapeBraces(src);
|
|
637
|
+
const tokenized = escaped.replace(
|
|
638
|
+
TOKEN_RE,
|
|
639
|
+
(match, name, arg, def) => {
|
|
640
|
+
if (name === "image") {
|
|
641
|
+
const idx = arg ? parseInt(arg, 10) : 0;
|
|
642
|
+
const ok = Number.isFinite(idx) && idx >= 0 && idx < images.length;
|
|
643
|
+
if (ok) return images[idx].url;
|
|
644
|
+
if (def !== void 0) return def;
|
|
645
|
+
dropped = true;
|
|
646
|
+
return "";
|
|
647
|
+
}
|
|
648
|
+
const value = resolveToken(name, arg, block, images, item);
|
|
649
|
+
if (value == null) return match;
|
|
650
|
+
if (value === "" && def !== void 0) return def;
|
|
651
|
+
return value;
|
|
598
652
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
return
|
|
653
|
+
);
|
|
654
|
+
if (dropped) return null;
|
|
655
|
+
return unescapeBraces(tokenized);
|
|
656
|
+
}
|
|
657
|
+
function parseMagnitude(value) {
|
|
658
|
+
if (typeof value === "number") return value;
|
|
659
|
+
if (typeof value === "string") {
|
|
660
|
+
const m = value.match(/-?\d*\.?\d+/);
|
|
661
|
+
if (m) return parseFloat(m[0]);
|
|
662
|
+
}
|
|
663
|
+
return 0;
|
|
664
|
+
}
|
|
665
|
+
function offsetCoord(base, delta) {
|
|
666
|
+
if (base == null) return delta;
|
|
667
|
+
if (typeof base === "number") return base + delta;
|
|
668
|
+
const m = base.match(/^(\s*)(-?\d*\.?\d+)(.*)$/);
|
|
669
|
+
if (m) {
|
|
670
|
+
const next = parseFloat(m[2]) + delta;
|
|
671
|
+
return `${m[1]}${next}${m[3]}`;
|
|
672
|
+
}
|
|
673
|
+
return base;
|
|
674
|
+
}
|
|
675
|
+
function collectRepeatItems(source, block, images) {
|
|
676
|
+
switch (source) {
|
|
677
|
+
case "images":
|
|
678
|
+
return images.map((img, i) => ({
|
|
679
|
+
text: img.alt,
|
|
680
|
+
src: img.url,
|
|
681
|
+
label: img.alt,
|
|
682
|
+
index: i + 1
|
|
683
|
+
}));
|
|
684
|
+
case "children":
|
|
685
|
+
return (block.children ?? []).map((child, i) => ({
|
|
686
|
+
text: child.title ?? "",
|
|
687
|
+
label: child.title ?? "",
|
|
688
|
+
index: i + 1
|
|
689
|
+
}));
|
|
690
|
+
case "listItems":
|
|
691
|
+
return collectListItems(block).map((text, i) => ({ text, label: text, index: i + 1 }));
|
|
692
|
+
default:
|
|
693
|
+
return [];
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function expandRepeat(layer, block, images) {
|
|
697
|
+
const repeat = layer.repeat;
|
|
698
|
+
if (!repeat) {
|
|
699
|
+
const resolved = resolveLayer(layer, block, images, void 0);
|
|
700
|
+
return resolved ? [resolved] : [];
|
|
701
|
+
}
|
|
702
|
+
const items = collectRepeatItems(repeat.source, block, images);
|
|
703
|
+
const direction = repeat.direction ?? "column";
|
|
704
|
+
const gap = repeat.gap ?? 24;
|
|
705
|
+
const limit = repeat.max != null && repeat.max >= 0 ? Math.min(repeat.max, items.length) : items.length;
|
|
706
|
+
const out = [];
|
|
707
|
+
let cursor = 0;
|
|
708
|
+
for (let i = 0; i < limit; i++) {
|
|
709
|
+
const position = { ...layer.position };
|
|
710
|
+
if (direction === "row") {
|
|
711
|
+
position.x = offsetCoord(position.x, cursor);
|
|
712
|
+
} else {
|
|
713
|
+
position.y = offsetCoord(position.y, cursor);
|
|
714
|
+
}
|
|
715
|
+
const clone = { ...layer, position, id: `${layer.id}-${i}` };
|
|
716
|
+
delete clone.repeat;
|
|
717
|
+
const resolved = resolveLayer(clone, block, images, items[i]);
|
|
718
|
+
if (resolved) out.push(resolved);
|
|
719
|
+
const dim = direction === "row" ? layer.position.width : layer.position.height;
|
|
720
|
+
cursor += parseMagnitude(dim) + gap;
|
|
721
|
+
}
|
|
722
|
+
return out;
|
|
602
723
|
}
|
|
603
724
|
function resolveTokens(layers, block) {
|
|
604
725
|
const images = collectImages(block);
|
|
605
726
|
const out = [];
|
|
606
727
|
for (const layer of layers) {
|
|
607
|
-
|
|
608
|
-
|
|
728
|
+
if (layer.repeat) {
|
|
729
|
+
out.push(...expandRepeat(layer, block, images));
|
|
730
|
+
} else {
|
|
731
|
+
const resolved = resolveLayer(layer, block, images, void 0);
|
|
732
|
+
if (resolved) out.push(resolved);
|
|
733
|
+
}
|
|
609
734
|
}
|
|
610
735
|
return out;
|
|
611
736
|
}
|
|
612
|
-
function resolveLayer(layer, block, images) {
|
|
737
|
+
function resolveLayer(layer, block, images, item) {
|
|
613
738
|
switch (layer.type) {
|
|
614
739
|
case "text":
|
|
615
740
|
return {
|
|
616
741
|
...layer,
|
|
617
742
|
content: {
|
|
618
743
|
...layer.content,
|
|
619
|
-
text: substituteText(layer.content.text, block, images)
|
|
744
|
+
text: substituteText(layer.content.text, block, images, item)
|
|
620
745
|
}
|
|
621
746
|
};
|
|
622
747
|
case "image": {
|
|
623
|
-
const nextSrc = resolveImageSrc(layer.content.src, images);
|
|
748
|
+
const nextSrc = resolveImageSrc(layer.content.src, block, images, item);
|
|
624
749
|
if (nextSrc == null) return null;
|
|
625
750
|
const next = {
|
|
626
751
|
...layer,
|
|
@@ -630,7 +755,7 @@ function resolveLayer(layer, block, images) {
|
|
|
630
755
|
}
|
|
631
756
|
};
|
|
632
757
|
if (layer.content.alt && layer.content.alt.includes("{")) {
|
|
633
|
-
next.content.alt = substituteText(layer.content.alt, block, images);
|
|
758
|
+
next.content.alt = substituteText(layer.content.alt, block, images, item);
|
|
634
759
|
}
|
|
635
760
|
return next;
|
|
636
761
|
}
|
|
@@ -641,12 +766,305 @@ function resolveLayer(layer, block, images) {
|
|
|
641
766
|
|
|
642
767
|
// src/doc/templates/customTemplate.ts
|
|
643
768
|
function makeCustomTemplateFn(def) {
|
|
644
|
-
return (
|
|
645
|
-
|
|
646
|
-
return
|
|
769
|
+
return (input, context) => {
|
|
770
|
+
const block = context.block ?? input;
|
|
771
|
+
if (!block) return def.layers.slice();
|
|
772
|
+
return resolveTokens(def.layers, block);
|
|
647
773
|
};
|
|
648
774
|
}
|
|
649
775
|
|
|
776
|
+
// src/doc/utils/nearest.ts
|
|
777
|
+
function levenshtein(a, b) {
|
|
778
|
+
if (a === b) return 0;
|
|
779
|
+
const m = a.length;
|
|
780
|
+
const n = b.length;
|
|
781
|
+
if (m === 0) return n;
|
|
782
|
+
if (n === 0) return m;
|
|
783
|
+
let prev = new Array(n + 1);
|
|
784
|
+
let curr = new Array(n + 1);
|
|
785
|
+
for (let j = 0; j <= n; j++) prev[j] = j;
|
|
786
|
+
for (let i = 1; i <= m; i++) {
|
|
787
|
+
curr[0] = i;
|
|
788
|
+
for (let j = 1; j <= n; j++) {
|
|
789
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
790
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
791
|
+
}
|
|
792
|
+
[prev, curr] = [curr, prev];
|
|
793
|
+
}
|
|
794
|
+
return prev[n];
|
|
795
|
+
}
|
|
796
|
+
function nearestName(input, candidates, options) {
|
|
797
|
+
const maxDistance = options?.maxDistance ?? 2;
|
|
798
|
+
let best;
|
|
799
|
+
let bestDist = Infinity;
|
|
800
|
+
const lower = input.toLowerCase();
|
|
801
|
+
for (const candidate of candidates) {
|
|
802
|
+
const dist = levenshtein(lower, candidate.toLowerCase());
|
|
803
|
+
if (dist < bestDist) {
|
|
804
|
+
bestDist = dist;
|
|
805
|
+
best = candidate;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (best && (bestDist <= maxDistance || bestDist <= Math.ceil(input.length * 0.4))) {
|
|
809
|
+
return options?.map ? options.map(best) : best;
|
|
810
|
+
}
|
|
811
|
+
return void 0;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// src/doc/templates/inputDescriptors.ts
|
|
815
|
+
var AMBIENT_MOTION_VALUES = ["zoomIn", "zoomOut", "panLeft", "panRight"];
|
|
816
|
+
var CAPTION_POSITION_VALUES = ["bottom", "top", "center"];
|
|
817
|
+
var MAP_STYLE_VALUES = ["terrain", "satellite", "road", "toner", "watercolor"];
|
|
818
|
+
var DATE_EVENT_MOOD_VALUES = ["neutral", "somber", "celebratory"];
|
|
819
|
+
var COLOR_SCHEME_HINT = "theme color-scheme name \u2014 e.g. 'blue', 'green'";
|
|
820
|
+
var BASE_INPUT_DESCRIPTORS = [
|
|
821
|
+
{
|
|
822
|
+
key: "imageTreatment",
|
|
823
|
+
description: "Per-block photographic grade override ('none' opts out)",
|
|
824
|
+
values: ["none", "mono", "duotone", "warm", "cool"]
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
key: "useBottomLayer",
|
|
828
|
+
description: "Show the doc's bottom persistent layers",
|
|
829
|
+
coerce: "boolean"
|
|
830
|
+
},
|
|
831
|
+
{ key: "useTopLayer", description: "Show the doc's top persistent layers", coerce: "boolean" }
|
|
832
|
+
];
|
|
833
|
+
var TEMPLATE_INPUT_DESCRIPTORS = {
|
|
834
|
+
title: [
|
|
835
|
+
{ key: "title", description: "Main title text" },
|
|
836
|
+
{ key: "subtitle", description: "Subtitle or tagline" },
|
|
837
|
+
{ key: "backgroundColor", description: "Background color", valueHint: "CSS color" }
|
|
838
|
+
],
|
|
839
|
+
statHighlight: [
|
|
840
|
+
{ key: "stat", description: 'The statistic (e.g. "89%", "2x")' },
|
|
841
|
+
{ key: "description", description: "What the stat means" },
|
|
842
|
+
{ key: "detail", description: "Additional detail or context" },
|
|
843
|
+
{ key: "colorScheme", description: "Color scheme for the stat", valueHint: COLOR_SCHEME_HINT }
|
|
844
|
+
],
|
|
845
|
+
quote: [
|
|
846
|
+
{ key: "quote", description: "The quote text" },
|
|
847
|
+
{ key: "attribution", description: "Attribution (author, source)" }
|
|
848
|
+
],
|
|
849
|
+
pullQuote: [
|
|
850
|
+
{ key: "text", description: "Quote text" },
|
|
851
|
+
{ key: "attribution", description: "Attribution" }
|
|
852
|
+
],
|
|
853
|
+
twoColumn: [
|
|
854
|
+
{
|
|
855
|
+
key: "left",
|
|
856
|
+
description: 'Left column, as "label|sublabel"',
|
|
857
|
+
coerce: "labeledPair",
|
|
858
|
+
required: true
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
key: "right",
|
|
862
|
+
description: 'Right column, as "label|sublabel"',
|
|
863
|
+
coerce: "labeledPair",
|
|
864
|
+
required: true
|
|
865
|
+
},
|
|
866
|
+
{ key: "header", description: "Optional header above the columns" },
|
|
867
|
+
{ key: "leftColor", description: "Left column color scheme", valueHint: COLOR_SCHEME_HINT },
|
|
868
|
+
{ key: "rightColor", description: "Right column color scheme", valueHint: COLOR_SCHEME_HINT }
|
|
869
|
+
],
|
|
870
|
+
dateEvent: [
|
|
871
|
+
{ key: "date", description: 'The date (e.g. "July 14, 1974")' },
|
|
872
|
+
{ key: "description", description: "What happened" },
|
|
873
|
+
{ key: "footer", description: "Optional footer text" },
|
|
874
|
+
{ key: "mood", description: "Emotional tone", values: DATE_EVENT_MOOD_VALUES }
|
|
875
|
+
],
|
|
876
|
+
imageWithCaption: [
|
|
877
|
+
{ key: "imageSrc", description: "Path to the image file", required: true },
|
|
878
|
+
{ key: "imageAlt", description: "Alt text for accessibility" },
|
|
879
|
+
{ key: "caption", description: "Caption text" },
|
|
880
|
+
{ key: "captionPosition", description: "Caption placement", values: CAPTION_POSITION_VALUES },
|
|
881
|
+
{ key: "ambientMotion", description: "Slow zoom/pan effect", values: AMBIENT_MOTION_VALUES },
|
|
882
|
+
{ key: "isTitle", description: "Style the caption as a title", coerce: "boolean" },
|
|
883
|
+
{ key: "subtitle", description: "Subtitle (only when isTitle)" },
|
|
884
|
+
{ key: "imageCredit", description: "Photo credit / artist name" },
|
|
885
|
+
{ key: "imageLicense", description: "License identifier" }
|
|
886
|
+
],
|
|
887
|
+
leftFeature: FEATURE_DESCRIPTORS(),
|
|
888
|
+
rightFeature: FEATURE_DESCRIPTORS(),
|
|
889
|
+
map: [
|
|
890
|
+
{
|
|
891
|
+
key: "center",
|
|
892
|
+
description: 'Map center as "lat,lng"',
|
|
893
|
+
coerce: "latLng",
|
|
894
|
+
required: true
|
|
895
|
+
},
|
|
896
|
+
{ key: "zoom", description: "Zoom level (4-16)", coerce: "number" },
|
|
897
|
+
{ key: "mapStyle", description: "Map tile style", values: MAP_STYLE_VALUES },
|
|
898
|
+
{ key: "title", description: "Optional title overlay" },
|
|
899
|
+
{ key: "caption", description: "Optional caption" }
|
|
900
|
+
],
|
|
901
|
+
photoGrid: [
|
|
902
|
+
{ key: "images", description: "Comma-separated image paths", coerce: "stringList" },
|
|
903
|
+
{ key: "caption", description: "Optional caption below the grid" },
|
|
904
|
+
{
|
|
905
|
+
key: "ambientMotion",
|
|
906
|
+
description: "Slow zoom/pan on the largest image",
|
|
907
|
+
values: AMBIENT_MOTION_VALUES
|
|
908
|
+
}
|
|
909
|
+
],
|
|
910
|
+
comparisonBar: [
|
|
911
|
+
{ key: "leftLabel", description: "Left bar label", required: true },
|
|
912
|
+
{ key: "leftValue", description: "Left bar numeric value", coerce: "number", required: true },
|
|
913
|
+
{ key: "rightLabel", description: "Right bar label", required: true },
|
|
914
|
+
{ key: "rightValue", description: "Right bar numeric value", coerce: "number", required: true },
|
|
915
|
+
{ key: "unit", description: 'Unit label (e.g. "km")' },
|
|
916
|
+
{ key: "colorScheme", description: "Color scheme", valueHint: COLOR_SCHEME_HINT }
|
|
917
|
+
],
|
|
918
|
+
dataTable: [
|
|
919
|
+
{ key: "title", description: "Optional title above the table" },
|
|
920
|
+
{ key: "headers", description: "Comma-separated header cells", coerce: "stringList" },
|
|
921
|
+
// `rows`/`align` are structured (string[][] / alignment[]) and can't be
|
|
922
|
+
// expressed as a single inline string — declared so they aren't flagged
|
|
923
|
+
// as unknown when supplied via a data fence's overriding params.
|
|
924
|
+
{ key: "rows", description: "Table rows (supply via a data fence)" },
|
|
925
|
+
{ key: "align", description: "Per-column alignment (supply via a data fence)" },
|
|
926
|
+
{ key: "colorScheme", description: "Header color scheme", valueHint: COLOR_SCHEME_HINT }
|
|
927
|
+
],
|
|
928
|
+
videoWithCaption: [
|
|
929
|
+
{ key: "videoSrc", description: "Path to the video file", required: true },
|
|
930
|
+
{ key: "posterSrc", description: "Poster frame path" },
|
|
931
|
+
{ key: "videoAlt", description: "Alt text for accessibility" },
|
|
932
|
+
{ key: "clipStart", description: "Clip start time (seconds)", coerce: "number" },
|
|
933
|
+
{ key: "clipEnd", description: "Clip end time (seconds)", coerce: "number" },
|
|
934
|
+
{ key: "caption", description: "Caption text" },
|
|
935
|
+
{ key: "captionPosition", description: "Caption placement", values: CAPTION_POSITION_VALUES },
|
|
936
|
+
{ key: "videoCredit", description: "Video credit / artist name" },
|
|
937
|
+
{ key: "videoLicense", description: "License identifier" }
|
|
938
|
+
]
|
|
939
|
+
};
|
|
940
|
+
function FEATURE_DESCRIPTORS() {
|
|
941
|
+
return [
|
|
942
|
+
{ key: "imageSrc", description: "Path to the feature image", required: true },
|
|
943
|
+
{ key: "imageAlt", description: "Alt text for accessibility" },
|
|
944
|
+
{ key: "imageWidth", description: "Explicit display width in px", coerce: "number" },
|
|
945
|
+
{ key: "imageHeight", description: "Explicit display height in px", coerce: "number" },
|
|
946
|
+
{ key: "title", description: "Heading text next to the image" },
|
|
947
|
+
{ key: "body", description: "Body text below the title" }
|
|
948
|
+
];
|
|
949
|
+
}
|
|
950
|
+
function descriptorsFor(canonical) {
|
|
951
|
+
const specific = TEMPLATE_INPUT_DESCRIPTORS[canonical];
|
|
952
|
+
if (!specific) return void 0;
|
|
953
|
+
return [...specific, ...BASE_INPUT_DESCRIPTORS];
|
|
954
|
+
}
|
|
955
|
+
function descriptorMap(descriptors) {
|
|
956
|
+
return new Map(descriptors.map((d) => [d.key, d]));
|
|
957
|
+
}
|
|
958
|
+
function coerceTemplateParams(template, params) {
|
|
959
|
+
const input = {};
|
|
960
|
+
const warnings = [];
|
|
961
|
+
const descriptors = template ? descriptorsFor(resolveTemplateName(template)) : void 0;
|
|
962
|
+
const byKey = descriptors ? descriptorMap(descriptors) : void 0;
|
|
963
|
+
for (const [key, raw] of Object.entries(params)) {
|
|
964
|
+
const coerce = byKey?.get(key)?.coerce ?? "string";
|
|
965
|
+
const { value, warning } = coerceValue(coerce, raw);
|
|
966
|
+
if (warning) {
|
|
967
|
+
warnings.push(`"${key}": ${warning}`);
|
|
968
|
+
input[key] = raw;
|
|
969
|
+
} else {
|
|
970
|
+
input[key] = value;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return { input, warnings };
|
|
974
|
+
}
|
|
975
|
+
function coerceValue(coerce, raw) {
|
|
976
|
+
switch (coerce) {
|
|
977
|
+
case "number": {
|
|
978
|
+
const n = parseNumber(raw);
|
|
979
|
+
return n == null ? { value: raw, warning: `not a number (${JSON.stringify(raw)})` } : { value: n };
|
|
980
|
+
}
|
|
981
|
+
case "boolean":
|
|
982
|
+
return coerceBoolean(raw);
|
|
983
|
+
case "latLng":
|
|
984
|
+
return coerceLatLng(raw);
|
|
985
|
+
case "labeledPair":
|
|
986
|
+
return { value: coerceLabeledPair(raw) };
|
|
987
|
+
case "stringList":
|
|
988
|
+
return { value: coerceStringList(raw) };
|
|
989
|
+
case "string":
|
|
990
|
+
default:
|
|
991
|
+
return { value: raw };
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function coerceBoolean(raw) {
|
|
995
|
+
const t = raw.trim().toLowerCase();
|
|
996
|
+
if (t === "true" || t === "") return { value: true };
|
|
997
|
+
if (t === "false") return { value: false };
|
|
998
|
+
return { value: raw, warning: `not a boolean (${JSON.stringify(raw)}) \u2014 expected true or false` };
|
|
999
|
+
}
|
|
1000
|
+
function coerceLatLng(raw) {
|
|
1001
|
+
const parts = raw.split(",");
|
|
1002
|
+
if (parts.length === 2) {
|
|
1003
|
+
const lat = parseNumber(parts[0]);
|
|
1004
|
+
const lng = parseNumber(parts[1]);
|
|
1005
|
+
if (lat != null && lng != null) return { value: { lat, lng } };
|
|
1006
|
+
}
|
|
1007
|
+
return { value: raw, warning: `not a "lat,lng" pair (${JSON.stringify(raw)})` };
|
|
1008
|
+
}
|
|
1009
|
+
function coerceLabeledPair(raw) {
|
|
1010
|
+
const sep = raw.indexOf("|");
|
|
1011
|
+
if (sep < 0) return { label: raw.trim() };
|
|
1012
|
+
const label = raw.slice(0, sep).trim();
|
|
1013
|
+
const sublabel = raw.slice(sep + 1).trim();
|
|
1014
|
+
return sublabel ? { label, sublabel } : { label };
|
|
1015
|
+
}
|
|
1016
|
+
function coerceStringList(raw) {
|
|
1017
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1018
|
+
}
|
|
1019
|
+
function lintTemplateParams(template, params) {
|
|
1020
|
+
const canonical = resolveTemplateName(template);
|
|
1021
|
+
const specific = TEMPLATE_INPUT_DESCRIPTORS[canonical];
|
|
1022
|
+
if (!specific) return [];
|
|
1023
|
+
const findings = [];
|
|
1024
|
+
const allDescriptors = [...specific, ...BASE_INPUT_DESCRIPTORS];
|
|
1025
|
+
const byKey = descriptorMap(allDescriptors);
|
|
1026
|
+
const knownKeys = /* @__PURE__ */ new Set([...byKey.keys(), ...Object.keys(KNOWN_BLOCK_META_KEYS)]);
|
|
1027
|
+
for (const [key, raw] of Object.entries(params)) {
|
|
1028
|
+
const descriptor = byKey.get(key);
|
|
1029
|
+
if (!descriptor) {
|
|
1030
|
+
if (knownKeys.has(key)) continue;
|
|
1031
|
+
const suggestion = nearestName(key, knownKeys);
|
|
1032
|
+
findings.push({
|
|
1033
|
+
kind: "unknown-input",
|
|
1034
|
+
key,
|
|
1035
|
+
message: `Unknown input "${key}" for template "${canonical}"` + (suggestion ? `. Did you mean "${suggestion}"?` : ""),
|
|
1036
|
+
...suggestion ? { suggestion } : {}
|
|
1037
|
+
});
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (descriptor.values) {
|
|
1041
|
+
const v = raw.trim();
|
|
1042
|
+
if (v !== "" && !descriptor.values.includes(v)) {
|
|
1043
|
+
findings.push({
|
|
1044
|
+
kind: "invalid-input-value",
|
|
1045
|
+
key,
|
|
1046
|
+
message: `Invalid value ${JSON.stringify(raw)} for "${key}" \u2014 expected one of: ${descriptor.values.join(", ")}`
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
} else if (descriptor.coerce && descriptor.coerce !== "string") {
|
|
1050
|
+
const { warning } = coerceValue(descriptor.coerce, raw);
|
|
1051
|
+
if (warning) {
|
|
1052
|
+
findings.push({ kind: "invalid-input-value", key, message: `"${key}": ${warning}` });
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
for (const descriptor of specific) {
|
|
1057
|
+
if (descriptor.required && params[descriptor.key] == null) {
|
|
1058
|
+
findings.push({
|
|
1059
|
+
kind: "missing-input",
|
|
1060
|
+
key: descriptor.key,
|
|
1061
|
+
message: `Missing required input "${descriptor.key}" for template "${canonical}"`
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return findings;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
650
1068
|
// src/doc/templates/metadata.ts
|
|
651
1069
|
var TEMPLATE_METADATA = {
|
|
652
1070
|
title: {
|
|
@@ -1440,7 +1858,7 @@ function factCard(input, context) {
|
|
|
1440
1858
|
const { theme, viewport } = context;
|
|
1441
1859
|
const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;
|
|
1442
1860
|
const factFontSize = themedFontSize(56, context, true);
|
|
1443
|
-
const explainFontSize = themedFontSize(
|
|
1861
|
+
const explainFontSize = themedFontSize(34, context, false);
|
|
1444
1862
|
const sourceFontSize = themedFontSize(20, context, false);
|
|
1445
1863
|
const layers = [createBackgroundLayer("bg", themedSurfaceGradient(context, 170))];
|
|
1446
1864
|
if (accentImage) {
|
|
@@ -1854,7 +2272,7 @@ function imageWithCaption(input, context) {
|
|
|
1854
2272
|
const caption = rawCaption ? cleanCaption(rawCaption) : rawCaption;
|
|
1855
2273
|
const { theme, layout } = context;
|
|
1856
2274
|
const treatment = themedImageTreatment(context, input.imageTreatment);
|
|
1857
|
-
const captionFontSize = themedFontSize(
|
|
2275
|
+
const captionFontSize = themedFontSize(30, context, false);
|
|
1858
2276
|
const titleFontSize = themedFontSize(96, context, true);
|
|
1859
2277
|
const subtitleFontSize = themedFontSize(36, context, false);
|
|
1860
2278
|
const legacyKenBurns = input.kenBurns;
|
|
@@ -1941,13 +2359,13 @@ function imageWithCaption(input, context) {
|
|
|
1941
2359
|
id: "caption-gradient",
|
|
1942
2360
|
content: {
|
|
1943
2361
|
shape: "rect",
|
|
1944
|
-
fill: `linear-gradient(
|
|
2362
|
+
fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`
|
|
1945
2363
|
},
|
|
1946
2364
|
position: {
|
|
1947
2365
|
x: 0,
|
|
1948
|
-
y: "
|
|
2366
|
+
y: "68%",
|
|
1949
2367
|
width: "100%",
|
|
1950
|
-
height: "
|
|
2368
|
+
height: "16%"
|
|
1951
2369
|
}
|
|
1952
2370
|
});
|
|
1953
2371
|
layers.push({
|
|
@@ -1960,14 +2378,16 @@ function imageWithCaption(input, context) {
|
|
|
1960
2378
|
fontFamily: getThemeFont(context, "body"),
|
|
1961
2379
|
color: theme.colors.text,
|
|
1962
2380
|
textAlign: "center",
|
|
1963
|
-
shadow: shouldUseShadow(context)
|
|
2381
|
+
shadow: shouldUseShadow(context),
|
|
2382
|
+
lineHeight: 1.18,
|
|
2383
|
+
maxLines: 2
|
|
1964
2384
|
}
|
|
1965
2385
|
},
|
|
1966
2386
|
position: {
|
|
1967
2387
|
x: "50%",
|
|
1968
|
-
y: "
|
|
2388
|
+
y: "74%",
|
|
1969
2389
|
anchor: "center",
|
|
1970
|
-
width:
|
|
2390
|
+
width: "78%"
|
|
1971
2391
|
},
|
|
1972
2392
|
animation: { type: "fadeIn", duration: 1.5, delay: 0.5 }
|
|
1973
2393
|
});
|
|
@@ -2351,10 +2771,8 @@ function photoGrid(input, context) {
|
|
|
2351
2771
|
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
2352
2772
|
}
|
|
2353
2773
|
];
|
|
2354
|
-
const
|
|
2355
|
-
const gridHeight = 100 - captionHeight;
|
|
2774
|
+
const gridHeight = 100;
|
|
2356
2775
|
const positions = getGridPositions(images.length, gridHeight, layout.stackColumns);
|
|
2357
|
-
const altFontSize = themedFontSize(24, context, false);
|
|
2358
2776
|
for (let i = 0; i < Math.min(images.length, 4); i++) {
|
|
2359
2777
|
const img = images[i];
|
|
2360
2778
|
const pos = positions[i];
|
|
@@ -2364,22 +2782,6 @@ function photoGrid(input, context) {
|
|
|
2364
2782
|
content: { shape: "rect", fill: theme.colors.background },
|
|
2365
2783
|
position: { x: `${pos.x}%`, y: `${pos.y}%`, width: `${pos.w}%`, height: `${pos.h}%` }
|
|
2366
2784
|
});
|
|
2367
|
-
if (img.alt) {
|
|
2368
|
-
layers.push({
|
|
2369
|
-
type: "text",
|
|
2370
|
-
id: `grid-alt-${i}`,
|
|
2371
|
-
content: {
|
|
2372
|
-
text: img.alt,
|
|
2373
|
-
style: {
|
|
2374
|
-
fontSize: altFontSize,
|
|
2375
|
-
fontFamily: getThemeFont(context, "body"),
|
|
2376
|
-
color: theme.colors.textMuted,
|
|
2377
|
-
textAlign: "center"
|
|
2378
|
-
}
|
|
2379
|
-
},
|
|
2380
|
-
position: { x: `${pos.x + pos.w / 2}%`, y: `${pos.y + pos.h / 2}%`, anchor: "center" }
|
|
2381
|
-
});
|
|
2382
|
-
}
|
|
2383
2785
|
layers.push({
|
|
2384
2786
|
type: "image",
|
|
2385
2787
|
id: `grid-img-${i}`,
|
|
@@ -2402,15 +2804,19 @@ function photoGrid(input, context) {
|
|
|
2402
2804
|
});
|
|
2403
2805
|
}
|
|
2404
2806
|
if (caption) {
|
|
2405
|
-
const captionFontSize = themedFontSize(layout.stackColumns ?
|
|
2807
|
+
const captionFontSize = themedFontSize(layout.stackColumns ? 24 : 26, context, false);
|
|
2808
|
+
const captionY = layout.stackColumns ? "78%" : "74%";
|
|
2809
|
+
const captionBgY = layout.stackColumns ? "72%" : "68%";
|
|
2810
|
+
const captionHeight = layout.stackColumns ? "13%" : "16%";
|
|
2811
|
+
const bg = theme.colors.background;
|
|
2406
2812
|
layers.push({
|
|
2407
2813
|
type: "shape",
|
|
2408
2814
|
id: "caption-bg",
|
|
2409
2815
|
content: {
|
|
2410
2816
|
shape: "rect",
|
|
2411
|
-
fill: `linear-gradient(${withAlpha(
|
|
2817
|
+
fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`
|
|
2412
2818
|
},
|
|
2413
|
-
position: { x: 0, y:
|
|
2819
|
+
position: { x: 0, y: captionBgY, width: "100%", height: captionHeight }
|
|
2414
2820
|
});
|
|
2415
2821
|
layers.push({
|
|
2416
2822
|
type: "text",
|
|
@@ -2422,14 +2828,16 @@ function photoGrid(input, context) {
|
|
|
2422
2828
|
fontFamily: getThemeFont(context, "body"),
|
|
2423
2829
|
color: theme.colors.text,
|
|
2424
2830
|
textAlign: "center",
|
|
2425
|
-
shadow:
|
|
2831
|
+
shadow: true,
|
|
2832
|
+
lineHeight: 1.18,
|
|
2833
|
+
maxLines: layout.stackColumns ? 2 : 1
|
|
2426
2834
|
}
|
|
2427
2835
|
},
|
|
2428
2836
|
position: {
|
|
2429
2837
|
x: "50%",
|
|
2430
|
-
y:
|
|
2838
|
+
y: captionY,
|
|
2431
2839
|
anchor: "center",
|
|
2432
|
-
width: "
|
|
2840
|
+
width: "78%"
|
|
2433
2841
|
},
|
|
2434
2842
|
animation: { type: "fadeIn", duration: 1, delay: 0.5 }
|
|
2435
2843
|
});
|
|
@@ -2585,12 +2993,15 @@ function definitionCard(input, context) {
|
|
|
2585
2993
|
|
|
2586
2994
|
// src/doc/templates/comparisonBar.ts
|
|
2587
2995
|
function comparisonBar(input, context) {
|
|
2588
|
-
const { leftLabel,
|
|
2996
|
+
const { leftLabel, rightLabel, unit, colorScheme = "blue" } = input;
|
|
2589
2997
|
const { theme, viewport } = context;
|
|
2590
2998
|
const colors = resolveColorScheme(context, colorScheme);
|
|
2999
|
+
const leftValue = Number.isFinite(input.leftValue) ? input.leftValue : 0;
|
|
3000
|
+
const rightValue = Number.isFinite(input.rightValue) ? input.rightValue : 0;
|
|
2591
3001
|
const labelFontSize = themedFontSize(28, context, false);
|
|
2592
3002
|
const valueFontSize = themedFontSize(48, context, true);
|
|
2593
3003
|
const formatValue = (v) => {
|
|
3004
|
+
if (!Number.isFinite(v)) return "\u2014";
|
|
2594
3005
|
if (v >= 1e6) return `${(v / 1e6).toFixed(1)}M`;
|
|
2595
3006
|
if (v >= 1e3) return `${(v / 1e3).toFixed(v >= 1e4 ? 0 : 1)}K`;
|
|
2596
3007
|
return v.toLocaleString();
|
|
@@ -2841,8 +3252,8 @@ function videoWithCaption(input, context) {
|
|
|
2841
3252
|
videoLicense
|
|
2842
3253
|
} = input;
|
|
2843
3254
|
const caption = rawCaption ? cleanCaption(rawCaption) : rawCaption;
|
|
2844
|
-
const { theme
|
|
2845
|
-
const captionFontSize = themedFontSize(
|
|
3255
|
+
const { theme } = context;
|
|
3256
|
+
const captionFontSize = themedFontSize(30, context, false);
|
|
2846
3257
|
const creditFontSize = themedFontSize(16, context, false);
|
|
2847
3258
|
const layers = [
|
|
2848
3259
|
// Background video clip
|
|
@@ -2865,22 +3276,22 @@ function videoWithCaption(input, context) {
|
|
|
2865
3276
|
];
|
|
2866
3277
|
if (caption) {
|
|
2867
3278
|
const bg = theme.colors.background;
|
|
2868
|
-
const
|
|
3279
|
+
const captionBgY = captionPosition === "top" ? "7%" : captionPosition === "center" ? "42%" : "68%";
|
|
3280
|
+
const captionY = captionPosition === "top" ? "13%" : captionPosition === "center" ? "48%" : "74%";
|
|
2869
3281
|
layers.push({
|
|
2870
3282
|
type: "shape",
|
|
2871
3283
|
id: "caption-gradient",
|
|
2872
3284
|
content: {
|
|
2873
3285
|
shape: "rect",
|
|
2874
|
-
fill: `linear-gradient(${
|
|
3286
|
+
fill: `linear-gradient(90deg, ${withAlpha(bg, 0)}, ${withAlpha(bg, 0.78)} 16%, ${withAlpha(bg, 0.78)} 84%, ${withAlpha(bg, 0)})`
|
|
2875
3287
|
},
|
|
2876
3288
|
position: {
|
|
2877
3289
|
x: 0,
|
|
2878
|
-
y:
|
|
3290
|
+
y: captionBgY,
|
|
2879
3291
|
width: "100%",
|
|
2880
|
-
height: "
|
|
3292
|
+
height: "16%"
|
|
2881
3293
|
}
|
|
2882
3294
|
});
|
|
2883
|
-
const captionY = captionPosition === "top" ? "15%" : captionPosition === "center" ? "50%" : "82%";
|
|
2884
3295
|
layers.push({
|
|
2885
3296
|
type: "text",
|
|
2886
3297
|
id: "caption",
|
|
@@ -2891,14 +3302,16 @@ function videoWithCaption(input, context) {
|
|
|
2891
3302
|
fontFamily: getThemeFont(context, "body"),
|
|
2892
3303
|
color: theme.colors.text,
|
|
2893
3304
|
textAlign: "center",
|
|
2894
|
-
shadow: shouldUseShadow(context)
|
|
3305
|
+
shadow: shouldUseShadow(context),
|
|
3306
|
+
lineHeight: 1.18,
|
|
3307
|
+
maxLines: 2
|
|
2895
3308
|
}
|
|
2896
3309
|
},
|
|
2897
3310
|
position: {
|
|
2898
3311
|
x: "50%",
|
|
2899
3312
|
y: captionY,
|
|
2900
3313
|
anchor: "center",
|
|
2901
|
-
width:
|
|
3314
|
+
width: "78%"
|
|
2902
3315
|
},
|
|
2903
3316
|
animation: { type: "fadeIn", duration: 1.5, delay: 0.5 }
|
|
2904
3317
|
});
|
|
@@ -3259,7 +3672,7 @@ function diagramBlock(input, context) {
|
|
|
3259
3672
|
const a = positions.get(edge.source);
|
|
3260
3673
|
const b = positions.get(edge.target);
|
|
3261
3674
|
if (!a || !b) continue;
|
|
3262
|
-
const { start, end } =
|
|
3675
|
+
const { start, end } = snapEndpoints(a, b);
|
|
3263
3676
|
const pathLayer = {
|
|
3264
3677
|
type: "path",
|
|
3265
3678
|
id: `edge-${edge.id}`,
|
|
@@ -3695,7 +4108,7 @@ function drawingBlock(input, context) {
|
|
|
3695
4108
|
const a = boxes.get(conn.from);
|
|
3696
4109
|
const b = boxes.get(conn.to);
|
|
3697
4110
|
if (!a || !b) continue;
|
|
3698
|
-
const { start, end } =
|
|
4111
|
+
const { start, end } = snapEndpoints(a, b);
|
|
3699
4112
|
const path = {
|
|
3700
4113
|
type: "path",
|
|
3701
4114
|
id: `connector-${conn.id}`,
|
|
@@ -4050,11 +4463,25 @@ function emptyHint2(input, context) {
|
|
|
4050
4463
|
}
|
|
4051
4464
|
|
|
4052
4465
|
// src/doc/templates/coverBlock.ts
|
|
4466
|
+
var COVER_TITLE_BASE_PX = 120;
|
|
4467
|
+
var COVER_TITLE_COMFORTABLE_CHARS = 26;
|
|
4468
|
+
var COVER_TITLE_MIN_BASE_PX = 62;
|
|
4469
|
+
function fitCoverTitleSize(title) {
|
|
4470
|
+
const len = title.trim().length;
|
|
4471
|
+
if (len <= COVER_TITLE_COMFORTABLE_CHARS) return COVER_TITLE_BASE_PX;
|
|
4472
|
+
const scaled = COVER_TITLE_BASE_PX * Math.sqrt(COVER_TITLE_COMFORTABLE_CHARS / len);
|
|
4473
|
+
return Math.max(COVER_TITLE_MIN_BASE_PX, Math.round(scaled));
|
|
4474
|
+
}
|
|
4475
|
+
function isStandardLightCover(context) {
|
|
4476
|
+
const { theme } = context;
|
|
4477
|
+
return theme.id === "standard" && relativeLuminance(theme.colors.background) > 0.85;
|
|
4478
|
+
}
|
|
4053
4479
|
function coverBlock(input, context) {
|
|
4054
4480
|
const treatment = themedImageTreatment(context, input.imageTreatment);
|
|
4055
4481
|
const { heroSrc, heroAlt, title, subtitle, ambientMotion, heroCredit, heroLicense } = input;
|
|
4056
4482
|
const { theme, layout } = context;
|
|
4057
|
-
const
|
|
4483
|
+
const standardLightCover = !heroSrc && isStandardLightCover(context);
|
|
4484
|
+
const titleFontSize = themedFontSize(fitCoverTitleSize(title), context, true);
|
|
4058
4485
|
const subtitleFontSize = themedFontSize(40, context, false);
|
|
4059
4486
|
const layers = [];
|
|
4060
4487
|
if (heroSrc) {
|
|
@@ -4085,21 +4512,44 @@ function coverBlock(input, context) {
|
|
|
4085
4512
|
}
|
|
4086
4513
|
);
|
|
4087
4514
|
} else {
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4515
|
+
if (standardLightCover) {
|
|
4516
|
+
layers.push(
|
|
4517
|
+
{
|
|
4518
|
+
type: "shape",
|
|
4519
|
+
id: "cover-bg",
|
|
4520
|
+
content: {
|
|
4521
|
+
shape: "rect",
|
|
4522
|
+
fill: theme.colors.background
|
|
4523
|
+
},
|
|
4524
|
+
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
4525
|
+
},
|
|
4526
|
+
{
|
|
4527
|
+
type: "shape",
|
|
4528
|
+
id: "cover-bg-tint",
|
|
4529
|
+
content: {
|
|
4530
|
+
shape: "rect",
|
|
4531
|
+
fill: `radial-gradient(ellipse at 50% 40%, ${withAlpha(theme.colors.primary, 0.16)} 0%, ${withAlpha(theme.colors.primary, 0.07)} 38%, ${withAlpha(theme.colors.primary, 0)} 78%)`
|
|
4532
|
+
},
|
|
4533
|
+
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
4534
|
+
}
|
|
4535
|
+
);
|
|
4536
|
+
} else {
|
|
4537
|
+
layers.push({
|
|
4538
|
+
type: "shape",
|
|
4539
|
+
id: "cover-bg",
|
|
4540
|
+
content: {
|
|
4541
|
+
shape: "rect",
|
|
4542
|
+
fill: `radial-gradient(ellipse at 50% 40%, ${theme.colors.primary} 0%, ${theme.colors.background} 100%)`
|
|
4543
|
+
},
|
|
4544
|
+
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
4545
|
+
});
|
|
4546
|
+
}
|
|
4097
4547
|
layers.push({
|
|
4098
4548
|
type: "shape",
|
|
4099
4549
|
id: "cover-accent",
|
|
4100
4550
|
content: {
|
|
4101
4551
|
shape: "rect",
|
|
4102
|
-
fill: "rgba(255, 255, 255, 0.2)"
|
|
4552
|
+
fill: standardLightCover ? withAlpha(theme.colors.primary, 0.24) : "rgba(255, 255, 255, 0.2)"
|
|
4103
4553
|
},
|
|
4104
4554
|
position: {
|
|
4105
4555
|
x: "35%",
|
|
@@ -4120,7 +4570,7 @@ function coverBlock(input, context) {
|
|
|
4120
4570
|
fontWeight: "bold",
|
|
4121
4571
|
color: theme.colors.text,
|
|
4122
4572
|
textAlign: "center",
|
|
4123
|
-
shadow:
|
|
4573
|
+
shadow: !standardLightCover
|
|
4124
4574
|
}
|
|
4125
4575
|
},
|
|
4126
4576
|
position: {
|
|
@@ -4141,7 +4591,8 @@ function coverBlock(input, context) {
|
|
|
4141
4591
|
style: {
|
|
4142
4592
|
fontSize: subtitleFontSize,
|
|
4143
4593
|
fontFamily: getThemeFont(context, "body"),
|
|
4144
|
-
|
|
4594
|
+
fontWeight: standardLightCover ? "bold" : "normal",
|
|
4595
|
+
color: standardLightCover ? withAlpha(theme.colors.text, 0.78) : theme.colors.textMuted,
|
|
4145
4596
|
textAlign: "center",
|
|
4146
4597
|
lineHeight: 1.5
|
|
4147
4598
|
}
|
|
@@ -4243,7 +4694,11 @@ function expandTemplateBlock(templateBlock, context, registry = templateRegistry
|
|
|
4243
4694
|
};
|
|
4244
4695
|
}
|
|
4245
4696
|
const { templateData, templateOverrides } = templateBlock;
|
|
4246
|
-
const input = templateData || templateOverrides ? {
|
|
4697
|
+
const input = templateData || templateOverrides ? {
|
|
4698
|
+
...templateBlock,
|
|
4699
|
+
...templateData,
|
|
4700
|
+
...coerceTemplateParams(templateBlock.template, templateOverrides ?? {}).input
|
|
4701
|
+
} : templateBlock;
|
|
4247
4702
|
let layers;
|
|
4248
4703
|
try {
|
|
4249
4704
|
layers = templateFn(input, context);
|
|
@@ -5047,7 +5502,7 @@ function readCustomTemplatesFromFrontmatter(frontmatter) {
|
|
|
5047
5502
|
}
|
|
5048
5503
|
return out.length > 0 ? out : void 0;
|
|
5049
5504
|
}
|
|
5050
|
-
function writeCustomTemplatesToFrontmatter(templates) {
|
|
5505
|
+
function writeCustomTemplatesToFrontmatter(templates, options) {
|
|
5051
5506
|
if (!templates || templates.length === 0) return void 0;
|
|
5052
5507
|
const map = {};
|
|
5053
5508
|
for (const def of templates) {
|
|
@@ -5057,7 +5512,7 @@ function writeCustomTemplatesToFrontmatter(templates) {
|
|
|
5057
5512
|
entry.ly = renameKeys(def.layers, LONG_TO_SHORT);
|
|
5058
5513
|
map[def.name] = entry;
|
|
5059
5514
|
}
|
|
5060
|
-
return JSON.stringify(map);
|
|
5515
|
+
return JSON.stringify(map, null, options?.pretty ? 2 : void 0);
|
|
5061
5516
|
}
|
|
5062
5517
|
function readViewport(raw) {
|
|
5063
5518
|
if (raw && typeof raw === "object") {
|
|
@@ -5139,11 +5594,11 @@ function readCustomThemesFromFrontmatter(frontmatter) {
|
|
|
5139
5594
|
}
|
|
5140
5595
|
return out.length > 0 ? out : void 0;
|
|
5141
5596
|
}
|
|
5142
|
-
function writeCustomThemesToFrontmatter(themes) {
|
|
5597
|
+
function writeCustomThemesToFrontmatter(themes, options) {
|
|
5143
5598
|
if (!themes || themes.length === 0) return void 0;
|
|
5144
5599
|
const map = {};
|
|
5145
5600
|
for (const theme of themes) map[theme.id] = theme;
|
|
5146
|
-
return JSON.stringify(map);
|
|
5601
|
+
return JSON.stringify(map, null, options?.pretty ? 2 : void 0);
|
|
5147
5602
|
}
|
|
5148
5603
|
|
|
5149
5604
|
// src/doc/structuredData.ts
|
|
@@ -5172,6 +5627,17 @@ function parseDataFence(node) {
|
|
|
5172
5627
|
return { error: `invalid YAML: ${err instanceof Error ? err.message : String(err)}` };
|
|
5173
5628
|
}
|
|
5174
5629
|
}
|
|
5630
|
+
function replaceDataFence(contents, data) {
|
|
5631
|
+
const value = JSON.stringify(data, null, 2);
|
|
5632
|
+
const index = contents.findIndex((node) => node.type === "code" && isDataFence(node));
|
|
5633
|
+
if (index < 0) {
|
|
5634
|
+
const fence = { type: "code", lang: "json", meta: "data", value };
|
|
5635
|
+
return [...contents, fence];
|
|
5636
|
+
}
|
|
5637
|
+
const next = [...contents];
|
|
5638
|
+
next[index] = { ...contents[index], value };
|
|
5639
|
+
return next;
|
|
5640
|
+
}
|
|
5175
5641
|
function parseScalar(raw) {
|
|
5176
5642
|
const trimmed = raw.trim();
|
|
5177
5643
|
if (trimmed.length >= 2) {
|
|
@@ -5217,25 +5683,59 @@ function parseInlineValue(raw) {
|
|
|
5217
5683
|
function parseYamlSubset(src) {
|
|
5218
5684
|
const result = {};
|
|
5219
5685
|
const lines = src.split("\n");
|
|
5220
|
-
let
|
|
5686
|
+
let pendingKey = null;
|
|
5687
|
+
let pendingKind = null;
|
|
5688
|
+
let childIndent = -1;
|
|
5221
5689
|
for (let i = 0; i < lines.length; i++) {
|
|
5222
5690
|
const line = lines[i];
|
|
5223
5691
|
const trimmed = line.trim();
|
|
5224
5692
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5225
|
-
const
|
|
5226
|
-
if (
|
|
5227
|
-
if (!
|
|
5228
|
-
throw new Error(`line ${i + 1}:
|
|
5693
|
+
const indent = line.length - line.trimStart().length;
|
|
5694
|
+
if (indent > 0) {
|
|
5695
|
+
if (!pendingKey) {
|
|
5696
|
+
throw new Error(`line ${i + 1}: unexpected indented line without a preceding "key:" line`);
|
|
5697
|
+
}
|
|
5698
|
+
if (childIndent < 0) childIndent = indent;
|
|
5699
|
+
if (indent > childIndent) {
|
|
5700
|
+
throw new Error(
|
|
5701
|
+
`line ${i + 1}: nesting deeper than one level is not supported \u2014 use a \`\`\`json data fence for nested input`
|
|
5702
|
+
);
|
|
5229
5703
|
}
|
|
5230
|
-
|
|
5704
|
+
if (trimmed.startsWith("- ")) {
|
|
5705
|
+
if (pendingKind === "map") {
|
|
5706
|
+
throw new Error(
|
|
5707
|
+
`line ${i + 1}: cannot mix list items and mapping keys under "${pendingKey}"`
|
|
5708
|
+
);
|
|
5709
|
+
}
|
|
5710
|
+
pendingKind = "list";
|
|
5711
|
+
result[pendingKey].push(parseInlineValue(trimmed.slice(2)));
|
|
5712
|
+
continue;
|
|
5713
|
+
}
|
|
5714
|
+
if (pendingKind === "list") {
|
|
5715
|
+
throw new Error(
|
|
5716
|
+
`line ${i + 1}: cannot mix mapping keys and list items under "${pendingKey}"`
|
|
5717
|
+
);
|
|
5718
|
+
}
|
|
5719
|
+
const subColon = trimmed.indexOf(":");
|
|
5720
|
+
if (subColon < 1) {
|
|
5721
|
+
throw new Error(`line ${i + 1}: expected "key: value"`);
|
|
5722
|
+
}
|
|
5723
|
+
const subValue = trimmed.slice(subColon + 1).trim();
|
|
5724
|
+
if (subValue === "") {
|
|
5725
|
+
throw new Error(
|
|
5726
|
+
`line ${i + 1}: nesting deeper than one level is not supported \u2014 use a \`\`\`json data fence for nested input`
|
|
5727
|
+
);
|
|
5728
|
+
}
|
|
5729
|
+
if (pendingKind !== "map") {
|
|
5730
|
+
pendingKind = "map";
|
|
5731
|
+
result[pendingKey] = {};
|
|
5732
|
+
}
|
|
5733
|
+
result[pendingKey][trimmed.slice(0, subColon).trim()] = parseInlineValue(subValue);
|
|
5231
5734
|
continue;
|
|
5232
5735
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
);
|
|
5237
|
-
}
|
|
5238
|
-
pendingListKey = null;
|
|
5736
|
+
pendingKey = null;
|
|
5737
|
+
pendingKind = null;
|
|
5738
|
+
childIndent = -1;
|
|
5239
5739
|
const colonIdx = trimmed.indexOf(":");
|
|
5240
5740
|
if (colonIdx < 1) {
|
|
5241
5741
|
throw new Error(`line ${i + 1}: expected "key: value"`);
|
|
@@ -5244,7 +5744,7 @@ function parseYamlSubset(src) {
|
|
|
5244
5744
|
const rawValue = trimmed.slice(colonIdx + 1).trim();
|
|
5245
5745
|
if (rawValue === "") {
|
|
5246
5746
|
result[key] = [];
|
|
5247
|
-
|
|
5747
|
+
pendingKey = key;
|
|
5248
5748
|
} else {
|
|
5249
5749
|
result[key] = parseInlineValue(rawValue);
|
|
5250
5750
|
}
|
|
@@ -5264,8 +5764,7 @@ function extractTableData(table) {
|
|
|
5264
5764
|
};
|
|
5265
5765
|
}
|
|
5266
5766
|
|
|
5267
|
-
// src/doc/
|
|
5268
|
-
var MEDIA_TEMPLATES = /* @__PURE__ */ new Set(["audio", "video", "media"]);
|
|
5767
|
+
// src/doc/standaloneAnnotation.ts
|
|
5269
5768
|
function parseStandaloneAnnotation(node) {
|
|
5270
5769
|
if (node.type !== "paragraph") return null;
|
|
5271
5770
|
const children = node.children ?? [];
|
|
@@ -5284,6 +5783,12 @@ function parseStandaloneAnnotation(node) {
|
|
|
5284
5783
|
}
|
|
5285
5784
|
return { template, params };
|
|
5286
5785
|
}
|
|
5786
|
+
|
|
5787
|
+
// src/doc/mediaAnnotations.ts
|
|
5788
|
+
var MEDIA_TEMPLATES = /* @__PURE__ */ new Set(["audio", "video", "media"]);
|
|
5789
|
+
function isMediaAnnotationName(name) {
|
|
5790
|
+
return name != null && MEDIA_TEMPLATES.has(name);
|
|
5791
|
+
}
|
|
5287
5792
|
function time(raw) {
|
|
5288
5793
|
if (raw == null) return null;
|
|
5289
5794
|
return parseTimeSeconds(raw);
|
|
@@ -5295,7 +5800,13 @@ function toMediaClip(parsed, id) {
|
|
|
5295
5800
|
if (!src) return null;
|
|
5296
5801
|
const kind = template === "video" ? "video" : "audio";
|
|
5297
5802
|
const anchor = params.anchor === "document" || params.span === "document" ? "document" : "block";
|
|
5298
|
-
const clip = {
|
|
5803
|
+
const clip = {
|
|
5804
|
+
id,
|
|
5805
|
+
src,
|
|
5806
|
+
kind,
|
|
5807
|
+
startAt: time(params.startAt ?? params.startTime) ?? 0,
|
|
5808
|
+
anchor
|
|
5809
|
+
};
|
|
5299
5810
|
const clipStart = time(params.clipStart);
|
|
5300
5811
|
const clipEnd = time(params.clipEnd);
|
|
5301
5812
|
if (clipStart != null) clip.clipStart = clipStart;
|
|
@@ -5323,6 +5834,58 @@ function extractMediaFromContents(contents, makeId) {
|
|
|
5323
5834
|
return { media, documentMedia, remaining };
|
|
5324
5835
|
}
|
|
5325
5836
|
|
|
5837
|
+
// src/doc/annotationBlocks.ts
|
|
5838
|
+
function applyBlockMeta(block, m) {
|
|
5839
|
+
if (m.x != null) block.x = m.x;
|
|
5840
|
+
if (m.y != null) block.y = m.y;
|
|
5841
|
+
if (m.startTime != null) block.startTime = m.startTime;
|
|
5842
|
+
if (m.duration != null) block.duration = m.duration;
|
|
5843
|
+
if (m.connectsTo) block.connectsTo = m.connectsTo;
|
|
5844
|
+
if (m.transition) block.transition = m.transition;
|
|
5845
|
+
}
|
|
5846
|
+
function makeStandaloneBlock(parsed, options) {
|
|
5847
|
+
const rawTemplate = parsed.template;
|
|
5848
|
+
const params = parsed.params;
|
|
5849
|
+
const hasParams = Object.keys(params).length > 0;
|
|
5850
|
+
const block = {
|
|
5851
|
+
id: options.makeId(parsed),
|
|
5852
|
+
startTime: 0,
|
|
5853
|
+
duration: options.defaultDuration,
|
|
5854
|
+
audioSegment: 0,
|
|
5855
|
+
template: resolveTemplateName(rawTemplate),
|
|
5856
|
+
standaloneAnnotation: true,
|
|
5857
|
+
// Keep the raw (un-resolved) name so validation can report the exact
|
|
5858
|
+
// requested name — symmetric with `sourceHeading.templateAnnotation`.
|
|
5859
|
+
sourceAnnotation: { template: rawTemplate, ...hasParams ? { params } : {} },
|
|
5860
|
+
// Only carry a display title when the author supplied one.
|
|
5861
|
+
...params.title ? { title: params.title } : {}
|
|
5862
|
+
};
|
|
5863
|
+
if (hasParams) {
|
|
5864
|
+
block.templateOverrides = params;
|
|
5865
|
+
applyBlockMeta(block, coerceAnnotationValues(params).blockMeta);
|
|
5866
|
+
}
|
|
5867
|
+
return block;
|
|
5868
|
+
}
|
|
5869
|
+
function extractTemplateBlocksFromContents(contents, options) {
|
|
5870
|
+
const leading = [];
|
|
5871
|
+
const blocks = [];
|
|
5872
|
+
let current = null;
|
|
5873
|
+
for (const node of contents ?? []) {
|
|
5874
|
+
const parsed = parseStandaloneAnnotation(node);
|
|
5875
|
+
const isTemplateAnnotation = parsed != null && parsed.template != null && !isMediaAnnotationName(parsed.template);
|
|
5876
|
+
if (isTemplateAnnotation) {
|
|
5877
|
+
const block = makeStandaloneBlock(parsed, options);
|
|
5878
|
+
blocks.push(block);
|
|
5879
|
+
current = block;
|
|
5880
|
+
} else if (current) {
|
|
5881
|
+
(current.contents ?? (current.contents = [])).push(node);
|
|
5882
|
+
} else {
|
|
5883
|
+
leading.push(node);
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
return { leading, blocks };
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5326
5889
|
// src/doc/markdownToDoc.ts
|
|
5327
5890
|
var SLUG_NON_WORD_RE = /[^\w\s-]/g;
|
|
5328
5891
|
var SLUG_SPACES_RE = /[\s_]+/g;
|
|
@@ -5345,9 +5908,15 @@ function createIdGenerator() {
|
|
|
5345
5908
|
generate.reserve = (id) => {
|
|
5346
5909
|
used.set(id, Math.max(used.get(id) ?? 0, 1));
|
|
5347
5910
|
};
|
|
5911
|
+
generate.fromText = (text) => {
|
|
5912
|
+
const base = slugify(text);
|
|
5913
|
+
const count = used.get(base) ?? 0;
|
|
5914
|
+
used.set(base, count + 1);
|
|
5915
|
+
return count === 0 ? base : `${base}-${count + 1}`;
|
|
5916
|
+
};
|
|
5348
5917
|
return generate;
|
|
5349
5918
|
}
|
|
5350
|
-
function
|
|
5919
|
+
function applyBlockMeta2(block, m) {
|
|
5351
5920
|
if (m.x != null) block.x = m.x;
|
|
5352
5921
|
if (m.y != null) block.y = m.y;
|
|
5353
5922
|
if (m.startTime != null) block.startTime = m.startTime;
|
|
@@ -5361,13 +5930,37 @@ function pinnedHeadingMeta(heading) {
|
|
|
5361
5930
|
const fromPandoc = heading.attributes?.blockMeta ?? {};
|
|
5362
5931
|
return { ...fromAnnotation, ...fromPandoc };
|
|
5363
5932
|
}
|
|
5933
|
+
function pinnedMeta(block) {
|
|
5934
|
+
if (block.sourceHeading) return pinnedHeadingMeta(block.sourceHeading);
|
|
5935
|
+
if (block.sourceAnnotation?.params) {
|
|
5936
|
+
return coerceAnnotationValues(block.sourceAnnotation.params).blockMeta;
|
|
5937
|
+
}
|
|
5938
|
+
return {};
|
|
5939
|
+
}
|
|
5940
|
+
function expandStandaloneAnnotations(blocks, makeId, defaultDuration) {
|
|
5941
|
+
const out = [];
|
|
5942
|
+
for (const block of blocks) {
|
|
5943
|
+
if (block.children && block.children.length > 0) {
|
|
5944
|
+
block.children = expandStandaloneAnnotations(block.children, makeId, defaultDuration);
|
|
5945
|
+
}
|
|
5946
|
+
const { leading, blocks: produced } = extractTemplateBlocksFromContents(block.contents, {
|
|
5947
|
+
makeId,
|
|
5948
|
+
defaultDuration
|
|
5949
|
+
});
|
|
5950
|
+
if (produced.length > 0) {
|
|
5951
|
+
block.contents = leading;
|
|
5952
|
+
}
|
|
5953
|
+
out.push(block, ...produced);
|
|
5954
|
+
}
|
|
5955
|
+
return out;
|
|
5956
|
+
}
|
|
5364
5957
|
function markdownToDoc(markdownDoc, options) {
|
|
5365
5958
|
const articleId = options?.articleId ?? "markdown-doc";
|
|
5366
5959
|
const defaultTemplate = options?.defaultTemplate ?? "sectionHeader";
|
|
5367
5960
|
const defaultDuration = options?.defaultDuration ?? 5;
|
|
5368
5961
|
const idGenerator = options?.generateId ? null : createIdGenerator();
|
|
5369
5962
|
const generateId = options?.generateId ?? idGenerator;
|
|
5370
|
-
|
|
5963
|
+
let rootBlocks = [];
|
|
5371
5964
|
const diagnostics = [];
|
|
5372
5965
|
let headingIndex = 0;
|
|
5373
5966
|
const stack = [];
|
|
@@ -5400,12 +5993,12 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5400
5993
|
};
|
|
5401
5994
|
if (annotation?.params) {
|
|
5402
5995
|
block.templateOverrides = annotation.params;
|
|
5403
|
-
|
|
5996
|
+
applyBlockMeta2(block, coerceAnnotationValues(annotation.params).blockMeta);
|
|
5404
5997
|
}
|
|
5405
5998
|
const attrs = heading?.attributes;
|
|
5406
5999
|
if (attrs) {
|
|
5407
6000
|
if (attrs.blockMeta) {
|
|
5408
|
-
|
|
6001
|
+
applyBlockMeta2(block, attrs.blockMeta);
|
|
5409
6002
|
}
|
|
5410
6003
|
if (attrs.classes && attrs.classes.length > 0) {
|
|
5411
6004
|
block.classes = attrs.classes;
|
|
@@ -5471,6 +6064,14 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5471
6064
|
if (rootBlocks.length > 0 && !rootBlocks[0].sourceHeading && (rootBlocks[0].contents?.length ?? 0) === 0 && (rootBlocks[0].children?.length ?? 0) === 0 && !rootBlocks[0].media) {
|
|
5472
6065
|
rootBlocks.shift();
|
|
5473
6066
|
}
|
|
6067
|
+
const makeStandaloneId = (parsed) => {
|
|
6068
|
+
const base = parsed.params.title ?? parsed.template ?? "block";
|
|
6069
|
+
return idGenerator ? idGenerator.fromText(base) : slugify(base);
|
|
6070
|
+
};
|
|
6071
|
+
rootBlocks = expandStandaloneAnnotations(rootBlocks, makeStandaloneId, defaultDuration);
|
|
6072
|
+
if (rootBlocks.length > 0 && !rootBlocks[0].sourceHeading && !rootBlocks[0].standaloneAnnotation && (rootBlocks[0].contents?.length ?? 0) === 0 && (rootBlocks[0].children?.length ?? 0) === 0 && !rootBlocks[0].media) {
|
|
6073
|
+
rootBlocks.shift();
|
|
6074
|
+
}
|
|
5474
6075
|
const allBlocks = flattenBlocks(rootBlocks);
|
|
5475
6076
|
for (const block of allBlocks) {
|
|
5476
6077
|
applyStructuredData(block, diagnostics);
|
|
@@ -5508,7 +6109,7 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5508
6109
|
const minDuration = 3;
|
|
5509
6110
|
const phrases = [];
|
|
5510
6111
|
for (const block of allBlocks) {
|
|
5511
|
-
if (
|
|
6112
|
+
if (pinnedMeta(block).duration != null) continue;
|
|
5512
6113
|
const bodyText3 = getBlockBodyText2(block);
|
|
5513
6114
|
if (bodyText3.length > 0) {
|
|
5514
6115
|
const estimate = estimateReadingTime(bodyText3);
|
|
@@ -5519,7 +6120,7 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5519
6120
|
}
|
|
5520
6121
|
let currentTime = 0;
|
|
5521
6122
|
for (const block of allBlocks) {
|
|
5522
|
-
const explicitStart =
|
|
6123
|
+
const explicitStart = pinnedMeta(block).startTime;
|
|
5523
6124
|
if (explicitStart == null) {
|
|
5524
6125
|
block.startTime = currentTime;
|
|
5525
6126
|
}
|
|
@@ -5759,12 +6360,15 @@ function buildStartBlock(markdownDoc, rootBlocks) {
|
|
|
5759
6360
|
}
|
|
5760
6361
|
|
|
5761
6362
|
// src/doc/docToMarkdown.ts
|
|
6363
|
+
var TRANSITION_PARAM_KEYS = ["transition", "transitionDuration", "transitionDirection"];
|
|
5762
6364
|
function docToMarkdown(doc) {
|
|
5763
6365
|
const children = [];
|
|
5764
6366
|
function emitBlock(block) {
|
|
5765
6367
|
if (block.sourceHeading) {
|
|
5766
6368
|
const heading = ensureAnnotation(block, block.sourceHeading);
|
|
5767
6369
|
children.push(heading);
|
|
6370
|
+
} else if (block.standaloneAnnotation) {
|
|
6371
|
+
children.push(synthesizeAnnotationParagraph(block));
|
|
5768
6372
|
}
|
|
5769
6373
|
if (block.contents) {
|
|
5770
6374
|
children.push(...block.contents);
|
|
@@ -5801,43 +6405,65 @@ function docToMarkdown(doc) {
|
|
|
5801
6405
|
...frontmatter && Object.keys(frontmatter).length > 0 ? { frontmatter } : {}
|
|
5802
6406
|
};
|
|
5803
6407
|
}
|
|
6408
|
+
function synthesizeAnnotationParagraph(block) {
|
|
6409
|
+
const text = serializeAnnotation(block.sourceAnnotation?.template, block.templateOverrides);
|
|
6410
|
+
return { type: "paragraph", children: [{ type: "text", value: text }] };
|
|
6411
|
+
}
|
|
5804
6412
|
function ensureAnnotation(block, heading) {
|
|
5805
|
-
const
|
|
6413
|
+
const { attributes, transitionParams } = ensureTransitionMetadata(block, heading);
|
|
5806
6414
|
const hasExplicitTemplate = block.template && block.template !== "sectionHeader" && block.autoTemplate !== true;
|
|
5807
6415
|
const hasOverrides = block.templateOverrides && Object.keys(block.templateOverrides).length > 0;
|
|
5808
|
-
|
|
5809
|
-
|
|
6416
|
+
let templateAnnotation = heading.templateAnnotation;
|
|
6417
|
+
if (!templateAnnotation && (hasExplicitTemplate || hasOverrides || transitionParams)) {
|
|
6418
|
+
templateAnnotation = {
|
|
6419
|
+
...hasExplicitTemplate ? { template: block.template ?? "sectionHeader" } : {},
|
|
6420
|
+
...hasOverrides ? { params: block.templateOverrides } : {}
|
|
6421
|
+
};
|
|
5810
6422
|
}
|
|
5811
|
-
if (
|
|
5812
|
-
|
|
6423
|
+
if (transitionParams) {
|
|
6424
|
+
templateAnnotation = {
|
|
6425
|
+
...templateAnnotation ?? {},
|
|
6426
|
+
params: {
|
|
6427
|
+
...templateAnnotation?.params ?? {},
|
|
6428
|
+
...transitionParams
|
|
6429
|
+
}
|
|
6430
|
+
};
|
|
5813
6431
|
}
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
templateAnnotation: {
|
|
5819
|
-
template: block.template ?? "sectionHeader",
|
|
5820
|
-
...hasOverrides ? { params: block.templateOverrides } : {}
|
|
5821
|
-
}
|
|
5822
|
-
};
|
|
6432
|
+
if (attributes === heading.attributes && templateAnnotation === heading.templateAnnotation) {
|
|
6433
|
+
return heading;
|
|
6434
|
+
}
|
|
6435
|
+
return { ...heading, children: [...heading.children], attributes, templateAnnotation };
|
|
5823
6436
|
}
|
|
5824
|
-
function
|
|
6437
|
+
function ensureTransitionMetadata(block, heading) {
|
|
5825
6438
|
const transition = block.transition;
|
|
5826
|
-
if (!transition)
|
|
5827
|
-
|
|
5828
|
-
|
|
6439
|
+
if (!transition) {
|
|
6440
|
+
return { attributes: heading.attributes, transitionParams: void 0 };
|
|
6441
|
+
}
|
|
6442
|
+
const attrs = removeTransitionParams(heading.attributes) ?? {};
|
|
6443
|
+
const attributes = {
|
|
5829
6444
|
...attrs,
|
|
5830
|
-
params: {
|
|
5831
|
-
...attrs.params ?? {},
|
|
5832
|
-
transition: transition.type,
|
|
5833
|
-
...transition.duration !== void 0 ? { transitionDuration: String(transition.duration) } : {},
|
|
5834
|
-
...transition.direction ? { transitionDirection: transition.direction } : {}
|
|
5835
|
-
},
|
|
5836
6445
|
blockMeta: {
|
|
5837
6446
|
...attrs.blockMeta ?? {},
|
|
5838
6447
|
transition
|
|
5839
6448
|
}
|
|
5840
6449
|
};
|
|
6450
|
+
return {
|
|
6451
|
+
attributes,
|
|
6452
|
+
transitionParams: {
|
|
6453
|
+
transition: transition.type,
|
|
6454
|
+
...transition.duration !== void 0 ? { transitionDuration: String(transition.duration) } : {},
|
|
6455
|
+
...transition.direction ? { transitionDirection: transition.direction } : {}
|
|
6456
|
+
}
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
function removeTransitionParams(attrs) {
|
|
6460
|
+
if (!attrs?.params) return attrs;
|
|
6461
|
+
const params = { ...attrs.params };
|
|
6462
|
+
for (const key of TRANSITION_PARAM_KEYS) delete params[key];
|
|
6463
|
+
return {
|
|
6464
|
+
...attrs,
|
|
6465
|
+
...Object.keys(params).length > 0 ? { params } : { params: void 0 }
|
|
6466
|
+
};
|
|
5841
6467
|
}
|
|
5842
6468
|
|
|
5843
6469
|
// src/doc/resolveDocTheme.ts
|
|
@@ -5950,7 +6576,11 @@ function getLayers(block, context = {}) {
|
|
|
5950
6576
|
templateCtx.children = maybeChildren;
|
|
5951
6577
|
}
|
|
5952
6578
|
const { templateData, templateOverrides } = block;
|
|
5953
|
-
const input = templateData || templateOverrides ? {
|
|
6579
|
+
const input = templateData || templateOverrides ? {
|
|
6580
|
+
...block,
|
|
6581
|
+
...templateData,
|
|
6582
|
+
...coerceTemplateParams(block.template, templateOverrides ?? {}).input
|
|
6583
|
+
} : block;
|
|
5954
6584
|
let layers;
|
|
5955
6585
|
try {
|
|
5956
6586
|
const templateFn = templateRegistry[templateName];
|
|
@@ -6215,9 +6845,13 @@ function validateMarkdownDoc(markdownDoc, options) {
|
|
|
6215
6845
|
const diagnostics = [...doc.diagnostics ?? []];
|
|
6216
6846
|
const blocks = flattenBlocks(doc.blocks);
|
|
6217
6847
|
const knownTemplates = collectKnownTemplates(doc, options);
|
|
6848
|
+
const customNames = new Set(doc.customTemplates?.map((t) => t.name) ?? []);
|
|
6218
6849
|
const visit = (block, parent) => {
|
|
6219
6850
|
checkTemplate(block, parent, knownTemplates, diagnostics);
|
|
6220
6851
|
checkAttributeCoercion(block, parent, diagnostics);
|
|
6852
|
+
checkConflictingAnnotationKeys(block, diagnostics);
|
|
6853
|
+
checkTemplateInputs(block, parent, customNames, diagnostics);
|
|
6854
|
+
checkPossibleDataFences(block, diagnostics);
|
|
6221
6855
|
if (isDrawingBlock(block)) checkDrawingConnectors(block, diagnostics);
|
|
6222
6856
|
for (const child of block.children ?? []) visit(child, block);
|
|
6223
6857
|
};
|
|
@@ -6234,6 +6868,7 @@ function validateMarkdownDoc(markdownDoc, options) {
|
|
|
6234
6868
|
diagnostics,
|
|
6235
6869
|
errorCount: diagnostics.filter((d) => d.severity === "error").length,
|
|
6236
6870
|
warningCount: diagnostics.filter((d) => d.severity === "warning").length,
|
|
6871
|
+
infoCount: diagnostics.filter((d) => d.severity === "info").length,
|
|
6237
6872
|
doc
|
|
6238
6873
|
};
|
|
6239
6874
|
}
|
|
@@ -6249,11 +6884,13 @@ function isDrawingBlock(block) {
|
|
|
6249
6884
|
return resolveTemplateName(block.template ?? "") === "drawing";
|
|
6250
6885
|
}
|
|
6251
6886
|
function checkTemplate(block, parent, known, diagnostics) {
|
|
6252
|
-
const requested = block.sourceHeading?.templateAnnotation?.template;
|
|
6887
|
+
const requested = block.sourceHeading?.templateAnnotation?.template ?? block.sourceAnnotation?.template;
|
|
6253
6888
|
if (!requested) return;
|
|
6254
6889
|
if (parent && isDrawingBlock(parent)) {
|
|
6255
6890
|
if (isShapeName(requested)) return;
|
|
6256
|
-
const suggestion2 = nearestName(requested, new Set(SHAPE_NAMES)
|
|
6891
|
+
const suggestion2 = nearestName(requested, new Set(SHAPE_NAMES), {
|
|
6892
|
+
map: resolveTemplateName
|
|
6893
|
+
});
|
|
6257
6894
|
diagnostics.push({
|
|
6258
6895
|
severity: "warning",
|
|
6259
6896
|
code: "unknown-shape",
|
|
@@ -6274,7 +6911,7 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
6274
6911
|
return;
|
|
6275
6912
|
}
|
|
6276
6913
|
if (known.has(requested) || known.has(resolveTemplateName(requested))) return;
|
|
6277
|
-
const suggestion = nearestName(requested, known);
|
|
6914
|
+
const suggestion = nearestName(requested, known, { map: resolveTemplateName });
|
|
6278
6915
|
diagnostics.push({
|
|
6279
6916
|
severity: "warning",
|
|
6280
6917
|
code: "unknown-template",
|
|
@@ -6283,6 +6920,33 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
6283
6920
|
...lineOf2(block)
|
|
6284
6921
|
});
|
|
6285
6922
|
}
|
|
6923
|
+
function checkTemplateInputs(block, parent, customNames, diagnostics) {
|
|
6924
|
+
const requested = block.sourceHeading?.templateAnnotation?.template;
|
|
6925
|
+
if (!requested) return;
|
|
6926
|
+
if (parent && isDrawingBlock(parent)) return;
|
|
6927
|
+
if (isShapeName(requested)) return;
|
|
6928
|
+
const canonical = resolveTemplateName(requested);
|
|
6929
|
+
if (customNames.has(requested) || customNames.has(canonical)) return;
|
|
6930
|
+
const params = block.sourceHeading?.templateAnnotation?.params ?? {};
|
|
6931
|
+
for (const finding of lintTemplateParams(canonical, params)) {
|
|
6932
|
+
if (finding.kind === "missing-input" && isInputSatisfiable(canonical, block, finding.key)) {
|
|
6933
|
+
continue;
|
|
6934
|
+
}
|
|
6935
|
+
diagnostics.push({
|
|
6936
|
+
severity: "warning",
|
|
6937
|
+
code: finding.kind,
|
|
6938
|
+
message: `Block "${block.id}": ${finding.message}`,
|
|
6939
|
+
blockId: block.id,
|
|
6940
|
+
...lineOf2(block)
|
|
6941
|
+
});
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6944
|
+
function isInputSatisfiable(canonical, block, key) {
|
|
6945
|
+
if (block.templateData && key in block.templateData) return true;
|
|
6946
|
+
if (block[key] != null) return true;
|
|
6947
|
+
const derived = deriveTemplateInputs(canonical, block.title ?? "", block.contents);
|
|
6948
|
+
return derived != null && key in derived;
|
|
6949
|
+
}
|
|
6286
6950
|
var NUMERIC_SHAPE_KEYS = ["x", "y", "width", "height", "w", "h", "strokeWidth", "borderRadius"];
|
|
6287
6951
|
function checkAttributeCoercion(block, parent, diagnostics) {
|
|
6288
6952
|
const params = block.sourceHeading?.attributes?.params;
|
|
@@ -6316,6 +6980,23 @@ function checkAttributeCoercion(block, parent, diagnostics) {
|
|
|
6316
6980
|
}
|
|
6317
6981
|
}
|
|
6318
6982
|
}
|
|
6983
|
+
function checkConflictingAnnotationKeys(block, diagnostics) {
|
|
6984
|
+
const annotationParams = block.sourceHeading?.templateAnnotation?.params;
|
|
6985
|
+
const pandocParams = block.sourceHeading?.attributes?.params;
|
|
6986
|
+
if (!annotationParams || !pandocParams) return;
|
|
6987
|
+
for (const key of Object.keys(KNOWN_BLOCK_META_KEYS)) {
|
|
6988
|
+
const fromAnnotation = annotationParams[key];
|
|
6989
|
+
const fromPandoc = pandocParams[key];
|
|
6990
|
+
if (fromAnnotation == null || fromPandoc == null || fromAnnotation === fromPandoc) continue;
|
|
6991
|
+
diagnostics.push({
|
|
6992
|
+
severity: "info",
|
|
6993
|
+
code: "conflicting-annotation-key",
|
|
6994
|
+
message: `"${key}" is set in both {[\u2026]} (${fromAnnotation}) and {\u2026} (${fromPandoc}) \u2014 the {\u2026} value wins.`,
|
|
6995
|
+
blockId: block.id,
|
|
6996
|
+
...lineOf2(block)
|
|
6997
|
+
});
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
6319
7000
|
function checkDrawingConnectors(block, diagnostics) {
|
|
6320
7001
|
const children = block.children ?? [];
|
|
6321
7002
|
const ids = new Set(children.map((c) => c.id));
|
|
@@ -6339,9 +7020,28 @@ function checkDrawingConnectors(block, diagnostics) {
|
|
|
6339
7020
|
}
|
|
6340
7021
|
}
|
|
6341
7022
|
}
|
|
7023
|
+
function checkPossibleDataFences(block, diagnostics) {
|
|
7024
|
+
const annotated = !!(block.sourceHeading?.templateAnnotation?.template || block.sourceAnnotation?.template);
|
|
7025
|
+
if (!annotated) return;
|
|
7026
|
+
for (const node of block.contents ?? []) {
|
|
7027
|
+
if (node.type !== "code") continue;
|
|
7028
|
+
const lang = node.lang?.toLowerCase();
|
|
7029
|
+
if (lang !== "json" && lang !== "yaml" && lang !== "yml") continue;
|
|
7030
|
+
if (isDataFence(node)) continue;
|
|
7031
|
+
const result = parseDataFence(node);
|
|
7032
|
+
if (!result.data || Object.keys(result.data).length === 0) continue;
|
|
7033
|
+
diagnostics.push({
|
|
7034
|
+
severity: "info",
|
|
7035
|
+
code: "possible-data-fence",
|
|
7036
|
+
message: `This ${lang} block renders as code \u2014 add "data" to the fence (\`\`\`${lang} data) if it's meant as template input.`,
|
|
7037
|
+
blockId: block.id,
|
|
7038
|
+
...node.position ? { line: node.position.start.line } : lineOf2(block)
|
|
7039
|
+
});
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
6342
7042
|
var ANNOTATION_LIKE_RE = /\{\[/;
|
|
6343
7043
|
function checkUnrecognizedAnnotations(markdownDoc, diagnostics) {
|
|
6344
|
-
const walk = (node, headingLine) => {
|
|
7044
|
+
const walk = (node, headingLine, topLevel) => {
|
|
6345
7045
|
if (node.type === "heading") {
|
|
6346
7046
|
const heading = node;
|
|
6347
7047
|
const text = extractPlainText(heading);
|
|
@@ -6355,22 +7055,26 @@ function checkUnrecognizedAnnotations(markdownDoc, diagnostics) {
|
|
|
6355
7055
|
}
|
|
6356
7056
|
return;
|
|
6357
7057
|
}
|
|
7058
|
+
if (node.type === "paragraph" && topLevel) {
|
|
7059
|
+
const parsed = parseStandaloneAnnotation(node);
|
|
7060
|
+
if (parsed && parsed.template != null) return;
|
|
7061
|
+
}
|
|
6358
7062
|
if (node.type === "text" && ANNOTATION_LIKE_RE.test(node.value)) {
|
|
6359
7063
|
diagnostics.push({
|
|
6360
7064
|
severity: "warning",
|
|
6361
7065
|
code: "unparsed-annotation",
|
|
6362
|
-
message: `Body text contains "{[\u2026]}" \u2014 template annotations are
|
|
7066
|
+
message: `Body text contains "{[\u2026]}" \u2014 template annotations are recognized on headings and as standalone paragraphs (or the token is not a known inline icon)`,
|
|
6363
7067
|
...node.position ? { line: node.position.start.line } : headingLine != null ? { line: headingLine } : {}
|
|
6364
7068
|
});
|
|
6365
7069
|
return;
|
|
6366
7070
|
}
|
|
6367
7071
|
if (node.type === "code") return;
|
|
6368
7072
|
for (const child of getChildren(node)) {
|
|
6369
|
-
walk(child, headingLine);
|
|
7073
|
+
walk(child, headingLine, false);
|
|
6370
7074
|
}
|
|
6371
7075
|
};
|
|
6372
7076
|
for (const child of markdownDoc.children) {
|
|
6373
|
-
walk(child, child.position?.start.line);
|
|
7077
|
+
walk(child, child.position?.start.line, true);
|
|
6374
7078
|
}
|
|
6375
7079
|
}
|
|
6376
7080
|
function normalizeAssetCheck(assets) {
|
|
@@ -6433,41 +7137,6 @@ function walkHtmlImages(nodes, report) {
|
|
|
6433
7137
|
walkHtmlImages(node.children, report);
|
|
6434
7138
|
}
|
|
6435
7139
|
}
|
|
6436
|
-
function nearestName(input, candidates) {
|
|
6437
|
-
let best;
|
|
6438
|
-
let bestDist = Infinity;
|
|
6439
|
-
const lower = input.toLowerCase();
|
|
6440
|
-
for (const candidate of candidates) {
|
|
6441
|
-
const dist = levenshtein(lower, candidate.toLowerCase());
|
|
6442
|
-
if (dist < bestDist) {
|
|
6443
|
-
bestDist = dist;
|
|
6444
|
-
best = candidate;
|
|
6445
|
-
}
|
|
6446
|
-
}
|
|
6447
|
-
if (best && (bestDist <= 2 || bestDist <= Math.ceil(input.length * 0.4))) {
|
|
6448
|
-
return resolveTemplateName(best);
|
|
6449
|
-
}
|
|
6450
|
-
return void 0;
|
|
6451
|
-
}
|
|
6452
|
-
function levenshtein(a, b) {
|
|
6453
|
-
if (a === b) return 0;
|
|
6454
|
-
const m = a.length;
|
|
6455
|
-
const n = b.length;
|
|
6456
|
-
if (m === 0) return n;
|
|
6457
|
-
if (n === 0) return m;
|
|
6458
|
-
let prev = new Array(n + 1);
|
|
6459
|
-
let curr = new Array(n + 1);
|
|
6460
|
-
for (let j = 0; j <= n; j++) prev[j] = j;
|
|
6461
|
-
for (let i = 1; i <= m; i++) {
|
|
6462
|
-
curr[0] = i;
|
|
6463
|
-
for (let j = 1; j <= n; j++) {
|
|
6464
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
6465
|
-
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
6466
|
-
}
|
|
6467
|
-
[prev, curr] = [curr, prev];
|
|
6468
|
-
}
|
|
6469
|
-
return prev[n];
|
|
6470
|
-
}
|
|
6471
7140
|
function lineOf2(block) {
|
|
6472
7141
|
const line = block.sourceHeading?.position?.start.line;
|
|
6473
7142
|
return line != null ? { line } : {};
|
|
@@ -6484,6 +7153,10 @@ export {
|
|
|
6484
7153
|
wrapWithPersistentLayers,
|
|
6485
7154
|
themeWantsAmbientMotion,
|
|
6486
7155
|
applyRenderStyleToLayers,
|
|
7156
|
+
BASE_INPUT_DESCRIPTORS,
|
|
7157
|
+
TEMPLATE_INPUT_DESCRIPTORS,
|
|
7158
|
+
coerceTemplateParams,
|
|
7159
|
+
lintTemplateParams,
|
|
6487
7160
|
TEMPLATE_METADATA,
|
|
6488
7161
|
resolveColorScheme,
|
|
6489
7162
|
getThemeFont,
|
|
@@ -6562,6 +7235,7 @@ export {
|
|
|
6562
7235
|
writeCustomThemesToFrontmatter,
|
|
6563
7236
|
isDataFence,
|
|
6564
7237
|
parseDataFence,
|
|
7238
|
+
replaceDataFence,
|
|
6565
7239
|
parseYamlSubset,
|
|
6566
7240
|
findFirstTable,
|
|
6567
7241
|
extractTableData,
|
|
@@ -6579,4 +7253,4 @@ export {
|
|
|
6579
7253
|
validateMarkdownSource,
|
|
6580
7254
|
validateMarkdownDoc
|
|
6581
7255
|
};
|
|
6582
|
-
//# sourceMappingURL=chunk-
|
|
7256
|
+
//# sourceMappingURL=chunk-5TYS5V3G.js.map
|