@ai-react-markdown/engine 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.dev.js CHANGED
@@ -625,7 +625,6 @@ function processConfirmedLine(cp, ln, text) {
625
625
  cp.unresolvedRefs.push({ offset: ln.start + m.index, label, footnote });
626
626
  }
627
627
  }
628
- const rawOpenAtStart = cp.piOpen || cp.declOpen || cp.cdataOpen;
629
628
  const rawSpans = [];
630
629
  let pos = 0;
631
630
  const poisonRawDivergence = () => {
@@ -696,14 +695,17 @@ function processConfirmedLine(cp, ln, text) {
696
695
  pos = decl + 2;
697
696
  }
698
697
  }
699
- const rawOpenAtEnd = cp.piOpen || cp.declOpen || cp.cdataOpen;
700
- if (!rawOpenAtStart && !rawOpenAtEnd) {
698
+ let tagText = scanText;
699
+ for (const [from, to] of rawSpans) {
700
+ tagText = tagText.slice(0, from) + " ".repeat(to - from) + tagText.slice(to);
701
+ }
702
+ {
701
703
  TAG_OR_COMMENT_RE.lastIndex = 0;
702
704
  let m;
703
705
  let lastCommentOpenerIdx = -1;
704
- while ((m = TAG_OR_COMMENT_RE.exec(scanText)) !== null) {
706
+ while ((m = TAG_OR_COMMENT_RE.exec(tagText)) !== null) {
705
707
  if (m[0] === "<!--") {
706
- const next = scanText.slice(m.index + 4, m.index + 6);
708
+ const next = tagText.slice(m.index + 4, m.index + 6);
707
709
  if (cp.commentOpen) {
708
710
  if (next.startsWith(">") || next === "->") cp.commentOpen = false;
709
711
  else if (next === "!>" || next === "-!") poisonRawDivergence();
@@ -732,34 +734,56 @@ function processConfirmedLine(cp, ln, text) {
732
734
  applyTag(tag, closing);
733
735
  }
734
736
  if (cp.commentOpen && lastCommentOpenerIdx !== -1 && !inRawText) {
735
- if (scanText.slice(0, lastCommentOpenerIdx).trim() !== "") {
737
+ if (tagText.slice(0, lastCommentOpenerIdx).trim() !== "") {
736
738
  cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastCommentOpenerIdx);
737
739
  }
738
740
  }
739
- if (cp.pendingTruncatedTags.length > 0 && scanText.includes(">")) {
741
+ if (masked !== null && masked !== ln.text) {
742
+ const inRaw = (i) => rawSpans.some(([from, to]) => i >= from && i < to);
743
+ TAG_OR_COMMENT_RE.lastIndex = 0;
744
+ let mr;
745
+ while ((mr = TAG_OR_COMMENT_RE.exec(ln.text)) !== null) {
746
+ if (mr[0] === "<!--" || mr[0] === "-->" || mr[0] === "--!>") continue;
747
+ const startMasked = masked[mr.index] !== ln.text[mr.index];
748
+ const wholeVisible = masked.slice(mr.index, mr.index + mr[0].length) === mr[0];
749
+ if (startMasked || wholeVisible || inRaw(mr.index) || cp.commentOpen) continue;
750
+ const closing = mr[1] === "/";
751
+ const tag = mr[2].toLowerCase();
752
+ const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
753
+ if (VOID_TAGS.has(tag) || selfClosing) continue;
754
+ applyTag(tag, closing);
755
+ }
756
+ }
757
+ if (cp.pendingTruncatedTags.length > 0 && ln.text.includes(">")) {
740
758
  cp.pendingTruncatedTags = [];
741
759
  }
742
760
  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));
761
+ const lastLt = tagText.lastIndexOf("<");
762
+ if (lastLt !== -1 && !tagText.includes(">", lastLt)) {
763
+ const m2 = TRUNCATED_TAG_RE.exec(tagText.slice(lastLt));
746
764
  if (m2) {
747
765
  const closing = m2[1] === "/";
748
766
  const tag = m2[2].toLowerCase();
749
767
  if (!VOID_TAGS.has(tag)) {
750
768
  applyTag(tag, closing);
751
- if (!closing && !inRawText) cp.pendingTruncatedTags.push(tag);
769
+ const rawLastLt = ln.text.lastIndexOf("<");
770
+ const rawTruncated = rawLastLt !== -1 && !ln.text.includes(">", rawLastLt);
771
+ if (!closing && !inRawText && rawTruncated) cp.pendingTruncatedTags.push(tag);
752
772
  }
753
773
  }
754
774
  }
755
775
  }
756
776
  }
757
- if ((inRawText || rawFlowStart) && cp.openTotal === 0) {
758
- let masked2 = scanText;
777
+ const effectiveOpen = cp.openTotal - cp.pendingTruncatedTags.length;
778
+ if ((inRawText || rawFlowStart) && effectiveOpen <= 0) {
779
+ let masked2 = "";
780
+ let cursor = 0;
759
781
  for (const [from, to] of rawSpans) {
760
- masked2 = masked2.slice(0, from) + " ".repeat(to - from) + masked2.slice(to);
782
+ masked2 += scanText.slice(cursor, from);
783
+ cursor = to;
761
784
  }
762
- if (floatingResidue(masked2, commentOpenAtLineStart).trim() !== "") {
785
+ masked2 += scanText.slice(cursor);
786
+ if (floatingResidue(masked2, commentOpenAtLineStart).length > 0) {
763
787
  cp.htmlSeamPending = true;
764
788
  }
765
789
  }
@@ -822,7 +846,7 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
822
846
  if (last && last.kind === "refs") last.tokens.push(token);
823
847
  else events.push({ kind: "refs", tokens: [token] });
824
848
  };
825
- const visit6 = (node, nested) => {
849
+ const visit7 = (node, nested) => {
826
850
  const type = node.type;
827
851
  if (type === "definition") {
828
852
  const start = node.position?.start?.offset;
@@ -863,7 +887,7 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
863
887
  }
864
888
  const children = node.children;
865
889
  if (children) {
866
- for (const child of children) visit6(child, true);
890
+ for (const child of children) visit7(child, true);
867
891
  }
868
892
  };
869
893
  let lastStart = -1;
@@ -876,11 +900,11 @@ function collectPrefixInjection(mdast, content, boundary, resume) {
876
900
  if (start === void 0) {
877
901
  if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
878
902
  cacheable = false;
879
- visit6(child, false);
903
+ visit7(child, false);
880
904
  continue;
881
905
  }
882
906
  if (start >= boundary || start < resumeAt) continue;
883
- visit6(child, false);
907
+ visit7(child, false);
884
908
  }
885
909
  return { events, uninjectable, cacheable };
886
910
  }
@@ -893,7 +917,7 @@ function cloneEventsForAppend(events) {
893
917
  var TERMINATOR_LABEL = "__aimd_injection_terminator__";
894
918
  var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
895
919
  function tailMentionsTerminator(tailSource) {
896
- return normalizeIdentifier2(tailSource).includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
920
+ return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
897
921
  }
898
922
  function buildInjectionPrefix(events) {
899
923
  if (events.length === 0) return { text: "", segments: [] };
@@ -973,12 +997,25 @@ function spliceTrees(input) {
973
997
  if (isTrailingLiteralText(node2)) {
974
998
  const prev = i > 0 ? prevHast.children[i - 1] : void 0;
975
999
  if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1000
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1001
+ return null;
976
1002
  }
977
1003
  cutRegion.push(node2);
978
1004
  continue;
979
1005
  }
980
1006
  const node = prevHast.children[i];
981
- if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node)) {
1007
+ 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
1008
+ // raw literal can only trail an `html` mdast node. A position-less
1009
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
1010
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1011
+ // separator — owned by the tail, which re-parses it; freezing it
1012
+ // here duplicated it (v2.4.0 review P3). Falls through to the
1013
+ // remnant look-ahead below, which bails to a full parse. And the
1014
+ // literal must really be THAT block's trailing text: the block's raw
1015
+ // source ends with it. A dropped-tag block right after a frozen html
1016
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1017
+ // position, and freezing it duplicated it (release soak of the fix).
1018
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
982
1019
  cutRegion.push(node);
983
1020
  break;
984
1021
  }
@@ -1193,8 +1230,16 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
1193
1230
  }
1194
1231
  function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1195
1232
  const firstText = tailHastChildren[0];
1196
- if (!firstText || !isSeparatorText(firstText)) return false;
1233
+ if (!firstText) return false;
1197
1234
  const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1235
+ if (!isSeparatorText(firstText)) {
1236
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1237
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1238
+ if (isCompleteRawConstruct(firstVisible.value)) return false;
1239
+ return null;
1240
+ }
1241
+ return false;
1242
+ }
1198
1243
  if (!firstVisible) return false;
1199
1244
  const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1200
1245
  if (firstContent) {
@@ -1216,6 +1261,14 @@ function isCompleteRawConstruct(value) {
1216
1261
  function isSeparatorText(node) {
1217
1262
  return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1218
1263
  }
1264
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1265
+ const start = el.position?.start?.offset;
1266
+ if (start === void 0) return false;
1267
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1268
+ if (!owner || owner.type !== "html") return false;
1269
+ const text = literal.value.trim();
1270
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1271
+ }
1219
1272
  function isTrailingLiteralText(node) {
1220
1273
  return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1221
1274
  }
@@ -1450,7 +1503,11 @@ function createDefLabelScanner(parse = collectDefLabels) {
1450
1503
 
1451
1504
  // src/components/extractDefBodiesFromHast.ts
1452
1505
  import { SKIP, visit as visit2 } from "unist-util-visit";
1506
+ import { normalizeUri } from "micromark-util-sanitize-uri";
1453
1507
  var FN_LI_ID_RE = /(?:^|-)user-content-fn-(.+)$/;
1508
+ function footnoteSafeId(identifier) {
1509
+ return normalizeUri(identifier.toLowerCase());
1510
+ }
1454
1511
  function sourceIdFromFootnoteLiId(idProp, clobberPrefix) {
1455
1512
  let raw = null;
1456
1513
  if (clobberPrefix !== void 0) {
@@ -1607,10 +1664,10 @@ function phantomSuffixCloser(content) {
1607
1664
  const confirmed = endsWithNewline ? content : content + "\n";
1608
1665
  const { checkpoint } = computeFreezeBoundary(confirmed, { defListEnabled: false, referenceTaint: false });
1609
1666
  if (checkpoint.phasePoisonedAt !== Infinity) return "";
1667
+ if (checkpoint.openIndent !== 0) return "";
1610
1668
  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)}`;
1669
+ if (checkpoint.inFence) return `${nl}${checkpoint.fenceChar.repeat(checkpoint.fenceLen)}`;
1670
+ if (checkpoint.inMath) return `${nl}${"$".repeat(checkpoint.mathFenceLen)}`;
1614
1671
  return "";
1615
1672
  }
1616
1673
 
@@ -1849,6 +1906,41 @@ function createRegistry(onEmpty) {
1849
1906
  import rehypeKatex from "rehype-katex";
1850
1907
  import rehypeRaw from "rehype-raw";
1851
1908
  import rehypeUnwrapImages from "rehype-unwrap-images";
1909
+
1910
+ // src/components/rehypeUnwrapCrossChunkImages.ts
1911
+ import { SKIP as SKIP3, visit as visit4 } from "unist-util-visit";
1912
+ var IMAGE_TAGS = /* @__PURE__ */ new Set(["img", "cross-chunk-image"]);
1913
+ var LINK_TAGS = /* @__PURE__ */ new Set(["a", "cross-chunk-link"]);
1914
+ function applicable(node, inLink) {
1915
+ let image = 0 /* Unknown */;
1916
+ for (const child of node.children) {
1917
+ if (child.type === "text" && /^\s*$/.test(child.value)) continue;
1918
+ if (child.type === "element" && IMAGE_TAGS.has(child.tagName)) {
1919
+ image = 1 /* ContainsImage */;
1920
+ } else if (!inLink && child.type === "element" && LINK_TAGS.has(child.tagName)) {
1921
+ const inner = applicable(child, true);
1922
+ if (inner === 2 /* ContainsOther */) return 2 /* ContainsOther */;
1923
+ if (inner === 1 /* ContainsImage */) image = 1 /* ContainsImage */;
1924
+ } else {
1925
+ return 2 /* ContainsOther */;
1926
+ }
1927
+ }
1928
+ return image;
1929
+ }
1930
+ function rehypeUnwrapCrossChunkImages() {
1931
+ return function transform(tree) {
1932
+ visit4(tree, "element", (node, index, parent) => {
1933
+ if (node.tagName === "p" && parent && typeof index === "number" && applicable(node, false) === 1 /* ContainsImage */ && // Only paragraphs that actually hold a placeholder — plain <img>
1934
+ // paragraphs were already unwrapped by rehype-unwrap-images.
1935
+ JSON.stringify(node.children).includes('"cross-chunk-image"')) {
1936
+ parent.children.splice(index, 1, ...node.children);
1937
+ return [SKIP3, index];
1938
+ }
1939
+ });
1940
+ };
1941
+ }
1942
+
1943
+ // src/components/pluginChain.ts
1852
1944
  import rehypeSanitize from "rehype-sanitize";
1853
1945
  import remarkBreaks from "remark-breaks";
1854
1946
  import remarkCjkFriendly from "remark-cjk-friendly";
@@ -1864,13 +1956,13 @@ import remarkPangu from "remark-pangu";
1864
1956
  import remarkRemoveComments from "remark-remove-comments";
1865
1957
 
1866
1958
  // src/components/rehypeRebaseHashLinks.ts
1867
- import { visit as visit4 } from "unist-util-visit";
1959
+ import { visit as visit5 } from "unist-util-visit";
1868
1960
  var DEFAULT_PREFIX = "user-content-";
1869
1961
  var rehypeRebaseHashLinks = (options) => {
1870
1962
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
1871
1963
  const hashPrefix = "#" + prefix;
1872
1964
  return (tree) => {
1873
- visit4(tree, "element", (node) => {
1965
+ visit5(tree, "element", (node) => {
1874
1966
  if (node.tagName !== "a") return;
1875
1967
  const href = node.properties?.href;
1876
1968
  if (typeof href !== "string" || !href.startsWith("#")) return;
@@ -1882,7 +1974,7 @@ var rehypeRebaseHashLinks = (options) => {
1882
1974
  var rehypeRebaseHashLinks_default = rehypeRebaseHashLinks;
1883
1975
 
1884
1976
  // src/components/rehypeFooterAdorn.ts
1885
- import { visit as visit5 } from "unist-util-visit";
1977
+ import { visit as visit6 } from "unist-util-visit";
1886
1978
  var FOOTNOTE_LABEL_ID_RE = /(?:^|-)footnote-label$/;
1887
1979
  function isFootnoteLabelH2(node) {
1888
1980
  if (node.type !== "element") return false;
@@ -1897,7 +1989,7 @@ function isHr(node) {
1897
1989
  }
1898
1990
  function rehypeFooterAdorn() {
1899
1991
  return (tree) => {
1900
- visit5(tree, "element", (n) => {
1992
+ visit6(tree, "element", (n) => {
1901
1993
  const el = n;
1902
1994
  if (el.tagName !== "section") return;
1903
1995
  if (!(el.properties && "dataFootnotes" in el.properties)) return;
@@ -1975,7 +2067,10 @@ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix) {
1975
2067
  // above.
1976
2068
  [rehypeRebaseHashLinks_default, { prefix: clobberPrefix }],
1977
2069
  rehypeKatex,
1978
- rehypeUnwrapImages
2070
+ rehypeUnwrapImages,
2071
+ // Same unwrap for `<cross-chunk-image>` placeholders (coordinated mode);
2072
+ // no-op on standalone documents.
2073
+ rehypeUnwrapCrossChunkImages
1979
2074
  ];
1980
2075
  }
1981
2076
  function buildCoreRemarkRehypeOptions(enableDefinitionList) {
@@ -2094,6 +2189,9 @@ function buildCrossChunkHandlers() {
2094
2189
  };
2095
2190
  }
2096
2191
 
2192
+ // src/components/crossChunkUrlSanitize.ts
2193
+ import { normalizeUri as normalizeUri2 } from "micromark-util-sanitize-uri";
2194
+
2097
2195
  // ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_listCacheClear.js
2098
2196
  function listCacheClear() {
2099
2197
  this.__data__ = [];
@@ -3321,6 +3419,7 @@ function isProtocolAllowed(url, allowed) {
3321
3419
  return allowed.some((p) => p === protocol);
3322
3420
  }
3323
3421
  function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
3422
+ rawUrl = normalizeUri2(rawUrl);
3324
3423
  const transformed = urlTransform(rawUrl, key, fakeElement(tagName, key, rawUrl));
3325
3424
  if (transformed == null) return "";
3326
3425
  const stringUrl = String(transformed);
@@ -4194,6 +4293,7 @@ export {
4194
4293
  extendSanitizeSchema,
4195
4294
  extractContributions,
4196
4295
  extractDefBodiesFromHast,
4296
+ footnoteSafeId,
4197
4297
  getEnginePluginInternals,
4198
4298
  hasLoneSurrogate,
4199
4299
  highlight,