@bendyline/squisq 1.5.0 → 1.5.1
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-KZNXZ5SM.js → chunk-6ATE2PSM.js} +18 -7
- package/dist/chunk-6ATE2PSM.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-ET53IIEP.js +1 -0
- package/dist/chunk-ET53IIEP.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-4ZFRZBU2.js → chunk-LH7I6SH7.js} +777 -151
- package/dist/chunk-LH7I6SH7.js.map +1 -0
- package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
- package/dist/chunk-T5UK6YOB.js.map +1 -0
- package/dist/{chunk-OH43RPTP.js → chunk-TEEEILMP.js} +12 -2
- package/dist/chunk-TEEEILMP.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 +16 -7
- 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/index.d.ts +8 -8
- package/dist/index.js +32 -23
- 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 +125 -11
- package/dist/story/index.js +16 -7
- 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 +46 -1
- 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 +168 -0
- package/src/__tests__/mediaAnnotations.test.ts +27 -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 +31 -1
- 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 +29 -2
- package/src/doc/templates/customTemplate.ts +10 -4
- 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/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/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/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-LRCV7N7F.js.map → chunk-UY7KGQ5R.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
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
|
|
@@ -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-TEEEILMP.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
|
});
|
|
@@ -4050,11 +4463,20 @@ 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
|
+
}
|
|
4053
4475
|
function coverBlock(input, context) {
|
|
4054
4476
|
const treatment = themedImageTreatment(context, input.imageTreatment);
|
|
4055
4477
|
const { heroSrc, heroAlt, title, subtitle, ambientMotion, heroCredit, heroLicense } = input;
|
|
4056
4478
|
const { theme, layout } = context;
|
|
4057
|
-
const titleFontSize = themedFontSize(
|
|
4479
|
+
const titleFontSize = themedFontSize(fitCoverTitleSize(title), context, true);
|
|
4058
4480
|
const subtitleFontSize = themedFontSize(40, context, false);
|
|
4059
4481
|
const layers = [];
|
|
4060
4482
|
if (heroSrc) {
|
|
@@ -4243,7 +4665,11 @@ function expandTemplateBlock(templateBlock, context, registry = templateRegistry
|
|
|
4243
4665
|
};
|
|
4244
4666
|
}
|
|
4245
4667
|
const { templateData, templateOverrides } = templateBlock;
|
|
4246
|
-
const input = templateData || templateOverrides ? {
|
|
4668
|
+
const input = templateData || templateOverrides ? {
|
|
4669
|
+
...templateBlock,
|
|
4670
|
+
...templateData,
|
|
4671
|
+
...coerceTemplateParams(templateBlock.template, templateOverrides ?? {}).input
|
|
4672
|
+
} : templateBlock;
|
|
4247
4673
|
let layers;
|
|
4248
4674
|
try {
|
|
4249
4675
|
layers = templateFn(input, context);
|
|
@@ -5047,7 +5473,7 @@ function readCustomTemplatesFromFrontmatter(frontmatter) {
|
|
|
5047
5473
|
}
|
|
5048
5474
|
return out.length > 0 ? out : void 0;
|
|
5049
5475
|
}
|
|
5050
|
-
function writeCustomTemplatesToFrontmatter(templates) {
|
|
5476
|
+
function writeCustomTemplatesToFrontmatter(templates, options) {
|
|
5051
5477
|
if (!templates || templates.length === 0) return void 0;
|
|
5052
5478
|
const map = {};
|
|
5053
5479
|
for (const def of templates) {
|
|
@@ -5057,7 +5483,7 @@ function writeCustomTemplatesToFrontmatter(templates) {
|
|
|
5057
5483
|
entry.ly = renameKeys(def.layers, LONG_TO_SHORT);
|
|
5058
5484
|
map[def.name] = entry;
|
|
5059
5485
|
}
|
|
5060
|
-
return JSON.stringify(map);
|
|
5486
|
+
return JSON.stringify(map, null, options?.pretty ? 2 : void 0);
|
|
5061
5487
|
}
|
|
5062
5488
|
function readViewport(raw) {
|
|
5063
5489
|
if (raw && typeof raw === "object") {
|
|
@@ -5139,11 +5565,11 @@ function readCustomThemesFromFrontmatter(frontmatter) {
|
|
|
5139
5565
|
}
|
|
5140
5566
|
return out.length > 0 ? out : void 0;
|
|
5141
5567
|
}
|
|
5142
|
-
function writeCustomThemesToFrontmatter(themes) {
|
|
5568
|
+
function writeCustomThemesToFrontmatter(themes, options) {
|
|
5143
5569
|
if (!themes || themes.length === 0) return void 0;
|
|
5144
5570
|
const map = {};
|
|
5145
5571
|
for (const theme of themes) map[theme.id] = theme;
|
|
5146
|
-
return JSON.stringify(map);
|
|
5572
|
+
return JSON.stringify(map, null, options?.pretty ? 2 : void 0);
|
|
5147
5573
|
}
|
|
5148
5574
|
|
|
5149
5575
|
// src/doc/structuredData.ts
|
|
@@ -5172,6 +5598,17 @@ function parseDataFence(node) {
|
|
|
5172
5598
|
return { error: `invalid YAML: ${err instanceof Error ? err.message : String(err)}` };
|
|
5173
5599
|
}
|
|
5174
5600
|
}
|
|
5601
|
+
function replaceDataFence(contents, data) {
|
|
5602
|
+
const value = JSON.stringify(data, null, 2);
|
|
5603
|
+
const index = contents.findIndex((node) => node.type === "code" && isDataFence(node));
|
|
5604
|
+
if (index < 0) {
|
|
5605
|
+
const fence = { type: "code", lang: "json", meta: "data", value };
|
|
5606
|
+
return [...contents, fence];
|
|
5607
|
+
}
|
|
5608
|
+
const next = [...contents];
|
|
5609
|
+
next[index] = { ...contents[index], value };
|
|
5610
|
+
return next;
|
|
5611
|
+
}
|
|
5175
5612
|
function parseScalar(raw) {
|
|
5176
5613
|
const trimmed = raw.trim();
|
|
5177
5614
|
if (trimmed.length >= 2) {
|
|
@@ -5217,25 +5654,59 @@ function parseInlineValue(raw) {
|
|
|
5217
5654
|
function parseYamlSubset(src) {
|
|
5218
5655
|
const result = {};
|
|
5219
5656
|
const lines = src.split("\n");
|
|
5220
|
-
let
|
|
5657
|
+
let pendingKey = null;
|
|
5658
|
+
let pendingKind = null;
|
|
5659
|
+
let childIndent = -1;
|
|
5221
5660
|
for (let i = 0; i < lines.length; i++) {
|
|
5222
5661
|
const line = lines[i];
|
|
5223
5662
|
const trimmed = line.trim();
|
|
5224
5663
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5225
|
-
const
|
|
5226
|
-
if (
|
|
5227
|
-
if (!
|
|
5228
|
-
throw new Error(`line ${i + 1}:
|
|
5664
|
+
const indent = line.length - line.trimStart().length;
|
|
5665
|
+
if (indent > 0) {
|
|
5666
|
+
if (!pendingKey) {
|
|
5667
|
+
throw new Error(`line ${i + 1}: unexpected indented line without a preceding "key:" line`);
|
|
5668
|
+
}
|
|
5669
|
+
if (childIndent < 0) childIndent = indent;
|
|
5670
|
+
if (indent > childIndent) {
|
|
5671
|
+
throw new Error(
|
|
5672
|
+
`line ${i + 1}: nesting deeper than one level is not supported \u2014 use a \`\`\`json data fence for nested input`
|
|
5673
|
+
);
|
|
5674
|
+
}
|
|
5675
|
+
if (trimmed.startsWith("- ")) {
|
|
5676
|
+
if (pendingKind === "map") {
|
|
5677
|
+
throw new Error(
|
|
5678
|
+
`line ${i + 1}: cannot mix list items and mapping keys under "${pendingKey}"`
|
|
5679
|
+
);
|
|
5680
|
+
}
|
|
5681
|
+
pendingKind = "list";
|
|
5682
|
+
result[pendingKey].push(parseInlineValue(trimmed.slice(2)));
|
|
5683
|
+
continue;
|
|
5684
|
+
}
|
|
5685
|
+
if (pendingKind === "list") {
|
|
5686
|
+
throw new Error(
|
|
5687
|
+
`line ${i + 1}: cannot mix mapping keys and list items under "${pendingKey}"`
|
|
5688
|
+
);
|
|
5689
|
+
}
|
|
5690
|
+
const subColon = trimmed.indexOf(":");
|
|
5691
|
+
if (subColon < 1) {
|
|
5692
|
+
throw new Error(`line ${i + 1}: expected "key: value"`);
|
|
5693
|
+
}
|
|
5694
|
+
const subValue = trimmed.slice(subColon + 1).trim();
|
|
5695
|
+
if (subValue === "") {
|
|
5696
|
+
throw new Error(
|
|
5697
|
+
`line ${i + 1}: nesting deeper than one level is not supported \u2014 use a \`\`\`json data fence for nested input`
|
|
5698
|
+
);
|
|
5229
5699
|
}
|
|
5230
|
-
|
|
5700
|
+
if (pendingKind !== "map") {
|
|
5701
|
+
pendingKind = "map";
|
|
5702
|
+
result[pendingKey] = {};
|
|
5703
|
+
}
|
|
5704
|
+
result[pendingKey][trimmed.slice(0, subColon).trim()] = parseInlineValue(subValue);
|
|
5231
5705
|
continue;
|
|
5232
5706
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
);
|
|
5237
|
-
}
|
|
5238
|
-
pendingListKey = null;
|
|
5707
|
+
pendingKey = null;
|
|
5708
|
+
pendingKind = null;
|
|
5709
|
+
childIndent = -1;
|
|
5239
5710
|
const colonIdx = trimmed.indexOf(":");
|
|
5240
5711
|
if (colonIdx < 1) {
|
|
5241
5712
|
throw new Error(`line ${i + 1}: expected "key: value"`);
|
|
@@ -5244,7 +5715,7 @@ function parseYamlSubset(src) {
|
|
|
5244
5715
|
const rawValue = trimmed.slice(colonIdx + 1).trim();
|
|
5245
5716
|
if (rawValue === "") {
|
|
5246
5717
|
result[key] = [];
|
|
5247
|
-
|
|
5718
|
+
pendingKey = key;
|
|
5248
5719
|
} else {
|
|
5249
5720
|
result[key] = parseInlineValue(rawValue);
|
|
5250
5721
|
}
|
|
@@ -5264,8 +5735,7 @@ function extractTableData(table) {
|
|
|
5264
5735
|
};
|
|
5265
5736
|
}
|
|
5266
5737
|
|
|
5267
|
-
// src/doc/
|
|
5268
|
-
var MEDIA_TEMPLATES = /* @__PURE__ */ new Set(["audio", "video", "media"]);
|
|
5738
|
+
// src/doc/standaloneAnnotation.ts
|
|
5269
5739
|
function parseStandaloneAnnotation(node) {
|
|
5270
5740
|
if (node.type !== "paragraph") return null;
|
|
5271
5741
|
const children = node.children ?? [];
|
|
@@ -5284,6 +5754,12 @@ function parseStandaloneAnnotation(node) {
|
|
|
5284
5754
|
}
|
|
5285
5755
|
return { template, params };
|
|
5286
5756
|
}
|
|
5757
|
+
|
|
5758
|
+
// src/doc/mediaAnnotations.ts
|
|
5759
|
+
var MEDIA_TEMPLATES = /* @__PURE__ */ new Set(["audio", "video", "media"]);
|
|
5760
|
+
function isMediaAnnotationName(name) {
|
|
5761
|
+
return name != null && MEDIA_TEMPLATES.has(name);
|
|
5762
|
+
}
|
|
5287
5763
|
function time(raw) {
|
|
5288
5764
|
if (raw == null) return null;
|
|
5289
5765
|
return parseTimeSeconds(raw);
|
|
@@ -5295,7 +5771,13 @@ function toMediaClip(parsed, id) {
|
|
|
5295
5771
|
if (!src) return null;
|
|
5296
5772
|
const kind = template === "video" ? "video" : "audio";
|
|
5297
5773
|
const anchor = params.anchor === "document" || params.span === "document" ? "document" : "block";
|
|
5298
|
-
const clip = {
|
|
5774
|
+
const clip = {
|
|
5775
|
+
id,
|
|
5776
|
+
src,
|
|
5777
|
+
kind,
|
|
5778
|
+
startAt: time(params.startAt ?? params.startTime) ?? 0,
|
|
5779
|
+
anchor
|
|
5780
|
+
};
|
|
5299
5781
|
const clipStart = time(params.clipStart);
|
|
5300
5782
|
const clipEnd = time(params.clipEnd);
|
|
5301
5783
|
if (clipStart != null) clip.clipStart = clipStart;
|
|
@@ -5323,6 +5805,58 @@ function extractMediaFromContents(contents, makeId) {
|
|
|
5323
5805
|
return { media, documentMedia, remaining };
|
|
5324
5806
|
}
|
|
5325
5807
|
|
|
5808
|
+
// src/doc/annotationBlocks.ts
|
|
5809
|
+
function applyBlockMeta(block, m) {
|
|
5810
|
+
if (m.x != null) block.x = m.x;
|
|
5811
|
+
if (m.y != null) block.y = m.y;
|
|
5812
|
+
if (m.startTime != null) block.startTime = m.startTime;
|
|
5813
|
+
if (m.duration != null) block.duration = m.duration;
|
|
5814
|
+
if (m.connectsTo) block.connectsTo = m.connectsTo;
|
|
5815
|
+
if (m.transition) block.transition = m.transition;
|
|
5816
|
+
}
|
|
5817
|
+
function makeStandaloneBlock(parsed, options) {
|
|
5818
|
+
const rawTemplate = parsed.template;
|
|
5819
|
+
const params = parsed.params;
|
|
5820
|
+
const hasParams = Object.keys(params).length > 0;
|
|
5821
|
+
const block = {
|
|
5822
|
+
id: options.makeId(parsed),
|
|
5823
|
+
startTime: 0,
|
|
5824
|
+
duration: options.defaultDuration,
|
|
5825
|
+
audioSegment: 0,
|
|
5826
|
+
template: resolveTemplateName(rawTemplate),
|
|
5827
|
+
standaloneAnnotation: true,
|
|
5828
|
+
// Keep the raw (un-resolved) name so validation can report the exact
|
|
5829
|
+
// requested name — symmetric with `sourceHeading.templateAnnotation`.
|
|
5830
|
+
sourceAnnotation: { template: rawTemplate, ...hasParams ? { params } : {} },
|
|
5831
|
+
// Only carry a display title when the author supplied one.
|
|
5832
|
+
...params.title ? { title: params.title } : {}
|
|
5833
|
+
};
|
|
5834
|
+
if (hasParams) {
|
|
5835
|
+
block.templateOverrides = params;
|
|
5836
|
+
applyBlockMeta(block, coerceAnnotationValues(params).blockMeta);
|
|
5837
|
+
}
|
|
5838
|
+
return block;
|
|
5839
|
+
}
|
|
5840
|
+
function extractTemplateBlocksFromContents(contents, options) {
|
|
5841
|
+
const leading = [];
|
|
5842
|
+
const blocks = [];
|
|
5843
|
+
let current = null;
|
|
5844
|
+
for (const node of contents ?? []) {
|
|
5845
|
+
const parsed = parseStandaloneAnnotation(node);
|
|
5846
|
+
const isTemplateAnnotation = parsed != null && parsed.template != null && !isMediaAnnotationName(parsed.template);
|
|
5847
|
+
if (isTemplateAnnotation) {
|
|
5848
|
+
const block = makeStandaloneBlock(parsed, options);
|
|
5849
|
+
blocks.push(block);
|
|
5850
|
+
current = block;
|
|
5851
|
+
} else if (current) {
|
|
5852
|
+
(current.contents ?? (current.contents = [])).push(node);
|
|
5853
|
+
} else {
|
|
5854
|
+
leading.push(node);
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
return { leading, blocks };
|
|
5858
|
+
}
|
|
5859
|
+
|
|
5326
5860
|
// src/doc/markdownToDoc.ts
|
|
5327
5861
|
var SLUG_NON_WORD_RE = /[^\w\s-]/g;
|
|
5328
5862
|
var SLUG_SPACES_RE = /[\s_]+/g;
|
|
@@ -5345,9 +5879,15 @@ function createIdGenerator() {
|
|
|
5345
5879
|
generate.reserve = (id) => {
|
|
5346
5880
|
used.set(id, Math.max(used.get(id) ?? 0, 1));
|
|
5347
5881
|
};
|
|
5882
|
+
generate.fromText = (text) => {
|
|
5883
|
+
const base = slugify(text);
|
|
5884
|
+
const count = used.get(base) ?? 0;
|
|
5885
|
+
used.set(base, count + 1);
|
|
5886
|
+
return count === 0 ? base : `${base}-${count + 1}`;
|
|
5887
|
+
};
|
|
5348
5888
|
return generate;
|
|
5349
5889
|
}
|
|
5350
|
-
function
|
|
5890
|
+
function applyBlockMeta2(block, m) {
|
|
5351
5891
|
if (m.x != null) block.x = m.x;
|
|
5352
5892
|
if (m.y != null) block.y = m.y;
|
|
5353
5893
|
if (m.startTime != null) block.startTime = m.startTime;
|
|
@@ -5361,13 +5901,37 @@ function pinnedHeadingMeta(heading) {
|
|
|
5361
5901
|
const fromPandoc = heading.attributes?.blockMeta ?? {};
|
|
5362
5902
|
return { ...fromAnnotation, ...fromPandoc };
|
|
5363
5903
|
}
|
|
5904
|
+
function pinnedMeta(block) {
|
|
5905
|
+
if (block.sourceHeading) return pinnedHeadingMeta(block.sourceHeading);
|
|
5906
|
+
if (block.sourceAnnotation?.params) {
|
|
5907
|
+
return coerceAnnotationValues(block.sourceAnnotation.params).blockMeta;
|
|
5908
|
+
}
|
|
5909
|
+
return {};
|
|
5910
|
+
}
|
|
5911
|
+
function expandStandaloneAnnotations(blocks, makeId, defaultDuration) {
|
|
5912
|
+
const out = [];
|
|
5913
|
+
for (const block of blocks) {
|
|
5914
|
+
if (block.children && block.children.length > 0) {
|
|
5915
|
+
block.children = expandStandaloneAnnotations(block.children, makeId, defaultDuration);
|
|
5916
|
+
}
|
|
5917
|
+
const { leading, blocks: produced } = extractTemplateBlocksFromContents(block.contents, {
|
|
5918
|
+
makeId,
|
|
5919
|
+
defaultDuration
|
|
5920
|
+
});
|
|
5921
|
+
if (produced.length > 0) {
|
|
5922
|
+
block.contents = leading;
|
|
5923
|
+
}
|
|
5924
|
+
out.push(block, ...produced);
|
|
5925
|
+
}
|
|
5926
|
+
return out;
|
|
5927
|
+
}
|
|
5364
5928
|
function markdownToDoc(markdownDoc, options) {
|
|
5365
5929
|
const articleId = options?.articleId ?? "markdown-doc";
|
|
5366
5930
|
const defaultTemplate = options?.defaultTemplate ?? "sectionHeader";
|
|
5367
5931
|
const defaultDuration = options?.defaultDuration ?? 5;
|
|
5368
5932
|
const idGenerator = options?.generateId ? null : createIdGenerator();
|
|
5369
5933
|
const generateId = options?.generateId ?? idGenerator;
|
|
5370
|
-
|
|
5934
|
+
let rootBlocks = [];
|
|
5371
5935
|
const diagnostics = [];
|
|
5372
5936
|
let headingIndex = 0;
|
|
5373
5937
|
const stack = [];
|
|
@@ -5400,12 +5964,12 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5400
5964
|
};
|
|
5401
5965
|
if (annotation?.params) {
|
|
5402
5966
|
block.templateOverrides = annotation.params;
|
|
5403
|
-
|
|
5967
|
+
applyBlockMeta2(block, coerceAnnotationValues(annotation.params).blockMeta);
|
|
5404
5968
|
}
|
|
5405
5969
|
const attrs = heading?.attributes;
|
|
5406
5970
|
if (attrs) {
|
|
5407
5971
|
if (attrs.blockMeta) {
|
|
5408
|
-
|
|
5972
|
+
applyBlockMeta2(block, attrs.blockMeta);
|
|
5409
5973
|
}
|
|
5410
5974
|
if (attrs.classes && attrs.classes.length > 0) {
|
|
5411
5975
|
block.classes = attrs.classes;
|
|
@@ -5471,6 +6035,14 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5471
6035
|
if (rootBlocks.length > 0 && !rootBlocks[0].sourceHeading && (rootBlocks[0].contents?.length ?? 0) === 0 && (rootBlocks[0].children?.length ?? 0) === 0 && !rootBlocks[0].media) {
|
|
5472
6036
|
rootBlocks.shift();
|
|
5473
6037
|
}
|
|
6038
|
+
const makeStandaloneId = (parsed) => {
|
|
6039
|
+
const base = parsed.params.title ?? parsed.template ?? "block";
|
|
6040
|
+
return idGenerator ? idGenerator.fromText(base) : slugify(base);
|
|
6041
|
+
};
|
|
6042
|
+
rootBlocks = expandStandaloneAnnotations(rootBlocks, makeStandaloneId, defaultDuration);
|
|
6043
|
+
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) {
|
|
6044
|
+
rootBlocks.shift();
|
|
6045
|
+
}
|
|
5474
6046
|
const allBlocks = flattenBlocks(rootBlocks);
|
|
5475
6047
|
for (const block of allBlocks) {
|
|
5476
6048
|
applyStructuredData(block, diagnostics);
|
|
@@ -5508,7 +6080,7 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5508
6080
|
const minDuration = 3;
|
|
5509
6081
|
const phrases = [];
|
|
5510
6082
|
for (const block of allBlocks) {
|
|
5511
|
-
if (
|
|
6083
|
+
if (pinnedMeta(block).duration != null) continue;
|
|
5512
6084
|
const bodyText3 = getBlockBodyText2(block);
|
|
5513
6085
|
if (bodyText3.length > 0) {
|
|
5514
6086
|
const estimate = estimateReadingTime(bodyText3);
|
|
@@ -5519,7 +6091,7 @@ function markdownToDoc(markdownDoc, options) {
|
|
|
5519
6091
|
}
|
|
5520
6092
|
let currentTime = 0;
|
|
5521
6093
|
for (const block of allBlocks) {
|
|
5522
|
-
const explicitStart =
|
|
6094
|
+
const explicitStart = pinnedMeta(block).startTime;
|
|
5523
6095
|
if (explicitStart == null) {
|
|
5524
6096
|
block.startTime = currentTime;
|
|
5525
6097
|
}
|
|
@@ -5765,6 +6337,8 @@ function docToMarkdown(doc) {
|
|
|
5765
6337
|
if (block.sourceHeading) {
|
|
5766
6338
|
const heading = ensureAnnotation(block, block.sourceHeading);
|
|
5767
6339
|
children.push(heading);
|
|
6340
|
+
} else if (block.standaloneAnnotation) {
|
|
6341
|
+
children.push(synthesizeAnnotationParagraph(block));
|
|
5768
6342
|
}
|
|
5769
6343
|
if (block.contents) {
|
|
5770
6344
|
children.push(...block.contents);
|
|
@@ -5801,6 +6375,10 @@ function docToMarkdown(doc) {
|
|
|
5801
6375
|
...frontmatter && Object.keys(frontmatter).length > 0 ? { frontmatter } : {}
|
|
5802
6376
|
};
|
|
5803
6377
|
}
|
|
6378
|
+
function synthesizeAnnotationParagraph(block) {
|
|
6379
|
+
const text = serializeAnnotation(block.sourceAnnotation?.template, block.templateOverrides);
|
|
6380
|
+
return { type: "paragraph", children: [{ type: "text", value: text }] };
|
|
6381
|
+
}
|
|
5804
6382
|
function ensureAnnotation(block, heading) {
|
|
5805
6383
|
const attrs = ensureTransitionAttributes(block, heading);
|
|
5806
6384
|
const hasExplicitTemplate = block.template && block.template !== "sectionHeader" && block.autoTemplate !== true;
|
|
@@ -5950,7 +6528,11 @@ function getLayers(block, context = {}) {
|
|
|
5950
6528
|
templateCtx.children = maybeChildren;
|
|
5951
6529
|
}
|
|
5952
6530
|
const { templateData, templateOverrides } = block;
|
|
5953
|
-
const input = templateData || templateOverrides ? {
|
|
6531
|
+
const input = templateData || templateOverrides ? {
|
|
6532
|
+
...block,
|
|
6533
|
+
...templateData,
|
|
6534
|
+
...coerceTemplateParams(block.template, templateOverrides ?? {}).input
|
|
6535
|
+
} : block;
|
|
5954
6536
|
let layers;
|
|
5955
6537
|
try {
|
|
5956
6538
|
const templateFn = templateRegistry[templateName];
|
|
@@ -6215,9 +6797,13 @@ function validateMarkdownDoc(markdownDoc, options) {
|
|
|
6215
6797
|
const diagnostics = [...doc.diagnostics ?? []];
|
|
6216
6798
|
const blocks = flattenBlocks(doc.blocks);
|
|
6217
6799
|
const knownTemplates = collectKnownTemplates(doc, options);
|
|
6800
|
+
const customNames = new Set(doc.customTemplates?.map((t) => t.name) ?? []);
|
|
6218
6801
|
const visit = (block, parent) => {
|
|
6219
6802
|
checkTemplate(block, parent, knownTemplates, diagnostics);
|
|
6220
6803
|
checkAttributeCoercion(block, parent, diagnostics);
|
|
6804
|
+
checkConflictingAnnotationKeys(block, diagnostics);
|
|
6805
|
+
checkTemplateInputs(block, parent, customNames, diagnostics);
|
|
6806
|
+
checkPossibleDataFences(block, diagnostics);
|
|
6221
6807
|
if (isDrawingBlock(block)) checkDrawingConnectors(block, diagnostics);
|
|
6222
6808
|
for (const child of block.children ?? []) visit(child, block);
|
|
6223
6809
|
};
|
|
@@ -6234,6 +6820,7 @@ function validateMarkdownDoc(markdownDoc, options) {
|
|
|
6234
6820
|
diagnostics,
|
|
6235
6821
|
errorCount: diagnostics.filter((d) => d.severity === "error").length,
|
|
6236
6822
|
warningCount: diagnostics.filter((d) => d.severity === "warning").length,
|
|
6823
|
+
infoCount: diagnostics.filter((d) => d.severity === "info").length,
|
|
6237
6824
|
doc
|
|
6238
6825
|
};
|
|
6239
6826
|
}
|
|
@@ -6249,11 +6836,13 @@ function isDrawingBlock(block) {
|
|
|
6249
6836
|
return resolveTemplateName(block.template ?? "") === "drawing";
|
|
6250
6837
|
}
|
|
6251
6838
|
function checkTemplate(block, parent, known, diagnostics) {
|
|
6252
|
-
const requested = block.sourceHeading?.templateAnnotation?.template;
|
|
6839
|
+
const requested = block.sourceHeading?.templateAnnotation?.template ?? block.sourceAnnotation?.template;
|
|
6253
6840
|
if (!requested) return;
|
|
6254
6841
|
if (parent && isDrawingBlock(parent)) {
|
|
6255
6842
|
if (isShapeName(requested)) return;
|
|
6256
|
-
const suggestion2 = nearestName(requested, new Set(SHAPE_NAMES)
|
|
6843
|
+
const suggestion2 = nearestName(requested, new Set(SHAPE_NAMES), {
|
|
6844
|
+
map: resolveTemplateName
|
|
6845
|
+
});
|
|
6257
6846
|
diagnostics.push({
|
|
6258
6847
|
severity: "warning",
|
|
6259
6848
|
code: "unknown-shape",
|
|
@@ -6274,7 +6863,7 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
6274
6863
|
return;
|
|
6275
6864
|
}
|
|
6276
6865
|
if (known.has(requested) || known.has(resolveTemplateName(requested))) return;
|
|
6277
|
-
const suggestion = nearestName(requested, known);
|
|
6866
|
+
const suggestion = nearestName(requested, known, { map: resolveTemplateName });
|
|
6278
6867
|
diagnostics.push({
|
|
6279
6868
|
severity: "warning",
|
|
6280
6869
|
code: "unknown-template",
|
|
@@ -6283,6 +6872,33 @@ function checkTemplate(block, parent, known, diagnostics) {
|
|
|
6283
6872
|
...lineOf2(block)
|
|
6284
6873
|
});
|
|
6285
6874
|
}
|
|
6875
|
+
function checkTemplateInputs(block, parent, customNames, diagnostics) {
|
|
6876
|
+
const requested = block.sourceHeading?.templateAnnotation?.template;
|
|
6877
|
+
if (!requested) return;
|
|
6878
|
+
if (parent && isDrawingBlock(parent)) return;
|
|
6879
|
+
if (isShapeName(requested)) return;
|
|
6880
|
+
const canonical = resolveTemplateName(requested);
|
|
6881
|
+
if (customNames.has(requested) || customNames.has(canonical)) return;
|
|
6882
|
+
const params = block.sourceHeading?.templateAnnotation?.params ?? {};
|
|
6883
|
+
for (const finding of lintTemplateParams(canonical, params)) {
|
|
6884
|
+
if (finding.kind === "missing-input" && isInputSatisfiable(canonical, block, finding.key)) {
|
|
6885
|
+
continue;
|
|
6886
|
+
}
|
|
6887
|
+
diagnostics.push({
|
|
6888
|
+
severity: "warning",
|
|
6889
|
+
code: finding.kind,
|
|
6890
|
+
message: `Block "${block.id}": ${finding.message}`,
|
|
6891
|
+
blockId: block.id,
|
|
6892
|
+
...lineOf2(block)
|
|
6893
|
+
});
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
function isInputSatisfiable(canonical, block, key) {
|
|
6897
|
+
if (block.templateData && key in block.templateData) return true;
|
|
6898
|
+
if (block[key] != null) return true;
|
|
6899
|
+
const derived = deriveTemplateInputs(canonical, block.title ?? "", block.contents);
|
|
6900
|
+
return derived != null && key in derived;
|
|
6901
|
+
}
|
|
6286
6902
|
var NUMERIC_SHAPE_KEYS = ["x", "y", "width", "height", "w", "h", "strokeWidth", "borderRadius"];
|
|
6287
6903
|
function checkAttributeCoercion(block, parent, diagnostics) {
|
|
6288
6904
|
const params = block.sourceHeading?.attributes?.params;
|
|
@@ -6316,6 +6932,23 @@ function checkAttributeCoercion(block, parent, diagnostics) {
|
|
|
6316
6932
|
}
|
|
6317
6933
|
}
|
|
6318
6934
|
}
|
|
6935
|
+
function checkConflictingAnnotationKeys(block, diagnostics) {
|
|
6936
|
+
const annotationParams = block.sourceHeading?.templateAnnotation?.params;
|
|
6937
|
+
const pandocParams = block.sourceHeading?.attributes?.params;
|
|
6938
|
+
if (!annotationParams || !pandocParams) return;
|
|
6939
|
+
for (const key of Object.keys(KNOWN_BLOCK_META_KEYS)) {
|
|
6940
|
+
const fromAnnotation = annotationParams[key];
|
|
6941
|
+
const fromPandoc = pandocParams[key];
|
|
6942
|
+
if (fromAnnotation == null || fromPandoc == null || fromAnnotation === fromPandoc) continue;
|
|
6943
|
+
diagnostics.push({
|
|
6944
|
+
severity: "info",
|
|
6945
|
+
code: "conflicting-annotation-key",
|
|
6946
|
+
message: `"${key}" is set in both {[\u2026]} (${fromAnnotation}) and {\u2026} (${fromPandoc}) \u2014 the {\u2026} value wins.`,
|
|
6947
|
+
blockId: block.id,
|
|
6948
|
+
...lineOf2(block)
|
|
6949
|
+
});
|
|
6950
|
+
}
|
|
6951
|
+
}
|
|
6319
6952
|
function checkDrawingConnectors(block, diagnostics) {
|
|
6320
6953
|
const children = block.children ?? [];
|
|
6321
6954
|
const ids = new Set(children.map((c) => c.id));
|
|
@@ -6339,9 +6972,28 @@ function checkDrawingConnectors(block, diagnostics) {
|
|
|
6339
6972
|
}
|
|
6340
6973
|
}
|
|
6341
6974
|
}
|
|
6975
|
+
function checkPossibleDataFences(block, diagnostics) {
|
|
6976
|
+
const annotated = !!(block.sourceHeading?.templateAnnotation?.template || block.sourceAnnotation?.template);
|
|
6977
|
+
if (!annotated) return;
|
|
6978
|
+
for (const node of block.contents ?? []) {
|
|
6979
|
+
if (node.type !== "code") continue;
|
|
6980
|
+
const lang = node.lang?.toLowerCase();
|
|
6981
|
+
if (lang !== "json" && lang !== "yaml" && lang !== "yml") continue;
|
|
6982
|
+
if (isDataFence(node)) continue;
|
|
6983
|
+
const result = parseDataFence(node);
|
|
6984
|
+
if (!result.data || Object.keys(result.data).length === 0) continue;
|
|
6985
|
+
diagnostics.push({
|
|
6986
|
+
severity: "info",
|
|
6987
|
+
code: "possible-data-fence",
|
|
6988
|
+
message: `This ${lang} block renders as code \u2014 add "data" to the fence (\`\`\`${lang} data) if it's meant as template input.`,
|
|
6989
|
+
blockId: block.id,
|
|
6990
|
+
...node.position ? { line: node.position.start.line } : lineOf2(block)
|
|
6991
|
+
});
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6342
6994
|
var ANNOTATION_LIKE_RE = /\{\[/;
|
|
6343
6995
|
function checkUnrecognizedAnnotations(markdownDoc, diagnostics) {
|
|
6344
|
-
const walk = (node, headingLine) => {
|
|
6996
|
+
const walk = (node, headingLine, topLevel) => {
|
|
6345
6997
|
if (node.type === "heading") {
|
|
6346
6998
|
const heading = node;
|
|
6347
6999
|
const text = extractPlainText(heading);
|
|
@@ -6355,22 +7007,26 @@ function checkUnrecognizedAnnotations(markdownDoc, diagnostics) {
|
|
|
6355
7007
|
}
|
|
6356
7008
|
return;
|
|
6357
7009
|
}
|
|
7010
|
+
if (node.type === "paragraph" && topLevel) {
|
|
7011
|
+
const parsed = parseStandaloneAnnotation(node);
|
|
7012
|
+
if (parsed && parsed.template != null) return;
|
|
7013
|
+
}
|
|
6358
7014
|
if (node.type === "text" && ANNOTATION_LIKE_RE.test(node.value)) {
|
|
6359
7015
|
diagnostics.push({
|
|
6360
7016
|
severity: "warning",
|
|
6361
7017
|
code: "unparsed-annotation",
|
|
6362
|
-
message: `Body text contains "{[\u2026]}" \u2014 template annotations are
|
|
7018
|
+
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
7019
|
...node.position ? { line: node.position.start.line } : headingLine != null ? { line: headingLine } : {}
|
|
6364
7020
|
});
|
|
6365
7021
|
return;
|
|
6366
7022
|
}
|
|
6367
7023
|
if (node.type === "code") return;
|
|
6368
7024
|
for (const child of getChildren(node)) {
|
|
6369
|
-
walk(child, headingLine);
|
|
7025
|
+
walk(child, headingLine, false);
|
|
6370
7026
|
}
|
|
6371
7027
|
};
|
|
6372
7028
|
for (const child of markdownDoc.children) {
|
|
6373
|
-
walk(child, child.position?.start.line);
|
|
7029
|
+
walk(child, child.position?.start.line, true);
|
|
6374
7030
|
}
|
|
6375
7031
|
}
|
|
6376
7032
|
function normalizeAssetCheck(assets) {
|
|
@@ -6433,41 +7089,6 @@ function walkHtmlImages(nodes, report) {
|
|
|
6433
7089
|
walkHtmlImages(node.children, report);
|
|
6434
7090
|
}
|
|
6435
7091
|
}
|
|
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
7092
|
function lineOf2(block) {
|
|
6472
7093
|
const line = block.sourceHeading?.position?.start.line;
|
|
6473
7094
|
return line != null ? { line } : {};
|
|
@@ -6484,6 +7105,10 @@ export {
|
|
|
6484
7105
|
wrapWithPersistentLayers,
|
|
6485
7106
|
themeWantsAmbientMotion,
|
|
6486
7107
|
applyRenderStyleToLayers,
|
|
7108
|
+
BASE_INPUT_DESCRIPTORS,
|
|
7109
|
+
TEMPLATE_INPUT_DESCRIPTORS,
|
|
7110
|
+
coerceTemplateParams,
|
|
7111
|
+
lintTemplateParams,
|
|
6487
7112
|
TEMPLATE_METADATA,
|
|
6488
7113
|
resolveColorScheme,
|
|
6489
7114
|
getThemeFont,
|
|
@@ -6562,6 +7187,7 @@ export {
|
|
|
6562
7187
|
writeCustomThemesToFrontmatter,
|
|
6563
7188
|
isDataFence,
|
|
6564
7189
|
parseDataFence,
|
|
7190
|
+
replaceDataFence,
|
|
6565
7191
|
parseYamlSubset,
|
|
6566
7192
|
findFirstTable,
|
|
6567
7193
|
extractTableData,
|
|
@@ -6579,4 +7205,4 @@ export {
|
|
|
6579
7205
|
validateMarkdownSource,
|
|
6580
7206
|
validateMarkdownDoc
|
|
6581
7207
|
};
|
|
6582
|
-
//# sourceMappingURL=chunk-
|
|
7208
|
+
//# sourceMappingURL=chunk-LH7I6SH7.js.map
|