@mirrordown/core 0.1.0 → 0.1.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/CHANGELOG.md +24 -0
- package/dist/index.d.mts +68 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +531 -167
- package/dist/index.mjs.map +1 -1
- package/dist/mermaid-pele-BMyyDozN.mjs +92 -0
- package/dist/mermaid-pele-BMyyDozN.mjs.map +1 -0
- package/dist/tab-icons.d.mts +54 -0
- package/dist/tab-icons.d.mts.map +1 -0
- package/dist/tab-icons.mjs +299 -0
- package/dist/tab-icons.mjs.map +1 -0
- package/package.json +6 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as mermaidSource, i as mermaidIdPrefixes, n as mermaidDiagram, o as mermaidSvg, r as mermaidFailure, t as hastAttributes } from "./mermaid-pele-BMyyDozN.mjs";
|
|
1
2
|
// src/escapes.ts
|
|
2
3
|
const ASCII_PUNCTUATION = /^[!-/:-@[-`{-~]$/;
|
|
3
4
|
const ENTITY = /^&(?:#\d+|#x[\da-f]+|[a-z][\da-z]*);/i;
|
|
@@ -133,6 +134,8 @@ const ICONS = {
|
|
|
133
134
|
};
|
|
134
135
|
// src/github-alerts/index.ts
|
|
135
136
|
const MARKER_RE = /^\[!([^\]]+)\]([+-])?/;
|
|
137
|
+
/** Whether a source line opens an alert (`> [!NOTE]`), after its `>` markers and indentation, whether or not the alerts plugin reads it: a block that takes the quote after it as more of its content (a step body) leaves an alert be. */
|
|
138
|
+
const isAlertMarkerLine = (line) => MARKER_RE.test(line.replace(/^[ >]*/, ``));
|
|
136
139
|
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
137
140
|
/** Resolve alert keywords, titles and icons the same way in every engine. */
|
|
138
141
|
const createAlertResolver = (options = {}) => {
|
|
@@ -244,9 +247,9 @@ const INLINE_PARENTS = /* @__PURE__ */ new Set([
|
|
|
244
247
|
`subscript`,
|
|
245
248
|
`superscript`
|
|
246
249
|
]);
|
|
247
|
-
const WHITESPACE$
|
|
250
|
+
const WHITESPACE$3 = /^[\t\n\v\f\r \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000]$/u;
|
|
248
251
|
const PUNCTUATION$1 = /^[\p{P}\p{S}]$/u;
|
|
249
|
-
const classify$1 = (char) => WHITESPACE$
|
|
252
|
+
const classify$1 = (char) => WHITESPACE$3.test(char) ? `space` : PUNCTUATION$1.test(char) ? `punct` : `other`;
|
|
250
253
|
/**
|
|
251
254
|
* markdown-it's `balance_pairs` for a single marker. Every delimiter has `length: 0`, as in markdown-it-mark, so the emphasis "rule of 3" never applies.
|
|
252
255
|
*/
|
|
@@ -484,8 +487,8 @@ const tokenType = (type) => type;
|
|
|
484
487
|
const temporary = (type) => tokenType(`${type}SequenceTemporary`);
|
|
485
488
|
const scriptOf = (tokenType) => Object.values(SCRIPTS).find(({ type }) => temporary(type) === tokenType);
|
|
486
489
|
const PUNCTUATION = /\p{P}|\p{S}/u;
|
|
487
|
-
const WHITESPACE$
|
|
488
|
-
const classify = (code) => code === null || code < 0 || code === 32 || WHITESPACE$
|
|
490
|
+
const WHITESPACE$2 = /\s/;
|
|
491
|
+
const classify = (code) => code === null || code < 0 || code === 32 || WHITESPACE$2.test(String.fromCharCode(code)) ? 1 : PUNCTUATION.test(String.fromCharCode(code)) ? 2 : void 0;
|
|
489
492
|
const splice = (list, start, remove, items) => {
|
|
490
493
|
list.splice(start, remove);
|
|
491
494
|
for (let i = 0; i < items.length; i += 1e4) list.splice(start + i, 0, ...items.slice(i, i + 1e4));
|
|
@@ -603,156 +606,6 @@ const scriptSyntax = (marker) => {
|
|
|
603
606
|
insideSpan: { null: [construct] }
|
|
604
607
|
};
|
|
605
608
|
};
|
|
606
|
-
// src/hast.ts
|
|
607
|
-
const WHITESPACE = /^[ \t\n\f\r]*$/;
|
|
608
|
-
const isWhitespaceText = (node) => node.type === `text` && WHITESPACE.test(node.value ?? ``);
|
|
609
|
-
/** A `<p>` with no children, or only whitespace text: squeeze-paragraphs removes it. */
|
|
610
|
-
const isEmptyParagraph = (node) => node.tagName === `p` && (node.children ?? []).every(isWhitespaceText);
|
|
611
|
-
/**
|
|
612
|
-
* A `<p>` whose content is only images (each possibly inside a link or a lightbox button) and whitespace: unwrap-images replaces it with its children.
|
|
613
|
-
*/
|
|
614
|
-
const isImageParagraph = (node) => {
|
|
615
|
-
const scan = (parent, inLink) => {
|
|
616
|
-
let result = `empty`;
|
|
617
|
-
for (const child of parent.children ?? []) {
|
|
618
|
-
if (isWhitespaceText(child)) continue;
|
|
619
|
-
if (child.type !== `element`) return `other`;
|
|
620
|
-
if (child.tagName === `img`) {
|
|
621
|
-
result = `image`;
|
|
622
|
-
continue;
|
|
623
|
-
}
|
|
624
|
-
if (!inLink && (child.tagName === `a` || child.tagName === `button`)) {
|
|
625
|
-
const inner = scan(child, true);
|
|
626
|
-
if (inner === `other`) return `other`;
|
|
627
|
-
if (inner === `image`) result = `image`;
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
return `other`;
|
|
631
|
-
}
|
|
632
|
-
return result;
|
|
633
|
-
};
|
|
634
|
-
return node.tagName === `p` && scan(node, false) === `image`;
|
|
635
|
-
};
|
|
636
|
-
/** The rank of an `h1`–`h6` element, or `undefined`. */
|
|
637
|
-
const headingRank = (node) => {
|
|
638
|
-
if (node.type !== `element`) return void 0;
|
|
639
|
-
const match = /^h([1-6])$/i.exec(node.tagName ?? ``);
|
|
640
|
-
return match ? Number(match[1]) : void 0;
|
|
641
|
-
};
|
|
642
|
-
/**
|
|
643
|
-
* Wrap heading-delimited runs of `nodes` into `<section data-depth>` elements, nesting deeper headings inside shallower ones. Content before the first heading stays unwrapped.
|
|
644
|
-
*/
|
|
645
|
-
const sectionize = (nodes, section, floor = 0) => {
|
|
646
|
-
let minRank = Infinity;
|
|
647
|
-
for (const node of nodes) {
|
|
648
|
-
const rank = headingRank(node);
|
|
649
|
-
if (rank !== void 0 && rank > floor && rank < minRank) minRank = rank;
|
|
650
|
-
}
|
|
651
|
-
if (minRank === Infinity) return [...nodes];
|
|
652
|
-
const result = [];
|
|
653
|
-
let i = 0;
|
|
654
|
-
for (; i < nodes.length; i++) {
|
|
655
|
-
const node = nodes[i];
|
|
656
|
-
if (!node || headingRank(node) === minRank) break;
|
|
657
|
-
result.push(node);
|
|
658
|
-
}
|
|
659
|
-
while (i < nodes.length) {
|
|
660
|
-
const start = i;
|
|
661
|
-
for (i++; i < nodes.length; i++) {
|
|
662
|
-
const node = nodes[i];
|
|
663
|
-
if (!node || headingRank(node) === minRank) break;
|
|
664
|
-
}
|
|
665
|
-
result.push(section(minRank, sectionize(nodes.slice(start, i), section, minRank)));
|
|
666
|
-
}
|
|
667
|
-
return result;
|
|
668
|
-
};
|
|
669
|
-
/**
|
|
670
|
-
* The lightbox opt-in: an image directly after a text run that ends in a `!` at a boundary (start of the run or after whitespace), which is what `!` leaves behind.
|
|
671
|
-
*/
|
|
672
|
-
const LIGHTBOX_MARKER = /(?:^|\s)!$/;
|
|
673
|
-
/**
|
|
674
|
-
* A short, stable lightbox id from an image src (FNV-1a, base36): the same image always gets the same id, so repeated references share one dialog.
|
|
675
|
-
*/
|
|
676
|
-
const lightboxId = (src) => {
|
|
677
|
-
let hash = 2166136261;
|
|
678
|
-
for (let i = 0; i < src.length; i += 1) {
|
|
679
|
-
hash ^= src.charCodeAt(i);
|
|
680
|
-
hash = Math.imul(hash, 16777619);
|
|
681
|
-
}
|
|
682
|
-
return `markdown-lightbox-${(hash >>> 0).toString(36)}`;
|
|
683
|
-
};
|
|
684
|
-
/** The button that replaces an opted-in image and opens its dialog. */
|
|
685
|
-
const lightboxTrigger = (id, image) => ({
|
|
686
|
-
type: `element`,
|
|
687
|
-
tagName: `button`,
|
|
688
|
-
properties: {
|
|
689
|
-
type: `button`,
|
|
690
|
-
className: [`markdown-lightbox-trigger`],
|
|
691
|
-
command: `show-modal`,
|
|
692
|
-
commandfor: id
|
|
693
|
-
},
|
|
694
|
-
children: [image]
|
|
695
|
-
});
|
|
696
|
-
/** The modal dialog, appended at the document end, showing the image full size. */
|
|
697
|
-
const lightboxDialog = (id, image) => ({
|
|
698
|
-
type: `element`,
|
|
699
|
-
tagName: `dialog`,
|
|
700
|
-
properties: {
|
|
701
|
-
id,
|
|
702
|
-
className: [`markdown-lightbox`],
|
|
703
|
-
closedby: `any`
|
|
704
|
-
},
|
|
705
|
-
children: [{
|
|
706
|
-
type: `element`,
|
|
707
|
-
tagName: `button`,
|
|
708
|
-
properties: {
|
|
709
|
-
type: `button`,
|
|
710
|
-
className: [`markdown-lightbox-close`],
|
|
711
|
-
command: `close`,
|
|
712
|
-
commandfor: id,
|
|
713
|
-
ariaLabel: `Close image`
|
|
714
|
-
},
|
|
715
|
-
children: [image]
|
|
716
|
-
}]
|
|
717
|
-
});
|
|
718
|
-
// src/abbr.ts
|
|
719
|
-
const DEFINITION = /^\*\[([^[\]]+)\]:\s*(.+)$/;
|
|
720
|
-
const REGEX_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
721
|
-
/**
|
|
722
|
-
* The `[term, title]` pairs a paragraph's text defines, when every line of it is a `*[term]: title` definition; otherwise `undefined` (the paragraph is ordinary text).
|
|
723
|
-
*/
|
|
724
|
-
const parseAbbreviations = (text) => {
|
|
725
|
-
const definitions = [];
|
|
726
|
-
for (const line of text.split(`\n`)) {
|
|
727
|
-
const match = DEFINITION.exec(line);
|
|
728
|
-
if (!match) return void 0;
|
|
729
|
-
definitions.push([match[1] ?? ``, (match[2] ?? ``).trim()]);
|
|
730
|
-
}
|
|
731
|
-
return definitions;
|
|
732
|
-
};
|
|
733
|
-
/**
|
|
734
|
-
* Build the scan for a document's abbreviations. Terms match on word boundaries, longest first; the scan returns the pieces of a text value, or `undefined` when it holds no term.
|
|
735
|
-
*/
|
|
736
|
-
const createAbbreviationScan = (abbreviations) => {
|
|
737
|
-
const terms = [...abbreviations.keys()].sort((a, b) => b.length - a.length).map((term) => term.replace(REGEX_SPECIAL, `\\$&`));
|
|
738
|
-
const pattern = new RegExp(`\\b(${terms.join(`|`)})\\b`, `g`);
|
|
739
|
-
return (value) => {
|
|
740
|
-
const parts = [];
|
|
741
|
-
let last = 0;
|
|
742
|
-
for (const match of value.matchAll(pattern)) {
|
|
743
|
-
const term = match[1] ?? ``;
|
|
744
|
-
if (match.index > last) parts.push({ text: value.slice(last, match.index) });
|
|
745
|
-
parts.push({
|
|
746
|
-
term,
|
|
747
|
-
title: abbreviations.get(term) ?? ``
|
|
748
|
-
});
|
|
749
|
-
last = match.index + term.length;
|
|
750
|
-
}
|
|
751
|
-
if (parts.length === 0) return void 0;
|
|
752
|
-
if (last < value.length) parts.push({ text: value.slice(last) });
|
|
753
|
-
return parts;
|
|
754
|
-
};
|
|
755
|
-
};
|
|
756
609
|
// src/cut-attributes.ts
|
|
757
610
|
/** Paragraphs recognition rebuilt from lines, as it does where a tab or step header lazily continued them. */
|
|
758
611
|
const rebuiltParagraphs = /* @__PURE__ */ new WeakSet();
|
|
@@ -955,6 +808,492 @@ const rebuildLazyLines = (lines, level) => {
|
|
|
955
808
|
}
|
|
956
809
|
return blocks;
|
|
957
810
|
};
|
|
811
|
+
/** Blocks whose lines carry one more `>` marker than the block around them: a blockquote (or an alert, Sätteri's node for one) and a tab panel or step body. */
|
|
812
|
+
const MARKED_BLOCKS = /* @__PURE__ */ new Set([
|
|
813
|
+
`blockquote`,
|
|
814
|
+
`alert`,
|
|
815
|
+
`tabsItem`,
|
|
816
|
+
`stepsItem`
|
|
817
|
+
]);
|
|
818
|
+
/** Blocks a line can lazily continue into by its indentation rather than its markers: a list item or a definition. */
|
|
819
|
+
const INDENTED_BLOCKS = /* @__PURE__ */ new Set([
|
|
820
|
+
`listItem`,
|
|
821
|
+
`defListDescription`,
|
|
822
|
+
`descriptionDetails`
|
|
823
|
+
]);
|
|
824
|
+
/**
|
|
825
|
+
* A paragraph split before its first line (after its first) that `opens` a group, written at the paragraph's level (`quotes` `>` markers), as markdown-it's block rules interrupt a paragraph; `null` when there's none. Attributes ending the paragraph go with the part they end.
|
|
826
|
+
*/
|
|
827
|
+
const splitAtOpeningLine = (para, sourceLines, quotes, opens) => {
|
|
828
|
+
const start = para.position?.start.line;
|
|
829
|
+
if (start === void 0) return null;
|
|
830
|
+
const lines = paraToLines(para, sourceLines);
|
|
831
|
+
const at = lines.findIndex((line, index) => {
|
|
832
|
+
const first = line[0];
|
|
833
|
+
return index > 0 && first?.type === `text` && opens(first.value) && markerCount(sourceLines[start - 1 + index] ?? ``) === quotes;
|
|
834
|
+
});
|
|
835
|
+
if (at === -1) return null;
|
|
836
|
+
const head = linesToParagraph(lines.slice(0, at));
|
|
837
|
+
const tail = linesToParagraph(lines.slice(at));
|
|
838
|
+
if (!head || !tail) return null;
|
|
839
|
+
if (para.data) tail.data = para.data;
|
|
840
|
+
return [head, tail];
|
|
841
|
+
};
|
|
842
|
+
const lastParagraph = (node) => {
|
|
843
|
+
const last = node?.children?.at(-1);
|
|
844
|
+
if (!last) return void 0;
|
|
845
|
+
if (last.type === `paragraph`) return last;
|
|
846
|
+
return lastParagraph(last);
|
|
847
|
+
};
|
|
848
|
+
/**
|
|
849
|
+
* End each group of `listType` (tab groups or steps) where its last panel's `>` markers stop, as markdown-it reads a panel: a line CommonMark lazily continued into the panel's last paragraph, written with fewer markers than the panel's own lines, starts a paragraph after the group, with the lines after it. Groups nested in a panel are ended first, so a line that leaves an outer panel too moves on past its group. The paragraph's attributes (Sätteri reads them off its end) go with its last line.
|
|
850
|
+
*/
|
|
851
|
+
const endGroupsAtMarkers = (root, sourceLines, listType) => {
|
|
852
|
+
const visit = (node, quotes) => {
|
|
853
|
+
const children = node.children;
|
|
854
|
+
if (!children) return;
|
|
855
|
+
for (let i = 0; i < children.length; i++) {
|
|
856
|
+
const child = children[i];
|
|
857
|
+
visit(child, quotes + (MARKED_BLOCKS.has(child.type) ? 1 : 0));
|
|
858
|
+
if (child.type !== listType) continue;
|
|
859
|
+
const para = lastParagraph(child.children?.at(-1));
|
|
860
|
+
const start = para?.position?.start.line;
|
|
861
|
+
if (!para || start === void 0) continue;
|
|
862
|
+
const lines = paraToLines(para, sourceLines);
|
|
863
|
+
const outside = lines.findIndex((_, index) => index > 0 && markerCount(sourceLines[start - 1 + index] ?? ``) < quotes + 1);
|
|
864
|
+
if (outside === -1) continue;
|
|
865
|
+
const kept = linesToParagraph(lines.slice(0, outside));
|
|
866
|
+
const moved = linesToParagraph(lines.slice(outside));
|
|
867
|
+
if (!kept || !moved) continue;
|
|
868
|
+
para.children = kept.children;
|
|
869
|
+
const data = para.data;
|
|
870
|
+
if (data?.hProperties) {
|
|
871
|
+
const { hProperties, ...rest } = data;
|
|
872
|
+
moved.data = { hProperties };
|
|
873
|
+
para.data = rest;
|
|
874
|
+
}
|
|
875
|
+
children.splice(i + 1, 0, moved);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
visit(root, 0);
|
|
879
|
+
};
|
|
880
|
+
// src/line-containers.ts
|
|
881
|
+
/**
|
|
882
|
+
* How many of `containers`, outermost first, a line is written in. Tabs count as one column.
|
|
883
|
+
*/
|
|
884
|
+
const containersKept = (line, containers) => {
|
|
885
|
+
const prefix = /^[ \t>]*/.exec(line)?.[0] ?? ``;
|
|
886
|
+
const markers = prefix.split(`>`).length - 1;
|
|
887
|
+
let kept = 0;
|
|
888
|
+
for (const container of containers) {
|
|
889
|
+
if (container.kind === `quote` ? markers < container.markers : prefix.length < container.column) break;
|
|
890
|
+
kept++;
|
|
891
|
+
}
|
|
892
|
+
return kept;
|
|
893
|
+
};
|
|
894
|
+
/**
|
|
895
|
+
* The columns the content of the list items opened on a line starts at, in the order they open, as CommonMark reads a list marker: up to four spaces after it belong to the marker, more start indented content one column after.
|
|
896
|
+
*/
|
|
897
|
+
const itemColumns = (line) => {
|
|
898
|
+
const columns = [];
|
|
899
|
+
let pos = 0;
|
|
900
|
+
for (;;) {
|
|
901
|
+
while (line[pos] === ` ` || line[pos] === `\t`) pos++;
|
|
902
|
+
if (line[pos] === `>`) {
|
|
903
|
+
pos++;
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
const marker = /^(?:[-+*]|\d{1,9}[.)])(?=[ \t]|$)/.exec(line.slice(pos));
|
|
907
|
+
if (!marker) return columns;
|
|
908
|
+
const end = pos + marker[0].length;
|
|
909
|
+
let spaces = 0;
|
|
910
|
+
while (line[end + spaces] === ` ` || line[end + spaces] === `\t`) spaces++;
|
|
911
|
+
const column = end + spaces === line.length || spaces > 4 ? end + 1 : end + spaces;
|
|
912
|
+
columns.push(column);
|
|
913
|
+
if (spaces > 4) return columns;
|
|
914
|
+
pos = column;
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
// src/caption.ts
|
|
918
|
+
/** The class of the `<figure>` a caption wraps its block in. */
|
|
919
|
+
const CAPTION_CLASS = "markdown-caption";
|
|
920
|
+
/** Whether a source line is a caption line: `~:`, whitespace and the caption's first word after its `>` markers and indentation. An escaped `\~:` isn't one, nor is a marker with nothing after it. */
|
|
921
|
+
const isCaptionLine = (line) => /^~:[ \t]+\S/.test(line.replace(/^[ \t>]*/, ``));
|
|
922
|
+
/** Whether text starts with a caption marker, as a parser gives a caption line's text (inline markdown may follow in other nodes). */
|
|
923
|
+
const startsCaption = (text) => /^~:(?:[ \t]|$)/.test(text);
|
|
924
|
+
/** The text after a caption marker. */
|
|
925
|
+
const stripCaptionMarker = (text) => text.replace(/^~:[ \t]*/, ``);
|
|
926
|
+
/**
|
|
927
|
+
* Blocks a caption doesn't attach to, by mdast (and custom node) type: a caption after them stays text. Headings, thematic breaks, raw HTML, definitions and front matter, another caption's figure, and the parts of containers that aren't blocks of their own (titles, labels, terms).
|
|
928
|
+
*/
|
|
929
|
+
const UNCAPTIONED = /* @__PURE__ */ new Set([
|
|
930
|
+
`heading`,
|
|
931
|
+
`thematicBreak`,
|
|
932
|
+
`html`,
|
|
933
|
+
`definition`,
|
|
934
|
+
`footnoteDefinition`,
|
|
935
|
+
`yaml`,
|
|
936
|
+
`toml`,
|
|
937
|
+
`figure`,
|
|
938
|
+
`alertTitle`,
|
|
939
|
+
`stepsTitle`,
|
|
940
|
+
`tabsLabel`,
|
|
941
|
+
`tabsLabels`,
|
|
942
|
+
`tabsInput`,
|
|
943
|
+
`defListTerm`,
|
|
944
|
+
`descriptionTerm`
|
|
945
|
+
]);
|
|
946
|
+
/** The properties of a caption's `<figure>`: its class, then the attributes written at the end of the caption (a class joins it). */
|
|
947
|
+
const figureProperties = (author) => {
|
|
948
|
+
const { class: extraClass, className: extraClassName, ...rest } = author ?? {};
|
|
949
|
+
const extra = [extraClass, extraClassName].flat().flatMap((value) => typeof value === `string` ? value.split(` `) : []).filter((value) => value !== ``);
|
|
950
|
+
return {
|
|
951
|
+
className: [CAPTION_CLASS, ...extra],
|
|
952
|
+
...rest
|
|
953
|
+
};
|
|
954
|
+
};
|
|
955
|
+
const WHITESPACE$1 = /^\s*$/;
|
|
956
|
+
const onlyImages = (children) => children.some((child) => child.type !== `text`) && children.every((child, index) => child.type === `image` || child.type === `text` && (WHITESPACE$1.test(child.value ?? ``) || /^\s*!$/.test(child.value ?? ``) && children[index + 1]?.type === `image`) || child.type === `link` && (child.children ?? []).length > 0 && (child.children ?? []).every((inner) => inner.type === `image`));
|
|
957
|
+
const figure = (content, caption, author) => ({
|
|
958
|
+
type: `figure`,
|
|
959
|
+
data: {
|
|
960
|
+
hName: `figure`,
|
|
961
|
+
hProperties: figureProperties(author)
|
|
962
|
+
},
|
|
963
|
+
children: [...content, {
|
|
964
|
+
type: `figcaption`,
|
|
965
|
+
data: { hName: `figcaption` },
|
|
966
|
+
children: caption
|
|
967
|
+
}]
|
|
968
|
+
});
|
|
969
|
+
const captionContent = (lines) => {
|
|
970
|
+
const first = lines[0]?.[0];
|
|
971
|
+
if (first?.type === `text`) {
|
|
972
|
+
const value = stripCaptionMarker(first.value);
|
|
973
|
+
lines[0] = value ? [{
|
|
974
|
+
...first,
|
|
975
|
+
value
|
|
976
|
+
}, ...lines[0].slice(1)] : lines[0].slice(1);
|
|
977
|
+
}
|
|
978
|
+
return linesToParagraph(lines)?.children ?? [];
|
|
979
|
+
};
|
|
980
|
+
const takeProperties = (node) => {
|
|
981
|
+
const data = node.data;
|
|
982
|
+
if (!data?.hProperties) return void 0;
|
|
983
|
+
const { hProperties, ...rest } = data;
|
|
984
|
+
node.data = rest;
|
|
985
|
+
return hProperties;
|
|
986
|
+
};
|
|
987
|
+
/**
|
|
988
|
+
* Wrap each captioned block of an mdast tree, with its caption, in a `figure` node (`hName: "figure"`, class `markdown-caption`) whose last child is a `figcaption` node. `source` is the document the tree was parsed from: caption lines are found, and placed, by their source lines.
|
|
989
|
+
*/
|
|
990
|
+
const processCaptions = (root, source) => {
|
|
991
|
+
if (!source.includes(`~:`)) return;
|
|
992
|
+
const sourceLines = source.split(/\r?\n/);
|
|
993
|
+
const parents = /* @__PURE__ */ new Map();
|
|
994
|
+
const paragraphs = [];
|
|
995
|
+
const tables = [];
|
|
996
|
+
const walk = (node) => {
|
|
997
|
+
if (node.type === `paragraph`) paragraphs.push(node);
|
|
998
|
+
else if (node.type === `table`) tables.push(node);
|
|
999
|
+
for (const child of node.children ?? []) {
|
|
1000
|
+
parents.set(child, node);
|
|
1001
|
+
walk(child);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
walk(root);
|
|
1005
|
+
const replace = (node, replacement) => {
|
|
1006
|
+
const parent = parents.get(node);
|
|
1007
|
+
const siblings = parent?.children;
|
|
1008
|
+
if (!parent || !siblings) return;
|
|
1009
|
+
const index = siblings.indexOf(node);
|
|
1010
|
+
if (index === -1) return;
|
|
1011
|
+
if (replacement) {
|
|
1012
|
+
siblings.splice(index, 1, replacement);
|
|
1013
|
+
parents.set(replacement, parent);
|
|
1014
|
+
parents.set(node, replacement);
|
|
1015
|
+
} else siblings.splice(index, 1);
|
|
1016
|
+
};
|
|
1017
|
+
const captioned = (node) => parents.get(node)?.type === `figure`;
|
|
1018
|
+
const ancestorsOf = (node) => {
|
|
1019
|
+
const ancestors = [];
|
|
1020
|
+
for (let p = parents.get(node); p; p = parents.get(p)) ancestors.unshift(p);
|
|
1021
|
+
return ancestors;
|
|
1022
|
+
};
|
|
1023
|
+
const containersOf = (ancestors) => {
|
|
1024
|
+
const containers = [];
|
|
1025
|
+
const nearest = (index, type) => ancestors.slice(0, index).reverse().find((a) => a.type === type);
|
|
1026
|
+
let quotes = 0;
|
|
1027
|
+
for (const [index, ancestor] of ancestors.entries()) {
|
|
1028
|
+
const quote = (target) => {
|
|
1029
|
+
containers.push({
|
|
1030
|
+
target: target ?? ancestor,
|
|
1031
|
+
line: {
|
|
1032
|
+
kind: `quote`,
|
|
1033
|
+
markers: ++quotes
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
};
|
|
1037
|
+
switch (ancestor.type) {
|
|
1038
|
+
case `blockquote`:
|
|
1039
|
+
case `alert`:
|
|
1040
|
+
quote(ancestor);
|
|
1041
|
+
break;
|
|
1042
|
+
case `tabsItem`:
|
|
1043
|
+
case `tabsPanel`:
|
|
1044
|
+
quote(nearest(index, `tabsList`));
|
|
1045
|
+
break;
|
|
1046
|
+
case `stepsItem`:
|
|
1047
|
+
if (!(ancestor.children ?? []).some((c) => c.type === `stepsBody`)) quote(nearest(index, `stepsList`));
|
|
1048
|
+
break;
|
|
1049
|
+
case `stepsBody`:
|
|
1050
|
+
quote(nearest(index, `stepsList`));
|
|
1051
|
+
break;
|
|
1052
|
+
case `listItem`:
|
|
1053
|
+
case `defListDescription`:
|
|
1054
|
+
case `descriptionDetails`: {
|
|
1055
|
+
const column = ancestor.children?.[0]?.position?.start.column;
|
|
1056
|
+
if (column === void 0) return void 0;
|
|
1057
|
+
containers.push({
|
|
1058
|
+
target: ancestors[index - 1] ?? ancestor,
|
|
1059
|
+
line: {
|
|
1060
|
+
kind: `item`,
|
|
1061
|
+
column: column - 1
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return containers;
|
|
1069
|
+
};
|
|
1070
|
+
for (const table of tables) {
|
|
1071
|
+
const rows = table.children ?? [];
|
|
1072
|
+
const first = rows.findIndex((row, index) => {
|
|
1073
|
+
const line = row.position?.start.line;
|
|
1074
|
+
const text = row.children?.[0]?.children?.[0];
|
|
1075
|
+
return index > 0 && line !== void 0 && isCaptionLine(sourceLines[line - 1] ?? ``) && text?.type === `text` && startsCaption(text.value ?? ``);
|
|
1076
|
+
});
|
|
1077
|
+
if (first === -1) continue;
|
|
1078
|
+
const captionRows = rows.slice(first);
|
|
1079
|
+
const lastCell = captionRows.at(-1)?.children?.[0];
|
|
1080
|
+
const author = lastCell ? takeProperties(lastCell) : void 0;
|
|
1081
|
+
const caption = captionContent(captionRows.map((row) => row.children?.[0]?.children ?? []));
|
|
1082
|
+
table.children = rows.slice(0, first);
|
|
1083
|
+
replace(table, figure([table], caption, author));
|
|
1084
|
+
}
|
|
1085
|
+
for (const para of paragraphs.reverse()) {
|
|
1086
|
+
const first = para.position?.start.line;
|
|
1087
|
+
if (first === void 0) continue;
|
|
1088
|
+
const lines = paraToLines(para, sourceLines);
|
|
1089
|
+
const parent = parents.get(para);
|
|
1090
|
+
const start = (parent?.type === `alert` || parent?.type === `blockquote` && parent.data?.alertType !== void 0) && isAlertMarkerLine(sourceLines[first - 1] ?? ``) ? first + 1 : first;
|
|
1091
|
+
const isCaptionAt = (index) => {
|
|
1092
|
+
const text = lines[index]?.[0];
|
|
1093
|
+
return isCaptionLine(sourceLines[start + index - 1] ?? ``) && text?.type === `text` && startsCaption(text.value);
|
|
1094
|
+
};
|
|
1095
|
+
if (isCaptionAt(0)) {
|
|
1096
|
+
const siblings = parents.get(para)?.children ?? [];
|
|
1097
|
+
const before = siblings[siblings.indexOf(para) - 1];
|
|
1098
|
+
const above = sourceLines[start - 2];
|
|
1099
|
+
if (!before || UNCAPTIONED.has(before.type) || above === void 0 || above.replace(/^[ \t>]*/, ``) === ``) continue;
|
|
1100
|
+
const author = takeProperties(para);
|
|
1101
|
+
replace(para, null);
|
|
1102
|
+
replace(before, figure([before], captionContent(lines), author));
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
let containers;
|
|
1106
|
+
const segments = [];
|
|
1107
|
+
for (let index = 1; index < lines.length; index++) {
|
|
1108
|
+
if (!isCaptionAt(index)) continue;
|
|
1109
|
+
containers ??= containersOf(ancestorsOf(para));
|
|
1110
|
+
if (!containers) break;
|
|
1111
|
+
const kept = containersKept(sourceLines[start + index - 1] ?? ``, containers.map((c) => c.line));
|
|
1112
|
+
const previous = segments.at(-1);
|
|
1113
|
+
if (previous && kept >= previous.kept) continue;
|
|
1114
|
+
const target = containers[kept]?.target ?? para;
|
|
1115
|
+
if (captioned(target)) continue;
|
|
1116
|
+
segments.push({
|
|
1117
|
+
index,
|
|
1118
|
+
kept,
|
|
1119
|
+
target
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
if (segments.length === 0) continue;
|
|
1123
|
+
const head = lines.slice(0, segments[0].index);
|
|
1124
|
+
const lastLine = head.at(-1);
|
|
1125
|
+
if (lastLine?.at(-1)?.type === `break`) head[head.length - 1] = lastLine.slice(0, -1);
|
|
1126
|
+
para.children = linesToParagraph(head)?.children ?? [];
|
|
1127
|
+
const author = takeProperties(para);
|
|
1128
|
+
for (const [i, segment] of segments.entries()) {
|
|
1129
|
+
const caption = captionContent(lines.slice(segment.index, segments[i + 1]?.index));
|
|
1130
|
+
const properties = i === segments.length - 1 ? author : void 0;
|
|
1131
|
+
const { target } = segment;
|
|
1132
|
+
if (target === para && onlyImages(para.children ?? [])) replace(para, figure(para.children ?? [], caption, properties));
|
|
1133
|
+
else replace(target, figure([target], caption, properties));
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
// src/hast.ts
|
|
1138
|
+
const WHITESPACE = /^[ \t\n\f\r]*$/;
|
|
1139
|
+
const isWhitespaceText = (node) => node.type === `text` && WHITESPACE.test(node.value ?? ``);
|
|
1140
|
+
/** A `<p>` with no children, or only whitespace text: squeeze-paragraphs removes it. */
|
|
1141
|
+
const isEmptyParagraph = (node) => node.tagName === `p` && (node.children ?? []).every(isWhitespaceText);
|
|
1142
|
+
/**
|
|
1143
|
+
* A `<p>` whose content is only images (each possibly inside a link or a lightbox button) and whitespace: unwrap-images replaces it with its children.
|
|
1144
|
+
*/
|
|
1145
|
+
const isImageParagraph = (node) => {
|
|
1146
|
+
const scan = (parent, inLink) => {
|
|
1147
|
+
let result = `empty`;
|
|
1148
|
+
for (const child of parent.children ?? []) {
|
|
1149
|
+
if (isWhitespaceText(child)) continue;
|
|
1150
|
+
if (child.type !== `element`) return `other`;
|
|
1151
|
+
if (child.tagName === `img`) {
|
|
1152
|
+
result = `image`;
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
if (!inLink && (child.tagName === `a` || child.tagName === `button`)) {
|
|
1156
|
+
const inner = scan(child, true);
|
|
1157
|
+
if (inner === `other`) return `other`;
|
|
1158
|
+
if (inner === `image`) result = `image`;
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
return `other`;
|
|
1162
|
+
}
|
|
1163
|
+
return result;
|
|
1164
|
+
};
|
|
1165
|
+
return node.tagName === `p` && scan(node, false) === `image`;
|
|
1166
|
+
};
|
|
1167
|
+
/** The rank of an `h1`–`h6` element, or `undefined`. */
|
|
1168
|
+
const headingRank = (node) => {
|
|
1169
|
+
if (node.type !== `element`) return void 0;
|
|
1170
|
+
const match = /^h([1-6])$/i.exec(node.tagName ?? ``);
|
|
1171
|
+
return match ? Number(match[1]) : void 0;
|
|
1172
|
+
};
|
|
1173
|
+
/**
|
|
1174
|
+
* Wrap heading-delimited runs of `nodes` into `<section data-depth>` elements, nesting deeper headings inside shallower ones. Content before the first heading stays unwrapped.
|
|
1175
|
+
*/
|
|
1176
|
+
const sectionize = (nodes, section, floor = 0) => {
|
|
1177
|
+
let minRank = Infinity;
|
|
1178
|
+
for (const node of nodes) {
|
|
1179
|
+
const rank = headingRank(node);
|
|
1180
|
+
if (rank !== void 0 && rank > floor && rank < minRank) minRank = rank;
|
|
1181
|
+
}
|
|
1182
|
+
if (minRank === Infinity) return [...nodes];
|
|
1183
|
+
const result = [];
|
|
1184
|
+
let i = 0;
|
|
1185
|
+
for (; i < nodes.length; i++) {
|
|
1186
|
+
const node = nodes[i];
|
|
1187
|
+
if (!node || headingRank(node) === minRank) break;
|
|
1188
|
+
result.push(node);
|
|
1189
|
+
}
|
|
1190
|
+
while (i < nodes.length) {
|
|
1191
|
+
const start = i;
|
|
1192
|
+
for (i++; i < nodes.length; i++) {
|
|
1193
|
+
const node = nodes[i];
|
|
1194
|
+
if (!node || headingRank(node) === minRank) break;
|
|
1195
|
+
}
|
|
1196
|
+
result.push(section(minRank, sectionize(nodes.slice(start, i), section, minRank)));
|
|
1197
|
+
}
|
|
1198
|
+
return result;
|
|
1199
|
+
};
|
|
1200
|
+
/**
|
|
1201
|
+
* The lightbox opt-in: an image directly after a text run that ends in a `!` at a boundary (start of the run or after whitespace), which is what `!` leaves behind.
|
|
1202
|
+
*/
|
|
1203
|
+
const LIGHTBOX_MARKER = /(?:^|\s)!$/;
|
|
1204
|
+
/**
|
|
1205
|
+
* A short, stable lightbox id from an image src (FNV-1a, base36): the same image always gets the same id, so repeated references share one dialog.
|
|
1206
|
+
*/
|
|
1207
|
+
const lightboxId = (src) => {
|
|
1208
|
+
let hash = 2166136261;
|
|
1209
|
+
for (let i = 0; i < src.length; i += 1) {
|
|
1210
|
+
hash ^= src.charCodeAt(i);
|
|
1211
|
+
hash = Math.imul(hash, 16777619);
|
|
1212
|
+
}
|
|
1213
|
+
return `markdown-lightbox-${(hash >>> 0).toString(36)}`;
|
|
1214
|
+
};
|
|
1215
|
+
/** The button that replaces an opted-in image and opens its dialog. */
|
|
1216
|
+
const lightboxTrigger = (id, image) => ({
|
|
1217
|
+
type: `element`,
|
|
1218
|
+
tagName: `button`,
|
|
1219
|
+
properties: {
|
|
1220
|
+
type: `button`,
|
|
1221
|
+
className: [`markdown-lightbox-trigger`],
|
|
1222
|
+
command: `show-modal`,
|
|
1223
|
+
commandfor: id
|
|
1224
|
+
},
|
|
1225
|
+
children: [image]
|
|
1226
|
+
});
|
|
1227
|
+
/** The modal dialog, appended at the document end, showing the image full size, and its caption under it when one is given. */
|
|
1228
|
+
const lightboxDialog = (id, image, caption) => ({
|
|
1229
|
+
type: `element`,
|
|
1230
|
+
tagName: `dialog`,
|
|
1231
|
+
properties: {
|
|
1232
|
+
id,
|
|
1233
|
+
className: [`markdown-lightbox`],
|
|
1234
|
+
closedby: `any`
|
|
1235
|
+
},
|
|
1236
|
+
children: [{
|
|
1237
|
+
type: `element`,
|
|
1238
|
+
tagName: `button`,
|
|
1239
|
+
properties: {
|
|
1240
|
+
type: `button`,
|
|
1241
|
+
className: [`markdown-lightbox-close`],
|
|
1242
|
+
command: `close`,
|
|
1243
|
+
commandfor: id,
|
|
1244
|
+
ariaLabel: `Close image`
|
|
1245
|
+
},
|
|
1246
|
+
children: [image]
|
|
1247
|
+
}, ...caption ? [{
|
|
1248
|
+
type: `element`,
|
|
1249
|
+
tagName: `p`,
|
|
1250
|
+
properties: { className: [`markdown-lightbox-caption`] },
|
|
1251
|
+
children: caption
|
|
1252
|
+
}] : []]
|
|
1253
|
+
});
|
|
1254
|
+
/** Whether a node is a caption's figure (`<figure class="markdown-caption">`, as the caption plugins make): an image that is its content shows the figure's caption in its lightbox dialog, with the `caption` option. */
|
|
1255
|
+
const isCaptionFigure = (node) => {
|
|
1256
|
+
const className = node?.properties?.className;
|
|
1257
|
+
return node?.tagName === `figure` && Array.isArray(className) && className.includes("markdown-caption");
|
|
1258
|
+
};
|
|
1259
|
+
// src/abbr.ts
|
|
1260
|
+
const DEFINITION = /^\*\[([^[\]]+)\]:\s*(.+)$/;
|
|
1261
|
+
const REGEX_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
1262
|
+
/**
|
|
1263
|
+
* The `[term, title]` pairs a paragraph's text defines, when every line of it is a `*[term]: title` definition; otherwise `undefined` (the paragraph is ordinary text).
|
|
1264
|
+
*/
|
|
1265
|
+
const parseAbbreviations = (text) => {
|
|
1266
|
+
const definitions = [];
|
|
1267
|
+
for (const line of text.split(`\n`)) {
|
|
1268
|
+
const match = DEFINITION.exec(line);
|
|
1269
|
+
if (!match) return void 0;
|
|
1270
|
+
definitions.push([match[1] ?? ``, (match[2] ?? ``).trim()]);
|
|
1271
|
+
}
|
|
1272
|
+
return definitions;
|
|
1273
|
+
};
|
|
1274
|
+
/**
|
|
1275
|
+
* Build the scan for a document's abbreviations. Terms match on word boundaries, longest first; the scan returns the pieces of a text value, or `undefined` when it holds no term.
|
|
1276
|
+
*/
|
|
1277
|
+
const createAbbreviationScan = (abbreviations) => {
|
|
1278
|
+
const terms = [...abbreviations.keys()].sort((a, b) => b.length - a.length).map((term) => term.replace(REGEX_SPECIAL, `\\$&`));
|
|
1279
|
+
const pattern = new RegExp(`\\b(${terms.join(`|`)})\\b`, `g`);
|
|
1280
|
+
return (value) => {
|
|
1281
|
+
const parts = [];
|
|
1282
|
+
let last = 0;
|
|
1283
|
+
for (const match of value.matchAll(pattern)) {
|
|
1284
|
+
const term = match[1] ?? ``;
|
|
1285
|
+
if (match.index > last) parts.push({ text: value.slice(last, match.index) });
|
|
1286
|
+
parts.push({
|
|
1287
|
+
term,
|
|
1288
|
+
title: abbreviations.get(term) ?? ``
|
|
1289
|
+
});
|
|
1290
|
+
last = match.index + term.length;
|
|
1291
|
+
}
|
|
1292
|
+
if (parts.length === 0) return void 0;
|
|
1293
|
+
if (last < value.length) parts.push({ text: value.slice(last) });
|
|
1294
|
+
return parts;
|
|
1295
|
+
};
|
|
1296
|
+
};
|
|
958
1297
|
// src/steps.ts
|
|
959
1298
|
const STEP_HEADER_RE = /^(@{1,6})(\d+)\. ?(.*)/s;
|
|
960
1299
|
const parseHeaderPrefix = (text) => {
|
|
@@ -1111,8 +1450,10 @@ const splitBlockquote$1 = (bq, counters, currentDepth, sourceLines) => {
|
|
|
1111
1450
|
}
|
|
1112
1451
|
let bodyLines = [];
|
|
1113
1452
|
const lines = paraToLines(child, sourceLines);
|
|
1453
|
+
const first = child.position?.start.line;
|
|
1454
|
+
const outsideBody = (index) => !sourceLines || base === void 0 || first === void 0 || markerCount(sourceLines[first - 1 + index] ?? "") < base;
|
|
1114
1455
|
for (const [index, lineSegs] of lines.entries()) {
|
|
1115
|
-
const header = stepHeader(lineSegs);
|
|
1456
|
+
const header = outsideBody(index) ? stepHeader(lineSegs) : null;
|
|
1116
1457
|
if (header) {
|
|
1117
1458
|
const bodyPara = linesToParagraph(bodyLines);
|
|
1118
1459
|
if (bodyPara) currentSegment.body.push(bodyPara);
|
|
@@ -1168,12 +1509,19 @@ const buildTree$1 = (steps, targetDepth, start, end) => {
|
|
|
1168
1509
|
data: { attrsRole: "container" }
|
|
1169
1510
|
};
|
|
1170
1511
|
};
|
|
1171
|
-
const processStepsInChildren = (children, containerClass, sourceLines) => {
|
|
1512
|
+
const processStepsInChildren = (children, containerClass, sourceLines, quotes = 0, parentType = "root") => {
|
|
1172
1513
|
const newChildren = [];
|
|
1173
1514
|
let i = 0;
|
|
1174
1515
|
while (i < children.length) {
|
|
1175
1516
|
const node = children[i];
|
|
1176
1517
|
if (!node) break;
|
|
1518
|
+
if (node.type === "paragraph" && sourceLines && !INDENTED_BLOCKS.has(parentType)) {
|
|
1519
|
+
const split = splitAtOpeningLine(node, sourceLines, quotes, (text) => parseHeaderPrefix(text)?.depth === 1);
|
|
1520
|
+
if (split) {
|
|
1521
|
+
children.splice(i, 1, ...split);
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1177
1525
|
if (node.type === "paragraph") {
|
|
1178
1526
|
const counters = /* @__PURE__ */ new Map();
|
|
1179
1527
|
const headerPara = extractHeaderPara$1(node, counters);
|
|
@@ -1189,6 +1537,8 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
|
|
|
1189
1537
|
const sibling = children[j];
|
|
1190
1538
|
if (!sibling) break;
|
|
1191
1539
|
if (sibling.type === "blockquote") {
|
|
1540
|
+
const line = sibling.position?.start.line;
|
|
1541
|
+
if (sourceLines && line !== void 0 && isAlertMarkerLine(sourceLines[line - 1] ?? "")) break;
|
|
1192
1542
|
const segments = splitBlockquote$1(sibling, counters, currentDepth, sourceLines);
|
|
1193
1543
|
const firstSeg = segments[0];
|
|
1194
1544
|
if (firstSeg?.header === null) rawSteps[currentStepIdx]?.body.push(...firstSeg.body);
|
|
@@ -1221,6 +1571,7 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
|
|
|
1221
1571
|
}
|
|
1222
1572
|
const list = buildTree$1(rawSteps, 1, 0, rawSteps.length);
|
|
1223
1573
|
list.containerClass = containerClass;
|
|
1574
|
+
if (parentType === "stepsItem") list.inBody = true;
|
|
1224
1575
|
newChildren.push(list);
|
|
1225
1576
|
children.splice(j, 0, ...splitAtGroupStarts$1(rest, restLine));
|
|
1226
1577
|
i = j;
|
|
@@ -1237,13 +1588,15 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
|
|
|
1237
1588
|
*/
|
|
1238
1589
|
const processSteps = (root, containerClass, source) => {
|
|
1239
1590
|
const sourceLines = source?.split(/\r?\n/);
|
|
1240
|
-
const walk = (node) => {
|
|
1591
|
+
const walk = (node, quotes) => {
|
|
1241
1592
|
if (!Array.isArray(node.children) || node.children.length === 0) return;
|
|
1242
|
-
|
|
1243
|
-
|
|
1593
|
+
const inner = quotes + (MARKED_BLOCKS.has(node.type ?? "") ? 1 : 0);
|
|
1594
|
+
processStepsInChildren(node.children, containerClass, sourceLines, inner, node.type);
|
|
1595
|
+
for (const child of node.children) walk(child, inner);
|
|
1244
1596
|
};
|
|
1245
1597
|
processStepsInChildren(root.children, containerClass, sourceLines);
|
|
1246
|
-
for (const child of root.children) walk(child);
|
|
1598
|
+
for (const child of root.children) walk(child, 0);
|
|
1599
|
+
if (sourceLines) endGroupsAtMarkers(root, sourceLines, `stepsList`);
|
|
1247
1600
|
};
|
|
1248
1601
|
// src/tabs.ts
|
|
1249
1602
|
const TAB_HEADER_RE = /^(%{1,6})(\+)? (.*)/;
|
|
@@ -1399,11 +1752,13 @@ const splitBlockquote = (bq, explicit, sourceLines) => {
|
|
|
1399
1752
|
explicit,
|
|
1400
1753
|
lines: []
|
|
1401
1754
|
};
|
|
1755
|
+
const base = sourceLines && !explicit ? blockquoteMarkers(bq, sourceLines) : void 0;
|
|
1402
1756
|
const splitLines = (lines, markersOf, para) => {
|
|
1403
1757
|
let bodyLines = [];
|
|
1404
1758
|
for (const [index, lineSegs] of lines.entries()) {
|
|
1405
1759
|
const first = lineSegs[0];
|
|
1406
|
-
|
|
1760
|
+
const outsidePanel = !markersOf || base === void 0 || markersOf(index) < base;
|
|
1761
|
+
if (first?.type === "text" && outsidePanel) {
|
|
1407
1762
|
const header = parseTabHeader(first.value);
|
|
1408
1763
|
if (header) {
|
|
1409
1764
|
if (bodyLines.length > 0) {
|
|
@@ -1568,12 +1923,19 @@ const buildTree = (rawTabs, targetDepth, start, end, containerClass) => {
|
|
|
1568
1923
|
}
|
|
1569
1924
|
};
|
|
1570
1925
|
};
|
|
1571
|
-
const processTabsInChildren = (children, containerClass, checkBlankLines = true, sourceLines) => {
|
|
1926
|
+
const processTabsInChildren = (children, containerClass, checkBlankLines = true, sourceLines, quotes = 0, parentType = "root") => {
|
|
1572
1927
|
const newChildren = [];
|
|
1573
1928
|
let i = 0;
|
|
1574
1929
|
while (i < children.length) {
|
|
1575
1930
|
const node = children[i];
|
|
1576
1931
|
if (!node) break;
|
|
1932
|
+
if (node.type === "paragraph" && sourceLines && !INDENTED_BLOCKS.has(parentType)) {
|
|
1933
|
+
const split = splitAtOpeningLine(node, sourceLines, quotes, (text) => parseTabHeader(text)?.depth === 1);
|
|
1934
|
+
if (split) {
|
|
1935
|
+
children.splice(i, 1, ...split);
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1577
1939
|
if (node.type === "paragraph") {
|
|
1578
1940
|
const headerPara = extractHeaderPara(node);
|
|
1579
1941
|
if (headerPara) {
|
|
@@ -1719,13 +2081,15 @@ const processTabsInChildren = (children, containerClass, checkBlankLines = true,
|
|
|
1719
2081
|
*/
|
|
1720
2082
|
const processTabs = (root, containerClass, source) => {
|
|
1721
2083
|
const sourceLines = source?.split(/\r?\n/);
|
|
1722
|
-
const walk = (node) => {
|
|
2084
|
+
const walk = (node, quotes) => {
|
|
1723
2085
|
if (!Array.isArray(node.children) || node.children.length === 0) return;
|
|
1724
|
-
|
|
1725
|
-
|
|
2086
|
+
const inner = quotes + (MARKED_BLOCKS.has(node.type ?? "") ? 1 : 0);
|
|
2087
|
+
processTabsInChildren(node.children, containerClass, false, sourceLines, inner, node.type);
|
|
2088
|
+
for (const child of node.children) walk(child, inner);
|
|
1726
2089
|
};
|
|
1727
2090
|
processTabsInChildren(root.children, containerClass, true, sourceLines);
|
|
1728
|
-
for (const child of root.children) walk(child);
|
|
2091
|
+
for (const child of root.children) walk(child, 0);
|
|
2092
|
+
if (sourceLines) endGroupsAtMarkers(root, sourceLines, `tabsList`);
|
|
1729
2093
|
};
|
|
1730
2094
|
// src/editable.ts
|
|
1731
2095
|
const editableTree = (root, build, setChildren, setData) => {
|
|
@@ -1813,6 +2177,6 @@ const inlineSvgMarkup = (content, alts, relativePath, limits) => {
|
|
|
1813
2177
|
return index === 0 ? sprite + use : use;
|
|
1814
2178
|
});
|
|
1815
2179
|
};
|
|
1816
|
-
export { ALERT_ALIASES, DEFAULT_TITLE, ICONS, LIGHTBOX_MARKER, createAbbreviationScan, createAlertResolver, createDelimitedTransform, createKbdTransform, editableTree, escapedIndices, headingRank, inlineSvgMarkup, isEmptyParagraph, isImageParagraph, lightboxDialog, lightboxId, lightboxTrigger, maskEscaped, maskTreeEscapes, parseAbbreviations, placeCutAttributes, plainText, processSteps, processStepsInChildren, processTabs, processTabsInChildren, scriptSyntax, sectionize, splitAlertParagraph, stripAttrs, svgSpriteId, tabLabelText, tabsBlockId, unmaskEscaped };
|
|
2180
|
+
export { ALERT_ALIASES, CAPTION_CLASS, DEFAULT_TITLE, ICONS, LIGHTBOX_MARKER, containersKept, createAbbreviationScan, createAlertResolver, createDelimitedTransform, createKbdTransform, editableTree, escapedIndices, figureProperties, hastAttributes, headingRank, inlineSvgMarkup, isAlertMarkerLine, isCaptionFigure, isCaptionLine, isEmptyParagraph, isImageParagraph, itemColumns, lightboxDialog, lightboxId, lightboxTrigger, maskEscaped, maskTreeEscapes, mermaidDiagram, mermaidFailure, mermaidIdPrefixes, mermaidSource, mermaidSvg, parseAbbreviations, placeCutAttributes, plainText, processCaptions, processSteps, processStepsInChildren, processTabs, processTabsInChildren, scriptSyntax, sectionize, splitAlertParagraph, startsCaption, stripAttrs, stripCaptionMarker, svgSpriteId, tabLabelText, tabsBlockId, unmaskEscaped };
|
|
1817
2181
|
|
|
1818
2182
|
//# sourceMappingURL=index.mjs.map
|