@gmb/bitmark-parser-generator 5.40.0 → 5.41.0

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.cjs CHANGED
@@ -5103,6 +5103,9 @@ var GROUPS = {
5103
5103
  key: ConfigKey.tag_item,
5104
5104
  jsonKey: "item",
5105
5105
  exportJsonKey: { item: "$" },
5106
+ // PLAN-140 W3 (NISO import): the clause/note label of a mapped
5107
+ // source element ([%1], [%ANMERKUNG 1]) — same as group_standardItemLead.
5108
+ mappingKeys: { "xml-niso-iec": { "@el": "label", "@text": "$" } },
5106
5109
  description: "The item for the bit"
5107
5110
  }
5108
5111
  ]
@@ -16728,7 +16731,7 @@ var instance2 = new Config();
16728
16731
  // src/generated/package_info.ts
16729
16732
  var PACKAGE_INFO = {
16730
16733
  "name": "@gmb/bitmark-parser-generator",
16731
- "version": "5.40.0",
16734
+ "version": "5.41.0",
16732
16735
  "author": "Get More Brain Ltd",
16733
16736
  "license": "ISC",
16734
16737
  "description": "A bitmark parser and generator using Peggy.js"
@@ -18582,6 +18585,752 @@ var AstWalkerGenerator = class {
18582
18585
  }
18583
18586
  };
18584
18587
 
18588
+ // src/generator/text/TextGrammarConstraints.ts
18589
+ var COLORS = [
18590
+ "aqua",
18591
+ "black",
18592
+ "blue",
18593
+ "brown",
18594
+ "fuchsia",
18595
+ "lightgrey",
18596
+ "gray",
18597
+ "darkgray",
18598
+ "green",
18599
+ "lime",
18600
+ "magenta",
18601
+ "maroon",
18602
+ "navy",
18603
+ "olive",
18604
+ "orange",
18605
+ "pink",
18606
+ "purple",
18607
+ "red",
18608
+ "silver",
18609
+ "teal",
18610
+ "violet",
18611
+ "white",
18612
+ "yellow"
18613
+ ];
18614
+ var HIGHLIGHT_COLORS = [
18615
+ "orange",
18616
+ "yellow",
18617
+ "green",
18618
+ "blue",
18619
+ "purple",
18620
+ "pink",
18621
+ "brown",
18622
+ "white",
18623
+ "black",
18624
+ "gray"
18625
+ ];
18626
+ var ALTERNATIVE_STYLE_TAGS = [
18627
+ "bold",
18628
+ "italic",
18629
+ "light",
18630
+ "highlightOrange",
18631
+ "highlightYellow",
18632
+ "highlightGreen",
18633
+ "highlightBlue",
18634
+ "highlightPurple",
18635
+ "highlightPink",
18636
+ "highlightBrown",
18637
+ "highlightBlack",
18638
+ "highlightWhite",
18639
+ "highlightGray",
18640
+ "highlight",
18641
+ "strike",
18642
+ "subscript",
18643
+ "superscript",
18644
+ "ins",
18645
+ "del",
18646
+ "underline",
18647
+ "doubleUnderline",
18648
+ "smallcaps",
18649
+ "circle",
18650
+ "languageEmRed",
18651
+ "languageEmOrange",
18652
+ "languageEmYellow",
18653
+ "languageEmGreen",
18654
+ "languageEmBlue",
18655
+ "languageEmPurple",
18656
+ "languageEmPink",
18657
+ "languageEmBrown",
18658
+ "languageEmBlack",
18659
+ "languageEmWhite",
18660
+ "languageEmGray",
18661
+ "languageEm",
18662
+ "userUnderline",
18663
+ "userDoubleUnderline",
18664
+ "userStrike",
18665
+ "userCircle",
18666
+ "userHighlight",
18667
+ "notranslate"
18668
+ ];
18669
+ var MEDIA_ALIGNMENTS = ["left", "center", "right"];
18670
+ var INLINE_MEDIA_ALIGNMENTS = [
18671
+ "top",
18672
+ "middle",
18673
+ "bottom",
18674
+ "baseline",
18675
+ "sub",
18676
+ "super",
18677
+ "text-bottom",
18678
+ "text-top"
18679
+ ];
18680
+ var INLINE_MEDIA_SIZES = [
18681
+ "line-height",
18682
+ "font-height",
18683
+ "super",
18684
+ "sub",
18685
+ "explicit"
18686
+ ];
18687
+ var LINE_TERMINATOR_REGEX = /[\n\r\u2028\u2029]/;
18688
+ var CHAIN_STRING_INVALID_REGEX = /[|\n\r\u2028\u2029]/;
18689
+ var URL_CHAR_CLASS = "a-zA-Z0-9!*'()=+,\\-./_?#@[\\]$&;%:{}~^";
18690
+ var URL_CHAR_REGEX = new RegExp(`^[${URL_CHAR_CLASS}]$`);
18691
+ var URL_HTTP_REGEX = new RegExp(`^https?://[${URL_CHAR_CLASS}]*$`);
18692
+ var URL_REGEX = new RegExp(`^(https?://|mailto:)[${URL_CHAR_CLASS}]*$`);
18693
+ function isChainString(v) {
18694
+ return typeof v === "string" && !CHAIN_STRING_INVALID_REGEX.test(v);
18695
+ }
18696
+ function isSingleLine(v) {
18697
+ return typeof v === "string" && !LINE_TERMINATOR_REGEX.test(v);
18698
+ }
18699
+ function hasLineTerminator(v) {
18700
+ return LINE_TERMINATOR_REGEX.test(v);
18701
+ }
18702
+ function isColor(v) {
18703
+ return typeof v === "string" && COLORS.indexOf(v) !== -1;
18704
+ }
18705
+ function isHighlightColor(v) {
18706
+ return typeof v === "string" && HIGHLIGHT_COLORS.indexOf(v) !== -1;
18707
+ }
18708
+ function isMediaAlignment(v) {
18709
+ return typeof v === "string" && MEDIA_ALIGNMENTS.indexOf(v) !== -1;
18710
+ }
18711
+ function isInlineMediaAlignment(v) {
18712
+ return typeof v === "string" && INLINE_MEDIA_ALIGNMENTS.indexOf(v) !== -1;
18713
+ }
18714
+ function isInlineMediaSize(v) {
18715
+ return typeof v === "string" && INLINE_MEDIA_SIZES.indexOf(v) !== -1;
18716
+ }
18717
+ function toUInt(v) {
18718
+ if (typeof v === "number") return Number.isInteger(v) && v >= 0 ? v : void 0;
18719
+ if (typeof v === "string" && /^[0-9]+$/.test(v)) return parseInt(v, 10);
18720
+ return void 0;
18721
+ }
18722
+ function toBoolean(v) {
18723
+ if (typeof v === "boolean") return v;
18724
+ if (v === "true") return true;
18725
+ if (v === "false") return false;
18726
+ return void 0;
18727
+ }
18728
+ function isDuration(v) {
18729
+ return isChainString(v) && v.trim().startsWith("P");
18730
+ }
18731
+ function isUrlHttp(v) {
18732
+ return typeof v === "string" && URL_HTTP_REGEX.test(v);
18733
+ }
18734
+ function isUrl(v) {
18735
+ return typeof v === "string" && URL_REGEX.test(v);
18736
+ }
18737
+ function isUrlChar(c) {
18738
+ return URL_CHAR_REGEX.test(c);
18739
+ }
18740
+ function bareUrlText(href) {
18741
+ return href.replace(/^(https?:\/\/|mailto:)/, "");
18742
+ }
18743
+ function isInlineAlt(v) {
18744
+ return typeof v === "string" && v.length > 0 && v.indexOf("==") === -1 && !v.startsWith("=") && !v.endsWith("=");
18745
+ }
18746
+ function isHeadingLevel(v) {
18747
+ return typeof v === "number" && Number.isInteger(v) && v >= 1 && v <= 3;
18748
+ }
18749
+ function normalizeInlineCodeLanguage(v) {
18750
+ if (!isChainString(v)) return void 0;
18751
+ return v.trim().toLowerCase();
18752
+ }
18753
+ function normalizeBlockCodeLanguage(v) {
18754
+ if (!isSingleLine(v)) return void 0;
18755
+ return v.trim().toLowerCase();
18756
+ }
18757
+
18758
+ // src/generator/text/TextMediaAttrs.ts
18759
+ var uint = (v) => toUInt(v);
18760
+ var bool = (v) => toBoolean(v);
18761
+ var alignment = (v) => isMediaAlignment(v) ? v : void 0;
18762
+ var chainString = (v) => isChainString(v) ? v.trim() : void 0;
18763
+ var inlineAlignment = (v) => isInlineMediaAlignment(v) ? v : void 0;
18764
+ var inlineSize = (v) => isInlineMediaSize(v) ? v : void 0;
18765
+ var VALIDATORS = {
18766
+ image: {
18767
+ alt: chainString,
18768
+ title: chainString,
18769
+ alignment,
18770
+ textAlign: alignment,
18771
+ zoomDisabled: bool,
18772
+ width: uint,
18773
+ height: uint,
18774
+ comment: chainString
18775
+ },
18776
+ imageInline: {
18777
+ srcAlt: chainString,
18778
+ alignmentVertical: inlineAlignment,
18779
+ size: inlineSize,
18780
+ width: uint,
18781
+ height: uint,
18782
+ comment: chainString
18783
+ },
18784
+ symbol: {
18785
+ alt: chainString,
18786
+ title: chainString,
18787
+ caption: chainString,
18788
+ alignment,
18789
+ textAlign: alignment,
18790
+ captionAlign: alignment,
18791
+ zoomDisabled: bool,
18792
+ width: uint,
18793
+ height: uint,
18794
+ comment: chainString
18795
+ }
18796
+ };
18797
+ var TAG_NAMES = {
18798
+ textAlign: "captionAlign",
18799
+ title: "caption"
18800
+ };
18801
+ var DEFAULTS = {
18802
+ image: { alignment: "center", textAlign: "left", zoomDisabled: true },
18803
+ imageInline: { alignmentVertical: "top", size: "line-height" },
18804
+ symbol: {}
18805
+ };
18806
+ function sanitizeMediaAttrs(kind, attrs) {
18807
+ const result = {};
18808
+ if (!attrs || typeof attrs !== "object") return result;
18809
+ const validators = VALIDATORS[kind];
18810
+ for (const [k, v] of Object.entries(attrs)) {
18811
+ const validator = validators[k];
18812
+ if (!validator || v == null) continue;
18813
+ const valid = validator(v);
18814
+ if (valid !== void 0) result[k] = valid;
18815
+ }
18816
+ if (kind === "symbol") {
18817
+ if ("caption" in result) delete result.title;
18818
+ if ("captionAlign" in result) delete result.textAlign;
18819
+ }
18820
+ return result;
18821
+ }
18822
+ function serializeMediaChain(kind, attrs) {
18823
+ const defaults = DEFAULTS[kind];
18824
+ let s = "";
18825
+ for (const k of Object.keys(VALIDATORS[kind])) {
18826
+ const v = attrs[k];
18827
+ if (v == null) continue;
18828
+ if (k in defaults && defaults[k] === v) continue;
18829
+ if (k === "comment") {
18830
+ if (v) s += `|#${v}`;
18831
+ } else if (typeof v === "string" && v === "") {
18832
+ continue;
18833
+ } else {
18834
+ s += `|${TAG_NAMES[k] ?? k}:${v}`;
18835
+ }
18836
+ }
18837
+ return s;
18838
+ }
18839
+
18840
+ // src/generator/text/TextMarkSanitizer.ts
18841
+ var STANDARD_SHORT_FORM_MARKS = [
18842
+ TextMarkType.bold,
18843
+ TextMarkType.italic,
18844
+ TextMarkType.light,
18845
+ TextMarkType.highlight
18846
+ ];
18847
+ function attrsOf(mark2) {
18848
+ const a = mark2.attrs;
18849
+ return a && typeof a === "object" && !Array.isArray(a) ? a : {};
18850
+ }
18851
+ function chainValue(v) {
18852
+ if (v == null) return "";
18853
+ return isChainString(v) ? v.trim() : void 0;
18854
+ }
18855
+ function mark(type, attrs) {
18856
+ return attrs ? { type, attrs } : { type };
18857
+ }
18858
+ function sanitizeMark(m, options) {
18859
+ const attrs = attrsOf(m);
18860
+ switch (m.type) {
18861
+ case TextMarkType.highlight:
18862
+ case TextMarkType.userHighlight: {
18863
+ const color = attrs.color;
18864
+ return isHighlightColor(color) ? mark(m.type, { color }) : mark(m.type);
18865
+ }
18866
+ case TextMarkType.textStyle:
18867
+ case TextMarkType.color: {
18868
+ const color = attrs.color;
18869
+ return isColor(color) ? mark(TextMarkType.textStyle, { color }) : void 0;
18870
+ }
18871
+ case TextMarkType.link: {
18872
+ const href = chainValue(attrs.href);
18873
+ return href === void 0 ? void 0 : mark(m.type, { href });
18874
+ }
18875
+ case TextMarkType.ref: {
18876
+ const reference = chainValue(attrs.reference);
18877
+ return reference === void 0 ? void 0 : mark(m.type, { reference });
18878
+ }
18879
+ case TextMarkType.xref: {
18880
+ const xref = chainValue(attrs.xref);
18881
+ if (xref === void 0) return void 0;
18882
+ const reference = chainValue(attrs.reference) ?? "";
18883
+ return mark(m.type, { xref, reference });
18884
+ }
18885
+ case TextMarkType.extref: {
18886
+ const extref = chainValue(attrs.extref);
18887
+ const provider = chainValue(attrs.provider);
18888
+ if (extref === void 0 || provider === void 0) return void 0;
18889
+ const refs = Array.isArray(attrs.references) ? attrs.references : [];
18890
+ const references = refs.map((r) => chainValue(r)).filter((r) => r !== void 0);
18891
+ return mark(m.type, { extref, references, provider });
18892
+ }
18893
+ case TextMarkType.footnote:
18894
+ case TextMarkType.footnoteStar: {
18895
+ const content = options.normalizeChainValue ? options.normalizeChainValue(attrs.content) : Array.isArray(attrs.content) ? attrs.content : [];
18896
+ return content === void 0 ? void 0 : mark(m.type, { content });
18897
+ }
18898
+ case TextMarkType.symbol: {
18899
+ const src = chainValue(attrs.src);
18900
+ if (src === void 0) return void 0;
18901
+ return mark(m.type, { src, ...sanitizeMediaAttrs("symbol", attrs) });
18902
+ }
18903
+ case TextMarkType.var: {
18904
+ const name = chainValue(attrs.name);
18905
+ return name === void 0 ? void 0 : mark(m.type, { name });
18906
+ }
18907
+ case TextMarkType.code: {
18908
+ const language = normalizeInlineCodeLanguage(attrs.language);
18909
+ return language === void 0 ? mark(m.type) : mark(m.type, { language });
18910
+ }
18911
+ case TextMarkType.timer: {
18912
+ const duration = attrs.duration;
18913
+ if (!isDuration(duration)) return void 0;
18914
+ const name = chainValue(attrs.name) ?? "";
18915
+ return mark(m.type, { name, duration: duration.trim() });
18916
+ }
18917
+ case TextMarkType.colorPicker: {
18918
+ const propertyRef = chainValue(attrs.propertyRef);
18919
+ return propertyRef === void 0 ? void 0 : mark(m.type, { propertyRef });
18920
+ }
18921
+ case TextMarkType.comment: {
18922
+ const comment = chainValue(m.comment);
18923
+ return comment === void 0 ? void 0 : { type: m.type, comment };
18924
+ }
18925
+ default:
18926
+ if (ALTERNATIVE_STYLE_TAGS.indexOf(m.type) !== -1) return mark(m.type);
18927
+ return void 0;
18928
+ }
18929
+ }
18930
+ function mergeLegacyTimer(marks) {
18931
+ const timer = marks.find((m) => m.type === TextMarkType.timer);
18932
+ const duration = marks.find((m) => m.type === TextMarkType.duration);
18933
+ if (!timer || !duration || attrsOf(timer).duration) return marks;
18934
+ return marks.map(
18935
+ (m) => m === timer ? { ...timer, attrs: { ...attrsOf(timer), duration: attrsOf(duration).duration } } : m
18936
+ );
18937
+ }
18938
+ function orderMarks(marks) {
18939
+ const a = (m) => m.attrs ?? {};
18940
+ const isBareHighlight = (m) => (m.type === TextMarkType.highlight || m.type === TextMarkType.userHighlight) && !a(m).color;
18941
+ const isHighlightColorStyle = (m) => m.type === TextMarkType.textStyle && isHighlightColor(a(m).color);
18942
+ const isEmptyXref = (m) => m.type === TextMarkType.xref && !a(m).reference;
18943
+ const result = [];
18944
+ for (const m of marks) {
18945
+ if (m.type === TextMarkType.ref) {
18946
+ const xrefIdx = result.findIndex(isEmptyXref);
18947
+ if (xrefIdx !== -1) {
18948
+ result.splice(xrefIdx, 0, m);
18949
+ continue;
18950
+ }
18951
+ }
18952
+ result.push(m);
18953
+ }
18954
+ for (let i = 0; i < result.length - 1; i++) {
18955
+ if (isBareHighlight(result[i]) && isHighlightColorStyle(result[i + 1])) {
18956
+ [result[i], result[i + 1]] = [result[i + 1], result[i]];
18957
+ }
18958
+ }
18959
+ const symbols = result.filter((m) => m.type === TextMarkType.symbol);
18960
+ if (symbols.length === 0) return result;
18961
+ return [...result.filter((m) => m.type !== TextMarkType.symbol), symbols[0]];
18962
+ }
18963
+ function sanitizeMarks(marks, options = {}) {
18964
+ if (!Array.isArray(marks)) return [];
18965
+ let loose = marks.filter(
18966
+ (m) => !!m && typeof m === "object" && typeof m.type === "string"
18967
+ );
18968
+ loose = mergeLegacyTimer(loose);
18969
+ const result = [];
18970
+ for (const m of loose) {
18971
+ const s = sanitizeMark(m, options);
18972
+ if (s) result.push(s);
18973
+ }
18974
+ if (options.chainValue) {
18975
+ if (result.length === 1 && STANDARD_SHORT_FORM_MARKS.indexOf(result[0].type) !== -1) {
18976
+ return [mark(result[0].type)];
18977
+ }
18978
+ if (result.length === 1 && result[0].type === TextMarkType.link) return result;
18979
+ return [];
18980
+ }
18981
+ return orderMarks(result);
18982
+ }
18983
+
18984
+ // src/generator/text/TextAstNormalizer.ts
18985
+ var LIST_TYPES = [
18986
+ TextNodeType.noBulletList,
18987
+ TextNodeType.bulletList,
18988
+ TextNodeType.orderedList,
18989
+ TextNodeType.orderedListRoman,
18990
+ TextNodeType.orderedListRomanLower,
18991
+ TextNodeType.letteredList,
18992
+ TextNodeType.letteredListLower,
18993
+ TextNodeType.taskList
18994
+ ];
18995
+ var ORDERED_LIST_TYPES = [
18996
+ TextNodeType.orderedList,
18997
+ TextNodeType.orderedListRoman,
18998
+ TextNodeType.orderedListRomanLower
18999
+ ];
19000
+ var BODY_BIT_TYPES = [
19001
+ TextNodeType.gap,
19002
+ TextNodeType.select,
19003
+ TextNodeType.highlight,
19004
+ TextNodeType.mark
19005
+ ];
19006
+ var LEADING_TAB_REGEX = /(^|[\n\r\u2028\u2029])\t+/g;
19007
+ var isList = (n) => LIST_TYPES.indexOf(n.type) !== -1;
19008
+ var isListItem = (n) => n.type === TextNodeType.listItem || n.type === TextNodeType.taskItem;
19009
+ var isBodyBit = (n) => BODY_BIT_TYPES.indexOf(n.type) !== -1;
19010
+ var isNode = (n) => !!n && typeof n === "object" && typeof n.type === "string";
19011
+ var contentOf = (n) => Array.isArray(n.content) ? n.content.filter(isNode) : [];
19012
+ var attrsOf2 = (n) => n.attrs && typeof n.attrs === "object" ? n.attrs : {};
19013
+ var asNode = (n) => n;
19014
+ var paragraph = (content) => asNode({ type: TextNodeType.paragraph, attrs: {}, content });
19015
+ var hardBreak = () => ({ type: TextNodeType.hardBreak });
19016
+ var isHardBreak = (n) => n?.type === TextNodeType.hardBreak;
19017
+ var hasText = (nodes) => nodes.some(
19018
+ (n) => n.type !== TextNodeType.hardBreak && (n.type !== TextNodeType.text || typeof n.text === "string" && n.text.trim() !== "")
19019
+ );
19020
+ function canFollowBareUrl(next) {
19021
+ if (!next) return true;
19022
+ if (next.type === TextNodeType.hardBreak) return true;
19023
+ if (next.type !== TextNodeType.text) return false;
19024
+ if (next.marks && next.marks.length > 0) return false;
19025
+ const first = (next.text ?? "").charAt(0);
19026
+ return first === "" || !isUrlChar(first);
19027
+ }
19028
+ function isSimpleLinkNode(node, next) {
19029
+ if (node.type !== TextNodeType.text || node.marks?.length !== 1) return false;
19030
+ const m = node.marks[0];
19031
+ if (m.type !== TextMarkType.link) return false;
19032
+ const href = m.attrs?.href;
19033
+ return isUrl(href) && bareUrlText(href) === node.text && canFollowBareUrl(next);
19034
+ }
19035
+ var FLATTENED_BLOCK_TYPES = [
19036
+ TextNodeType.paragraph,
19037
+ TextNodeType.heading,
19038
+ TextNodeType.section,
19039
+ TextNodeType.listItem,
19040
+ TextNodeType.taskItem,
19041
+ TextNodeType.codeBlock
19042
+ ];
19043
+ var TextAstNormalizer = class {
19044
+ constructor(options) {
19045
+ __publicField(this, "options");
19046
+ this.options = options;
19047
+ }
19048
+ normalize(ast) {
19049
+ if (!Array.isArray(ast)) return [];
19050
+ const nodes = ast.filter(isNode);
19051
+ if (this.options.chainValue) return this.inline(nodes, { plain: true, chainValue: true });
19052
+ if (this.options.location === TextLocation.tag) {
19053
+ return nodes.flatMap((n) => this.inlineRootNode(n, {}));
19054
+ }
19055
+ return nodes.flatMap((n) => this.block(n));
19056
+ }
19057
+ //
19058
+ // Block context
19059
+ //
19060
+ block(n) {
19061
+ switch (n.type) {
19062
+ case TextNodeType.paragraph:
19063
+ return [{ ...n, content: this.inline(contentOf(n)) }];
19064
+ case TextNodeType.heading:
19065
+ return this.heading(n);
19066
+ case TextNodeType.section:
19067
+ return [paragraph(this.inline(contentOf(n)))];
19068
+ case TextNodeType.image: {
19069
+ const image = this.image(n);
19070
+ return image ? [image] : [];
19071
+ }
19072
+ case TextNodeType.codeBlock:
19073
+ return [{ ...n, content: this.code(contentOf(n)) }];
19074
+ case TextNodeType.text:
19075
+ case TextNodeType.hardBreak:
19076
+ case TextNodeType.imageInline:
19077
+ case TextNodeType.latex:
19078
+ return this.inline([n]);
19079
+ default:
19080
+ if (isList(n)) return this.list(n);
19081
+ if (isListItem(n)) return [paragraph(this.inline(contentOf(n)))];
19082
+ if (isBodyBit(n)) return [n];
19083
+ if (Array.isArray(n.content))
19084
+ return [{ ...n, content: this.blocks(contentOf(n)) }];
19085
+ return [n];
19086
+ }
19087
+ }
19088
+ blocks(nodes) {
19089
+ return nodes.flatMap((n) => this.block(n));
19090
+ }
19091
+ heading(n) {
19092
+ const level = attrsOf2(n).level;
19093
+ if (level != null && !isHeadingLevel(level)) {
19094
+ return [paragraph(this.inline(contentOf(n)))];
19095
+ }
19096
+ const content = this.inline(contentOf(n), { singleLine: true });
19097
+ if (!hasText(content)) return [];
19098
+ return [{ ...n, content }];
19099
+ }
19100
+ image(n) {
19101
+ const attrs = attrsOf2(n);
19102
+ if (!isUrlHttp(attrs.src)) return void 0;
19103
+ return asNode({ ...n, attrs: { src: attrs.src, ...sanitizeMediaAttrs("image", attrs) } });
19104
+ }
19105
+ code(nodes) {
19106
+ const result = [];
19107
+ for (const n of nodes) {
19108
+ if (n.type === TextNodeType.text) {
19109
+ if (typeof n.text === "string" && n.text) {
19110
+ result.push({ type: TextNodeType.text, text: n.text });
19111
+ }
19112
+ } else if (n.type === TextNodeType.hardBreak) {
19113
+ result.push(hardBreak());
19114
+ } else {
19115
+ result.push(...this.code(contentOf(n)));
19116
+ }
19117
+ }
19118
+ return result;
19119
+ }
19120
+ //
19121
+ // List context
19122
+ //
19123
+ list(n) {
19124
+ const attrs = attrsOf2(n);
19125
+ const ordered = ORDERED_LIST_TYPES.indexOf(n.type) !== -1;
19126
+ let start;
19127
+ if (ordered && attrs.start != null) {
19128
+ start = toUInt(attrs.start);
19129
+ if (start === void 0) {
19130
+ return contentOf(n).map(
19131
+ (item) => paragraph(this.inline([item], { stripLeadingTabs: true }))
19132
+ );
19133
+ }
19134
+ }
19135
+ const items = [];
19136
+ for (const child of contentOf(n)) {
19137
+ if (isListItem(child)) {
19138
+ items.push(...this.listItem(child));
19139
+ } else {
19140
+ items.push(...this.listItem({ type: TextNodeType.listItem, content: [child] }));
19141
+ }
19142
+ }
19143
+ if (items.length === 0) return [];
19144
+ const newAttrs = { ...attrs };
19145
+ if (ordered && start !== void 0) newAttrs.start = start;
19146
+ return [asNode({ ...n, attrs: newAttrs, content: items })];
19147
+ }
19148
+ /**
19149
+ * A list item holds a paragraph, then at most one sublist. Everything else is merged into the
19150
+ * paragraph; further sublists become new items (with an empty paragraph, which keeps them
19151
+ * attached). Returns the resulting item(s).
19152
+ */
19153
+ listItem(item) {
19154
+ const inlineOpts = { stripLeadingTabs: true };
19155
+ const acc = {};
19156
+ let sublist;
19157
+ const extraItems = [];
19158
+ const merge = (inline) => {
19159
+ if (!acc.para) acc.para = inline;
19160
+ else if (inline.length > 0) acc.para.push(hardBreak(), ...inline);
19161
+ };
19162
+ for (const child of contentOf(item)) {
19163
+ if (isList(child)) {
19164
+ for (const list of this.list(child)) {
19165
+ if (list.type === TextNodeType.paragraph) {
19166
+ merge(list.content ?? []);
19167
+ } else if (!sublist) {
19168
+ sublist = list;
19169
+ } else {
19170
+ extraItems.push({ ...item, content: [paragraph([]), list] });
19171
+ }
19172
+ }
19173
+ } else if (child.type === TextNodeType.image) {
19174
+ } else {
19175
+ merge(this.inline([child], inlineOpts));
19176
+ }
19177
+ }
19178
+ const paraContent = acc.para ?? [];
19179
+ if (!hasText(paraContent) && !sublist) return extraItems;
19180
+ const content = [paragraph(paraContent)];
19181
+ if (sublist) content.push(sublist);
19182
+ return [{ ...item, content }, ...extraItems];
19183
+ }
19184
+ //
19185
+ // Inline context
19186
+ //
19187
+ /** A root node at tag location: paragraphs of inline content only */
19188
+ inlineRootNode(n, opts) {
19189
+ switch (n.type) {
19190
+ case TextNodeType.text:
19191
+ case TextNodeType.hardBreak:
19192
+ case TextNodeType.imageInline:
19193
+ case TextNodeType.latex:
19194
+ return this.inline([n], opts);
19195
+ case TextNodeType.image:
19196
+ return [];
19197
+ default:
19198
+ if (isBodyBit(n)) return [n];
19199
+ if (isList(n)) {
19200
+ return contentOf(n).map(
19201
+ (item) => paragraph(this.inline([item], { ...opts, stripLeadingTabs: true }))
19202
+ );
19203
+ }
19204
+ return [paragraph(this.inline(contentOf(n), opts))];
19205
+ }
19206
+ }
19207
+ inline(nodes, opts = {}) {
19208
+ const result = [];
19209
+ const separator = () => opts.singleLine ? this.spaceText() : hardBreak();
19210
+ const separate = () => {
19211
+ if (result.length > 0 && !isHardBreak(result[result.length - 1])) result.push(separator());
19212
+ };
19213
+ for (const n of nodes) {
19214
+ switch (n.type) {
19215
+ case TextNodeType.text: {
19216
+ const text = this.text(n, opts);
19217
+ if (text) result.push(text);
19218
+ break;
19219
+ }
19220
+ case TextNodeType.hardBreak:
19221
+ result.push(separator());
19222
+ break;
19223
+ case TextNodeType.imageInline: {
19224
+ if (opts.plain) break;
19225
+ const image = this.imageInline(n);
19226
+ if (image) result.push(image);
19227
+ break;
19228
+ }
19229
+ case TextNodeType.latex:
19230
+ if (opts.plain) break;
19231
+ if (typeof attrsOf2(n).formula === "string") result.push(n);
19232
+ break;
19233
+ case TextNodeType.image:
19234
+ break;
19235
+ default:
19236
+ if (isBodyBit(n)) {
19237
+ result.push(n);
19238
+ } else if (isList(n)) {
19239
+ for (const item of contentOf(n)) {
19240
+ separate();
19241
+ result.push(...this.inline([item], opts));
19242
+ }
19243
+ } else if (FLATTENED_BLOCK_TYPES.indexOf(n.type) !== -1) {
19244
+ separate();
19245
+ result.push(...this.inline(contentOf(n), opts));
19246
+ } else if (Array.isArray(n.content)) {
19247
+ result.push({ ...n, content: this.inline(contentOf(n), opts) });
19248
+ } else {
19249
+ result.push(n);
19250
+ }
19251
+ }
19252
+ }
19253
+ return this.dropSymbolsBeforePipes(result);
19254
+ }
19255
+ /**
19256
+ * A symbol's media chain swallows every following `...|` segment on its line (even when sealed
19257
+ * with `^`), so a symbol mark has no representable form when the rest of the line contains `|`.
19258
+ */
19259
+ dropSymbolsBeforePipes(nodes) {
19260
+ for (let i = 0; i < nodes.length; i++) {
19261
+ const n = nodes[i];
19262
+ if (!n.marks?.some((m) => m.type === TextMarkType.symbol)) continue;
19263
+ let pipeFollows = false;
19264
+ for (let j = i + 1; j < nodes.length && !isHardBreak(nodes[j]); j++) {
19265
+ const text = nodes[j].text;
19266
+ if (typeof text !== "string") continue;
19267
+ const firstLine = text.split(/[\n\r\u2028\u2029]/, 1)[0];
19268
+ if (firstLine.indexOf("|") !== -1) pipeFollows = true;
19269
+ if (hasLineTerminator(text)) break;
19270
+ }
19271
+ if (pipeFollows) {
19272
+ const marks = n.marks.filter((m) => m.type !== TextMarkType.symbol);
19273
+ const { marks: _m, ...rest } = n;
19274
+ nodes[i] = marks.length > 0 ? { ...rest, marks } : rest;
19275
+ }
19276
+ }
19277
+ return nodes;
19278
+ }
19279
+ text(n, opts) {
19280
+ if (typeof n.text !== "string") return void 0;
19281
+ let text = n.text;
19282
+ if (opts.stripLeadingTabs) text = text.replace(LEADING_TAB_REGEX, "$1");
19283
+ if (opts.singleLine) text = text.replace(/[\n\r\u2028\u2029]+/g, " ");
19284
+ if (!text) return void 0;
19285
+ const marks = sanitizeMarks(n.marks, {
19286
+ chainValue: opts.chainValue,
19287
+ normalizeChainValue: (content) => this.chainValue(content)
19288
+ });
19289
+ const { marks: _m, ...rest } = n;
19290
+ return marks.length > 0 ? { ...rest, text, marks } : { ...rest, text };
19291
+ }
19292
+ spaceText() {
19293
+ return { type: TextNodeType.text, text: " " };
19294
+ }
19295
+ imageInline(n) {
19296
+ const attrs = attrsOf2(n);
19297
+ if (!isUrl(attrs.src)) return void 0;
19298
+ const alt = attrs.alt;
19299
+ if (!isInlineAlt(alt)) return void 0;
19300
+ return asNode({
19301
+ ...n,
19302
+ attrs: { src: attrs.src, alt, ...sanitizeMediaAttrs("imageInline", attrs) }
19303
+ });
19304
+ }
19305
+ /**
19306
+ * Footnote content: a `|`-free single line of plain text with short-form standard marks.
19307
+ * Returns undefined when the content cannot be represented.
19308
+ */
19309
+ chainValue(content) {
19310
+ if (!Array.isArray(content)) return [];
19311
+ const nodes = this.inline(content.filter(isNode), {
19312
+ plain: true,
19313
+ chainValue: true,
19314
+ singleLine: true
19315
+ });
19316
+ for (let i = 0; i < nodes.length; i++) {
19317
+ const n = nodes[i];
19318
+ const text = n.text;
19319
+ if (typeof text === "string" && (text.indexOf("|") !== -1 || hasLineTerminator(text))) {
19320
+ return void 0;
19321
+ }
19322
+ if (n.marks?.some((m) => m.type === TextMarkType.link) && !isSimpleLinkNode(n, nodes[i + 1])) {
19323
+ const { marks: _m, ...rest } = n;
19324
+ nodes[i] = rest;
19325
+ }
19326
+ }
19327
+ return nodes;
19328
+ }
19329
+ };
19330
+ function normalizeTextAst(ast, options) {
19331
+ return new TextAstNormalizer(options).normalize(ast);
19332
+ }
19333
+
18585
19334
  // src/generator/text/TextGenerator.ts
18586
19335
  var DEFAULT_OPTIONS = {
18587
19336
  bodyBitCallback: void 0,
@@ -18674,21 +19423,9 @@ var INLINE_MARK_TYPES = [
18674
19423
  TextMarkType.colorPicker,
18675
19424
  TextMarkType.comment
18676
19425
  ];
18677
- var HIGHLIGHT_COLORS = [
18678
- "orange",
18679
- "yellow",
18680
- "green",
18681
- "blue",
18682
- "purple",
18683
- "pink",
18684
- "brown",
18685
- "white",
18686
- "black",
18687
- "gray"
18688
- ];
18689
19426
  var INDENTATION_REGEX = new RegExp(/(\n|\r\n)/, "g");
18690
- var LINK_REGEX = new RegExp(/https?:\/\/|mailto:(.*)/, "g");
18691
19427
  var LINK_BREAKSCAPE_REGEX = new RegExp(/\]/, "g");
19428
+ var CHAIN_CONTINUATION_REGEX = /^[^\n\r\u2028\u2029]*\|/;
18692
19429
  var Stage = {
18693
19430
  enter: "enter",
18694
19431
  between: "between",
@@ -18725,6 +19462,8 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18725
19462
  __publicField(this, "textDepth", 0);
18726
19463
  __publicField(this, "placeholderIndex", 0);
18727
19464
  __publicField(this, "placeholders", {});
19465
+ __publicField(this, "chainJustClosed", false);
19466
+ __publicField(this, "simpleLinkAllowed", true);
18728
19467
  // For pre-text
18729
19468
  __publicField(this, "rootParagraphNodeContentIndex", 0);
18730
19469
  __publicField(this, "previousRootParagraphContextType", TextNodeType.text);
@@ -18770,6 +19509,11 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18770
19509
  generateSync(ast, textFormat, textLocation, options) {
18771
19510
  if (!ast || !Array.isArray(ast)) return "";
18772
19511
  this.generateOptions = Object.assign({}, options);
19512
+ ast = normalizeTextAst(ast, {
19513
+ format: textFormat ?? TextFormat.bitmarkText,
19514
+ location: textLocation,
19515
+ chainValue: this.generateOptions.chainValueContext
19516
+ });
18773
19517
  this.validateGenerateOptions(ast);
18774
19518
  if (!this.generateOptions.plainTextDividerAllowed) {
18775
19519
  this.resetState(textFormat, textLocation);
@@ -18813,6 +19557,8 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18813
19557
  this.textDepth = 0;
18814
19558
  this.placeholderIndex = 0;
18815
19559
  this.placeholders = {};
19560
+ this.chainJustClosed = false;
19561
+ this.simpleLinkAllowed = true;
18816
19562
  this.rootParagraphNodeContentIndex = 0;
18817
19563
  this.previousRootParagraphContextType = TextNodeType.hardBreak;
18818
19564
  this.inPreText = false;
@@ -18878,6 +19624,7 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18878
19624
  this.writeHardBreak(node);
18879
19625
  break;
18880
19626
  case TextNodeType.text: {
19627
+ this.simpleLinkAllowed = this.isSimpleLinkAllowedAfter(route);
18881
19628
  this.writeMarks(node, Stage.enter);
18882
19629
  this.writeText(node);
18883
19630
  this.writeMarks(node, Stage.between);
@@ -18891,9 +19638,6 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18891
19638
  this.writeHeading(node);
18892
19639
  this.inHeading = true;
18893
19640
  break;
18894
- case TextNodeType.section:
18895
- this.writeSection(node);
18896
- break;
18897
19641
  case TextNodeType.listItem:
18898
19642
  case TextNodeType.taskItem:
18899
19643
  this.writeBullet(node, route);
@@ -18951,7 +19695,6 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
18951
19695
  break;
18952
19696
  case TextNodeType.heading:
18953
19697
  this.inHeading = false;
18954
- case TextNodeType.section:
18955
19698
  case TextNodeType.image:
18956
19699
  if (!this.inParagraph) {
18957
19700
  this.writeNL();
@@ -19063,9 +19806,9 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19063
19806
  */
19064
19807
  getLinkHref(node) {
19065
19808
  if (node.type === TextNodeType.text && node.marks) {
19066
- const href = node.marks.reduce((acc, mark) => {
19067
- if (mark.type === TextMarkType.link) {
19068
- const linkMark = mark;
19809
+ const href = node.marks.reduce((acc, mark2) => {
19810
+ if (mark2.type === TextMarkType.link) {
19811
+ const linkMark = mark2;
19069
19812
  const href2 = linkMark.attrs?.href;
19070
19813
  if (href2) return href2;
19071
19814
  }
@@ -19130,6 +19873,9 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19130
19873
  const indentationString = this.getIndentationString();
19131
19874
  s = s.replace(INDENTATION_REGEX, `$1${indentationString}`);
19132
19875
  }
19876
+ if (this.chainJustClosed && CHAIN_CONTINUATION_REGEX.test(s)) {
19877
+ s = `^${s}`;
19878
+ }
19133
19879
  if (this.havePreText && this.rootParagraphNodeContentIndex === this.preTextIndex) {
19134
19880
  this.writePlainTextDivider();
19135
19881
  this.writeNL();
@@ -19159,14 +19905,14 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19159
19905
  * @returns true if a simple link, otherwise false
19160
19906
  */
19161
19907
  isSimpleLink(node) {
19162
- if (node.text == null) return false;
19163
- if (node.marks?.length !== 1) return false;
19164
- const href = this.getLinkHref(node);
19165
- if (href) {
19166
- const hrefText = href.replace(LINK_REGEX, "$1");
19167
- return hrefText === node.text;
19168
- }
19169
- return false;
19908
+ return this.simpleLinkAllowed && isSimpleLinkNode(node, void 0);
19909
+ }
19910
+ /**
19911
+ * The bare-URL link short form is only safe when whatever is written next cannot be read as
19912
+ * part of the URL (the parser's Url rule consumes every following UrlChar).
19913
+ */
19914
+ isSimpleLinkAllowedAfter(route) {
19915
+ return canFollowBareUrl(this.getSiblingNodes(route).right);
19170
19916
  }
19171
19917
  /**
19172
19918
  * Get the link text if the node is a link, otherwise return false
@@ -19178,15 +19924,8 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19178
19924
  if (node.text == null) return false;
19179
19925
  const href = this.getLinkHref(node);
19180
19926
  if (href) {
19181
- let res;
19182
- const hrefText = href.replace(LINK_REGEX, "$1");
19183
- if (hrefText === node.text) {
19184
- res = href;
19185
- } else {
19186
- res = node.text;
19187
- }
19188
- res = (res ?? "").replace(LINK_BREAKSCAPE_REGEX, "^]");
19189
- return res;
19927
+ const res = this.isSimpleLink(node) ? href : node.text;
19928
+ return res.replace(LINK_BREAKSCAPE_REGEX, "^]");
19190
19929
  }
19191
19930
  return false;
19192
19931
  }
@@ -19243,22 +19982,17 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19243
19982
  writeMarks(node, stage) {
19244
19983
  if (node.marks) {
19245
19984
  const forceSingleMark = this.generateOptions.forceInline || !!(this.inInline || this.inHeading);
19246
- this.thisNodeIsPreText = false;
19247
- const emptyMarks = node.marks.length === 0;
19248
- if (emptyMarks) {
19249
- this.writeMarkTextWrapper(INLINE_MARK);
19250
- return;
19251
- }
19252
- const marks = this.getWritableMarks(node.marks);
19985
+ const marks = node.marks;
19253
19986
  if (marks.length === 0) return;
19987
+ this.thisNodeIsPreText = false;
19254
19988
  const singleMark = marks.length === 1 && forceSingleMark;
19255
19989
  const markStartEnd = marks.reduce(
19256
- (acc, mark) => {
19990
+ (acc, mark2) => {
19257
19991
  if (acc) return acc;
19258
- if (this.isStandardMark(mark)) {
19259
- if (singleMark) return STANDARD_MARKS[mark.type];
19992
+ if (this.isStandardMark(mark2)) {
19993
+ if (singleMark) return STANDARD_MARKS[mark2.type];
19260
19994
  return INLINE_MARK;
19261
- } else if (this.isInlineMark(mark)) {
19995
+ } else if (this.isInlineMark(mark2)) {
19262
19996
  return INLINE_MARK;
19263
19997
  } else {
19264
19998
  }
@@ -19273,56 +20007,59 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19273
20007
  this.writeMarkTextWrapper(markStartEnd);
19274
20008
  }
19275
20009
  if (stage == Stage.between) {
19276
- for (const mark of marks) {
19277
- if (this.isStandardMark(mark)) {
20010
+ for (const mark2 of marks) {
20011
+ if (this.isStandardMark(mark2)) {
19278
20012
  if (!singleMark) {
19279
20013
  this.writeInlineMarkStartEnd();
19280
- this.writeInlineMark(mark);
20014
+ this.writeInlineMark(mark2);
19281
20015
  }
19282
- } else if (TextMarkType.comment === mark.type) {
20016
+ } else if (TextMarkType.comment === mark2.type) {
19283
20017
  this.writeInlineMarkStartEnd();
19284
- this.writeCommentMark(mark);
19285
- } else if (TextMarkType.link === mark.type) {
20018
+ this.writeCommentMark(mark2);
20019
+ } else if (TextMarkType.link === mark2.type) {
19286
20020
  this.writeInlineMarkStartEnd();
19287
- this.writeLinkMark(mark);
19288
- } else if (TextMarkType.ref === mark.type) {
20021
+ this.writeLinkMark(mark2);
20022
+ } else if (TextMarkType.ref === mark2.type) {
19289
20023
  this.writeInlineMarkStartEnd();
19290
- this.writeRefMark(mark);
19291
- } else if (TextMarkType.xref === mark.type) {
20024
+ this.writeRefMark(mark2);
20025
+ } else if (TextMarkType.xref === mark2.type) {
19292
20026
  this.writeInlineMarkStartEnd();
19293
- this.writeXRefMark(mark);
19294
- } else if (TextMarkType.extref === mark.type) {
20027
+ this.writeXRefMark(mark2);
20028
+ } else if (TextMarkType.extref === mark2.type) {
19295
20029
  this.writeInlineMarkStartEnd();
19296
- this.writeExtRefMark(mark);
19297
- } else if (TextMarkType.footnote === mark.type) {
20030
+ this.writeExtRefMark(mark2);
20031
+ } else if (TextMarkType.footnote === mark2.type) {
19298
20032
  this.writeInlineMarkStartEnd();
19299
- this.writeFootnoteMark(mark);
19300
- } else if (TextMarkType.footnoteStar === mark.type) {
20033
+ this.writeFootnoteMark(mark2);
20034
+ } else if (TextMarkType.footnoteStar === mark2.type) {
19301
20035
  this.writeInlineMarkStartEnd();
19302
- this.writeFootnoteStarMark(mark);
19303
- } else if (TextMarkType.symbol === mark.type) {
20036
+ this.writeFootnoteStarMark(mark2);
20037
+ } else if (TextMarkType.symbol === mark2.type) {
19304
20038
  this.writeInlineMarkStartEnd();
19305
- this.writeSymbolMark(mark);
19306
- } else if (this.isInlineMark(mark)) {
20039
+ this.writeSymbolMark(mark2);
20040
+ } else if (this.isInlineMark(mark2)) {
19307
20041
  this.writeInlineMarkStartEnd();
19308
- this.writeInlineMark(mark);
20042
+ this.writeInlineMark(mark2);
19309
20043
  } else {
19310
20044
  }
19311
20045
  }
19312
20046
  }
19313
20047
  if (stage == Stage.exit) {
19314
20048
  let inlineMarkWritten = false;
19315
- for (const mark of marks) {
19316
- if (this.isStandardMark(mark)) {
20049
+ for (const mark2 of marks) {
20050
+ if (this.isStandardMark(mark2)) {
19317
20051
  if (!singleMark) {
19318
20052
  inlineMarkWritten = true;
19319
20053
  }
19320
- } else if (TextMarkType.comment === mark.type || TextMarkType.link === mark.type || TextMarkType.ref === mark.type || TextMarkType.xref === mark.type || TextMarkType.footnote === mark.type || TextMarkType.footnoteStar === mark.type || this.isInlineMark(mark)) {
20054
+ } else if (TextMarkType.comment === mark2.type || TextMarkType.link === mark2.type || TextMarkType.ref === mark2.type || TextMarkType.xref === mark2.type || TextMarkType.footnote === mark2.type || TextMarkType.footnoteStar === mark2.type || this.isInlineMark(mark2)) {
19321
20055
  inlineMarkWritten = true;
19322
20056
  } else {
19323
20057
  }
19324
20058
  }
19325
- if (inlineMarkWritten) this.writeInlineMarkStartEnd();
20059
+ if (inlineMarkWritten) {
20060
+ this.writeInlineMarkStartEnd();
20061
+ this.chainJustClosed = true;
20062
+ }
19326
20063
  }
19327
20064
  }
19328
20065
  }
@@ -19358,15 +20095,6 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19358
20095
  s += " ";
19359
20096
  this.write(s);
19360
20097
  }
19361
- writeSection(node) {
19362
- let s = "";
19363
- if (node.section) {
19364
- s = `|${node.section}: `;
19365
- } else {
19366
- s = "|";
19367
- }
19368
- this.write(s);
19369
- }
19370
20098
  writeBullet(node, route) {
19371
20099
  let bullet = this.getIndentationString();
19372
20100
  const listParent = this.getParentNode(route, 2);
@@ -19389,7 +20117,7 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19389
20117
  bullet += "\u2022a ";
19390
20118
  } else if (listType === TextNodeType.taskList) {
19391
20119
  const taskList = node;
19392
- const checked = taskList.attrs?.checked ?? false;
20120
+ const checked = taskList.attrs?.checked === true;
19393
20121
  bullet += checked ? "\u2022+ " : "\u2022- ";
19394
20122
  }
19395
20123
  if (bullet) this.write(bullet);
@@ -19398,28 +20126,20 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19398
20126
  if (node.attrs == null || !node.attrs.src) return;
19399
20127
  const attrs = node.attrs;
19400
20128
  const inlineImage = node.type === TextNodeType.imageInline;
19401
- let ignoreAttributes;
19402
- if (inlineImage) {
19403
- ignoreAttributes = /* @__PURE__ */ new Set(["alt", "zoomDisabled", "title"]);
19404
- const a = attrs;
19405
- if (a.alignmentVertical === "top") ignoreAttributes.add("alignmentVertical");
19406
- if (a.size === "line-height") ignoreAttributes.add("size");
19407
- }
19408
- const mediaAttrs = this.getMediaAttrs(inlineImage ? "imageInline" : "image", attrs, {
19409
- ignoreAttributes
19410
- });
20129
+ const kind = inlineImage ? "imageInline" : "image";
20130
+ const chain = serializeMediaChain(kind, attrs);
19411
20131
  let s = "";
19412
20132
  if (inlineImage) {
19413
20133
  s = `==${attrs.alt ?? ""}==`;
19414
20134
  }
19415
- s += mediaAttrs ? `|${mediaAttrs}|` : "";
20135
+ s += `|${kind}:${attrs.src}${chain}|`;
19416
20136
  this.write(s);
19417
20137
  }
19418
20138
  writeCodeBlock(node) {
19419
- if (node.attrs == null || !node.attrs.language) return;
19420
- const attrs = node.attrs;
19421
- const s = `|code:${attrs.language}
19422
- `;
20139
+ const raw = node.attrs?.language ?? node.language;
20140
+ const language = normalizeBlockCodeLanguage(raw) ?? "";
20141
+ const s = language ? `|code:${language}
20142
+ ` : "|code\n";
19423
20143
  this.write(s);
19424
20144
  }
19425
20145
  writeLatex(node) {
@@ -19435,10 +20155,10 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19435
20155
  writeMarkTextWrapper(s) {
19436
20156
  if (s) this.write(s);
19437
20157
  }
19438
- writeInlineMark(mark) {
19439
- const attrs = mark.attrs ?? {};
20158
+ writeInlineMark(mark2) {
20159
+ const attrs = mark2.attrs ?? {};
19440
20160
  let s;
19441
- switch (mark.type) {
20161
+ switch (mark2.type) {
19442
20162
  case TextMarkType.textStyle:
19443
20163
  case TextMarkType.color:
19444
20164
  s = `color:${attrs.color ?? ""}`;
@@ -19449,10 +20169,10 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19449
20169
  break;
19450
20170
  case TextMarkType.highlight:
19451
20171
  case TextMarkType.userHighlight:
19452
- s = attrs.color ? `${mark.type}|color:${attrs.color}` : `${mark.type}`;
20172
+ s = attrs.color ? `${mark2.type}|color:${attrs.color}` : `${mark2.type}`;
19453
20173
  break;
19454
20174
  default: {
19455
- s = `${mark.type}`;
20175
+ s = `${mark2.type}`;
19456
20176
  for (const [k, v] of Object.entries(attrs)) {
19457
20177
  if (k === "language" && v !== "plain text" || k === "propertyRef" || k === "name") {
19458
20178
  s = `${s}:${v}`;
@@ -19462,88 +20182,49 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19462
20182
  }
19463
20183
  this.write(s);
19464
20184
  }
19465
- /**
19466
- * Resolve the marks of a text node into the marks that will actually be written.
19467
- *
19468
- * - Merges a legacy standalone 'duration' mark into its sibling 'timer' mark.
19469
- * - Drops marks the current grammar cannot express (standalone 'duration', 'timer'
19470
- * without a duration).
19471
- * - Moves a 'textStyle'/'color' mark before a preceding bare 'highlight'/'userHighlight'
19472
- * mark when its color is a valid highlight color; written in the original order the
19473
- * output would re-parse as a highlight-with-color compound mark and change meaning.
19474
- */
19475
- getWritableMarks(nodeMarks) {
19476
- const attrsOf = (m) => m.attrs ?? {};
19477
- let marks = [...nodeMarks];
19478
- const timer = marks.find((m) => m.type === TextMarkType.timer);
19479
- const duration = marks.find((m) => m.type === TextMarkType.duration);
19480
- if (timer && duration && !attrsOf(timer).duration) {
19481
- marks = marks.map(
19482
- (m) => m === timer ? {
19483
- ...timer,
19484
- attrs: { ...attrsOf(timer), duration: attrsOf(duration).duration }
19485
- } : m
19486
- );
19487
- }
19488
- marks = marks.filter((m) => {
19489
- if (m.type === TextMarkType.duration) return false;
19490
- if (m.type === TextMarkType.timer) return !!attrsOf(m).duration;
19491
- return true;
19492
- });
19493
- const isBareHighlight = (m) => (m.type === TextMarkType.highlight || m.type === TextMarkType.userHighlight) && !attrsOf(m).color;
19494
- const isHighlightColorStyle = (m) => (m.type === TextMarkType.textStyle || m.type === TextMarkType.color) && HIGHLIGHT_COLORS.indexOf(attrsOf(m).color) !== -1;
19495
- for (let i = 0; i < marks.length - 1; i++) {
19496
- if (isBareHighlight(marks[i]) && isHighlightColorStyle(marks[i + 1])) {
19497
- const tmp = marks[i];
19498
- marks[i] = marks[i + 1];
19499
- marks[i + 1] = tmp;
19500
- }
19501
- }
19502
- return marks;
19503
- }
19504
20185
  /**
19505
20186
  * True if the mark is written using its standard form (e.g. **bold**, !!highlight!!)
19506
20187
  * when it is the only mark. A highlight mark with a color attribute is excluded - it
19507
20188
  * can only be expressed using the inline chain form (==text==|highlight|color:x|).
19508
20189
  */
19509
- isStandardMark(mark) {
19510
- if (STANDARD_MARK_TYPES.indexOf(mark.type) === -1) return false;
19511
- return !(mark.attrs && mark.attrs.color);
20190
+ isStandardMark(mark2) {
20191
+ if (STANDARD_MARK_TYPES.indexOf(mark2.type) === -1) return false;
20192
+ return !(mark2.attrs && mark2.attrs.color);
19512
20193
  }
19513
20194
  /**
19514
20195
  * True if the mark is written using the inline chain form (==text==|mark|).
19515
20196
  */
19516
- isInlineMark(mark) {
19517
- if (INLINE_MARK_TYPES.indexOf(mark.type) !== -1) return true;
19518
- return mark.type === TextMarkType.highlight && !!(mark.attrs && mark.attrs.color);
20197
+ isInlineMark(mark2) {
20198
+ if (INLINE_MARK_TYPES.indexOf(mark2.type) !== -1) return true;
20199
+ return mark2.type === TextMarkType.highlight && !!(mark2.attrs && mark2.attrs.color);
19519
20200
  }
19520
- writeCommentMark(mark) {
19521
- const comment = mark.comment || "";
20201
+ writeCommentMark(mark2) {
20202
+ const comment = mark2.comment || "";
19522
20203
  const s = `#${comment}`;
19523
20204
  this.write(s);
19524
20205
  }
19525
- writeLinkMark(mark) {
19526
- const href = mark.attrs?.href || "";
20206
+ writeLinkMark(mark2) {
20207
+ const href = mark2.attrs?.href || "";
19527
20208
  const linkText = (href ?? "").replace(LINK_BREAKSCAPE_REGEX, "^]");
19528
20209
  const s = `link:${linkText}`;
19529
20210
  this.write(s);
19530
20211
  }
19531
- writeRefMark(mark) {
19532
- const ref = mark.attrs?.reference ?? "";
20212
+ writeRefMark(mark2) {
20213
+ const ref = mark2.attrs?.reference ?? "";
19533
20214
  const s = `\u25BA${ref}`;
19534
20215
  this.write(s);
19535
20216
  }
19536
- writeXRefMark(mark) {
19537
- const xref = mark.attrs?.xref ?? "";
19538
- const ref = mark.attrs?.reference ?? "";
20217
+ writeXRefMark(mark2) {
20218
+ const xref = mark2.attrs?.xref ?? "";
20219
+ const ref = mark2.attrs?.reference ?? "";
19539
20220
  let s = `xref:${xref}`;
19540
20221
  if (ref) s += `|\u25BA${ref}`;
19541
20222
  this.write(s);
19542
20223
  }
19543
- writeExtRefMark(mark) {
19544
- const extref = mark.attrs?.extref ?? "";
19545
- const refs = mark.attrs?.references ?? [];
19546
- const provider = mark.attrs?.provider ?? "";
20224
+ writeExtRefMark(mark2) {
20225
+ const extref = mark2.attrs?.extref ?? "";
20226
+ const refs = mark2.attrs?.references ?? [];
20227
+ const provider = mark2.attrs?.provider ?? "";
19547
20228
  let s = `extref:${extref}`;
19548
20229
  for (const ref of refs) {
19549
20230
  s += `|\u25BA${ref ?? ""}`;
@@ -19551,39 +20232,39 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19551
20232
  s += `|provider:${provider}`;
19552
20233
  this.write(s);
19553
20234
  }
19554
- writeFootnoteMark(mark) {
20235
+ writeFootnoteMark(mark2) {
19555
20236
  const s = `footnote:`;
19556
20237
  this.write(s);
19557
20238
  const text = this.internalTextGenerator?.generateSync(
19558
- mark.attrs?.content,
20239
+ mark2.attrs?.content,
19559
20240
  this.textFormat,
19560
20241
  this.textLocation,
19561
20242
  {
19562
20243
  ...this.generateOptions,
19563
- forceInline: true
20244
+ forceInline: true,
20245
+ chainValueContext: true
19564
20246
  }
19565
20247
  ) ?? "";
19566
20248
  this.write(text);
19567
20249
  }
19568
- writeFootnoteStarMark(mark) {
20250
+ writeFootnoteStarMark(mark2) {
19569
20251
  const s = `footnote*:`;
19570
20252
  this.write(s);
19571
20253
  const text = this.internalTextGenerator?.generateSync(
19572
- mark.attrs?.content,
20254
+ mark2.attrs?.content,
19573
20255
  this.textFormat,
19574
20256
  this.textLocation,
19575
20257
  {
19576
20258
  ...this.generateOptions,
19577
- forceInline: true
20259
+ forceInline: true,
20260
+ chainValueContext: true
19578
20261
  }
19579
20262
  ) ?? "";
19580
20263
  this.write(text);
19581
20264
  }
19582
- writeSymbolMark(mark) {
19583
- if (mark.attrs == null) return;
19584
- const attrs = mark.attrs;
19585
- const mediaAttrs = this.getMediaAttrs("symbol", attrs);
19586
- const s = mediaAttrs ?? "";
20265
+ writeSymbolMark(mark2) {
20266
+ const attrs = mark2.attrs ?? {};
20267
+ const s = `symbol:${attrs.src ?? ""}${serializeMediaChain("symbol", attrs)}`;
19587
20268
  this.write(s);
19588
20269
  }
19589
20270
  writeInlineMarkStartEnd() {
@@ -19613,52 +20294,6 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19613
20294
  }
19614
20295
  this.writeString(tag);
19615
20296
  }
19616
- getMediaAttrs(mediaType, attrs, options) {
19617
- if (!mediaType) return void 0;
19618
- const opts = Object.assign({}, options);
19619
- const ignoreAttributes = opts.ignoreAttributes ?? /* @__PURE__ */ new Set();
19620
- let s = `${mediaType}:${attrs?.src ?? ""}`;
19621
- const entries = Object.entries(attrs).filter(([k, _v]) => k !== "src");
19622
- for (let i = 0; i < entries.length; i++) {
19623
- const [k, v] = entries[i];
19624
- if (ignoreAttributes.has(k)) continue;
19625
- switch (k) {
19626
- case "textAlign":
19627
- if (v !== "left") s += `|captionAlign:${v}`;
19628
- break;
19629
- case "alignment":
19630
- if (v !== "center") {
19631
- if (v) s += `|alignment:${v}`;
19632
- }
19633
- break;
19634
- case "title":
19635
- if (v) s += `|caption:${v}`;
19636
- break;
19637
- case "class":
19638
- if (v !== "center") {
19639
- if (v) s += `|align:${v}`;
19640
- }
19641
- break;
19642
- case "comment":
19643
- if (v) s += `|#${v}`;
19644
- break;
19645
- case "":
19646
- if (stringUtils.isString(v)) s += `|:${v}`;
19647
- else s += `|`;
19648
- break;
19649
- case "zoomDisabled":
19650
- if (!v) s += "|zoomDisabled:false";
19651
- break;
19652
- case "alt":
19653
- case "width":
19654
- case "height":
19655
- default:
19656
- if (k && v) s += `|${k}:${v}`;
19657
- break;
19658
- }
19659
- }
19660
- return s;
19661
- }
19662
20297
  //
19663
20298
  // Helper functions
19664
20299
  //
@@ -19684,6 +20319,7 @@ var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
19684
20319
  */
19685
20320
  write(value) {
19686
20321
  value = this.getInterTextBreakscape(value) + value;
20322
+ this.chainJustClosed = false;
19687
20323
  if (this.options.writeCallback) {
19688
20324
  this.options.writeCallback(value);
19689
20325
  }
@@ -33869,8 +34505,8 @@ var Builder = class extends BaseBuilder {
33869
34505
  break;
33870
34506
  }
33871
34507
  case BodyBitType.mark: {
33872
- const mark = part;
33873
- instance7.fillBooleanExample([mark], __isDefaultExample, example, false);
34508
+ const mark2 = part;
34509
+ instance7.fillBooleanExample([mark2], __isDefaultExample, example, false);
33874
34510
  break;
33875
34511
  }
33876
34512
  case BodyBitType.select: {
@@ -34651,10 +35287,10 @@ var BitmarkGenerator = class extends AstWalkerGenerator {
34651
35287
  const markConfig = node.value;
34652
35288
  const parent = this.getParentNode(route);
34653
35289
  if (parent?.key !== NodeType.markConfig) return true;
34654
- const { mark, color, emphasis } = markConfig;
34655
- if (mark) {
35290
+ const { mark: mark2, color, emphasis } = markConfig;
35291
+ if (mark2) {
34656
35292
  this.writeNL();
34657
- this.writeProperty("mark", mark, route, {
35293
+ this.writeProperty("mark", mark2, route, {
34658
35294
  format: TagFormat.plainText
34659
35295
  });
34660
35296
  if (color) {
@@ -34871,9 +35507,9 @@ var BitmarkGenerator = class extends AstWalkerGenerator {
34871
35507
  }
34872
35508
  // bodyBit -> mark
34873
35509
  enter_mark(node, _route) {
34874
- const mark = node.value;
35510
+ const mark2 = node.value;
34875
35511
  this.writeOPE();
34876
- this.writeTextOrValue(mark.solution, TextFormat.plainText, TextLocation.tag);
35512
+ this.writeTextOrValue(mark2.solution, TextFormat.plainText, TextLocation.tag);
34877
35513
  this.writeCL();
34878
35514
  return true;
34879
35515
  }
@@ -34933,9 +35569,9 @@ var BitmarkGenerator = class extends AstWalkerGenerator {
34933
35569
  leaf_mark(node, route) {
34934
35570
  const root = route[0];
34935
35571
  if (root?.key !== NodeType.mark) return;
34936
- const mark = node.value;
34937
- if (mark) {
34938
- this.writeProperty("mark", mark, route, {
35572
+ const mark2 = node.value;
35573
+ if (mark2) {
35574
+ this.writeProperty("mark", mark2, route, {
34939
35575
  format: TagFormat.plainText
34940
35576
  });
34941
35577
  }
@@ -36692,8 +37328,6 @@ var MARK_TO_TAG = {
36692
37328
  highlight: "mark",
36693
37329
  code: "code"
36694
37330
  };
36695
- var INLINE_IMAGE_RE = /==([^=]*(?:=(?!=)[^=]*)*)==\|imageInline:([^|\s]+)((?:\|(?:@?[A-Za-z][A-Za-z0-9_-]*:[^|\s]+|#[^|\s]*))*)\|?/g;
36696
- var MAX_IMAGE_DIMENSION = 9999;
36697
37331
  var HtmlTableGenerator = class {
36698
37332
  constructor() {
36699
37333
  __publicField(this, "indentUnit", " ");
@@ -36835,13 +37469,13 @@ var HtmlTableGenerator = class {
36835
37469
  if (!marks || marks.length === 0) return text;
36836
37470
  let open = "";
36837
37471
  let close = "";
36838
- for (const mark of marks) {
36839
- if (mark.type === "link") {
36840
- const href = mark.attrs?.href ?? "";
37472
+ for (const mark2 of marks) {
37473
+ if (mark2.type === "link") {
37474
+ const href = mark2.attrs?.href ?? "";
36841
37475
  open += `<a href="${this.escapeAttr(href)}">`;
36842
37476
  close = `</a>${close}`;
36843
37477
  } else {
36844
- const tag = MARK_TO_TAG[mark.type];
37478
+ const tag = MARK_TO_TAG[mark2.type];
36845
37479
  if (tag) {
36846
37480
  open += `<${tag}>`;
36847
37481
  close = `</${tag}>${close}`;
@@ -36905,37 +37539,7 @@ var HtmlTableGenerator = class {
36905
37539
  return `<img ${parts.join(" ")}>`;
36906
37540
  }
36907
37541
  textToHtml(text) {
36908
- let html = "";
36909
- let lastIndex = 0;
36910
- for (const match of text.matchAll(INLINE_IMAGE_RE)) {
36911
- const index = match.index ?? 0;
36912
- html += this.escapeText(text.slice(lastIndex, index));
36913
- html += this.inlineImageTokenToHtml(match);
36914
- lastIndex = index + match[0].length;
36915
- }
36916
- html += this.escapeText(text.slice(lastIndex));
36917
- return html;
36918
- }
36919
- inlineImageTokenToHtml(match) {
36920
- const attrs = {
36921
- alt: match[1],
36922
- src: match[2]
36923
- };
36924
- const rawAttrs = match[3] ?? "";
36925
- for (const attr of rawAttrs.split("|")) {
36926
- if (!attr || attr.startsWith("#")) continue;
36927
- const colon = attr.indexOf(":");
36928
- if (colon < 1) continue;
36929
- const key = attr.slice(0, colon).replace(/^@/, "");
36930
- const value = attr.slice(colon + 1);
36931
- if (key === "width" || key === "height") {
36932
- const n = Number.parseInt(value, 10);
36933
- if (!Number.isNaN(n) && n > 0 && n <= MAX_IMAGE_DIMENSION) attrs[key] = n;
36934
- } else if (key === "class" || key === "title") {
36935
- attrs[key] = value;
36936
- }
36937
- }
36938
- return this.imageToHtml({ type: "imageInline", attrs });
37542
+ return this.escapeText(text);
36939
37543
  }
36940
37544
  // --- formulas -------------------------------------------------------------
36941
37545
  latexToHtml(node) {
@@ -41437,13 +42041,13 @@ function markChainContentProcessor(context, contentDepth, tagsConfig, content, t
41437
42041
  if (contentDepth === ContentDepth.Chain) {
41438
42042
  markTagContentProcessor(context, ContentDepth.Chain, content, target);
41439
42043
  } else {
41440
- const mark = buildMark(
42044
+ const mark2 = buildMark(
41441
42045
  context,
41442
42046
  contentDepth,
41443
42047
  tagsConfig,
41444
42048
  content
41445
42049
  );
41446
- if (mark) bodyParts.push(mark);
42050
+ if (mark2) bodyParts.push(mark2);
41447
42051
  }
41448
42052
  }
41449
42053
  function buildMark(context, _contentDepth, tagsConfig, content) {
@@ -41458,13 +42062,13 @@ function buildMark(context, _contentDepth, tagsConfig, content) {
41458
42062
  if (context.DEBUG_CHAIN_TAGS) context.debugPrint("mark TAGS", chainTags);
41459
42063
  const { solution } = tags2;
41460
42064
  const { mark: markType, ...rest } = chainTags;
41461
- const mark = {
42065
+ const mark2 = {
41462
42066
  type: BodyBitType.mark,
41463
42067
  solution: solution ?? "",
41464
42068
  mark: instance4.asSingle(markType) ?? "",
41465
42069
  ...rest
41466
42070
  };
41467
- return mark;
42071
+ return mark2;
41468
42072
  }
41469
42073
 
41470
42074
  // src/parser/bitmark/peg/contentProcessors/PropertyContentProcessor.ts
@@ -41716,7 +42320,7 @@ function markConfigChainContentProcessor(context, _contentDepth, tagsConfig, con
41716
42320
  content.chain
41717
42321
  );
41718
42322
  if (context.DEBUG_CHAIN_TAGS) context.debugPrint("mark TAGS", tags2);
41719
- const mark = instance3.unbreakscape(
42323
+ const mark2 = instance3.unbreakscape(
41720
42324
  stringUtils.trimmedString(content.value) ?? "unknown",
41721
42325
  {
41722
42326
  format: TextFormat.bitmarkText,
@@ -41724,7 +42328,7 @@ function markConfigChainContentProcessor(context, _contentDepth, tagsConfig, con
41724
42328
  }
41725
42329
  );
41726
42330
  const config = {
41727
- mark,
42331
+ mark: mark2,
41728
42332
  emphasis: "underline",
41729
42333
  ...tags2
41730
42334
  };
@@ -47540,9 +48144,9 @@ ${nested}` : line;
47540
48144
  parts.push(href);
47541
48145
  }
47542
48146
  if (marks) {
47543
- for (const mark of marks) {
47544
- if (mark.type === "footnote") {
47545
- const footnote = mark;
48147
+ for (const mark2 of marks) {
48148
+ if (mark2.type === "footnote") {
48149
+ const footnote = mark2;
47546
48150
  if (footnote.attrs?.content) {
47547
48151
  const footnoteText = footnote.attrs.content.map((c) => this.textNodeToPlainText(c)).join("");
47548
48152
  if (footnoteText) parts.push(footnoteText);