@ai-react-markdown/engine 2.4.0 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.dev.js CHANGED
@@ -202,6 +202,10 @@ var TAG_OR_COMMENT_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)(?=[\s/>])([^>]*)>|<!--|--
202
202
  var TRUNCATED_TAG_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)([^<>]*)$/;
203
203
  var REF_RE = /!?\[((?:[^[\]\\]|\\.)*)\]/g;
204
204
  var BACKTICK_RUN_RE = /`+/g;
205
+ var MD_BLANK_RE = /^[ \t\r]*$/;
206
+ var isMdBlank = (text) => MD_BLANK_RE.test(text);
207
+ var mdTrim = (text) => text.replace(/^[ \t\r]+|[ \t\r]+$/g, "");
208
+ var mdTrimStart = (text) => text.replace(/^[ \t\r]+/, "");
205
209
  function computeIndent(text) {
206
210
  let indent = 0;
207
211
  for (const ch of text) {
@@ -211,8 +215,25 @@ function computeIndent(text) {
211
215
  }
212
216
  return indent;
213
217
  }
218
+ function firstUnescaped(text, ch) {
219
+ for (let i = 0; i < text.length; i++) {
220
+ if (text[i] === "\\") i += 1;
221
+ else if (text[i] === ch) return i;
222
+ }
223
+ return -1;
224
+ }
225
+ function lastUnclosedBracket(text) {
226
+ let open = -1;
227
+ for (let i = 0; i < text.length; i++) {
228
+ const c = text[i];
229
+ if (c === "\\") i += 1;
230
+ else if (c === "[") open = i;
231
+ else if (c === "]") open = -1;
232
+ }
233
+ return open;
234
+ }
214
235
  function normalizeLabel(label) {
215
- const collapsed = label.trim().replace(/[ \t\r\n]+/g, " ");
236
+ const collapsed = label.replace(/[ \t\r\n]+/g, " ").replace(/^ | $/g, "");
216
237
  return collapsed ? normalizeIdentifier(collapsed) : "";
217
238
  }
218
239
  function canBecomeDdLine(text, confirmed) {
@@ -289,6 +310,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
289
310
  prevLineWasText: false,
290
311
  prevLineWasValidDef: false,
291
312
  paragraphHasUnpairedRun: false,
313
+ openBracket: null,
292
314
  htmlFlowSinceBlank: false,
293
315
  htmlSeamPending: false,
294
316
  phasePoisonedAt: Infinity,
@@ -296,11 +318,11 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
296
318
  };
297
319
  }
298
320
  function isPlausibleLinkDefRest(rest) {
299
- const t = rest.trim();
321
+ const t = mdTrim(rest);
300
322
  if (t === "") return false;
301
323
  const destEnd = linkDestinationEnd(t);
302
324
  if (destEnd === -1) return false;
303
- const after = t.slice(destEnd).trim();
325
+ const after = mdTrim(t.slice(destEnd));
304
326
  if (after === "") return true;
305
327
  const opener = after[0];
306
328
  if (opener !== '"' && opener !== "'" && opener !== "(") return false;
@@ -310,7 +332,7 @@ function isPlausibleLinkDefRest(rest) {
310
332
  i += 1;
311
333
  continue;
312
334
  }
313
- if (after[i] === closer) return after.slice(i + 1).trim() === "";
335
+ if (after[i] === closer) return isMdBlank(after.slice(i + 1));
314
336
  }
315
337
  return false;
316
338
  }
@@ -347,6 +369,36 @@ function linkDestinationEnd(t) {
347
369
  if (balance !== 0 || i === 0) return -1;
348
370
  return i;
349
371
  }
372
+ function inlineResourceEnd(text, openIdx) {
373
+ let i = openIdx + 1;
374
+ const skipWs = () => {
375
+ while (i < text.length && (text[i] === " " || text[i] === " ")) i += 1;
376
+ };
377
+ skipWs();
378
+ if (text[i] === ")") return i + 1;
379
+ const destEnd = linkDestinationEnd(text.slice(i));
380
+ if (destEnd === -1) return -1;
381
+ i += destEnd;
382
+ const beforeWs = i;
383
+ skipWs();
384
+ if (text[i] === ")") return i + 1;
385
+ if (i === beforeWs) return -1;
386
+ const opener = text[i];
387
+ if (opener !== '"' && opener !== "'" && opener !== "(") return -1;
388
+ const closer = opener === "(" ? ")" : opener;
389
+ for (i += 1; i < text.length; i++) {
390
+ if (text[i] === "\\") {
391
+ i += 1;
392
+ continue;
393
+ }
394
+ if (text[i] === closer) {
395
+ i += 1;
396
+ skipWs();
397
+ return text[i] === ")" ? i + 1 : -1;
398
+ }
399
+ }
400
+ return -1;
401
+ }
350
402
  function classifyBlockStart(text, indent, defListEnabled) {
351
403
  if (indent >= 4) return true;
352
404
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -364,12 +416,13 @@ function computeFreezeBoundary(text, options, resume) {
364
416
  let end = text.indexOf("\n", start);
365
417
  if (end === -1) end = text.length;
366
418
  const confirmed = end < text.length;
367
- const lineText = text.slice(start, end);
419
+ const rawLine = text.slice(start, end);
420
+ const lineText = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
368
421
  const ln = {
369
422
  start,
370
423
  end,
371
424
  text: lineText,
372
- blank: confirmed && lineText.trim() === "",
425
+ blank: confirmed && isMdBlank(lineText),
373
426
  indent: computeIndent(lineText)
374
427
  };
375
428
  if (!confirmed) {
@@ -458,7 +511,7 @@ function processConfirmedLine(cp, ln, text) {
458
511
  const isBlockStart = cp.prevLineBlank;
459
512
  if (cp.htmlSeamPending && !ln.blank && !cp.htmlFlowSinceBlank && !(cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen)) {
460
513
  const defShapedLine = DEF_RE.test(ln.text) || FOOTNOTE_DEF_RE.test(ln.text);
461
- const commentOnly = ln.text.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!--[\s\S]*$/, " ").trim() === "";
514
+ const commentOnly = ln.text.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!--[\s\S]*$/, " ").replace(/[ \t\r]/g, "") === "";
462
515
  if (!defShapedLine && !commentOnly) {
463
516
  cp.htmlSeamPending = false;
464
517
  }
@@ -479,13 +532,14 @@ function processConfirmedLine(cp, ln, text) {
479
532
  const rawOpenAtLineStart = cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen;
480
533
  if (cp.inFence) {
481
534
  const close = FENCE_RE.exec(ln.text);
482
- if (close && close[1][0] === cp.fenceChar && close[1].length >= cp.fenceLen && ln.text.trim() === close[1]) {
535
+ if (close && close[1][0] === cp.fenceChar && close[1].length >= cp.fenceLen && isMdBlank(ln.text.slice(close[0].length))) {
483
536
  cp.inFence = false;
484
537
  cp.fenceChar = "";
485
538
  cp.fenceLen = 0;
486
539
  }
487
540
  cp.blankRun = 0;
488
541
  cp.paragraphHasUnpairedRun = false;
542
+ cp.openBracket = null;
489
543
  cp.prevLineBlank = false;
490
544
  cp.prevLineWasText = false;
491
545
  cp.prevLineWasValidDef = false;
@@ -507,6 +561,7 @@ function processConfirmedLine(cp, ln, text) {
507
561
  cp.openIndent = ln.indent;
508
562
  cp.blankRun = 0;
509
563
  cp.paragraphHasUnpairedRun = false;
564
+ cp.openBracket = null;
510
565
  cp.prevLineBlank = false;
511
566
  cp.prevLineWasText = false;
512
567
  cp.prevLineWasValidDef = false;
@@ -515,12 +570,13 @@ function processConfirmedLine(cp, ln, text) {
515
570
  }
516
571
  if (cp.inMath) {
517
572
  const close = MATH_RUN_RE.exec(ln.text);
518
- if (close && close[1].length >= cp.mathFenceLen && ln.text.trim() === close[1]) {
573
+ if (close && close[1].length >= cp.mathFenceLen && isMdBlank(ln.text.slice(close[0].length))) {
519
574
  cp.inMath = false;
520
575
  cp.mathFenceLen = 0;
521
576
  }
522
577
  cp.blankRun = 0;
523
578
  cp.paragraphHasUnpairedRun = false;
579
+ cp.openBracket = null;
524
580
  cp.prevLineBlank = false;
525
581
  cp.prevLineWasText = false;
526
582
  cp.prevLineWasValidDef = false;
@@ -542,6 +598,7 @@ function processConfirmedLine(cp, ln, text) {
542
598
  cp.openIndent = ln.indent;
543
599
  cp.blankRun = 0;
544
600
  cp.paragraphHasUnpairedRun = false;
601
+ cp.openBracket = null;
545
602
  cp.prevLineBlank = false;
546
603
  cp.prevLineWasText = false;
547
604
  cp.prevLineWasValidDef = false;
@@ -565,6 +622,7 @@ function processConfirmedLine(cp, ln, text) {
565
622
  defListSettled: null
566
623
  });
567
624
  cp.paragraphHasUnpairedRun = false;
625
+ cp.openBracket = null;
568
626
  cp.htmlFlowSinceBlank = false;
569
627
  cp.prevLineBlank = true;
570
628
  cp.prevLineWasText = false;
@@ -577,13 +635,13 @@ function processConfirmedLine(cp, ln, text) {
577
635
  } else if (LIST_MARKER_RE.test(ln.text) || FOOTNOTE_DEF_RE.test(ln.text) || cp.defListEnabled && DEF_LIST_DD_RE.test(ln.text) || ln.indent >= 4) {
578
636
  cp.hazardVerdict = true;
579
637
  }
580
- const tagStart = ln.indent <= 3 ? /^<\/?([A-Za-z][A-Za-z0-9-]*)/.exec(ln.text.trimStart()) : null;
638
+ const tagStart = ln.indent <= 3 ? /^<\/?([A-Za-z][A-Za-z0-9-]*)/.exec(mdTrimStart(ln.text)) : null;
581
639
  if (tagStart) {
582
640
  cp.htmlFlowSinceBlank = true;
583
641
  if (!TYPE6_NAMES.has(tagStart[1].toLowerCase())) cp.hazardVerdict = true;
584
642
  }
585
643
  const inRawText = cp.htmlFlowSinceBlank || rawOpenAtLineStart;
586
- const rawFlowStart = ln.indent <= 3 && /^<(?:!--|\?|![A-Za-z]|!\[CDATA\[)/.test(ln.text.trimStart());
644
+ const rawFlowStart = ln.indent <= 3 && /^<(?:!--|\?|![A-Za-z]|!\[CDATA\[)/.test(mdTrimStart(ln.text));
587
645
  const { masked, unpaired } = inRawText ? { masked: null, unpaired: false } : maskIntraLineCodeSpans(ln.text, cp.paragraphHasUnpairedRun);
588
646
  if (unpaired) cp.paragraphHasUnpairedRun = true;
589
647
  const scanText = masked ?? ln.text;
@@ -601,31 +659,52 @@ function processConfirmedLine(cp, ln, text) {
601
659
  if (key && !cp.defs.has(key)) cp.defs.set(key, ln.end);
602
660
  }
603
661
  }
604
- if (cp.referenceTaint && scanText.includes("[")) {
605
- const defBracket = validDef ? def.index + def[0].indexOf("[") : -1;
606
- REF_RE.lastIndex = 0;
607
- let m;
608
- while ((m = REF_RE.exec(scanText)) !== null) {
609
- const follow = scanText[m.index + m[0].length];
610
- if (follow === "(") continue;
611
- if (follow === ":" && m.index === defBracket) continue;
612
- const inner = m[1];
662
+ if (cp.referenceTaint) {
663
+ const pushRef = (offset, inner, followAt) => {
664
+ const follow = scanText[followAt];
665
+ if (follow === "(" && inlineResourceEnd(scanText, followAt) !== -1) return;
613
666
  let label;
614
667
  let footnote = false;
615
668
  if (inner.startsWith("^")) {
616
669
  footnote = true;
617
670
  label = normalizeLabel(inner.slice(1));
618
671
  } else if (follow === "[") {
619
- const explicit = /^\[((?:[^[\]\\]|\\.)*)\]/.exec(scanText.slice(m.index + m[0].length));
672
+ const explicit = /^\[((?:[^[\]\\]|\\.)*)\]/.exec(scanText.slice(followAt));
620
673
  label = normalizeLabel(explicit && explicit[1] ? explicit[1] : inner);
621
674
  } else {
622
675
  label = normalizeLabel(inner);
623
676
  }
624
- if (!label) continue;
625
- cp.unresolvedRefs.push({ offset: ln.start + m.index, label, footnote });
677
+ if (label) cp.unresolvedRefs.push({ offset, label, footnote });
678
+ };
679
+ const pending = cp.openBracket;
680
+ cp.openBracket = null;
681
+ if (pending) {
682
+ const close = firstUnescaped(scanText, "]");
683
+ const open = firstUnescaped(scanText, "[");
684
+ const cont = (t) => t.replace(/^ {0,3}>[ \t]?/, "");
685
+ if (close !== -1 && (open === -1 || close < open)) {
686
+ pushRef(pending.offset, `${pending.text}
687
+ ${cont(scanText.slice(0, close))}`, close + 1);
688
+ } else if (close === -1 && open === -1) {
689
+ cp.openBracket = { offset: pending.offset, text: `${pending.text}
690
+ ${cont(scanText)}` };
691
+ }
692
+ }
693
+ if (scanText.includes("[")) {
694
+ const defBracket = validDef ? def.index + def[0].indexOf("[") : -1;
695
+ REF_RE.lastIndex = 0;
696
+ let m;
697
+ while ((m = REF_RE.exec(scanText)) !== null) {
698
+ const followAt = m.index + m[0].length;
699
+ if (scanText[followAt] === ":" && m.index === defBracket) continue;
700
+ pushRef(ln.start + m.index, m[1], followAt);
701
+ }
702
+ const trailingOpen = lastUnclosedBracket(scanText);
703
+ if (trailingOpen !== -1) {
704
+ cp.openBracket = { offset: ln.start + trailingOpen, text: scanText.slice(trailingOpen + 1) };
705
+ }
626
706
  }
627
707
  }
628
- const rawOpenAtStart = cp.piOpen || cp.declOpen || cp.cdataOpen;
629
708
  const rawSpans = [];
630
709
  let pos = 0;
631
710
  const poisonRawDivergence = () => {
@@ -684,7 +763,7 @@ function processConfirmedLine(cp, ln, text) {
684
763
  if (scanText[pi + 2] === ">") {
685
764
  rawSpans.push([pi, pi + 3]);
686
765
  pos = pi + 3;
687
- if (scanText.slice(0, pi).trim() !== "" || ln.indent > 3) poisonRawDivergence();
766
+ if (!isMdBlank(scanText.slice(0, pi)) || ln.indent > 3) poisonRawDivergence();
688
767
  continue;
689
768
  }
690
769
  rawSpans.push([pi, pi + 2]);
@@ -696,14 +775,17 @@ function processConfirmedLine(cp, ln, text) {
696
775
  pos = decl + 2;
697
776
  }
698
777
  }
699
- const rawOpenAtEnd = cp.piOpen || cp.declOpen || cp.cdataOpen;
700
- if (!rawOpenAtStart && !rawOpenAtEnd) {
778
+ let tagText = scanText;
779
+ for (const [from, to] of rawSpans) {
780
+ tagText = tagText.slice(0, from) + " ".repeat(to - from) + tagText.slice(to);
781
+ }
782
+ {
701
783
  TAG_OR_COMMENT_RE.lastIndex = 0;
702
784
  let m;
703
785
  let lastCommentOpenerIdx = -1;
704
- while ((m = TAG_OR_COMMENT_RE.exec(scanText)) !== null) {
786
+ while ((m = TAG_OR_COMMENT_RE.exec(tagText)) !== null) {
705
787
  if (m[0] === "<!--") {
706
- const next = scanText.slice(m.index + 4, m.index + 6);
788
+ const next = tagText.slice(m.index + 4, m.index + 6);
707
789
  if (cp.commentOpen) {
708
790
  if (next.startsWith(">") || next === "->") cp.commentOpen = false;
709
791
  else if (next === "!>" || next === "-!") poisonRawDivergence();
@@ -732,34 +814,56 @@ function processConfirmedLine(cp, ln, text) {
732
814
  applyTag(tag, closing);
733
815
  }
734
816
  if (cp.commentOpen && lastCommentOpenerIdx !== -1 && !inRawText) {
735
- if (scanText.slice(0, lastCommentOpenerIdx).trim() !== "") {
817
+ if (!isMdBlank(tagText.slice(0, lastCommentOpenerIdx))) {
736
818
  cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastCommentOpenerIdx);
737
819
  }
738
820
  }
739
- if (cp.pendingTruncatedTags.length > 0 && scanText.includes(">")) {
821
+ if (masked !== null && masked !== ln.text) {
822
+ const inRaw = (i) => rawSpans.some(([from, to]) => i >= from && i < to);
823
+ TAG_OR_COMMENT_RE.lastIndex = 0;
824
+ let mr;
825
+ while ((mr = TAG_OR_COMMENT_RE.exec(ln.text)) !== null) {
826
+ if (mr[0] === "<!--" || mr[0] === "-->" || mr[0] === "--!>") continue;
827
+ const startMasked = masked[mr.index] !== ln.text[mr.index];
828
+ const wholeVisible = masked.slice(mr.index, mr.index + mr[0].length) === mr[0];
829
+ if (startMasked || wholeVisible || inRaw(mr.index) || cp.commentOpen) continue;
830
+ const closing = mr[1] === "/";
831
+ const tag = mr[2].toLowerCase();
832
+ const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
833
+ if (VOID_TAGS.has(tag) || selfClosing) continue;
834
+ applyTag(tag, closing);
835
+ }
836
+ }
837
+ if (cp.pendingTruncatedTags.length > 0 && ln.text.includes(">")) {
740
838
  cp.pendingTruncatedTags = [];
741
839
  }
742
840
  if (!cp.commentOpen) {
743
- const lastLt = scanText.lastIndexOf("<");
744
- if (lastLt !== -1 && !scanText.includes(">", lastLt)) {
745
- const m2 = TRUNCATED_TAG_RE.exec(scanText.slice(lastLt));
841
+ const lastLt = tagText.lastIndexOf("<");
842
+ if (lastLt !== -1 && !tagText.includes(">", lastLt)) {
843
+ const m2 = TRUNCATED_TAG_RE.exec(tagText.slice(lastLt));
746
844
  if (m2) {
747
845
  const closing = m2[1] === "/";
748
846
  const tag = m2[2].toLowerCase();
749
847
  if (!VOID_TAGS.has(tag)) {
750
848
  applyTag(tag, closing);
751
- if (!closing && !inRawText) cp.pendingTruncatedTags.push(tag);
849
+ const rawLastLt = ln.text.lastIndexOf("<");
850
+ const rawTruncated = rawLastLt !== -1 && !ln.text.includes(">", rawLastLt);
851
+ if (!closing && !inRawText && rawTruncated) cp.pendingTruncatedTags.push(tag);
752
852
  }
753
853
  }
754
854
  }
755
855
  }
756
856
  }
757
- if ((inRawText || rawFlowStart) && cp.openTotal === 0) {
758
- let masked2 = scanText;
857
+ const effectiveOpen = cp.openTotal - cp.pendingTruncatedTags.length;
858
+ if ((inRawText || rawFlowStart) && effectiveOpen <= 0) {
859
+ let masked2 = "";
860
+ let cursor = 0;
759
861
  for (const [from, to] of rawSpans) {
760
- masked2 = masked2.slice(0, from) + " ".repeat(to - from) + masked2.slice(to);
862
+ masked2 += scanText.slice(cursor, from);
863
+ cursor = to;
761
864
  }
762
- if (floatingResidue(masked2, commentOpenAtLineStart).trim() !== "") {
865
+ masked2 += scanText.slice(cursor);
866
+ if (floatingResidue(masked2, commentOpenAtLineStart).length > 0) {
763
867
  cp.htmlSeamPending = true;
764
868
  }
765
869
  }
@@ -822,7 +926,7 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
822
926
  if (last && last.kind === "refs") last.tokens.push(token);
823
927
  else events.push({ kind: "refs", tokens: [token] });
824
928
  };
825
- const visit6 = (node, nested) => {
929
+ const visit7 = (node, nested) => {
826
930
  const type = node.type;
827
931
  if (type === "definition") {
828
932
  const start = node.position?.start?.offset;
@@ -863,7 +967,7 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
863
967
  }
864
968
  const children = node.children;
865
969
  if (children) {
866
- for (const child of children) visit6(child, true);
970
+ for (const child of children) visit7(child, true);
867
971
  }
868
972
  };
869
973
  let lastStart = -1;
@@ -876,11 +980,11 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
876
980
  if (start === void 0) {
877
981
  if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
878
982
  cacheable = false;
879
- visit6(child, false);
983
+ visit7(child, false);
880
984
  continue;
881
985
  }
882
986
  if (start >= boundary || start < resumeAt) continue;
883
- visit6(child, false);
987
+ visit7(child, false);
884
988
  }
885
989
  return { events, uninjectable, cacheable };
886
990
  }
@@ -893,7 +997,7 @@ function cloneEventsForAppend(events) {
893
997
  var TERMINATOR_LABEL = "__aimd_injection_terminator__";
894
998
  var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
895
999
  function tailMentionsTerminator(tailSource) {
896
- return normalizeIdentifier2(tailSource).includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
1000
+ return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
897
1001
  }
898
1002
  function buildInjectionPrefix(events) {
899
1003
  if (events.length === 0) return { text: "", segments: [] };
@@ -936,13 +1040,14 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
936
1040
  const position = node.position;
937
1041
  if (position) {
938
1042
  for (const point of [position.start, position.end]) {
1043
+ if (!point) continue;
939
1044
  const seg = point.offset !== void 0 && point.offset <= maxEnd ? segments.find((s) => point.offset >= s.injStart && point.offset <= s.injEnd) : void 0;
940
1045
  if (seg) {
941
1046
  point.offset += seg.offsetDelta;
942
- point.line += seg.lineDelta;
1047
+ if (typeof point.line === "number") point.line += seg.lineDelta;
943
1048
  } else {
944
1049
  if (point.offset !== void 0) point.offset += offsetDelta;
945
- point.line += lineDelta;
1050
+ if (typeof point.line === "number") point.line += lineDelta;
946
1051
  }
947
1052
  }
948
1053
  }
@@ -973,12 +1078,25 @@ function spliceTrees(input) {
973
1078
  if (isTrailingLiteralText(node2)) {
974
1079
  const prev = i > 0 ? prevHast.children[i - 1] : void 0;
975
1080
  if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1081
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1082
+ return null;
976
1083
  }
977
1084
  cutRegion.push(node2);
978
1085
  continue;
979
1086
  }
980
1087
  const node = prevHast.children[i];
981
- if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node)) {
1088
+ if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node) && // …and only when that element really IS an html block's output: a
1089
+ // raw literal can only trail an `html` mdast node. A position-less
1090
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
1091
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1092
+ // separator — owned by the tail, which re-parses it; freezing it
1093
+ // here duplicated it (v2.4.0 review P3). Falls through to the
1094
+ // remnant look-ahead below, which bails to a full parse. And the
1095
+ // literal must really be THAT block's trailing text: the block's raw
1096
+ // source ends with it. A dropped-tag block right after a frozen html
1097
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1098
+ // position, and freezing it duplicated it (release soak of the fix).
1099
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
982
1100
  cutRegion.push(node);
983
1101
  break;
984
1102
  }
@@ -1096,6 +1214,9 @@ function alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible) {
1096
1214
  nextIdx = pairIdx + 1 + stripped;
1097
1215
  const candidate = visibles[nextIdx];
1098
1216
  if (!candidate) return null;
1217
+ if (start === void 0 && sepBuffer.some((sep) => sep.type !== "text" || sep.value !== "\n")) {
1218
+ return null;
1219
+ }
1099
1220
  if (start !== void 0) {
1100
1221
  const cStart = candidate.position?.start?.offset;
1101
1222
  const cEnd = candidate.position?.end?.offset;
@@ -1112,6 +1233,9 @@ function alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible) {
1112
1233
  const trailingGaps = sawContent ? trailingStripped : Math.max(0, visibles.length - 1);
1113
1234
  const seam = visibles.length > 0 && tailWrapVisible ? 1 : 0;
1114
1235
  const last = out[out.length - 1];
1236
+ if (last !== void 0 && last.type === "text" && last.position !== void 0 && last.value.trim() === "") {
1237
+ return null;
1238
+ }
1115
1239
  const lastIsLiteral = last !== void 0 && last.type === "text" && last.value.trim() !== "";
1116
1240
  const litOwnerEnd = pairIdx >= 0 ? visibles[pairIdx].position?.end?.offset : void 0;
1117
1241
  const litEnd = lastIsLiteral ? last.position?.end?.offset : void 0;
@@ -1149,8 +1273,16 @@ function alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible) {
1149
1273
  const v = lastPaired.value;
1150
1274
  const lastLt = v.lastIndexOf("<");
1151
1275
  if (lastLt !== -1 && /^<[!?]/.test(v.slice(lastLt))) return null;
1276
+ const lastOut = out[out.length - 1];
1277
+ const outEnd = lastOut?.type === "element" ? lastOut.position?.end?.offset : void 0;
1278
+ const blockEnd = lastPaired.position?.end?.offset;
1279
+ if (outEnd !== void 0 && blockEnd !== void 0 && outEnd < blockEnd) return null;
1152
1280
  }
1153
1281
  if (sepBuffer.length !== trailingGaps && sepBuffer.length !== trailingGaps + 1) return null;
1282
+ for (let j = pairIdx + 1; j < visibles.length; j++) {
1283
+ const v = visibles[j];
1284
+ if (v.type === "html" && !/^\s*<[!?]/.test(v.value)) return null;
1285
+ }
1154
1286
  for (let i = 0; i < trailingGaps + seam; i++) {
1155
1287
  out.push({ type: "text", value: "\n" });
1156
1288
  }
@@ -1193,8 +1325,16 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
1193
1325
  }
1194
1326
  function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1195
1327
  const firstText = tailHastChildren[0];
1196
- if (!firstText || !isSeparatorText(firstText)) return false;
1328
+ if (!firstText) return false;
1197
1329
  const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1330
+ if (!isSeparatorText(firstText)) {
1331
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1332
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1333
+ if (isCompleteRawConstruct(firstVisible.value)) return false;
1334
+ return null;
1335
+ }
1336
+ return false;
1337
+ }
1198
1338
  if (!firstVisible) return false;
1199
1339
  const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1200
1340
  if (firstContent) {
@@ -1216,6 +1356,14 @@ function isCompleteRawConstruct(value) {
1216
1356
  function isSeparatorText(node) {
1217
1357
  return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1218
1358
  }
1359
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1360
+ const start = el.position?.start?.offset;
1361
+ if (start === void 0) return false;
1362
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1363
+ if (!owner || owner.type !== "html") return false;
1364
+ const text = literal.value.trim();
1365
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1366
+ }
1219
1367
  function isTrailingLiteralText(node) {
1220
1368
  return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1221
1369
  }
@@ -1450,7 +1598,11 @@ function createDefLabelScanner(parse = collectDefLabels) {
1450
1598
 
1451
1599
  // src/components/extractDefBodiesFromHast.ts
1452
1600
  import { SKIP, visit as visit2 } from "unist-util-visit";
1601
+ import { normalizeUri } from "micromark-util-sanitize-uri";
1453
1602
  var FN_LI_ID_RE = /(?:^|-)user-content-fn-(.+)$/;
1603
+ function footnoteSafeId(identifier) {
1604
+ return normalizeUri(identifier.toLowerCase());
1605
+ }
1454
1606
  function sourceIdFromFootnoteLiId(idProp, clobberPrefix) {
1455
1607
  let raw = null;
1456
1608
  if (clobberPrefix !== void 0) {
@@ -1607,10 +1759,10 @@ function phantomSuffixCloser(content) {
1607
1759
  const confirmed = endsWithNewline ? content : content + "\n";
1608
1760
  const { checkpoint } = computeFreezeBoundary(confirmed, { defListEnabled: false, referenceTaint: false });
1609
1761
  if (checkpoint.phasePoisonedAt !== Infinity) return "";
1762
+ if (checkpoint.openIndent !== 0) return "";
1610
1763
  const nl = endsWithNewline ? "" : "\n";
1611
- const indent = " ".repeat(checkpoint.openIndent);
1612
- if (checkpoint.inFence) return `${nl}${indent}${checkpoint.fenceChar.repeat(checkpoint.fenceLen)}`;
1613
- if (checkpoint.inMath) return `${nl}${indent}${"$".repeat(checkpoint.mathFenceLen)}`;
1764
+ if (checkpoint.inFence) return `${nl}${checkpoint.fenceChar.repeat(checkpoint.fenceLen)}`;
1765
+ if (checkpoint.inMath) return `${nl}${"$".repeat(checkpoint.mathFenceLen)}`;
1614
1766
  return "";
1615
1767
  }
1616
1768
 
@@ -1725,6 +1877,7 @@ function createRegistry(onEmpty) {
1725
1877
  }
1726
1878
  },
1727
1879
  contributeLabels(symbol, footnotes, links) {
1880
+ if (!this.chunkOrder.includes(symbol)) return;
1728
1881
  const data = this.chunkData.get(symbol);
1729
1882
  if (data) {
1730
1883
  data.ownFootnoteLabels = footnotes;
@@ -1749,6 +1902,7 @@ function createRegistry(onEmpty) {
1749
1902
  this._notify();
1750
1903
  },
1751
1904
  contributeChunkData(symbol, data) {
1905
+ if (!this.chunkOrder.includes(symbol)) return;
1752
1906
  this.chunkData.set(symbol, data);
1753
1907
  this.labelSet.footnoteLabels = /* @__PURE__ */ new Set();
1754
1908
  this.labelSet.linkLabels = /* @__PURE__ */ new Set();
@@ -1849,6 +2003,43 @@ function createRegistry(onEmpty) {
1849
2003
  import rehypeKatex from "rehype-katex";
1850
2004
  import rehypeRaw from "rehype-raw";
1851
2005
  import rehypeUnwrapImages from "rehype-unwrap-images";
2006
+
2007
+ // src/components/rehypeUnwrapCrossChunkImages.ts
2008
+ import { SKIP as SKIP3, visit as visit4 } from "unist-util-visit";
2009
+ var IMAGE_TAGS = /* @__PURE__ */ new Set(["img", "cross-chunk-image"]);
2010
+ var LINK_TAGS = /* @__PURE__ */ new Set(["a", "cross-chunk-link"]);
2011
+ function applicable(node, inLink, seen) {
2012
+ let image = 0 /* Unknown */;
2013
+ for (const child of node.children) {
2014
+ if (child.type === "text" && /^\s*$/.test(child.value)) continue;
2015
+ if (child.type === "element" && IMAGE_TAGS.has(child.tagName)) {
2016
+ if (child.tagName !== "img") seen.placeholder = true;
2017
+ image = 1 /* ContainsImage */;
2018
+ } else if (!inLink && child.type === "element" && LINK_TAGS.has(child.tagName)) {
2019
+ if (child.tagName !== "a") seen.placeholder = true;
2020
+ const inner = applicable(child, true, seen);
2021
+ if (inner === 2 /* ContainsOther */) return 2 /* ContainsOther */;
2022
+ if (inner === 1 /* ContainsImage */) image = 1 /* ContainsImage */;
2023
+ } else {
2024
+ return 2 /* ContainsOther */;
2025
+ }
2026
+ }
2027
+ return image;
2028
+ }
2029
+ function rehypeUnwrapCrossChunkImages() {
2030
+ return function transform(tree) {
2031
+ visit4(tree, "element", (node, index, parent) => {
2032
+ if (node.tagName !== "p" || !parent || typeof index !== "number") return;
2033
+ const seen = { placeholder: false };
2034
+ if (applicable(node, false, seen) === 1 /* ContainsImage */ && seen.placeholder) {
2035
+ parent.children.splice(index, 1, ...node.children);
2036
+ return [SKIP3, index];
2037
+ }
2038
+ });
2039
+ };
2040
+ }
2041
+
2042
+ // src/components/pluginChain.ts
1852
2043
  import rehypeSanitize from "rehype-sanitize";
1853
2044
  import remarkBreaks from "remark-breaks";
1854
2045
  import remarkCjkFriendly from "remark-cjk-friendly";
@@ -1864,13 +2055,13 @@ import remarkPangu from "remark-pangu";
1864
2055
  import remarkRemoveComments from "remark-remove-comments";
1865
2056
 
1866
2057
  // src/components/rehypeRebaseHashLinks.ts
1867
- import { visit as visit4 } from "unist-util-visit";
2058
+ import { visit as visit5 } from "unist-util-visit";
1868
2059
  var DEFAULT_PREFIX = "user-content-";
1869
2060
  var rehypeRebaseHashLinks = (options) => {
1870
2061
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
1871
2062
  const hashPrefix = "#" + prefix;
1872
2063
  return (tree) => {
1873
- visit4(tree, "element", (node) => {
2064
+ visit5(tree, "element", (node) => {
1874
2065
  if (node.tagName !== "a") return;
1875
2066
  const href = node.properties?.href;
1876
2067
  if (typeof href !== "string" || !href.startsWith("#")) return;
@@ -1882,7 +2073,7 @@ var rehypeRebaseHashLinks = (options) => {
1882
2073
  var rehypeRebaseHashLinks_default = rehypeRebaseHashLinks;
1883
2074
 
1884
2075
  // src/components/rehypeFooterAdorn.ts
1885
- import { visit as visit5 } from "unist-util-visit";
2076
+ import { visit as visit6 } from "unist-util-visit";
1886
2077
  var FOOTNOTE_LABEL_ID_RE = /(?:^|-)footnote-label$/;
1887
2078
  function isFootnoteLabelH2(node) {
1888
2079
  if (node.type !== "element") return false;
@@ -1897,7 +2088,7 @@ function isHr(node) {
1897
2088
  }
1898
2089
  function rehypeFooterAdorn() {
1899
2090
  return (tree) => {
1900
- visit5(tree, "element", (n) => {
2091
+ visit6(tree, "element", (n) => {
1901
2092
  const el = n;
1902
2093
  if (el.tagName !== "section") return;
1903
2094
  if (!(el.properties && "dataFootnotes" in el.properties)) return;
@@ -1975,7 +2166,10 @@ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix) {
1975
2166
  // above.
1976
2167
  [rehypeRebaseHashLinks_default, { prefix: clobberPrefix }],
1977
2168
  rehypeKatex,
1978
- rehypeUnwrapImages
2169
+ rehypeUnwrapImages,
2170
+ // Same unwrap for `<cross-chunk-image>` placeholders (coordinated mode);
2171
+ // no-op on standalone documents.
2172
+ rehypeUnwrapCrossChunkImages
1979
2173
  ];
1980
2174
  }
1981
2175
  function buildCoreRemarkRehypeOptions(enableDefinitionList) {
@@ -2094,6 +2288,9 @@ function buildCrossChunkHandlers() {
2094
2288
  };
2095
2289
  }
2096
2290
 
2291
+ // src/components/crossChunkUrlSanitize.ts
2292
+ import { normalizeUri as normalizeUri2 } from "micromark-util-sanitize-uri";
2293
+
2097
2294
  // ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_listCacheClear.js
2098
2295
  function listCacheClear() {
2099
2296
  this.__data__ = [];
@@ -3321,14 +3518,13 @@ function isProtocolAllowed(url, allowed) {
3321
3518
  return allowed.some((p) => p === protocol);
3322
3519
  }
3323
3520
  function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
3324
- const transformed = urlTransform(rawUrl, key, fakeElement(tagName, key, rawUrl));
3325
- if (transformed == null) return "";
3326
- const stringUrl = String(transformed);
3327
- if (stringUrl === "") return "";
3521
+ rawUrl = normalizeUri2(rawUrl);
3328
3522
  const callerProtocols = schema.protocols;
3329
3523
  const allowed = callerProtocols === void 0 || callerProtocols === null ? sanitizeSchema.protocols?.[key] : callerProtocols[key];
3330
- if (!allowed || allowed.length === 0) return stringUrl;
3331
- return isProtocolAllowed(stringUrl, allowed) ? stringUrl : "";
3524
+ if (allowed && allowed.length > 0 && !isProtocolAllowed(rawUrl, allowed)) return null;
3525
+ const transformed = urlTransform(rawUrl, key, fakeElement(tagName, key, rawUrl));
3526
+ if (transformed == null) return null;
3527
+ return String(transformed);
3332
3528
  }
3333
3529
 
3334
3530
  // src/plugins/defs.ts
@@ -3720,6 +3916,20 @@ var LITERAL_CONTENT_CLOSE_REGEX = {
3720
3916
  math: /<\/math\s*>/gi,
3721
3917
  svg: /<\/svg\s*>/gi
3722
3918
  };
3919
+ function restOfLineIsBlank(content, pos) {
3920
+ for (let i = pos; i < content.length; i++) {
3921
+ const c = content[i];
3922
+ if (c === "\n") return true;
3923
+ if (c !== " " && c !== " " && c !== "\r") return false;
3924
+ }
3925
+ return true;
3926
+ }
3927
+ function lineHasBacktick(content, pos) {
3928
+ for (let i = pos; i < content.length && content[i] !== "\n"; i++) {
3929
+ if (content[i] === "`") return true;
3930
+ }
3931
+ return false;
3932
+ }
3723
3933
  function isAtLineStart(content, pos) {
3724
3934
  let i = pos - 1;
3725
3935
  let spaces = 0;
@@ -3762,7 +3972,7 @@ function splitByProtectedRegions(content) {
3762
3972
  if (multilineStart !== -1) {
3763
3973
  if (char === multilineFenceMarker) {
3764
3974
  const runLen = getRepeatedMarkerLength(content, i, multilineFenceMarker);
3765
- if (runLen >= multilineFenceLength && isAtLineStart(content, i)) {
3975
+ if (runLen >= multilineFenceLength && isAtLineStart(content, i) && restOfLineIsBlank(content, i + runLen)) {
3766
3976
  pushProtected(multilineStart, i + runLen);
3767
3977
  multilineStart = -1;
3768
3978
  multilineFenceMarker = null;
@@ -3778,7 +3988,7 @@ function splitByProtectedRegions(content) {
3778
3988
  }
3779
3989
  if (char === "`" || char === "~") {
3780
3990
  const runLen = getRepeatedMarkerLength(content, i, char);
3781
- if (runLen >= 3 && isAtLineStart(content, i)) {
3991
+ if (runLen >= 3 && isAtLineStart(content, i) && !(char === "`" && lineHasBacktick(content, i + runLen))) {
3782
3992
  multilineStart = i;
3783
3993
  multilineFenceMarker = char;
3784
3994
  multilineFenceLength = runLen;
@@ -4194,6 +4404,7 @@ export {
4194
4404
  extendSanitizeSchema,
4195
4405
  extractContributions,
4196
4406
  extractDefBodiesFromHast,
4407
+ footnoteSafeId,
4197
4408
  getEnginePluginInternals,
4198
4409
  hasLoneSurrogate,
4199
4410
  highlight,