@ai-react-markdown/engine 2.4.5 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  DEFAULT_PAYLOAD: () => DEFAULT_PAYLOAD,
34
- DEF_LINE_START_RE: () => DEF_LINE_START_RE,
35
34
  PIPELINE_STAGES: () => PIPELINE_STAGES,
36
35
  SENTINEL_FN_CONTENT: () => SENTINEL_FN_CONTENT,
37
36
  SENTINEL_LINK_URL: () => SENTINEL_LINK_URL,
@@ -66,7 +65,6 @@ __export(src_exports, {
66
65
  hasLoneSurrogate: () => hasLoneSurrogate,
67
66
  highlight: () => highlight,
68
67
  isFootnoteSection: () => isFootnoteSection,
69
- lastRegionStart: () => lastRegionStart,
70
68
  measureStage: () => measureStage,
71
69
  mergeClassNameAllowlist: () => mergeClassNameAllowlist,
72
70
  normalizeForMatch: () => normalizeForMatch,
@@ -270,10 +268,73 @@ var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name"
270
268
  var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
271
269
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
272
270
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
271
+ var HTML_BREAKOUT_TAGS = /* @__PURE__ */ new Set([
272
+ "b",
273
+ "big",
274
+ "blockquote",
275
+ "body",
276
+ "br",
277
+ "center",
278
+ "code",
279
+ "dd",
280
+ "div",
281
+ "dl",
282
+ "dt",
283
+ "em",
284
+ "embed",
285
+ "h1",
286
+ "h2",
287
+ "h3",
288
+ "h4",
289
+ "h5",
290
+ "h6",
291
+ "head",
292
+ "hr",
293
+ "i",
294
+ "img",
295
+ "li",
296
+ "listing",
297
+ "menu",
298
+ "meta",
299
+ "nobr",
300
+ "ol",
301
+ "p",
302
+ "pre",
303
+ "ruby",
304
+ "s",
305
+ "small",
306
+ "span",
307
+ "strong",
308
+ "strike",
309
+ "sub",
310
+ "sup",
311
+ "table",
312
+ "tt",
313
+ "u",
314
+ "ul",
315
+ "var"
316
+ ]);
317
+ var HTML_INTEGRATION_POINTS = ["foreignobject", "desc", "mi", "mo", "mn", "ms", "mtext", "annotation-xml"];
273
318
  var TYPE1_NAMES = /* @__PURE__ */ new Set(["script", "pre", "style", "textarea"]);
319
+ var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set([
320
+ "script",
321
+ "style",
322
+ "textarea",
323
+ "title",
324
+ "xmp",
325
+ "iframe",
326
+ "noembed",
327
+ "noframes",
328
+ // NOT `noscript`: hast-util-raw constructs parse5 with
329
+ // `scriptingEnabled: false`, under which `<noscript>` content is ordinary
330
+ // HTML — modelling it as raw text ignored a `<b>` inside and under-blocked
331
+ // (oracle review of the r2 batch; regression caught before release).
332
+ "plaintext"
333
+ ]);
274
334
  var TYPE6_START_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r]|\/?>|$)/;
275
335
  var TYPE1_START_RE = /^<(script|pre|style|textarea)(?:[ \t\r]|>|$)/i;
276
- var TYPE7_LINE_RE = /^<(\/?)([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r][^>]*|\/)?>[ \t\r]*$/;
336
+ var TYPE7_LINE_RE = /^(?:<[A-Za-z][A-Za-z0-9-]*(?:[ \t\r][^>]*|\/)?>|<\/[A-Za-z][A-Za-z0-9-]*[ \t\r]*>)[ \t\r]*$/;
337
+ var t7Name = (line) => /^<\/?([A-Za-z][A-Za-z0-9-]*)/.exec(line)[1];
277
338
  var VOID_TAGS = /* @__PURE__ */ new Set([
278
339
  "area",
279
340
  "base",
@@ -297,7 +358,35 @@ var DEF_RE = /^ {0,3}\[((?:[^[\]\\]|\\.)+)\]:/;
297
358
  var FENCE_RE = /^ {0,3}(`{3,}|~{3,})/;
298
359
  var MATH_RUN_RE = /^ {0,3}(\$\$+)/;
299
360
  var TAG_OR_COMMENT_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)(?=[\s/>])([^>]*)>|<!--|-->|--!>/g;
300
- var TRUNCATED_TAG_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)([^<>]*)$/;
361
+ var TRUNCATED_TAG_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)([^>]*)$/;
362
+ var TAG_START_LT_RE = /<\/?[A-Za-z]/g;
363
+ function scanTagAttrs(text, from, to, out) {
364
+ let st = out.state;
365
+ for (let i = from; i < to; i++) {
366
+ const c = text[i];
367
+ if (st === '"' || st === "'") {
368
+ if (c === st) st = "outside";
369
+ continue;
370
+ }
371
+ const ws = c === " " || c === " " || c === "\n" || c === "\r" || c === "\f";
372
+ if (st === "afterEq") {
373
+ if (ws) continue;
374
+ if (c === '"' || c === "'") st = c;
375
+ else if (c === ">") return i;
376
+ else st = "unquoted";
377
+ continue;
378
+ }
379
+ if (st === "unquoted") {
380
+ if (ws) st = "outside";
381
+ else if (c === ">") return i;
382
+ continue;
383
+ }
384
+ if (c === "=") st = "afterEq";
385
+ else if (c === ">") return i;
386
+ }
387
+ out.state = st;
388
+ return -1;
389
+ }
301
390
  var REF_RE = /!?\[((?:[^[\]\\]|\\.)*)\]/g;
302
391
  var BACKTICK_RUN_RE = /`+/g;
303
392
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -392,6 +481,8 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
392
481
  openTotal: 0,
393
482
  commentOpen: false,
394
483
  piOpen: false,
484
+ bogusOpen: false,
485
+ rawTextOpen: null,
395
486
  declOpen: false,
396
487
  cdataOpen: false,
397
488
  inFence: false,
@@ -416,6 +507,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
416
507
  pendingTruncatedCloses: [],
417
508
  tagAcrossLines: false,
418
509
  tagAcrossLinesIndent: 0,
510
+ tagAcrossLinesState: "outside",
419
511
  htmlFlowReal: false
420
512
  };
421
513
  }
@@ -515,11 +607,21 @@ function computeFreezeBoundary(text, options, resume) {
515
607
  let start = cp.confirmedOffset;
516
608
  let tailLine = null;
517
609
  while (start < text.length) {
518
- let end = text.indexOf("\n", start);
519
- if (end === -1) end = text.length;
520
- const confirmed = end < text.length;
521
- const rawLine = text.slice(start, end);
522
- const lineText = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
610
+ let end = text.length;
611
+ let textEnd = text.length;
612
+ {
613
+ const nl = text.indexOf("\n", start);
614
+ const cr = text.indexOf("\r", start);
615
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
616
+ textEnd = cr;
617
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
618
+ } else if (nl !== -1) {
619
+ textEnd = nl;
620
+ end = nl;
621
+ }
622
+ }
623
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
624
+ const lineText = text.slice(start, textEnd);
523
625
  const ln = {
524
626
  start,
525
627
  end,
@@ -611,14 +713,33 @@ function processConfirmedLine(cp, ln, text) {
611
713
  newest.defListSettled = ln.blank ? true : !canBecomeDdLine(ln.text, true);
612
714
  }
613
715
  const isBlockStart = cp.prevLineBlank;
614
- if (cp.htmlSeamPending && !ln.blank && !cp.htmlFlowSinceBlank && !(cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen)) {
716
+ if (cp.htmlSeamPending && !ln.blank && !cp.htmlFlowSinceBlank && !(cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen || cp.bogusOpen)) {
615
717
  const defShapedLine = DEF_RE.test(ln.text) || FOOTNOTE_DEF_RE.test(ln.text);
616
718
  const commentOnly = ln.text.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!--[\s\S]*$/, " ").replace(/[ \t\r]/g, "") === "";
617
719
  if (!defShapedLine && !commentOnly) {
618
720
  cp.htmlSeamPending = false;
619
721
  }
620
722
  }
723
+ const inForeignContent = () => (cp.tagBalance.get("svg") ?? 0) > 0 || (cp.tagBalance.get("math") ?? 0) > 0;
724
+ const honoursSelfClosing = (tag) => {
725
+ if (tag === "svg" || tag === "math") return true;
726
+ if (!inForeignContent() || HTML_BREAKOUT_TAGS.has(tag)) return false;
727
+ for (const ip of HTML_INTEGRATION_POINTS) if ((cp.tagBalance.get(ip) ?? 0) > 0) return false;
728
+ return true;
729
+ };
730
+ const htmlRulesApply = () => {
731
+ if (!inForeignContent()) return true;
732
+ for (const ip of HTML_INTEGRATION_POINTS) if ((cp.tagBalance.get(ip) ?? 0) > 0) return true;
733
+ return false;
734
+ };
621
735
  const applyTag = (tag, closing) => {
736
+ if (cp.rawTextOpen !== null) {
737
+ if (!(closing && tag === cp.rawTextOpen)) return;
738
+ cp.rawTextOpen = null;
739
+ } else if (!closing && RAW_TEXT_ELEMENTS.has(tag) && htmlRulesApply()) {
740
+ cp.rawTextOpen = tag;
741
+ if (tag === "plaintext") cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
742
+ }
622
743
  if (closing) {
623
744
  const count = cp.tagBalance.get(tag) ?? 0;
624
745
  if (count > 0) {
@@ -630,8 +751,9 @@ function processConfirmedLine(cp, ln, text) {
630
751
  cp.openTotal += 1;
631
752
  }
632
753
  };
754
+ const strayTablePart = (tag) => TABLE_PART_NAMES.has(tag) && (cp.tagBalance.get("table") ?? 0) === 0;
633
755
  const commentOpenAtLineStart = cp.commentOpen;
634
- const rawOpenAtLineStart = cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen;
756
+ const rawOpenAtLineStart = cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen || cp.bogusOpen;
635
757
  if (cp.inFence) {
636
758
  const close = FENCE_RE.exec(ln.text);
637
759
  if (close && close[1][0] === cp.fenceChar && close[1].length >= cp.fenceLen && isMdBlank(ln.text.slice(close[0].length))) {
@@ -714,13 +836,21 @@ function processConfirmedLine(cp, ln, text) {
714
836
  cp.pendingTruncatedTags = [];
715
837
  }
716
838
  cp.pendingTruncatedCloses = [];
839
+ if (cp.tagAcrossLines && (cp.tagAcrossLinesState === '"' || cp.tagAcrossLinesState === "'")) {
840
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
841
+ }
717
842
  cp.tagAcrossLines = false;
843
+ cp.tagAcrossLinesState = "outside";
844
+ if (cp.bogusOpen) {
845
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
846
+ cp.bogusOpen = false;
847
+ }
718
848
  cp.blankRun += 1;
719
849
  cp.lastBlankStart = ln.start;
720
850
  cp.candidates.push({
721
851
  offset: Math.min(ln.end + 1, text.length),
722
852
  blankRun: cp.blankRun,
723
- htmlBalanced: cp.openTotal === 0 && !cp.commentOpen && !cp.piOpen && !cp.declOpen && !cp.cdataOpen,
853
+ htmlBalanced: cp.openTotal === 0 && !cp.commentOpen && !cp.piOpen && !cp.declOpen && !cp.cdataOpen && !cp.bogusOpen,
724
854
  hazard: cp.hazardVerdict,
725
855
  seamRisk: cp.htmlSeamPending,
726
856
  defListSettled: null
@@ -750,7 +880,7 @@ function processConfirmedLine(cp, ln, text) {
750
880
  const t7 = TYPE7_LINE_RE.exec(t);
751
881
  if (t6 !== null && TYPE6_NAMES.has(t6[1].toLowerCase()) || TYPE1_START_RE.test(t) || // Type 7 cannot interrupt a paragraph, and excludes the raw-text
752
882
  // names (those are type 1 as start tags, paragraph as end tags).
753
- t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7[2].toLowerCase())) {
883
+ t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7Name(t).toLowerCase())) {
754
884
  cp.htmlFlowReal = true;
755
885
  }
756
886
  }
@@ -852,7 +982,7 @@ ${cont(scanText)}` };
852
982
  pos = c + 3;
853
983
  continue;
854
984
  }
855
- if (cp.declOpen) {
985
+ if (cp.declOpen || cp.bogusOpen) {
856
986
  const c = scanText.indexOf(">", pos);
857
987
  if (c === -1) {
858
988
  rawSpans.push([pos, scanText.length]);
@@ -860,6 +990,7 @@ ${cont(scanText)}` };
860
990
  }
861
991
  rawSpans.push([pos, c + 1]);
862
992
  cp.declOpen = false;
993
+ cp.bogusOpen = false;
863
994
  pos = c + 1;
864
995
  continue;
865
996
  }
@@ -867,10 +998,16 @@ ${cont(scanText)}` };
867
998
  const cd = scanText.indexOf("<![CDATA[", pos);
868
999
  const dm = scanText.slice(pos).search(/<![A-Za-z]/);
869
1000
  const decl = dm === -1 ? -1 : pos + dm;
870
- const starts = [pi, cd, decl].filter((x) => x !== -1);
1001
+ const bm = cp.htmlFlowReal ? scanText.slice(pos).search(/<!(?!--|[A-Za-z]|\[CDATA\[)|<\/(?![A-Za-z])/) : -1;
1002
+ const bogus = bm === -1 ? -1 : pos + bm;
1003
+ const starts = [pi, cd, decl, bogus].filter((x) => x !== -1);
871
1004
  if (starts.length === 0) break;
872
1005
  const first = Math.min(...starts);
873
- if (first === cd) {
1006
+ if (first === bogus) {
1007
+ rawSpans.push([bogus, bogus + 2]);
1008
+ cp.bogusOpen = true;
1009
+ pos = bogus + 2;
1010
+ } else if (first === cd) {
874
1011
  rawSpans.push([cd, cd + 9]);
875
1012
  cp.cdataOpen = true;
876
1013
  pos = cd + 9;
@@ -897,22 +1034,27 @@ ${cont(scanText)}` };
897
1034
  let skipTagScan = false;
898
1035
  if (cp.tagAcrossLines) {
899
1036
  if (ln.indent < cp.tagAcrossLinesIndent) poisonRawDivergence();
900
- const gt = ln.text.indexOf(">");
1037
+ const attrs = { state: cp.tagAcrossLinesState };
1038
+ const gt = scanTagAttrs(ln.text, 0, ln.text.length, attrs);
901
1039
  if (gt === -1) {
1040
+ scanTagAttrs("\n", 0, 1, attrs);
1041
+ cp.tagAcrossLinesState = attrs.state;
902
1042
  skipTagScan = true;
903
1043
  } else {
904
- if (/["']/.test(ln.text.slice(0, gt))) poisonRawDivergence();
905
1044
  for (const tag of cp.pendingTruncatedCloses) applyTag(tag, true);
906
1045
  cp.pendingTruncatedCloses = [];
907
1046
  cp.tagAcrossLines = false;
1047
+ cp.tagAcrossLinesState = "outside";
908
1048
  tagText = " ".repeat(gt + 1) + tagText.slice(gt + 1);
909
1049
  }
910
1050
  }
1051
+ let tagHandledAsTruncated = false;
911
1052
  if (!skipTagScan) {
912
1053
  TAG_OR_COMMENT_RE.lastIndex = 0;
913
1054
  let m;
914
1055
  let lastCommentOpenerIdx = -1;
915
1056
  while ((m = TAG_OR_COMMENT_RE.exec(tagText)) !== null) {
1057
+ if (cp.rawTextOpen !== null && (m[0] === "<!--" || m[0] === "-->" || m[0] === "--!>")) continue;
916
1058
  if (m[0] === "<!--") {
917
1059
  const next = tagText.slice(m.index + 4, m.index + 6);
918
1060
  if (cp.commentOpen) {
@@ -938,9 +1080,32 @@ ${cont(scanText)}` };
938
1080
  if (cp.commentOpen) continue;
939
1081
  const closing = m[1] === "/";
940
1082
  const tag = m[2].toLowerCase();
941
- if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + m.index);
942
- const selfClosing = m[3] !== void 0 && /\/\s*$/.test(m[3]);
943
- if (VOID_TAGS.has(tag) || selfClosing) continue;
1083
+ if (strayTablePart(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + m.index);
1084
+ let attrs = m[3] ?? "";
1085
+ if (cp.htmlFlowReal && (cp.rawTextOpen === null || closing && tag === cp.rawTextOpen)) {
1086
+ const attrStart = m.index + 1 + (closing ? 1 : 0) + m[2].length;
1087
+ const st = { state: "outside" };
1088
+ const gt = scanTagAttrs(tagText, attrStart, tagText.length, st);
1089
+ if (gt === -1) {
1090
+ if (!VOID_TAGS.has(tag)) {
1091
+ if (closing) cp.pendingTruncatedCloses.push(tag);
1092
+ else applyTag(tag, false);
1093
+ }
1094
+ scanTagAttrs("\n", 0, 1, st);
1095
+ cp.tagAcrossLines = true;
1096
+ cp.tagAcrossLinesIndent = ln.indent;
1097
+ cp.tagAcrossLinesState = st.state;
1098
+ tagHandledAsTruncated = true;
1099
+ break;
1100
+ }
1101
+ if (gt + 1 !== m.index + m[0].length) {
1102
+ attrs = tagText.slice(attrStart, gt);
1103
+ TAG_OR_COMMENT_RE.lastIndex = gt + 1;
1104
+ }
1105
+ }
1106
+ if (closing && !cp.htmlFlowReal && !/^\s*$/.test(attrs)) continue;
1107
+ const selfClosing = /\/\s*$/.test(attrs);
1108
+ if (VOID_TAGS.has(tag) || selfClosing && honoursSelfClosing(tag)) continue;
944
1109
  applyTag(tag, closing);
945
1110
  }
946
1111
  if (cp.commentOpen && lastCommentOpenerIdx !== -1 && !inRawText) {
@@ -959,26 +1124,40 @@ ${cont(scanText)}` };
959
1124
  if (startMasked || wholeVisible || inRaw(mr.index) || cp.commentOpen) continue;
960
1125
  const closing = mr[1] === "/";
961
1126
  const tag = mr[2].toLowerCase();
962
- if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + mr.index);
1127
+ if (strayTablePart(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + mr.index);
1128
+ if (closing && mr[3] !== void 0 && !/^\s*$/.test(mr[3])) continue;
963
1129
  const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
964
- if (VOID_TAGS.has(tag) || selfClosing) continue;
1130
+ if (VOID_TAGS.has(tag) || selfClosing && honoursSelfClosing(tag)) continue;
965
1131
  applyTag(tag, closing);
966
1132
  }
967
1133
  }
968
1134
  if (cp.pendingTruncatedTags.length > 0 && ln.text.includes(">")) {
1135
+ if (cp.pendingTruncatedTags.some((t) => strayTablePart(t))) {
1136
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
1137
+ }
969
1138
  cp.pendingTruncatedTags = [];
970
1139
  }
971
- if (!cp.commentOpen) {
972
- const lastLt = tagText.lastIndexOf("<");
1140
+ if (!cp.commentOpen && !tagHandledAsTruncated) {
1141
+ let lastLt = -1;
1142
+ TAG_START_LT_RE.lastIndex = 0;
1143
+ for (let ms = TAG_START_LT_RE.exec(tagText); ms !== null; ms = TAG_START_LT_RE.exec(tagText)) {
1144
+ lastLt = ms.index;
1145
+ TAG_START_LT_RE.lastIndex = ms.index + 1;
1146
+ }
973
1147
  if (lastLt !== -1 && !tagText.includes(">", lastLt)) {
974
1148
  const m2 = TRUNCATED_TAG_RE.exec(tagText.slice(lastLt));
975
1149
  if (m2) {
976
1150
  const closing = m2[1] === "/";
977
1151
  const tag = m2[2].toLowerCase();
978
- if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
1152
+ if (strayTablePart(tag) && cp.htmlFlowReal) {
1153
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
1154
+ }
979
1155
  if (cp.htmlFlowReal) {
980
1156
  cp.tagAcrossLines = true;
981
1157
  cp.tagAcrossLinesIndent = ln.indent;
1158
+ const attrs = { state: "outside" };
1159
+ scanTagAttrs(m2[3] + "\n", 0, m2[3].length + 1, attrs);
1160
+ cp.tagAcrossLinesState = attrs.state;
982
1161
  }
983
1162
  if (closing) {
984
1163
  if (!VOID_TAGS.has(tag) && cp.htmlFlowReal) cp.pendingTruncatedCloses.push(tag);
@@ -1195,6 +1374,24 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1195
1374
  }
1196
1375
  }
1197
1376
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1377
+ var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1378
+ function hasStrayTablePart(values) {
1379
+ let depth = 0;
1380
+ for (const value of values) {
1381
+ TABLE_TOKEN_RE.lastIndex = 0;
1382
+ let m;
1383
+ while ((m = TABLE_TOKEN_RE.exec(value)) !== null) {
1384
+ const closing = m[1] === "/";
1385
+ const tag = m[2].toLowerCase();
1386
+ if (tag === "table") {
1387
+ depth = closing ? Math.max(0, depth - 1) : depth + 1;
1388
+ continue;
1389
+ }
1390
+ if (depth === 0) return true;
1391
+ }
1392
+ }
1393
+ return false;
1394
+ }
1198
1395
  var STRAY_SYNTHESIZED_END_TAG_RE = /<\/(?:br|p)\b/i;
1199
1396
  function spliceTrees(input) {
1200
1397
  const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
@@ -1252,7 +1449,7 @@ function spliceTrees(input) {
1252
1449
  return !(start !== void 0 && start < injectedLen);
1253
1450
  });
1254
1451
  const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1255
- if (prefixMdast.some((c) => c.type === "html" && TABLE_PART_TAG_RE.test(c.value))) return null;
1452
+ if (hasStrayTablePart(prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []))) return null;
1256
1453
  for (const child of tailMdastChildren) {
1257
1454
  if (isWrapInvisible(child)) continue;
1258
1455
  if (child.type !== "html") break;
@@ -1647,7 +1844,7 @@ function normalizeId(s) {
1647
1844
  return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
1648
1845
  }
1649
1846
  function normalizeForMatch(s) {
1650
- return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\([!-/:-@[-`{-~])/g, "$1"));
1847
+ return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
1651
1848
  }
1652
1849
 
1653
1850
  // src/components/collectDefLabels.ts
@@ -1965,22 +2162,68 @@ function createRegistry(onEmpty) {
1965
2162
  labelSet: { footnoteLabels: /* @__PURE__ */ new Set(), linkLabels: /* @__PURE__ */ new Set() },
1966
2163
  version: 0,
1967
2164
  _reactIdMap: /* @__PURE__ */ new Map(),
2165
+ /** Symbol → its `documentIndex`, for chunks that supplied one. */
2166
+ _chunkIndex: /* @__PURE__ */ new Map(),
1968
2167
  _subscribers: /* @__PURE__ */ new Set(),
1969
2168
  _notifyScheduled: false,
1970
- allocateSymbol(reactId) {
2169
+ allocateSymbol(reactId, rawDocumentIndex) {
2170
+ const documentIndex = rawDocumentIndex !== void 0 && Number.isFinite(rawDocumentIndex) ? rawDocumentIndex : void 0;
2171
+ if (rawDocumentIndex !== void 0 && documentIndex === void 0) {
2172
+ console.warn(
2173
+ `[ai-react-markdown] documentIndex must be a finite number \u2014 received ${String(rawDocumentIndex)}; ignoring it for this chunk.`
2174
+ );
2175
+ }
1971
2176
  const existing = this._reactIdMap.get(reactId);
1972
2177
  if (existing) {
1973
2178
  existing.refcount++;
2179
+ const moved = documentIndex !== void 0 ? this._chunkIndex.get(existing.symbol) !== documentIndex : this._chunkIndex.has(existing.symbol);
2180
+ if (moved) {
2181
+ const at = this.chunkOrder.indexOf(existing.symbol);
2182
+ if (at !== -1) this.chunkOrder.splice(at, 1);
2183
+ this._placeChunk(existing.symbol, documentIndex);
2184
+ this._notify();
2185
+ }
1974
2186
  return existing.symbol;
1975
2187
  }
1976
2188
  const sym = Symbol(reactId);
1977
2189
  this._reactIdMap.set(reactId, { symbol: sym, refcount: 1 });
1978
- this.chunkOrder.push(sym);
2190
+ this._placeChunk(sym, documentIndex);
1979
2191
  this._notify();
1980
2192
  return sym;
1981
2193
  },
1982
- registerChunk(reactId, footnotes, links) {
1983
- const sym = this.allocateSymbol(reactId);
2194
+ /** Put `sym` into `chunkOrder` at its document position. Without an index
2195
+ * it goes last (mount order — the historical behaviour); with one it
2196
+ * sorts before the first chunk that sits later, where a chunk WITHOUT an
2197
+ * index counts as "later" so an indexed chunk never lands behind one
2198
+ * whose position is unknown. */
2199
+ _placeChunk(sym, documentIndex) {
2200
+ if (this.chunkOrder.length > 0) {
2201
+ const anyIndexed = documentIndex !== void 0 || this.chunkOrder.some((s) => this._chunkIndex.has(s));
2202
+ const anyPlain = documentIndex === void 0 || this.chunkOrder.some((s) => !this._chunkIndex.has(s));
2203
+ if (anyIndexed && anyPlain) {
2204
+ console.warn(
2205
+ "[ai-react-markdown] Some chunks of this document supply `documentIndex` and some do not. Indexed chunks always sort ahead of un-indexed ones, so footnote numbering and aggregate-footer placement will not follow document order. Pass `documentIndex` to every chunk, or to none."
2206
+ );
2207
+ }
2208
+ }
2209
+ if (documentIndex === void 0) {
2210
+ this._chunkIndex.delete(sym);
2211
+ this.chunkOrder.push(sym);
2212
+ return;
2213
+ }
2214
+ this._chunkIndex.set(sym, documentIndex);
2215
+ let at = this.chunkOrder.length;
2216
+ for (let i = 0; i < this.chunkOrder.length; i++) {
2217
+ const other = this._chunkIndex.get(this.chunkOrder[i]);
2218
+ if (other === void 0 || other > documentIndex) {
2219
+ at = i;
2220
+ break;
2221
+ }
2222
+ }
2223
+ this.chunkOrder.splice(at, 0, sym);
2224
+ },
2225
+ registerChunk(reactId, footnotes, links, documentIndex) {
2226
+ const sym = this.allocateSymbol(reactId, documentIndex);
1984
2227
  this.contributeLabels(sym, footnotes, links);
1985
2228
  return sym;
1986
2229
  },
@@ -2003,6 +2246,7 @@ function createRegistry(onEmpty) {
2003
2246
  this._reactIdMap.delete(reactId);
2004
2247
  const idx = this.chunkOrder.indexOf(entry.symbol);
2005
2248
  if (idx !== -1) this.chunkOrder.splice(idx, 1);
2249
+ this._chunkIndex.delete(entry.symbol);
2006
2250
  this.chunkData.delete(entry.symbol);
2007
2251
  const nextFn = /* @__PURE__ */ new Set();
2008
2252
  const nextLink = /* @__PURE__ */ new Set();
@@ -3629,17 +3873,28 @@ function mergeClassNameAllowlist(existing, extraClassNames) {
3629
3873
  return entries;
3630
3874
  }
3631
3875
  var crossChunkTags = ["cross-chunk-link", "cross-chunk-image", "footnote-sup"];
3632
- var sanitizeSchema = cloneDeep_default({
3633
- ...import_rehype_sanitize2.defaultSchema,
3634
- tagNames: [...import_rehype_sanitize2.defaultSchema.tagNames || [], "mark", ...crossChunkTags],
3635
- attributes: {
3636
- ...import_rehype_sanitize2.defaultSchema.attributes,
3637
- code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
3638
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
3639
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
3640
- "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
3641
- }
3642
- });
3876
+ function deepFreeze(value) {
3877
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
3878
+ Object.freeze(value);
3879
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
3880
+ }
3881
+ return value;
3882
+ }
3883
+ var STRIPPED_TAGS = ["script", "style", "title", "textarea", "noframes", "noembed", "xmp", "iframe", "plaintext"];
3884
+ var sanitizeSchema = deepFreeze(
3885
+ cloneDeep_default({
3886
+ ...import_rehype_sanitize2.defaultSchema,
3887
+ tagNames: [...import_rehype_sanitize2.defaultSchema.tagNames || [], "mark", ...crossChunkTags],
3888
+ attributes: {
3889
+ ...import_rehype_sanitize2.defaultSchema.attributes,
3890
+ code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
3891
+ "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
3892
+ "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
3893
+ "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
3894
+ },
3895
+ strip: [.../* @__PURE__ */ new Set([...import_rehype_sanitize2.defaultSchema.strip || [], ...STRIPPED_TAGS])]
3896
+ })
3897
+ );
3643
3898
 
3644
3899
  // src/components/crossChunkUrlSanitize.ts
3645
3900
  function fakeElement(tagName, key, url) {
@@ -3864,6 +4119,14 @@ var graphemeEnds = (text, base) => {
3864
4119
  }
3865
4120
  return ends;
3866
4121
  };
4122
+ var RESUME_LOOKBACK = 64;
4123
+ var isMidSurrogatePair = (text, at) => at > 0 && at < text.length && (text.charCodeAt(at - 1) & 64512) === 55296 && (text.charCodeAt(at) & 64512) === 56320;
4124
+ var RI_FIRST = 127462;
4125
+ var RI_LAST = 127487;
4126
+ var isRegionalIndicatorAt = (text, at) => {
4127
+ const cp = text.codePointAt(at);
4128
+ return cp !== void 0 && cp >= RI_FIRST && cp <= RI_LAST;
4129
+ };
3867
4130
  var createSmoothStreamController = (options = {}) => {
3868
4131
  const now = options.now ?? defaultNow;
3869
4132
  const schedule = options.schedule ?? defaultSchedule;
@@ -3872,6 +4135,7 @@ var createSmoothStreamController = (options = {}) => {
3872
4135
  let pending = [];
3873
4136
  let tentativeEnd = 0;
3874
4137
  let finished = false;
4138
+ let seam;
3875
4139
  let drainDeadlineAt;
3876
4140
  let initialized = false;
3877
4141
  let visibleCache = "";
@@ -3955,15 +4219,31 @@ var createSmoothStreamController = (options = {}) => {
3955
4219
  if (!disposed && !cancelFrame && pending.length > 0) cancelFrame = schedule(tick);
3956
4220
  };
3957
4221
  const resegmentTail = () => {
4222
+ if (seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4223
+ pending.pop();
4224
+ }
3958
4225
  const from = pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
3959
- const ends = graphemeEnds(source.slice(from), from);
4226
+ let anchor = from;
4227
+ if (seam !== void 0 && from <= seam) {
4228
+ anchor = Math.max(0, from - RESUME_LOOKBACK);
4229
+ if (isMidSurrogatePair(source, anchor)) anchor -= 1;
4230
+ while (anchor >= 2 && isRegionalIndicatorAt(source, anchor - 2)) anchor -= 2;
4231
+ } else {
4232
+ seam = void 0;
4233
+ }
4234
+ const ends = graphemeEnds(source.slice(anchor), anchor);
3960
4235
  if (ends.length === 0) {
3961
4236
  tentativeEnd = from;
3962
4237
  return;
3963
4238
  }
3964
4239
  tentativeEnd = ends[ends.length - 1];
3965
- const confirmedEnds = finished ? ends : ends.slice(0, -1);
3966
- for (const end of confirmedEnds) pending.push(end);
4240
+ let hold = 1;
4241
+ if (!finished && ends.length > 1) {
4242
+ const last = source.charCodeAt(source.length - 1);
4243
+ if (last >= 55296 && last <= 56319) hold = 2;
4244
+ }
4245
+ const confirmedEnds = finished ? ends : ends.slice(0, -hold);
4246
+ for (const end of confirmedEnds) if (end > from) pending.push(end);
3967
4247
  };
3968
4248
  const snap = (next) => {
3969
4249
  disposed = false;
@@ -3977,6 +4257,7 @@ var createSmoothStreamController = (options = {}) => {
3977
4257
  visibleEnd = next.length;
3978
4258
  tentativeEnd = next.length;
3979
4259
  pending = [];
4260
+ seam = next.length;
3980
4261
  credit = 0;
3981
4262
  cancelScheduled();
3982
4263
  if (visibleCache !== next) notify();
@@ -4011,6 +4292,7 @@ var createSmoothStreamController = (options = {}) => {
4011
4292
  if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4012
4293
  pending.push(tentativeEnd);
4013
4294
  }
4295
+ seam = source.length;
4014
4296
  ensureScheduled();
4015
4297
  },
4016
4298
  snap,
@@ -4090,10 +4372,11 @@ function findClosingBacktickRun(content, start, n) {
4090
4372
  const runLen = getRepeatedMarkerLength(content, i, "`");
4091
4373
  if (runLen === n) return i;
4092
4374
  i += runLen;
4093
- } else if (ch === "\n") {
4375
+ } else if (ch === "\n" || ch === "\r") {
4094
4376
  let j = i + 1;
4095
- while (j < content.length && (content[j] === " " || content[j] === " " || content[j] === "\r")) j += 1;
4096
- if (j >= content.length || content[j] === "\n") return -1;
4377
+ if (ch === "\r" && content[j] === "\n") j += 1;
4378
+ while (j < content.length && (content[j] === " " || content[j] === " ")) j += 1;
4379
+ if (j >= content.length || content[j] === "\n" || content[j] === "\r") return -1;
4097
4380
  i += 1;
4098
4381
  } else {
4099
4382
  i += 1;
@@ -4194,26 +4477,54 @@ function escapeMhchemCommands(text) {
4194
4477
  return text.replaceAll("$\\ce{", "$\\\\ce{").replaceAll("$\\pu{", "$\\\\pu{");
4195
4478
  }
4196
4479
  var CURRENCY_REGEX = /(?<![\\$])\$(?!\$)(?=\d+(?:,\d{3})*(?:\.\d+)?(?:[KMBkmb])?(?:\s|$|[^a-zA-Z\d]))/g;
4197
- var NO_ESCAPED_DOLLAR_REGEX = /(?<![\\$])\$(?!\$)/g;
4198
4480
  var DELIMITERS_REGEX = /(?<!!)\\\[([\S\s]*?[^\\])\\](?!\()|\\\((.*?)\\\)/g;
4199
4481
  var ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\begin\{(?:array|tabular[x*]?)\}\{[^}]*\})|(?<!\\)\|/g;
4200
- var LATEX_BLOCK_REGEX = /\$\$([\S\s]*?)\$\$|(?<![\\$])\$(?!\$)((?:[^$\n]|\\\$)*?)(?<![\\`])\$(?!\$)/g;
4482
+ var EVEN_BACKSLASHES = String.raw`(?<=(?:^|[^\\])(?:\\\\)*)`;
4483
+ var LATEX_BLOCK_REGEX = new RegExp(
4484
+ String.raw`${EVEN_BACKSLASHES}\$\$([\S\s]*?)${EVEN_BACKSLASHES}\$\$|(?<![\\$])\$(?!\$)((?:[^$\n]|\\\$)*?)(?<![\\` + "`" + String.raw`])\$(?!\$)`,
4485
+ "g"
4486
+ );
4201
4487
  var TEXT_COMMAND = "\\text{";
4202
4488
  var SINGLE_DOLLAR_REGEX = /(?<![\\$])\$(?!\$)((?:[^$\n]|\\[$])+?)(?<!\\)(?<!`)\$(?!\$)/g;
4489
+ function countBareDollars(str, from, to, prev, next) {
4490
+ let n = 0;
4491
+ for (let j = from; j < to; j++) {
4492
+ if (str.charCodeAt(j) !== 36) continue;
4493
+ const before = j > from ? str[j - 1] : prev;
4494
+ const after = j + 1 < to ? str[j + 1] : next;
4495
+ if (before !== "\\" && before !== "$" && after !== "$") n += 1;
4496
+ }
4497
+ return n;
4498
+ }
4203
4499
  function escapeCurrencyDollarSigns(text) {
4204
4500
  const parts = [];
4205
4501
  let lastIndex = 0;
4206
4502
  const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));
4207
4503
  let currentLineProcessed = "";
4504
+ let currentLineDollars = 0;
4505
+ const appendToLine = (piece) => {
4506
+ if (piece.length === 0) return;
4507
+ const prevLast = currentLineProcessed.length > 0 ? currentLineProcessed[currentLineProcessed.length - 1] : "";
4508
+ const prevBeforeLast = currentLineProcessed.length > 1 ? currentLineProcessed[currentLineProcessed.length - 2] : "";
4509
+ const prevLastCounted = prevLast === "$" && prevBeforeLast !== "\\" && prevBeforeLast !== "$";
4510
+ if (prevLastCounted && piece[0] === "$") currentLineDollars -= 1;
4511
+ currentLineDollars += countBareDollars(piece, 0, piece.length, prevLast, "");
4512
+ currentLineProcessed += piece;
4513
+ };
4514
+ const resetLine = (rest) => {
4515
+ currentLineProcessed = "";
4516
+ currentLineDollars = 0;
4517
+ appendToLine(rest);
4518
+ };
4208
4519
  for (let i = 0; i < currencyMatches.length; i++) {
4209
4520
  const match = currencyMatches[i];
4210
4521
  const segment = text.substring(lastIndex, match.index);
4211
4522
  parts.push(segment);
4212
4523
  const newlineIdx = Math.max(segment.lastIndexOf("\n"), segment.lastIndexOf("\r"));
4213
4524
  if (newlineIdx !== -1) {
4214
- currentLineProcessed = segment.substring(newlineIdx + 1);
4525
+ resetLine(segment.substring(newlineIdx + 1));
4215
4526
  } else {
4216
- currentLineProcessed += segment;
4527
+ appendToLine(segment);
4217
4528
  }
4218
4529
  let needEscape = true;
4219
4530
  const restStart = match.index + 1;
@@ -4230,15 +4541,21 @@ function escapeCurrencyDollarSigns(text) {
4230
4541
  }
4231
4542
  firstLineBeforeNextMatch = text.substring(restStart, eol);
4232
4543
  }
4233
- if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
4234
- const wholeLineBeforeNextMatchWithoutCurrentDollar = currentLineProcessed + firstLineBeforeNextMatch;
4235
- if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
4236
- needEscape = false;
4237
- }
4544
+ const restDollars = countBareDollars(firstLineBeforeNextMatch, 0, firstLineBeforeNextMatch.length, "", "");
4545
+ if (restDollars % 2 !== 0) {
4546
+ const L = currentLineProcessed;
4547
+ const lLast = L.length > 0 ? L[L.length - 1] : "";
4548
+ const lBeforeLast = L.length > 1 ? L[L.length - 2] : "";
4549
+ const lLastCounted = lLast === "$" && lBeforeLast !== "\\" && lBeforeLast !== "$";
4550
+ const f0 = firstLineBeforeNextMatch[0];
4551
+ let whole = currentLineDollars + restDollars;
4552
+ if (lLastCounted && f0 === "$") whole -= 1;
4553
+ if (f0 === "$" && (lLast === "\\" || lLast === "$") && firstLineBeforeNextMatch[1] !== "$") whole -= 1;
4554
+ if (whole % 2 !== 0) needEscape = false;
4238
4555
  }
4239
4556
  const replacement = needEscape ? "\\$" : "$";
4240
4557
  parts.push(replacement);
4241
- currentLineProcessed += replacement;
4558
+ appendToLine(replacement);
4242
4559
  lastIndex = match.index + 1;
4243
4560
  }
4244
4561
  parts.push(text.substring(lastIndex));
@@ -4306,8 +4623,7 @@ function escapeLatexPipesInUnclosed(text) {
4306
4623
  const tail = text.substring(unclosedStart + delimLen);
4307
4624
  return before + delim + replaceUnescapedPipes(tail);
4308
4625
  }
4309
- function truncateUnclosedLatexBlock(text) {
4310
- const unclosedStart = findUnclosedDelimiterStart(text, "double-only");
4626
+ function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
4311
4627
  if (unclosedStart === -1) return text;
4312
4628
  return text.substring(0, unclosedStart).trimEnd();
4313
4629
  }
@@ -4418,8 +4734,9 @@ function processSliceInstrumented(slice, probe = true) {
4418
4734
  if (probe && hasUnclosedTextCommand(text)) quiescent = false;
4419
4735
  text = escapeTextUnderscores(text);
4420
4736
  text = convertSingleToDoubleDollar(text);
4737
+ let unclosedDouble;
4421
4738
  if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
4422
- const unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4739
+ unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4423
4740
  if (unclosedDouble !== -1) {
4424
4741
  quiescent = false;
4425
4742
  if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
@@ -4427,7 +4744,7 @@ function processSliceInstrumented(slice, probe = true) {
4427
4744
  }
4428
4745
  }
4429
4746
  }
4430
- text = truncateUnclosedLatexBlock(text);
4747
+ text = truncateUnclosedLatexBlock(text, unclosedDouble);
4431
4748
  parts.push(text);
4432
4749
  }
4433
4750
  return { out: parts.join(""), quiescent, truncatedAtSeamStart };
@@ -4556,7 +4873,6 @@ function createRemendPreprocessor(options) {
4556
4873
  // Annotate the CommonJS export names for ESM import in node:
4557
4874
  0 && (module.exports = {
4558
4875
  DEFAULT_PAYLOAD,
4559
- DEF_LINE_START_RE,
4560
4876
  PIPELINE_STAGES,
4561
4877
  SENTINEL_FN_CONTENT,
4562
4878
  SENTINEL_LINK_URL,
@@ -4591,7 +4907,6 @@ function createRemendPreprocessor(options) {
4591
4907
  hasLoneSurrogate,
4592
4908
  highlight,
4593
4909
  isFootnoteSection,
4594
- lastRegionStart,
4595
4910
  measureStage,
4596
4911
  mergeClassNameAllowlist,
4597
4912
  normalizeForMatch,