@mirrordown/core 0.1.0 → 0.1.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/dist/index.mjs CHANGED
@@ -133,6 +133,8 @@ const ICONS = {
133
133
  };
134
134
  // src/github-alerts/index.ts
135
135
  const MARKER_RE = /^\[!([^\]]+)\]([+-])?/;
136
+ /** 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. */
137
+ const isAlertMarkerLine = (line) => MARKER_RE.test(line.replace(/^[ >]*/, ``));
136
138
  const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
137
139
  /** Resolve alert keywords, titles and icons the same way in every engine. */
138
140
  const createAlertResolver = (options = {}) => {
@@ -244,9 +246,9 @@ const INLINE_PARENTS = /* @__PURE__ */ new Set([
244
246
  `subscript`,
245
247
  `superscript`
246
248
  ]);
247
- const WHITESPACE$2 = /^[\t\n\v\f\r \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000]$/u;
249
+ const WHITESPACE$3 = /^[\t\n\v\f\r \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000]$/u;
248
250
  const PUNCTUATION$1 = /^[\p{P}\p{S}]$/u;
249
- const classify$1 = (char) => WHITESPACE$2.test(char) ? `space` : PUNCTUATION$1.test(char) ? `punct` : `other`;
251
+ const classify$1 = (char) => WHITESPACE$3.test(char) ? `space` : PUNCTUATION$1.test(char) ? `punct` : `other`;
250
252
  /**
251
253
  * 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
254
  */
@@ -484,8 +486,8 @@ const tokenType = (type) => type;
484
486
  const temporary = (type) => tokenType(`${type}SequenceTemporary`);
485
487
  const scriptOf = (tokenType) => Object.values(SCRIPTS).find(({ type }) => temporary(type) === tokenType);
486
488
  const PUNCTUATION = /\p{P}|\p{S}/u;
487
- const WHITESPACE$1 = /\s/;
488
- const classify = (code) => code === null || code < 0 || code === 32 || WHITESPACE$1.test(String.fromCharCode(code)) ? 1 : PUNCTUATION.test(String.fromCharCode(code)) ? 2 : void 0;
489
+ const WHITESPACE$2 = /\s/;
490
+ 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
491
  const splice = (list, start, remove, items) => {
490
492
  list.splice(start, remove);
491
493
  for (let i = 0; i < items.length; i += 1e4) list.splice(start + i, 0, ...items.slice(i, i + 1e4));
@@ -603,156 +605,6 @@ const scriptSyntax = (marker) => {
603
605
  insideSpan: { null: [construct] }
604
606
  };
605
607
  };
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 `!![alt](src)` 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
608
  // src/cut-attributes.ts
757
609
  /** Paragraphs recognition rebuilt from lines, as it does where a tab or step header lazily continued them. */
758
610
  const rebuiltParagraphs = /* @__PURE__ */ new WeakSet();
@@ -955,6 +807,492 @@ const rebuildLazyLines = (lines, level) => {
955
807
  }
956
808
  return blocks;
957
809
  };
810
+ /** 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. */
811
+ const MARKED_BLOCKS = /* @__PURE__ */ new Set([
812
+ `blockquote`,
813
+ `alert`,
814
+ `tabsItem`,
815
+ `stepsItem`
816
+ ]);
817
+ /** Blocks a line can lazily continue into by its indentation rather than its markers: a list item or a definition. */
818
+ const INDENTED_BLOCKS = /* @__PURE__ */ new Set([
819
+ `listItem`,
820
+ `defListDescription`,
821
+ `descriptionDetails`
822
+ ]);
823
+ /**
824
+ * 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.
825
+ */
826
+ const splitAtOpeningLine = (para, sourceLines, quotes, opens) => {
827
+ const start = para.position?.start.line;
828
+ if (start === void 0) return null;
829
+ const lines = paraToLines(para, sourceLines);
830
+ const at = lines.findIndex((line, index) => {
831
+ const first = line[0];
832
+ return index > 0 && first?.type === `text` && opens(first.value) && markerCount(sourceLines[start - 1 + index] ?? ``) === quotes;
833
+ });
834
+ if (at === -1) return null;
835
+ const head = linesToParagraph(lines.slice(0, at));
836
+ const tail = linesToParagraph(lines.slice(at));
837
+ if (!head || !tail) return null;
838
+ if (para.data) tail.data = para.data;
839
+ return [head, tail];
840
+ };
841
+ const lastParagraph = (node) => {
842
+ const last = node?.children?.at(-1);
843
+ if (!last) return void 0;
844
+ if (last.type === `paragraph`) return last;
845
+ return lastParagraph(last);
846
+ };
847
+ /**
848
+ * 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.
849
+ */
850
+ const endGroupsAtMarkers = (root, sourceLines, listType) => {
851
+ const visit = (node, quotes) => {
852
+ const children = node.children;
853
+ if (!children) return;
854
+ for (let i = 0; i < children.length; i++) {
855
+ const child = children[i];
856
+ visit(child, quotes + (MARKED_BLOCKS.has(child.type) ? 1 : 0));
857
+ if (child.type !== listType) continue;
858
+ const para = lastParagraph(child.children?.at(-1));
859
+ const start = para?.position?.start.line;
860
+ if (!para || start === void 0) continue;
861
+ const lines = paraToLines(para, sourceLines);
862
+ const outside = lines.findIndex((_, index) => index > 0 && markerCount(sourceLines[start - 1 + index] ?? ``) < quotes + 1);
863
+ if (outside === -1) continue;
864
+ const kept = linesToParagraph(lines.slice(0, outside));
865
+ const moved = linesToParagraph(lines.slice(outside));
866
+ if (!kept || !moved) continue;
867
+ para.children = kept.children;
868
+ const data = para.data;
869
+ if (data?.hProperties) {
870
+ const { hProperties, ...rest } = data;
871
+ moved.data = { hProperties };
872
+ para.data = rest;
873
+ }
874
+ children.splice(i + 1, 0, moved);
875
+ }
876
+ };
877
+ visit(root, 0);
878
+ };
879
+ // src/line-containers.ts
880
+ /**
881
+ * How many of `containers`, outermost first, a line is written in. Tabs count as one column.
882
+ */
883
+ const containersKept = (line, containers) => {
884
+ const prefix = /^[ \t>]*/.exec(line)?.[0] ?? ``;
885
+ const markers = prefix.split(`>`).length - 1;
886
+ let kept = 0;
887
+ for (const container of containers) {
888
+ if (container.kind === `quote` ? markers < container.markers : prefix.length < container.column) break;
889
+ kept++;
890
+ }
891
+ return kept;
892
+ };
893
+ /**
894
+ * 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.
895
+ */
896
+ const itemColumns = (line) => {
897
+ const columns = [];
898
+ let pos = 0;
899
+ for (;;) {
900
+ while (line[pos] === ` ` || line[pos] === `\t`) pos++;
901
+ if (line[pos] === `>`) {
902
+ pos++;
903
+ continue;
904
+ }
905
+ const marker = /^(?:[-+*]|\d{1,9}[.)])(?=[ \t]|$)/.exec(line.slice(pos));
906
+ if (!marker) return columns;
907
+ const end = pos + marker[0].length;
908
+ let spaces = 0;
909
+ while (line[end + spaces] === ` ` || line[end + spaces] === `\t`) spaces++;
910
+ const column = end + spaces === line.length || spaces > 4 ? end + 1 : end + spaces;
911
+ columns.push(column);
912
+ if (spaces > 4) return columns;
913
+ pos = column;
914
+ }
915
+ };
916
+ // src/caption.ts
917
+ /** The class of the `<figure>` a caption wraps its block in. */
918
+ const CAPTION_CLASS = "markdown-caption";
919
+ /** 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. */
920
+ const isCaptionLine = (line) => /^~:[ \t]+\S/.test(line.replace(/^[ \t>]*/, ``));
921
+ /** Whether text starts with a caption marker, as a parser gives a caption line's text (inline markdown may follow in other nodes). */
922
+ const startsCaption = (text) => /^~:(?:[ \t]|$)/.test(text);
923
+ /** The text after a caption marker. */
924
+ const stripCaptionMarker = (text) => text.replace(/^~:[ \t]*/, ``);
925
+ /**
926
+ * 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).
927
+ */
928
+ const UNCAPTIONED = /* @__PURE__ */ new Set([
929
+ `heading`,
930
+ `thematicBreak`,
931
+ `html`,
932
+ `definition`,
933
+ `footnoteDefinition`,
934
+ `yaml`,
935
+ `toml`,
936
+ `figure`,
937
+ `alertTitle`,
938
+ `stepsTitle`,
939
+ `tabsLabel`,
940
+ `tabsLabels`,
941
+ `tabsInput`,
942
+ `defListTerm`,
943
+ `descriptionTerm`
944
+ ]);
945
+ /** The properties of a caption's `<figure>`: its class, then the attributes written at the end of the caption (a class joins it). */
946
+ const figureProperties = (author) => {
947
+ const { class: extraClass, className: extraClassName, ...rest } = author ?? {};
948
+ const extra = [extraClass, extraClassName].flat().flatMap((value) => typeof value === `string` ? value.split(` `) : []).filter((value) => value !== ``);
949
+ return {
950
+ className: [CAPTION_CLASS, ...extra],
951
+ ...rest
952
+ };
953
+ };
954
+ const WHITESPACE$1 = /^\s*$/;
955
+ 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`));
956
+ const figure = (content, caption, author) => ({
957
+ type: `figure`,
958
+ data: {
959
+ hName: `figure`,
960
+ hProperties: figureProperties(author)
961
+ },
962
+ children: [...content, {
963
+ type: `figcaption`,
964
+ data: { hName: `figcaption` },
965
+ children: caption
966
+ }]
967
+ });
968
+ const captionContent = (lines) => {
969
+ const first = lines[0]?.[0];
970
+ if (first?.type === `text`) {
971
+ const value = stripCaptionMarker(first.value);
972
+ lines[0] = value ? [{
973
+ ...first,
974
+ value
975
+ }, ...lines[0].slice(1)] : lines[0].slice(1);
976
+ }
977
+ return linesToParagraph(lines)?.children ?? [];
978
+ };
979
+ const takeProperties = (node) => {
980
+ const data = node.data;
981
+ if (!data?.hProperties) return void 0;
982
+ const { hProperties, ...rest } = data;
983
+ node.data = rest;
984
+ return hProperties;
985
+ };
986
+ /**
987
+ * 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.
988
+ */
989
+ const processCaptions = (root, source) => {
990
+ if (!source.includes(`~:`)) return;
991
+ const sourceLines = source.split(/\r?\n/);
992
+ const parents = /* @__PURE__ */ new Map();
993
+ const paragraphs = [];
994
+ const tables = [];
995
+ const walk = (node) => {
996
+ if (node.type === `paragraph`) paragraphs.push(node);
997
+ else if (node.type === `table`) tables.push(node);
998
+ for (const child of node.children ?? []) {
999
+ parents.set(child, node);
1000
+ walk(child);
1001
+ }
1002
+ };
1003
+ walk(root);
1004
+ const replace = (node, replacement) => {
1005
+ const parent = parents.get(node);
1006
+ const siblings = parent?.children;
1007
+ if (!parent || !siblings) return;
1008
+ const index = siblings.indexOf(node);
1009
+ if (index === -1) return;
1010
+ if (replacement) {
1011
+ siblings.splice(index, 1, replacement);
1012
+ parents.set(replacement, parent);
1013
+ parents.set(node, replacement);
1014
+ } else siblings.splice(index, 1);
1015
+ };
1016
+ const captioned = (node) => parents.get(node)?.type === `figure`;
1017
+ const ancestorsOf = (node) => {
1018
+ const ancestors = [];
1019
+ for (let p = parents.get(node); p; p = parents.get(p)) ancestors.unshift(p);
1020
+ return ancestors;
1021
+ };
1022
+ const containersOf = (ancestors) => {
1023
+ const containers = [];
1024
+ const nearest = (index, type) => ancestors.slice(0, index).reverse().find((a) => a.type === type);
1025
+ let quotes = 0;
1026
+ for (const [index, ancestor] of ancestors.entries()) {
1027
+ const quote = (target) => {
1028
+ containers.push({
1029
+ target: target ?? ancestor,
1030
+ line: {
1031
+ kind: `quote`,
1032
+ markers: ++quotes
1033
+ }
1034
+ });
1035
+ };
1036
+ switch (ancestor.type) {
1037
+ case `blockquote`:
1038
+ case `alert`:
1039
+ quote(ancestor);
1040
+ break;
1041
+ case `tabsItem`:
1042
+ case `tabsPanel`:
1043
+ quote(nearest(index, `tabsList`));
1044
+ break;
1045
+ case `stepsItem`:
1046
+ if (!(ancestor.children ?? []).some((c) => c.type === `stepsBody`)) quote(nearest(index, `stepsList`));
1047
+ break;
1048
+ case `stepsBody`:
1049
+ quote(nearest(index, `stepsList`));
1050
+ break;
1051
+ case `listItem`:
1052
+ case `defListDescription`:
1053
+ case `descriptionDetails`: {
1054
+ const column = ancestor.children?.[0]?.position?.start.column;
1055
+ if (column === void 0) return void 0;
1056
+ containers.push({
1057
+ target: ancestors[index - 1] ?? ancestor,
1058
+ line: {
1059
+ kind: `item`,
1060
+ column: column - 1
1061
+ }
1062
+ });
1063
+ break;
1064
+ }
1065
+ }
1066
+ }
1067
+ return containers;
1068
+ };
1069
+ for (const table of tables) {
1070
+ const rows = table.children ?? [];
1071
+ const first = rows.findIndex((row, index) => {
1072
+ const line = row.position?.start.line;
1073
+ const text = row.children?.[0]?.children?.[0];
1074
+ return index > 0 && line !== void 0 && isCaptionLine(sourceLines[line - 1] ?? ``) && text?.type === `text` && startsCaption(text.value ?? ``);
1075
+ });
1076
+ if (first === -1) continue;
1077
+ const captionRows = rows.slice(first);
1078
+ const lastCell = captionRows.at(-1)?.children?.[0];
1079
+ const author = lastCell ? takeProperties(lastCell) : void 0;
1080
+ const caption = captionContent(captionRows.map((row) => row.children?.[0]?.children ?? []));
1081
+ table.children = rows.slice(0, first);
1082
+ replace(table, figure([table], caption, author));
1083
+ }
1084
+ for (const para of paragraphs.reverse()) {
1085
+ const first = para.position?.start.line;
1086
+ if (first === void 0) continue;
1087
+ const lines = paraToLines(para, sourceLines);
1088
+ const parent = parents.get(para);
1089
+ const start = (parent?.type === `alert` || parent?.type === `blockquote` && parent.data?.alertType !== void 0) && isAlertMarkerLine(sourceLines[first - 1] ?? ``) ? first + 1 : first;
1090
+ const isCaptionAt = (index) => {
1091
+ const text = lines[index]?.[0];
1092
+ return isCaptionLine(sourceLines[start + index - 1] ?? ``) && text?.type === `text` && startsCaption(text.value);
1093
+ };
1094
+ if (isCaptionAt(0)) {
1095
+ const siblings = parents.get(para)?.children ?? [];
1096
+ const before = siblings[siblings.indexOf(para) - 1];
1097
+ const above = sourceLines[start - 2];
1098
+ if (!before || UNCAPTIONED.has(before.type) || above === void 0 || above.replace(/^[ \t>]*/, ``) === ``) continue;
1099
+ const author = takeProperties(para);
1100
+ replace(para, null);
1101
+ replace(before, figure([before], captionContent(lines), author));
1102
+ continue;
1103
+ }
1104
+ let containers;
1105
+ const segments = [];
1106
+ for (let index = 1; index < lines.length; index++) {
1107
+ if (!isCaptionAt(index)) continue;
1108
+ containers ??= containersOf(ancestorsOf(para));
1109
+ if (!containers) break;
1110
+ const kept = containersKept(sourceLines[start + index - 1] ?? ``, containers.map((c) => c.line));
1111
+ const previous = segments.at(-1);
1112
+ if (previous && kept >= previous.kept) continue;
1113
+ const target = containers[kept]?.target ?? para;
1114
+ if (captioned(target)) continue;
1115
+ segments.push({
1116
+ index,
1117
+ kept,
1118
+ target
1119
+ });
1120
+ }
1121
+ if (segments.length === 0) continue;
1122
+ const head = lines.slice(0, segments[0].index);
1123
+ const lastLine = head.at(-1);
1124
+ if (lastLine?.at(-1)?.type === `break`) head[head.length - 1] = lastLine.slice(0, -1);
1125
+ para.children = linesToParagraph(head)?.children ?? [];
1126
+ const author = takeProperties(para);
1127
+ for (const [i, segment] of segments.entries()) {
1128
+ const caption = captionContent(lines.slice(segment.index, segments[i + 1]?.index));
1129
+ const properties = i === segments.length - 1 ? author : void 0;
1130
+ const { target } = segment;
1131
+ if (target === para && onlyImages(para.children ?? [])) replace(para, figure(para.children ?? [], caption, properties));
1132
+ else replace(target, figure([target], caption, properties));
1133
+ }
1134
+ }
1135
+ };
1136
+ // src/hast.ts
1137
+ const WHITESPACE = /^[ \t\n\f\r]*$/;
1138
+ const isWhitespaceText = (node) => node.type === `text` && WHITESPACE.test(node.value ?? ``);
1139
+ /** A `<p>` with no children, or only whitespace text: squeeze-paragraphs removes it. */
1140
+ const isEmptyParagraph = (node) => node.tagName === `p` && (node.children ?? []).every(isWhitespaceText);
1141
+ /**
1142
+ * 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.
1143
+ */
1144
+ const isImageParagraph = (node) => {
1145
+ const scan = (parent, inLink) => {
1146
+ let result = `empty`;
1147
+ for (const child of parent.children ?? []) {
1148
+ if (isWhitespaceText(child)) continue;
1149
+ if (child.type !== `element`) return `other`;
1150
+ if (child.tagName === `img`) {
1151
+ result = `image`;
1152
+ continue;
1153
+ }
1154
+ if (!inLink && (child.tagName === `a` || child.tagName === `button`)) {
1155
+ const inner = scan(child, true);
1156
+ if (inner === `other`) return `other`;
1157
+ if (inner === `image`) result = `image`;
1158
+ continue;
1159
+ }
1160
+ return `other`;
1161
+ }
1162
+ return result;
1163
+ };
1164
+ return node.tagName === `p` && scan(node, false) === `image`;
1165
+ };
1166
+ /** The rank of an `h1`–`h6` element, or `undefined`. */
1167
+ const headingRank = (node) => {
1168
+ if (node.type !== `element`) return void 0;
1169
+ const match = /^h([1-6])$/i.exec(node.tagName ?? ``);
1170
+ return match ? Number(match[1]) : void 0;
1171
+ };
1172
+ /**
1173
+ * Wrap heading-delimited runs of `nodes` into `<section data-depth>` elements, nesting deeper headings inside shallower ones. Content before the first heading stays unwrapped.
1174
+ */
1175
+ const sectionize = (nodes, section, floor = 0) => {
1176
+ let minRank = Infinity;
1177
+ for (const node of nodes) {
1178
+ const rank = headingRank(node);
1179
+ if (rank !== void 0 && rank > floor && rank < minRank) minRank = rank;
1180
+ }
1181
+ if (minRank === Infinity) return [...nodes];
1182
+ const result = [];
1183
+ let i = 0;
1184
+ for (; i < nodes.length; i++) {
1185
+ const node = nodes[i];
1186
+ if (!node || headingRank(node) === minRank) break;
1187
+ result.push(node);
1188
+ }
1189
+ while (i < nodes.length) {
1190
+ const start = i;
1191
+ for (i++; i < nodes.length; i++) {
1192
+ const node = nodes[i];
1193
+ if (!node || headingRank(node) === minRank) break;
1194
+ }
1195
+ result.push(section(minRank, sectionize(nodes.slice(start, i), section, minRank)));
1196
+ }
1197
+ return result;
1198
+ };
1199
+ /**
1200
+ * 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 `!![alt](src)` leaves behind.
1201
+ */
1202
+ const LIGHTBOX_MARKER = /(?:^|\s)!$/;
1203
+ /**
1204
+ * 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.
1205
+ */
1206
+ const lightboxId = (src) => {
1207
+ let hash = 2166136261;
1208
+ for (let i = 0; i < src.length; i += 1) {
1209
+ hash ^= src.charCodeAt(i);
1210
+ hash = Math.imul(hash, 16777619);
1211
+ }
1212
+ return `markdown-lightbox-${(hash >>> 0).toString(36)}`;
1213
+ };
1214
+ /** The button that replaces an opted-in image and opens its dialog. */
1215
+ const lightboxTrigger = (id, image) => ({
1216
+ type: `element`,
1217
+ tagName: `button`,
1218
+ properties: {
1219
+ type: `button`,
1220
+ className: [`markdown-lightbox-trigger`],
1221
+ command: `show-modal`,
1222
+ commandfor: id
1223
+ },
1224
+ children: [image]
1225
+ });
1226
+ /** The modal dialog, appended at the document end, showing the image full size, and its caption under it when one is given. */
1227
+ const lightboxDialog = (id, image, caption) => ({
1228
+ type: `element`,
1229
+ tagName: `dialog`,
1230
+ properties: {
1231
+ id,
1232
+ className: [`markdown-lightbox`],
1233
+ closedby: `any`
1234
+ },
1235
+ children: [{
1236
+ type: `element`,
1237
+ tagName: `button`,
1238
+ properties: {
1239
+ type: `button`,
1240
+ className: [`markdown-lightbox-close`],
1241
+ command: `close`,
1242
+ commandfor: id,
1243
+ ariaLabel: `Close image`
1244
+ },
1245
+ children: [image]
1246
+ }, ...caption ? [{
1247
+ type: `element`,
1248
+ tagName: `p`,
1249
+ properties: { className: [`markdown-lightbox-caption`] },
1250
+ children: caption
1251
+ }] : []]
1252
+ });
1253
+ /** 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. */
1254
+ const isCaptionFigure = (node) => {
1255
+ const className = node?.properties?.className;
1256
+ return node?.tagName === `figure` && Array.isArray(className) && className.includes("markdown-caption");
1257
+ };
1258
+ // src/abbr.ts
1259
+ const DEFINITION = /^\*\[([^[\]]+)\]:\s*(.+)$/;
1260
+ const REGEX_SPECIAL = /[.*+?^${}()|[\]\\]/g;
1261
+ /**
1262
+ * 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).
1263
+ */
1264
+ const parseAbbreviations = (text) => {
1265
+ const definitions = [];
1266
+ for (const line of text.split(`\n`)) {
1267
+ const match = DEFINITION.exec(line);
1268
+ if (!match) return void 0;
1269
+ definitions.push([match[1] ?? ``, (match[2] ?? ``).trim()]);
1270
+ }
1271
+ return definitions;
1272
+ };
1273
+ /**
1274
+ * 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.
1275
+ */
1276
+ const createAbbreviationScan = (abbreviations) => {
1277
+ const terms = [...abbreviations.keys()].sort((a, b) => b.length - a.length).map((term) => term.replace(REGEX_SPECIAL, `\\$&`));
1278
+ const pattern = new RegExp(`\\b(${terms.join(`|`)})\\b`, `g`);
1279
+ return (value) => {
1280
+ const parts = [];
1281
+ let last = 0;
1282
+ for (const match of value.matchAll(pattern)) {
1283
+ const term = match[1] ?? ``;
1284
+ if (match.index > last) parts.push({ text: value.slice(last, match.index) });
1285
+ parts.push({
1286
+ term,
1287
+ title: abbreviations.get(term) ?? ``
1288
+ });
1289
+ last = match.index + term.length;
1290
+ }
1291
+ if (parts.length === 0) return void 0;
1292
+ if (last < value.length) parts.push({ text: value.slice(last) });
1293
+ return parts;
1294
+ };
1295
+ };
958
1296
  // src/steps.ts
959
1297
  const STEP_HEADER_RE = /^(@{1,6})(\d+)\. ?(.*)/s;
960
1298
  const parseHeaderPrefix = (text) => {
@@ -1111,8 +1449,10 @@ const splitBlockquote$1 = (bq, counters, currentDepth, sourceLines) => {
1111
1449
  }
1112
1450
  let bodyLines = [];
1113
1451
  const lines = paraToLines(child, sourceLines);
1452
+ const first = child.position?.start.line;
1453
+ const outsideBody = (index) => !sourceLines || base === void 0 || first === void 0 || markerCount(sourceLines[first - 1 + index] ?? "") < base;
1114
1454
  for (const [index, lineSegs] of lines.entries()) {
1115
- const header = stepHeader(lineSegs);
1455
+ const header = outsideBody(index) ? stepHeader(lineSegs) : null;
1116
1456
  if (header) {
1117
1457
  const bodyPara = linesToParagraph(bodyLines);
1118
1458
  if (bodyPara) currentSegment.body.push(bodyPara);
@@ -1168,12 +1508,19 @@ const buildTree$1 = (steps, targetDepth, start, end) => {
1168
1508
  data: { attrsRole: "container" }
1169
1509
  };
1170
1510
  };
1171
- const processStepsInChildren = (children, containerClass, sourceLines) => {
1511
+ const processStepsInChildren = (children, containerClass, sourceLines, quotes = 0, parentType = "root") => {
1172
1512
  const newChildren = [];
1173
1513
  let i = 0;
1174
1514
  while (i < children.length) {
1175
1515
  const node = children[i];
1176
1516
  if (!node) break;
1517
+ if (node.type === "paragraph" && sourceLines && !INDENTED_BLOCKS.has(parentType)) {
1518
+ const split = splitAtOpeningLine(node, sourceLines, quotes, (text) => parseHeaderPrefix(text)?.depth === 1);
1519
+ if (split) {
1520
+ children.splice(i, 1, ...split);
1521
+ continue;
1522
+ }
1523
+ }
1177
1524
  if (node.type === "paragraph") {
1178
1525
  const counters = /* @__PURE__ */ new Map();
1179
1526
  const headerPara = extractHeaderPara$1(node, counters);
@@ -1189,6 +1536,8 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
1189
1536
  const sibling = children[j];
1190
1537
  if (!sibling) break;
1191
1538
  if (sibling.type === "blockquote") {
1539
+ const line = sibling.position?.start.line;
1540
+ if (sourceLines && line !== void 0 && isAlertMarkerLine(sourceLines[line - 1] ?? "")) break;
1192
1541
  const segments = splitBlockquote$1(sibling, counters, currentDepth, sourceLines);
1193
1542
  const firstSeg = segments[0];
1194
1543
  if (firstSeg?.header === null) rawSteps[currentStepIdx]?.body.push(...firstSeg.body);
@@ -1221,6 +1570,7 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
1221
1570
  }
1222
1571
  const list = buildTree$1(rawSteps, 1, 0, rawSteps.length);
1223
1572
  list.containerClass = containerClass;
1573
+ if (parentType === "stepsItem") list.inBody = true;
1224
1574
  newChildren.push(list);
1225
1575
  children.splice(j, 0, ...splitAtGroupStarts$1(rest, restLine));
1226
1576
  i = j;
@@ -1237,13 +1587,15 @@ const processStepsInChildren = (children, containerClass, sourceLines) => {
1237
1587
  */
1238
1588
  const processSteps = (root, containerClass, source) => {
1239
1589
  const sourceLines = source?.split(/\r?\n/);
1240
- const walk = (node) => {
1590
+ const walk = (node, quotes) => {
1241
1591
  if (!Array.isArray(node.children) || node.children.length === 0) return;
1242
- processStepsInChildren(node.children, containerClass, sourceLines);
1243
- for (const child of node.children) walk(child);
1592
+ const inner = quotes + (MARKED_BLOCKS.has(node.type ?? "") ? 1 : 0);
1593
+ processStepsInChildren(node.children, containerClass, sourceLines, inner, node.type);
1594
+ for (const child of node.children) walk(child, inner);
1244
1595
  };
1245
1596
  processStepsInChildren(root.children, containerClass, sourceLines);
1246
- for (const child of root.children) walk(child);
1597
+ for (const child of root.children) walk(child, 0);
1598
+ if (sourceLines) endGroupsAtMarkers(root, sourceLines, `stepsList`);
1247
1599
  };
1248
1600
  // src/tabs.ts
1249
1601
  const TAB_HEADER_RE = /^(%{1,6})(\+)? (.*)/;
@@ -1399,11 +1751,13 @@ const splitBlockquote = (bq, explicit, sourceLines) => {
1399
1751
  explicit,
1400
1752
  lines: []
1401
1753
  };
1754
+ const base = sourceLines && !explicit ? blockquoteMarkers(bq, sourceLines) : void 0;
1402
1755
  const splitLines = (lines, markersOf, para) => {
1403
1756
  let bodyLines = [];
1404
1757
  for (const [index, lineSegs] of lines.entries()) {
1405
1758
  const first = lineSegs[0];
1406
- if (first?.type === "text") {
1759
+ const outsidePanel = !markersOf || base === void 0 || markersOf(index) < base;
1760
+ if (first?.type === "text" && outsidePanel) {
1407
1761
  const header = parseTabHeader(first.value);
1408
1762
  if (header) {
1409
1763
  if (bodyLines.length > 0) {
@@ -1568,12 +1922,19 @@ const buildTree = (rawTabs, targetDepth, start, end, containerClass) => {
1568
1922
  }
1569
1923
  };
1570
1924
  };
1571
- const processTabsInChildren = (children, containerClass, checkBlankLines = true, sourceLines) => {
1925
+ const processTabsInChildren = (children, containerClass, checkBlankLines = true, sourceLines, quotes = 0, parentType = "root") => {
1572
1926
  const newChildren = [];
1573
1927
  let i = 0;
1574
1928
  while (i < children.length) {
1575
1929
  const node = children[i];
1576
1930
  if (!node) break;
1931
+ if (node.type === "paragraph" && sourceLines && !INDENTED_BLOCKS.has(parentType)) {
1932
+ const split = splitAtOpeningLine(node, sourceLines, quotes, (text) => parseTabHeader(text)?.depth === 1);
1933
+ if (split) {
1934
+ children.splice(i, 1, ...split);
1935
+ continue;
1936
+ }
1937
+ }
1577
1938
  if (node.type === "paragraph") {
1578
1939
  const headerPara = extractHeaderPara(node);
1579
1940
  if (headerPara) {
@@ -1719,13 +2080,15 @@ const processTabsInChildren = (children, containerClass, checkBlankLines = true,
1719
2080
  */
1720
2081
  const processTabs = (root, containerClass, source) => {
1721
2082
  const sourceLines = source?.split(/\r?\n/);
1722
- const walk = (node) => {
2083
+ const walk = (node, quotes) => {
1723
2084
  if (!Array.isArray(node.children) || node.children.length === 0) return;
1724
- processTabsInChildren(node.children, containerClass, false, sourceLines);
1725
- for (const child of node.children) walk(child);
2085
+ const inner = quotes + (MARKED_BLOCKS.has(node.type ?? "") ? 1 : 0);
2086
+ processTabsInChildren(node.children, containerClass, false, sourceLines, inner, node.type);
2087
+ for (const child of node.children) walk(child, inner);
1726
2088
  };
1727
2089
  processTabsInChildren(root.children, containerClass, true, sourceLines);
1728
- for (const child of root.children) walk(child);
2090
+ for (const child of root.children) walk(child, 0);
2091
+ if (sourceLines) endGroupsAtMarkers(root, sourceLines, `tabsList`);
1729
2092
  };
1730
2093
  // src/editable.ts
1731
2094
  const editableTree = (root, build, setChildren, setData) => {
@@ -1765,7 +2128,7 @@ const editableTree = (root, build, setChildren, setData) => {
1765
2128
  };
1766
2129
  };
1767
2130
  // src/inline-svg.ts
1768
- const hash = (str) => {
2131
+ const hash$1 = (str) => {
1769
2132
  let h = 2166136261;
1770
2133
  for (let i = 0; i < str.length; i++) h = Math.imul(h ^ str.charCodeAt(i), 16777619) >>> 0;
1771
2134
  return h.toString(36);
@@ -1773,7 +2136,7 @@ const hash = (str) => {
1773
2136
  /**
1774
2137
  * The id of a deduplicated SVG's sprite: a hash of its path relative to the document, with `/` separators, so the same document renders the same ids on any machine or OS (an absolute path would leak the build machine into the HTML).
1775
2138
  */
1776
- const svgSpriteId = (relativePath) => `svg-sprite-${hash(relativePath)}`;
2139
+ const svgSpriteId = (relativePath) => `svg-sprite-${hash$1(relativePath)}`;
1777
2140
  const extractSvgParts = (content) => {
1778
2141
  const openTag = /<svg([^>]*?)>/i.exec(content)?.[1] ?? ``;
1779
2142
  return {
@@ -1813,6 +2176,95 @@ const inlineSvgMarkup = (content, alts, relativePath, limits) => {
1813
2176
  return index === 0 ? sprite + use : use;
1814
2177
  });
1815
2178
  };
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 };
2179
+ // src/mermaid-pele.ts
2180
+ /** The class a fenced `mermaid` block's `<code>` takes. */
2181
+ const MERMAID_LANGUAGE_CLASS = `language-mermaid`;
2182
+ /** The class a `<pre>` holding a diagram written as HTML takes, as Mermaid reads it in the browser. */
2183
+ const MERMAID_PRE_CLASS = `mermaid`;
2184
+ const hash = (str) => {
2185
+ let h = 2166136261;
2186
+ for (let i = 0; i < str.length; i++) h = Math.imul(h ^ str.charCodeAt(i), 16777619) >>> 0;
2187
+ return h.toString(36);
2188
+ };
2189
+ /** A diagram's source as Pele reads it: line endings normalized, as markdown-it already has them. */
2190
+ const mermaidSource = (text) => text.replace(/\r\n?/g, `\n`);
2191
+ /**
2192
+ * A document's id prefixes, one per diagram in document order: `prefix`, then a hash of the diagram's source, so a diagram's ids don't change when others are edited, with `-2`, `-3`… for repeats of one diagram. Pele namespaces every id it writes (an accessible title, a gradient) under it. Create one per document.
2193
+ */
2194
+ const mermaidIdPrefixes = (prefix) => {
2195
+ const seen = /* @__PURE__ */ new Map();
2196
+ return (source) => {
2197
+ const id = `${prefix}-${hash(source)}`;
2198
+ const count = (seen.get(id) ?? 0) + 1;
2199
+ seen.set(id, count);
2200
+ return count === 1 ? id : `${id}-${count}`;
2201
+ };
2202
+ };
2203
+ const escapeAttribute = (value) => value.replace(/&/g, `&amp;`).replace(/"/g, `&quot;`);
2204
+ const classes = (value) => value.split(/\s+/).filter((name) => name !== `` && name !== MERMAID_LANGUAGE_CLASS && name !== MERMAID_PRE_CLASS);
2205
+ /**
2206
+ * Pele's `<svg>` with the attributes the diagram's element had (from an attribute block such as `{#id .class}`): classes are added after Pele's, a style after Pele's (whose theme variables the diagram needs), and anything else is set, replacing Pele's value. The class that marked the element as a diagram is dropped.
2207
+ */
2208
+ const mermaidSvg = (svg, attributes) => {
2209
+ const open = /^<svg\b[^>]*>/.exec(svg)?.[0];
2210
+ if (!open || attributes.length === 0) return svg;
2211
+ const own = [...open.slice(4, -1).matchAll(/\s([^\s=]+)="([^"]*)"/g)].map(([, name = ``, value = ``]) => [name, value]);
2212
+ const merged = new Map(own);
2213
+ for (const [name, value] of attributes) {
2214
+ const escaped = escapeAttribute(value);
2215
+ const current = merged.get(name);
2216
+ if (name === `class`) {
2217
+ const added = classes(value).map(escapeAttribute).join(` `);
2218
+ if (added) merged.set(name, current ? `${current} ${added}` : added);
2219
+ } else if (name === `style` && current) merged.set(name, `${current.replace(/;$/, ``)};${escaped}`);
2220
+ else merged.set(name, escaped);
2221
+ }
2222
+ return `<svg${[...merged].map(([name, value]) => ` ${name}="${value}"`).join(``)}>${svg.slice(open.length)}`;
2223
+ };
2224
+ /** A hast element's properties as attributes; `className` becomes `class`, and other names are kept as the attribute plugins write them. */
2225
+ const hastAttributes = (properties) => {
2226
+ const text = (value) => typeof value === `string` ? value : typeof value === `number` ? String(value) : value === true ? `` : Array.isArray(value) ? value.map((item) => text(item) ?? ``).join(` `) : void 0;
2227
+ const attributes = [];
2228
+ for (const [key, value] of Object.entries(properties ?? {})) {
2229
+ const written = text(value);
2230
+ if (written !== void 0) attributes.push([key === `className` ? `class` : key, written]);
2231
+ }
2232
+ return attributes;
2233
+ };
2234
+ const hasClass = (node, name) => {
2235
+ const value = node.properties?.className;
2236
+ return Array.isArray(value) ? value.includes(name) : typeof value === `string` && value.split(/\s+/).includes(name);
2237
+ };
2238
+ const textOf = (node) => node.type === `text` ? node.value ?? `` : (node.children ?? []).map(textOf).join(``);
2239
+ /**
2240
+ * The diagram a `<pre>` holds, as rehype-mermaid finds them: a `<pre>` whose only content (besides whitespace) is a `<code class="language-mermaid">`, which is how a fenced `mermaid` block renders, or a `<pre class="mermaid">`. `element` is the one whose attributes the `<svg>` takes. `undefined` for anything else.
2241
+ */
2242
+ const mermaidDiagram = (pre) => {
2243
+ if (pre.tagName !== `pre`) return void 0;
2244
+ if (hasClass(pre, MERMAID_PRE_CLASS)) return {
2245
+ source: mermaidSource(textOf(pre)),
2246
+ element: pre
2247
+ };
2248
+ let code;
2249
+ for (const child of pre.children ?? []) {
2250
+ if (child.type === `text` && /^\s*$/.test(child.value ?? ``)) continue;
2251
+ if (code || child.tagName !== `code`) return void 0;
2252
+ code = child;
2253
+ }
2254
+ return code && hasClass(code, MERMAID_LANGUAGE_CLASS) ? {
2255
+ source: mermaidSource(textOf(code)),
2256
+ element: code
2257
+ } : void 0;
2258
+ };
2259
+ /**
2260
+ * The error a diagram that failed to render throws: Pele's message, at the document line Pele located the problem on (counting from `line`, the line the diagram's element starts on), or at `line` itself when Pele has no location. The original error is the `cause`.
2261
+ */
2262
+ const mermaidFailure = (error, line) => {
2263
+ const reason = error instanceof Error ? error.message : String(error);
2264
+ const within = error instanceof Error && `line` in error && typeof error.line === `number` && error.line > 0 ? error.line : 0;
2265
+ const at = line === void 0 ? `` : ` on line ${String(line + within)}`;
2266
+ return new Error(`Could not render the mermaid diagram${at}: ${reason}`, { cause: error });
2267
+ };
2268
+ 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
2269
 
1818
2270
  //# sourceMappingURL=index.mjs.map