@ai-react-markdown/core 1.3.0 → 1.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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/index.tsx
4
- import { useMemo as useMemo4, memo as memo3, useId as useId2 } from "react";
4
+ import { useMemo as useMemo6, memo as memo4, useId as useId3 } from "react";
5
5
 
6
6
  // src/context.tsx
7
7
  import { createContext, useContext, useId, useMemo } from "react";
@@ -129,8 +129,8 @@ var root = freeGlobal_default || freeSelf || Function("return this")();
129
129
  var root_default = root;
130
130
 
131
131
  // ../../node_modules/.pnpm/lodash-es@4.17.23/node_modules/lodash-es/_Symbol.js
132
- var Symbol = root_default.Symbol;
133
- var Symbol_default = Symbol;
132
+ var Symbol2 = root_default.Symbol;
133
+ var Symbol_default = Symbol2;
134
134
 
135
135
  // ../../node_modules/.pnpm/lodash-es@4.17.23/node_modules/lodash-es/_getRawTag.js
136
136
  var objectProto = Object.prototype;
@@ -1067,7 +1067,8 @@ var defaultAIMarkdownRenderConfig = Object.freeze({
1067
1067
  "SMARTYPANTS" /* SMARTYPANTS */,
1068
1068
  "PANGU" /* PANGU */
1069
1069
  ]),
1070
- blockMemoEnabled: true
1070
+ blockMemoEnabled: true,
1071
+ preserveOrphanReferences: true
1071
1072
  });
1072
1073
 
1073
1074
  // src/context.tsx
@@ -1119,6 +1120,13 @@ var AIMarkdownRenderStateProvider = ({
1119
1120
  variant,
1120
1121
  colorScheme,
1121
1122
  documentId: resolvedDocumentId,
1123
+ // URI-fragment safe per-document prefix derived once here so downstream
1124
+ // consumers (MarkdownContent, cross-chunk placeholder components) read
1125
+ // from one canonical source. `encodeURIComponent` runs at the prefix
1126
+ // construction site, not at the documentId storage site, so consumers
1127
+ // accessing `documentId` directly still see the raw React-native value
1128
+ // (e.g. `useId()`'s `_r_0_`) while id="..."/href="#..." bytes are safe.
1129
+ clobberPrefix: `${encodeURIComponent(resolvedDocumentId)}-user-content-`,
1122
1130
  config: mergedConfig
1123
1131
  }),
1124
1132
  [streaming, fontSize, variant, colorScheme, resolvedDocumentId, mergedConfig]
@@ -1439,7 +1447,7 @@ function preprocessAIMDContent(content, extraPreprocessors = defaultExtraPreproc
1439
1447
  }
1440
1448
 
1441
1449
  // src/components/MarkdownContent.tsx
1442
- import { Fragment as Fragment2, memo, useMemo as useMemo2, useRef } from "react";
1450
+ import { Fragment as Fragment3, memo as memo2, useCallback as useCallback2, useEffect, useId as useId2, useMemo as useMemo4, useRef as useRef2, useState, useSyncExternalStore as useSyncExternalStore2 } from "react";
1443
1451
 
1444
1452
  // src/components/markdown/Markdown.tsx
1445
1453
  import { unreachable as unreachable2 } from "devlop";
@@ -1585,10 +1593,10 @@ function validateOptions(options) {
1585
1593
  }
1586
1594
  function parseStage(options) {
1587
1595
  validateOptions(options);
1588
- const processor = createProcessor(options);
1596
+ const processor2 = createProcessor(options);
1589
1597
  const file = createFile(options);
1590
- const mdast = processor.parse(file);
1591
- return { processor, file, mdast };
1598
+ const mdast = processor2.parse(file);
1599
+ return { processor: processor2, file, mdast };
1592
1600
  }
1593
1601
  function transformStage(parsed) {
1594
1602
  return parsed.processor.runSync(parsed.mdast, parsed.file);
@@ -1644,12 +1652,16 @@ function mergeClassNameAllowlist(existing, extraClassNames) {
1644
1652
  entries[idx] = merged;
1645
1653
  return entries;
1646
1654
  }
1655
+ var crossChunkTags = ["cross-chunk-link", "cross-chunk-image", "footnote-sup"];
1647
1656
  var sanitizeSchema = {
1648
1657
  ...defaultSchema,
1649
- tagNames: [...defaultSchema.tagNames || [], "mark"],
1658
+ tagNames: [...defaultSchema.tagNames || [], "mark", ...crossChunkTags],
1650
1659
  attributes: {
1651
1660
  ...defaultSchema.attributes,
1652
- code: mergeClassNameAllowlist(defaultSchema.attributes?.code, ["math-inline", "math-display"])
1661
+ code: mergeClassNameAllowlist(defaultSchema.attributes?.code, ["math-inline", "math-display"]),
1662
+ "cross-chunk-link": ["label", "referenceType", "documentId"],
1663
+ "cross-chunk-image": ["label", "referenceType", "documentId", "alt"],
1664
+ "footnote-sup": ["label", "localOccurrence", "documentId"]
1653
1665
  }
1654
1666
  };
1655
1667
 
@@ -1671,12 +1683,51 @@ var rehypeRebaseHashLinks = (options) => {
1671
1683
  };
1672
1684
  var rehypeRebaseHashLinks_default = rehypeRebaseHashLinks;
1673
1685
 
1686
+ // src/components/rehypeFooterAdorn.ts
1687
+ import { visit as visit3 } from "unist-util-visit";
1688
+ var FOOTNOTE_LABEL_ID_RE = /(?:^|-)footnote-label$/;
1689
+ function isFootnoteLabelH2(node) {
1690
+ if (node.type !== "element") return false;
1691
+ const el = node;
1692
+ if (el.tagName !== "h2") return false;
1693
+ const id = el.properties?.id;
1694
+ return typeof id === "string" && FOOTNOTE_LABEL_ID_RE.test(id);
1695
+ }
1696
+ function isHr(node) {
1697
+ if (node.type !== "element") return false;
1698
+ return node.tagName === "hr";
1699
+ }
1700
+ function rehypeFooterAdorn() {
1701
+ return (tree) => {
1702
+ visit3(tree, "element", (n) => {
1703
+ const el = n;
1704
+ if (el.tagName !== "section") return;
1705
+ if (!(el.properties && "dataFootnotes" in el.properties)) return;
1706
+ const filtered = el.children.filter((c) => !isFootnoteLabelH2(c));
1707
+ if (!filtered.some(isHr)) {
1708
+ const hr = {
1709
+ type: "element",
1710
+ tagName: "hr",
1711
+ properties: {},
1712
+ children: []
1713
+ };
1714
+ filtered.unshift(hr);
1715
+ }
1716
+ el.children = filtered;
1717
+ if (!el.properties) el.properties = {};
1718
+ if (!("ariaLabel" in el.properties)) {
1719
+ el.properties.ariaLabel = "Footnotes";
1720
+ }
1721
+ });
1722
+ };
1723
+ }
1724
+
1674
1725
  // src/components/MarkdownContent.tsx
1675
1726
  import remarkBreaks from "remark-breaks";
1676
1727
  import remarkCjkFriendly from "remark-cjk-friendly";
1677
1728
  import remarkCjkFriendlyGfmStrikethrough from "remark-cjk-friendly-gfm-strikethrough";
1678
1729
  import remarkEmoji from "remark-emoji";
1679
- import remarkGfm from "remark-gfm";
1730
+ import remarkGfm2 from "remark-gfm";
1680
1731
  import remarkMath from "remark-math";
1681
1732
  import { remarkDefinitionList, defListHastHandlers } from "remark-definition-list";
1682
1733
  import { remarkMark as remarkMarkHighlight } from "remark-mark-highlight";
@@ -1686,7 +1737,17 @@ import remarkPangu from "remark-pangu";
1686
1737
  import remarkRemoveComments from "remark-remove-comments";
1687
1738
 
1688
1739
  // src/components/blockMemo.ts
1689
- import { visit as visit3 } from "unist-util-visit";
1740
+ import { visit as visit4 } from "unist-util-visit";
1741
+
1742
+ // src/components/normalizeId.ts
1743
+ function normalizeId(s) {
1744
+ return s.replace(/\s+/g, " ").toUpperCase();
1745
+ }
1746
+ function normalizeForMatch(s) {
1747
+ return s.replace(/\\(.)/g, "$1").replace(/\s+/g, " ").toUpperCase();
1748
+ }
1749
+
1750
+ // src/components/blockMemo.ts
1690
1751
  var TAINT_TYPES = /* @__PURE__ */ new Set([
1691
1752
  "footnoteReference",
1692
1753
  "footnoteDefinition",
@@ -1713,14 +1774,41 @@ function hasMdastSource(node) {
1713
1774
  var FOOTNOTE_SECTION_KEY = "__footnote_section__";
1714
1775
  function buildBlocks(mdast, hast, source) {
1715
1776
  const mdastByOffset = /* @__PURE__ */ new Map();
1777
+ const mdastRanges = [];
1716
1778
  for (const child of mdast.children) {
1717
1779
  const off = child.position?.start.offset;
1780
+ const endOff = child.position?.end.offset;
1718
1781
  if (off !== void 0) {
1719
1782
  mdastByOffset.set(off, child);
1720
1783
  }
1784
+ if (off !== void 0 && endOff !== void 0) {
1785
+ mdastRanges.push({ start: off, end: endOff, node: child });
1786
+ }
1787
+ }
1788
+ if (process.env.NODE_ENV !== "production") {
1789
+ for (let i = 1; i < mdastRanges.length; i++) {
1790
+ if (mdastRanges[i].start < mdastRanges[i - 1].start) {
1791
+ throw new Error(
1792
+ "block-memo: mdast.children not sorted by source offset \u2014 a remark plugin is reordering top-level children."
1793
+ );
1794
+ }
1795
+ }
1796
+ }
1797
+ function findContainingMdast(offset) {
1798
+ let lo = 0;
1799
+ let hi = mdastRanges.length;
1800
+ while (lo < hi) {
1801
+ const mid = lo + hi >>> 1;
1802
+ if (mdastRanges[mid].start <= offset) lo = mid + 1;
1803
+ else hi = mid;
1804
+ }
1805
+ const idx = lo - 1;
1806
+ if (idx < 0) return void 0;
1807
+ const r = mdastRanges[idx];
1808
+ return offset < r.end ? r.node : void 0;
1721
1809
  }
1722
1810
  const ctxParts = [];
1723
- visit3(mdast, (n) => {
1811
+ visit4(mdast, (n) => {
1724
1812
  if (!CTX_TYPES.has(n.type)) return;
1725
1813
  if (n.type === "footnoteReference") ctxParts.push(["fr", n.identifier]);
1726
1814
  else if (n.type === "footnoteDefinition") ctxParts.push(["fd", n.identifier, extractRaw(n, source)]);
@@ -1754,11 +1842,7 @@ function buildBlocks(mdast, hast, source) {
1754
1842
  }
1755
1843
  let mdastNode = mdastByOffset.get(hastOffset);
1756
1844
  if (!mdastNode) {
1757
- mdastNode = mdast.children.findLast((child) => {
1758
- const startOff = child.position?.start.offset;
1759
- const endOff = child.position?.end.offset;
1760
- return startOff !== void 0 && endOff !== void 0 && startOff <= hastOffset && hastOffset < endOff;
1761
- });
1845
+ mdastNode = findContainingMdast(hastOffset);
1762
1846
  }
1763
1847
  if (!mdastNode) {
1764
1848
  if (process.env.NODE_ENV !== "production") {
@@ -1774,12 +1858,19 @@ function buildBlocks(mdast, hast, source) {
1774
1858
  continue;
1775
1859
  }
1776
1860
  let hasReference = false;
1777
- visit3(mdastNode, (n) => {
1778
- if (TAINT_TYPES.has(n.type)) {
1779
- hasReference = true;
1780
- return false;
1781
- }
1782
- return void 0;
1861
+ const footnoteRefLabels = [];
1862
+ const linkRefLabels = [];
1863
+ const imageRefLabels = [];
1864
+ const footnoteDefLabels = [];
1865
+ visit4(mdastNode, (n) => {
1866
+ if (!TAINT_TYPES.has(n.type)) return;
1867
+ hasReference = true;
1868
+ const id = "identifier" in n ? normalizeId(String(n.identifier)) : null;
1869
+ if (id === null) return;
1870
+ if (n.type === "footnoteReference") footnoteRefLabels.push(id);
1871
+ else if (n.type === "linkReference") linkRefLabels.push(id);
1872
+ else if (n.type === "imageReference") imageRefLabels.push(id);
1873
+ else if (n.type === "footnoteDefinition") footnoteDefLabels.push(id);
1783
1874
  });
1784
1875
  const mdastPos = mdastNode.position;
1785
1876
  if (!mdastPos || mdastPos.start.offset === void 0 || mdastPos.end.offset === void 0) {
@@ -1791,7 +1882,10 @@ function buildBlocks(mdast, hast, source) {
1791
1882
  endOffset: mdastPos.end.offset,
1792
1883
  startLine: mdastPos.start.line,
1793
1884
  startColumn: mdastPos.start.column,
1794
- hasReference
1885
+ hasReference,
1886
+ ...hasReference ? {
1887
+ taintLabels: { footnoteRefLabels, linkRefLabels, imageRefLabels, footnoteDefLabels }
1888
+ } : {}
1795
1889
  };
1796
1890
  blocks.push(info);
1797
1891
  blockHasts.push(el);
@@ -1799,6 +1893,25 @@ function buildBlocks(mdast, hast, source) {
1799
1893
  }
1800
1894
  return { plan, globalCtx, blocks, blockHasts, synthetic };
1801
1895
  }
1896
+ function computeBlockFingerprint(taintLabels, registry, thisChunkSym, clobberPrefix) {
1897
+ const parts = [clobberPrefix];
1898
+ for (const label of taintLabels.footnoteRefLabels) {
1899
+ parts.push(`fn:${label}=${registry.globalNumber(label) ?? "null"}`);
1900
+ }
1901
+ for (const label of taintLabels.linkRefLabels) {
1902
+ const def = registry.resolveLinkDef(label);
1903
+ parts.push(`lr:${label}=${def?.url ?? "null"}|${def?.title ?? ""}`);
1904
+ }
1905
+ for (const label of taintLabels.imageRefLabels) {
1906
+ const def = registry.resolveLinkDef(label);
1907
+ parts.push(`ir:${label}=${def?.url ?? "null"}|${def?.title ?? ""}`);
1908
+ }
1909
+ for (const label of taintLabels.footnoteDefLabels) {
1910
+ const isCanonical = registry.canonicalFootnoteFor(label) === thisChunkSym ? 1 : 0;
1911
+ parts.push(`fd:${label}=${isCanonical}/${registry.getRefsForLabel(label)}`);
1912
+ }
1913
+ return parts.join("|");
1914
+ }
1802
1915
  function renderBlocksWithCache(cacheRef, plan, globalCtx, postOptions) {
1803
1916
  const prev = cacheRef.current;
1804
1917
  const next = { blocks: /* @__PURE__ */ new Map() };
@@ -1821,10 +1934,18 @@ function renderBlocksWithCache(cacheRef, plan, globalCtx, postOptions) {
1821
1934
  continue;
1822
1935
  }
1823
1936
  if (item.kind === "synthetic") {
1937
+ if (postOptions.registry && postOptions.thisChunkSymbol) {
1938
+ continue;
1939
+ }
1824
1940
  const cached = prev.footnoteSection;
1825
- const node2 = cached && cached.ctx === globalCtx ? cached.node : renderHastSubtree(item.el, postOptions);
1826
- next.footnoteSection = { ctx: globalCtx, node: node2 };
1827
- rendered.push({ node: node2, reactKey: item.reactKey });
1941
+ let node;
1942
+ if (cached && cached.ctx === globalCtx) {
1943
+ node = cached.node;
1944
+ } else {
1945
+ node = renderHastSubtree(item.el, postOptions);
1946
+ }
1947
+ next.footnoteSection = { ctx: globalCtx, node };
1948
+ rendered.push({ node, reactKey: item.reactKey });
1828
1949
  continue;
1829
1950
  }
1830
1951
  const block = item.info;
@@ -1834,25 +1955,832 @@ function renderBlocksWithCache(cacheRef, plan, globalCtx, postOptions) {
1834
1955
  next.blocks.set(block.raw, bucket);
1835
1956
  }
1836
1957
  const occ = bucket.length;
1837
- const blockCtx = block.hasReference ? globalCtx : "";
1838
- const entry = prev.blocks.get(block.raw)?.[occ];
1839
- const valid = entry !== void 0 && entry.ctx === blockCtx && entry.startOffset === block.startOffset && entry.startLine === block.startLine && entry.startColumn === block.startColumn;
1840
- const node = valid ? entry.node : renderHastSubtree(item.el, postOptions);
1841
- bucket.push({
1842
- node,
1843
- ctx: blockCtx,
1844
- startOffset: block.startOffset,
1845
- startLine: block.startLine,
1846
- startColumn: block.startColumn
1847
- });
1848
- rendered.push({ node, reactKey: item.reactKey });
1958
+ if (block.hasReference) {
1959
+ const useFingerprint = postOptions.registry && block.taintLabels && postOptions.thisChunkSymbol && postOptions.clobberPrefix !== void 0;
1960
+ const blockCtx = useFingerprint ? computeBlockFingerprint(
1961
+ block.taintLabels,
1962
+ postOptions.registry,
1963
+ postOptions.thisChunkSymbol,
1964
+ postOptions.clobberPrefix
1965
+ ) : globalCtx;
1966
+ const entry = prev.blocks.get(block.raw)?.[occ];
1967
+ const valid = entry !== void 0 && entry.ctx === blockCtx && entry.startOffset === block.startOffset && entry.startLine === block.startLine && entry.startColumn === block.startColumn;
1968
+ let node;
1969
+ if (valid) {
1970
+ node = entry.node;
1971
+ } else {
1972
+ node = renderHastSubtree(item.el, postOptions);
1973
+ }
1974
+ bucket.push({
1975
+ node,
1976
+ ctx: blockCtx,
1977
+ startOffset: block.startOffset,
1978
+ startLine: block.startLine,
1979
+ startColumn: block.startColumn
1980
+ });
1981
+ rendered.push({ node, reactKey: item.reactKey });
1982
+ continue;
1983
+ }
1984
+ {
1985
+ const entry = prev.blocks.get(block.raw)?.[occ];
1986
+ const valid = entry !== void 0 && entry.ctx === "" && entry.startOffset === block.startOffset && entry.startLine === block.startLine && entry.startColumn === block.startColumn;
1987
+ const node = valid ? entry.node : renderHastSubtree(item.el, postOptions);
1988
+ bucket.push({
1989
+ node,
1990
+ ctx: "",
1991
+ startOffset: block.startOffset,
1992
+ startLine: block.startLine,
1993
+ startColumn: block.startColumn
1994
+ });
1995
+ rendered.push({ node, reactKey: item.reactKey });
1996
+ }
1849
1997
  }
1850
1998
  cacheRef.current = next;
1851
1999
  return rendered;
1852
2000
  }
1853
2001
 
2002
+ // src/components/collectDefLabels.ts
2003
+ import { unified as unified2 } from "unified";
2004
+ import remarkParse2 from "remark-parse";
2005
+ import remarkGfm from "remark-gfm";
2006
+ import { visit as visit5 } from "unist-util-visit";
2007
+ function buildProcessor() {
2008
+ return unified2().use(remarkParse2).use(remarkGfm);
2009
+ }
2010
+ var _processor = null;
2011
+ function processor() {
2012
+ if (!_processor) _processor = buildProcessor();
2013
+ return _processor;
2014
+ }
2015
+ function collectDefLabels(source) {
2016
+ if (!source) {
2017
+ return { footnoteLabels: /* @__PURE__ */ new Set(), linkLabels: /* @__PURE__ */ new Set() };
2018
+ }
2019
+ const mdast = processor().parse(source);
2020
+ const footnoteLabels = /* @__PURE__ */ new Set();
2021
+ const linkLabels = /* @__PURE__ */ new Set();
2022
+ visit5(mdast, (node) => {
2023
+ if (node.type === "footnoteDefinition" && "identifier" in node) {
2024
+ footnoteLabels.add(normalizeId(node.identifier));
2025
+ } else if (node.type === "definition" && "identifier" in node) {
2026
+ linkLabels.add(normalizeId(node.identifier));
2027
+ }
2028
+ });
2029
+ return { footnoteLabels, linkLabels };
2030
+ }
2031
+
2032
+ // src/components/AIMarkdownDocuments.tsx
2033
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2, useRef } from "react";
2034
+
2035
+ // src/components/documentRegistry.ts
2036
+ function createRegistry(onEmpty) {
2037
+ const reg = {
2038
+ chunkOrder: [],
2039
+ chunkData: /* @__PURE__ */ new Map(),
2040
+ labelSet: { footnoteLabels: /* @__PURE__ */ new Set(), linkLabels: /* @__PURE__ */ new Set() },
2041
+ version: 0,
2042
+ _reactIdMap: /* @__PURE__ */ new Map(),
2043
+ _subscribers: /* @__PURE__ */ new Set(),
2044
+ _notifyScheduled: false,
2045
+ allocateSymbol(reactId) {
2046
+ const existing = this._reactIdMap.get(reactId);
2047
+ if (existing) {
2048
+ existing.refcount++;
2049
+ return existing.symbol;
2050
+ }
2051
+ const sym = Symbol(reactId);
2052
+ this._reactIdMap.set(reactId, { symbol: sym, refcount: 1 });
2053
+ this.chunkOrder.push(sym);
2054
+ this._notify();
2055
+ return sym;
2056
+ },
2057
+ registerChunk(reactId, footnotes, links) {
2058
+ const sym = this.allocateSymbol(reactId);
2059
+ this.contributeLabels(sym, footnotes, links);
2060
+ return sym;
2061
+ },
2062
+ releaseSymbol(reactId) {
2063
+ const entry = this._reactIdMap.get(reactId);
2064
+ if (!entry) return;
2065
+ entry.refcount--;
2066
+ if (entry.refcount === 0) {
2067
+ queueMicrotask(() => {
2068
+ const latest = this._reactIdMap.get(reactId);
2069
+ if (latest && latest.refcount === 0) {
2070
+ this._reactIdMap.delete(reactId);
2071
+ const idx = this.chunkOrder.indexOf(entry.symbol);
2072
+ if (idx !== -1) this.chunkOrder.splice(idx, 1);
2073
+ this.chunkData.delete(entry.symbol);
2074
+ const nextFn = /* @__PURE__ */ new Set();
2075
+ const nextLink = /* @__PURE__ */ new Set();
2076
+ for (const cd of this.chunkData.values()) {
2077
+ for (const l of cd.ownFootnoteLabels) nextFn.add(l);
2078
+ for (const l of cd.ownLinkLabels) nextLink.add(l);
2079
+ }
2080
+ this.labelSet.footnoteLabels = nextFn;
2081
+ this.labelSet.linkLabels = nextLink;
2082
+ this._notify();
2083
+ if (this.chunkOrder.length === 0 && this.chunkData.size === 0 && onEmpty) {
2084
+ onEmpty();
2085
+ }
2086
+ }
2087
+ });
2088
+ }
2089
+ },
2090
+ contributeLabels(symbol, footnotes, links) {
2091
+ const data = this.chunkData.get(symbol);
2092
+ if (data) {
2093
+ data.ownFootnoteLabels = footnotes;
2094
+ data.ownLinkLabels = links;
2095
+ } else {
2096
+ this.chunkData.set(symbol, {
2097
+ refs: [],
2098
+ defs: /* @__PURE__ */ new Map(),
2099
+ linkDefs: /* @__PURE__ */ new Map(),
2100
+ ownFootnoteLabels: footnotes,
2101
+ ownLinkLabels: links
2102
+ });
2103
+ }
2104
+ const newFn = /* @__PURE__ */ new Set();
2105
+ const newLink = /* @__PURE__ */ new Set();
2106
+ for (const cd of this.chunkData.values()) {
2107
+ for (const l of cd.ownFootnoteLabels) newFn.add(l);
2108
+ for (const l of cd.ownLinkLabels) newLink.add(l);
2109
+ }
2110
+ this.labelSet.footnoteLabels = newFn;
2111
+ this.labelSet.linkLabels = newLink;
2112
+ this._notify();
2113
+ },
2114
+ contributeChunkData(symbol, data) {
2115
+ this.chunkData.set(symbol, data);
2116
+ this.labelSet.footnoteLabels = /* @__PURE__ */ new Set();
2117
+ this.labelSet.linkLabels = /* @__PURE__ */ new Set();
2118
+ for (const cd of this.chunkData.values()) {
2119
+ for (const l of cd.ownFootnoteLabels) this.labelSet.footnoteLabels.add(l);
2120
+ for (const l of cd.ownLinkLabels) this.labelSet.linkLabels.add(l);
2121
+ }
2122
+ this._notify();
2123
+ },
2124
+ subscribe(cb) {
2125
+ this._subscribers.add(cb);
2126
+ return () => {
2127
+ this._subscribers.delete(cb);
2128
+ };
2129
+ },
2130
+ canonicalFootnoteFor(label) {
2131
+ const id = normalizeId(label);
2132
+ for (const sym of this.chunkOrder) {
2133
+ const data = this.chunkData.get(sym);
2134
+ if (data?.defs.has(id)) return sym;
2135
+ }
2136
+ return null;
2137
+ },
2138
+ canonicalLinkFor(label) {
2139
+ const id = normalizeId(label);
2140
+ for (const sym of this.chunkOrder) {
2141
+ const data = this.chunkData.get(sym);
2142
+ if (data?.linkDefs.has(id)) return sym;
2143
+ }
2144
+ return null;
2145
+ },
2146
+ globalNumber(label) {
2147
+ const id = normalizeId(label);
2148
+ let n = 0;
2149
+ const seen = /* @__PURE__ */ new Set();
2150
+ for (const sym of this.chunkOrder) {
2151
+ const data = this.chunkData.get(sym);
2152
+ if (!data) continue;
2153
+ for (const ref of data.refs) {
2154
+ if (ref.kind !== "footnote") continue;
2155
+ if (!seen.has(ref.label)) {
2156
+ seen.add(ref.label);
2157
+ n++;
2158
+ if (ref.label === id) return n;
2159
+ }
2160
+ }
2161
+ }
2162
+ return null;
2163
+ },
2164
+ resolveLinkDef(label) {
2165
+ const sym = this.canonicalLinkFor(label);
2166
+ if (!sym) return null;
2167
+ return this.chunkData.get(sym)?.linkDefs.get(normalizeId(label)) ?? null;
2168
+ },
2169
+ getRefsForLabel(label) {
2170
+ const id = normalizeId(label);
2171
+ let n = 0;
2172
+ for (const sym of this.chunkOrder) {
2173
+ const data = this.chunkData.get(sym);
2174
+ if (!data) continue;
2175
+ for (const ref of data.refs) {
2176
+ if (ref.kind === "footnote" && ref.label === id) n++;
2177
+ }
2178
+ }
2179
+ return n;
2180
+ },
2181
+ globalOccurrenceForRef(chunkSym, label, localOccurrence) {
2182
+ const id = normalizeId(label);
2183
+ let global2 = 0;
2184
+ for (const sym of this.chunkOrder) {
2185
+ const data = this.chunkData.get(sym);
2186
+ if (!data) continue;
2187
+ let localCount = 0;
2188
+ for (const ref of data.refs) {
2189
+ if (ref.kind !== "footnote") continue;
2190
+ if (ref.label !== id) continue;
2191
+ localCount++;
2192
+ global2++;
2193
+ if (sym === chunkSym && localCount === localOccurrence) return global2;
2194
+ }
2195
+ }
2196
+ return null;
2197
+ },
2198
+ _notify() {
2199
+ this.version++;
2200
+ if (this._notifyScheduled) return;
2201
+ this._notifyScheduled = true;
2202
+ queueMicrotask(() => {
2203
+ this._notifyScheduled = false;
2204
+ for (const cb of [...this._subscribers]) cb();
2205
+ });
2206
+ }
2207
+ };
2208
+ return reg;
2209
+ }
2210
+
2211
+ // src/components/AIMarkdownDocuments.tsx
2212
+ import { jsx as jsx3 } from "react/jsx-runtime";
2213
+ var AIMarkdownDocumentsContext = createContext2(null);
2214
+ var AIMarkdownDocuments = ({ preserveOrphanReferences = true, children }) => {
2215
+ const parent = useContext2(AIMarkdownDocumentsContext);
2216
+ if (parent !== null) {
2217
+ throw new Error(
2218
+ "<AIMarkdownDocuments> must not be nested inside another <AIMarkdownDocuments>. Use a single top-level wrapper per coordinated scope."
2219
+ );
2220
+ }
2221
+ const registriesRef = useRef(/* @__PURE__ */ new Map());
2222
+ const value = useMemo2(
2223
+ () => ({
2224
+ preserveOrphanReferences,
2225
+ getRegistry(documentId) {
2226
+ let r = registriesRef.current.get(documentId);
2227
+ if (!r) {
2228
+ const created = createRegistry(() => {
2229
+ if (registriesRef.current.get(documentId) === created) {
2230
+ registriesRef.current.delete(documentId);
2231
+ }
2232
+ });
2233
+ r = created;
2234
+ registriesRef.current.set(documentId, r);
2235
+ }
2236
+ return r;
2237
+ }
2238
+ }),
2239
+ [preserveOrphanReferences]
2240
+ );
2241
+ return /* @__PURE__ */ jsx3(AIMarkdownDocumentsContext.Provider, { value, children });
2242
+ };
2243
+ function useDocumentRegistry(documentId) {
2244
+ const ctx = useContext2(AIMarkdownDocumentsContext);
2245
+ if (!ctx || !documentId) return null;
2246
+ return ctx.getRegistry(documentId);
2247
+ }
2248
+ function usePreserveOrphanReferences(fallback) {
2249
+ const ctx = useContext2(AIMarkdownDocumentsContext);
2250
+ return ctx?.preserveOrphanReferences ?? fallback;
2251
+ }
2252
+
2253
+ // src/components/remarkInjectPhantomDefs.ts
2254
+ var SENTINEL_LINK_URL = "__aimd_sentinel_link__";
2255
+ var SENTINEL_FN_CONTENT = "__aimd_sentinel_fn__";
2256
+ function augmentSourceWithPhantoms(source, phantoms) {
2257
+ if (phantoms.missingFootnotes.size === 0 && phantoms.missingLinks.size === 0) {
2258
+ return source;
2259
+ }
2260
+ let suffix = "\n\n";
2261
+ for (const label of phantoms.missingLinks) {
2262
+ suffix += `[${label}]: ${SENTINEL_LINK_URL}
2263
+ `;
2264
+ }
2265
+ for (const label of phantoms.missingFootnotes) {
2266
+ suffix += `[^${label}]: ${SENTINEL_FN_CONTENT}
2267
+ `;
2268
+ }
2269
+ return source + suffix;
2270
+ }
2271
+
2272
+ // src/components/customMdastHandlers.ts
2273
+ function buildCrossChunkHandlers() {
2274
+ return {
2275
+ footnoteDefinition: (state, node) => {
2276
+ const s = state;
2277
+ const id = normalizeId(node.identifier);
2278
+ if (s.options.phantomFootnoteLabels.has(id)) {
2279
+ return void 0;
2280
+ }
2281
+ if (s.options.preserveOrphan && !s.footnoteOrder.includes(id)) {
2282
+ s.footnoteOrder.push(id);
2283
+ }
2284
+ return void 0;
2285
+ },
2286
+ linkReference: ((state, node) => {
2287
+ const s = state;
2288
+ const id = normalizeId(node.identifier);
2289
+ const resolved = s.definitionById.has(id);
2290
+ if (!resolved) {
2291
+ return void 0;
2292
+ }
2293
+ return {
2294
+ type: "element",
2295
+ tagName: "cross-chunk-link",
2296
+ properties: {
2297
+ // `label` is the ORIGINAL source text (mdast's `label` field), NOT
2298
+ // the normalized `identifier`. The placeholder uses it to construct
2299
+ // hrefs that line up with mdast-util-to-hast's default `<li id>`
2300
+ // which also preserves source case. Registry lookups normalize
2301
+ // internally, so cross-chunk case-insensitive matching still works.
2302
+ label: node.label ?? node.identifier,
2303
+ referenceType: node.referenceType,
2304
+ documentId: s.options.documentId
2305
+ },
2306
+ children: s.all(node)
2307
+ };
2308
+ }),
2309
+ imageReference: ((state, node) => {
2310
+ const s = state;
2311
+ const id = normalizeId(node.identifier);
2312
+ const resolved = s.definitionById.has(id);
2313
+ if (!resolved) return void 0;
2314
+ return {
2315
+ type: "element",
2316
+ tagName: "cross-chunk-image",
2317
+ properties: {
2318
+ label: node.label ?? node.identifier,
2319
+ referenceType: node.referenceType,
2320
+ alt: node.alt ?? "",
2321
+ documentId: s.options.documentId
2322
+ },
2323
+ children: []
2324
+ };
2325
+ }),
2326
+ footnoteReference: ((state, node) => {
2327
+ const s = state;
2328
+ const id = normalizeId(node.identifier);
2329
+ const localOccurrence = (s.footnoteCounts.get(id) ?? 0) + 1;
2330
+ s.footnoteCounts.set(id, localOccurrence);
2331
+ if (s.options.phantomFootnoteLabels.has(id)) {
2332
+ return {
2333
+ type: "element",
2334
+ tagName: "footnote-sup",
2335
+ properties: {
2336
+ label: node.identifier,
2337
+ localOccurrence,
2338
+ documentId: s.options.documentId
2339
+ },
2340
+ children: []
2341
+ };
2342
+ }
2343
+ if (!s.footnoteOrder.includes(id)) s.footnoteOrder.push(id);
2344
+ return {
2345
+ type: "element",
2346
+ tagName: "footnote-sup",
2347
+ properties: {
2348
+ label: node.identifier,
2349
+ localOccurrence,
2350
+ documentId: s.options.documentId
2351
+ },
2352
+ children: []
2353
+ };
2354
+ })
2355
+ };
2356
+ }
2357
+
2358
+ // src/components/crossChunkPlaceholders.tsx
2359
+ import { isValidElement, useCallback, useContext as useContext3, useSyncExternalStore } from "react";
2360
+
2361
+ // src/components/chunkSymbolContext.ts
2362
+ import { createContext as createContext3 } from "react";
2363
+ var ChunkSymbolContext = createContext3(null);
2364
+
2365
+ // src/components/crossChunkPlaceholders.tsx
2366
+ import { jsx as jsx4 } from "react/jsx-runtime";
2367
+ var SSR_NUM_SNAPSHOT = () => 0;
2368
+ var SSR_DEF_SNAPSHOT = () => null;
2369
+ function coerceLocalOccurrence(v) {
2370
+ if (v === void 0) return null;
2371
+ if (typeof v === "number") return Number.isFinite(v) && v >= 1 ? Math.trunc(v) : null;
2372
+ const n = Number(v);
2373
+ return Number.isFinite(n) && n >= 1 ? Math.trunc(n) : null;
2374
+ }
2375
+ function FootnoteSupNumber({ label, localOccurrence: localOccurrenceRaw }) {
2376
+ const localOccurrence = coerceLocalOccurrence(localOccurrenceRaw);
2377
+ const { documentId, clobberPrefix } = useAIMarkdownRenderState();
2378
+ const registry = useDocumentRegistry(documentId);
2379
+ const chunkSym = useContext3(ChunkSymbolContext);
2380
+ const subscribe = useCallback((cb) => registry ? registry.subscribe(cb) : () => {
2381
+ }, [registry]);
2382
+ const getSnapshot = useCallback(() => registry?.version ?? 0, [registry]);
2383
+ useSyncExternalStore(subscribe, getSnapshot, SSR_NUM_SNAPSHOT);
2384
+ const num = registry?.globalNumber(label) ?? null;
2385
+ if (num === null) return null;
2386
+ if (localOccurrence !== null && !chunkSym) return null;
2387
+ const globalOcc = registry && chunkSym && localOccurrence !== null ? registry.globalOccurrenceForRef(chunkSym, label, localOccurrence) : null;
2388
+ if (localOccurrence !== null && globalOcc === null) return null;
2389
+ const occSuffix = globalOcc !== null && globalOcc > 1 ? `-${globalOcc}` : "";
2390
+ return /* @__PURE__ */ jsx4("sup", { children: /* @__PURE__ */ jsx4("a", { href: `#${clobberPrefix}fn-${label}`, id: `${clobberPrefix}fnref-${label}${occSuffix}`, "data-footnote-ref": true, children: num }) });
2391
+ }
2392
+ function reactNodeToText(node) {
2393
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
2394
+ if (typeof node === "string") return node;
2395
+ if (typeof node === "number" || typeof node === "bigint") return String(node);
2396
+ if (Array.isArray(node)) return node.map(reactNodeToText).join("");
2397
+ if (isValidElement(node)) {
2398
+ return reactNodeToText(node.props.children);
2399
+ }
2400
+ return "";
2401
+ }
2402
+ function literalLink(rt, label, children) {
2403
+ const text = reactNodeToText(children);
2404
+ switch (rt) {
2405
+ case "full":
2406
+ return `[${text}][${label}]`;
2407
+ case "collapsed":
2408
+ return `[${label}][]`;
2409
+ case "shortcut":
2410
+ default:
2411
+ return `[${label}]`;
2412
+ }
2413
+ }
2414
+ function CrossChunkLink({ label, referenceType, children }) {
2415
+ const { documentId } = useAIMarkdownRenderState();
2416
+ const registry = useDocumentRegistry(documentId);
2417
+ const subscribe = useCallback((cb) => registry ? registry.subscribe(cb) : () => {
2418
+ }, [registry]);
2419
+ const getSnapshot = useCallback(() => registry?.resolveLinkDef(label) ?? null, [registry, label]);
2420
+ const def = useSyncExternalStore(subscribe, getSnapshot, SSR_DEF_SNAPSHOT);
2421
+ if (!def) {
2422
+ return literalLink(referenceType, label, children);
2423
+ }
2424
+ return /* @__PURE__ */ jsx4("a", { href: def.url, title: def.title, children });
2425
+ }
2426
+ function literalImage(rt, label, alt) {
2427
+ switch (rt) {
2428
+ case "full":
2429
+ return `![${alt}][${label}]`;
2430
+ case "collapsed":
2431
+ return `![${alt}][]`;
2432
+ case "shortcut":
2433
+ default:
2434
+ return `![${label}]`;
2435
+ }
2436
+ }
2437
+ function CrossChunkImage({ label, referenceType, alt = "" }) {
2438
+ const { documentId } = useAIMarkdownRenderState();
2439
+ const registry = useDocumentRegistry(documentId);
2440
+ const subscribe = useCallback((cb) => registry ? registry.subscribe(cb) : () => {
2441
+ }, [registry]);
2442
+ const getSnapshot = useCallback(() => registry?.resolveLinkDef(label) ?? null, [registry, label]);
2443
+ const def = useSyncExternalStore(subscribe, getSnapshot, SSR_DEF_SNAPSHOT);
2444
+ if (!def) {
2445
+ return literalImage(referenceType, label, alt);
2446
+ }
2447
+ return /* @__PURE__ */ jsx4("img", { src: def.url, alt, title: def.title });
2448
+ }
2449
+ var crossChunkComponents = {
2450
+ "footnote-sup": FootnoteSupNumber,
2451
+ "cross-chunk-link": CrossChunkLink,
2452
+ "cross-chunk-image": CrossChunkImage
2453
+ };
2454
+
2455
+ // src/components/extractContributions.ts
2456
+ import { SKIP, visit as visit6 } from "unist-util-visit";
2457
+ function* extractContributions(mdast, options = {}) {
2458
+ const phantomFn = options.phantomFootnoteLabels;
2459
+ const out = [];
2460
+ visit6(mdast, (n) => {
2461
+ if (n.type === "footnoteReference") {
2462
+ out.push({
2463
+ kind: "ref",
2464
+ refKind: "footnote",
2465
+ label: normalizeId(n.identifier)
2466
+ });
2467
+ } else if (n.type === "linkReference") {
2468
+ const r = n;
2469
+ out.push({ kind: "ref", refKind: "link", label: normalizeId(r.identifier), referenceType: r.referenceType });
2470
+ } else if (n.type === "imageReference") {
2471
+ const r = n;
2472
+ out.push({ kind: "ref", refKind: "image", label: normalizeId(r.identifier), referenceType: r.referenceType });
2473
+ } else if (n.type === "footnoteDefinition") {
2474
+ const d = n;
2475
+ const label = normalizeId(d.identifier);
2476
+ if (phantomFn?.has(label)) return SKIP;
2477
+ const content = JSON.stringify(d.children ?? []);
2478
+ out.push({ kind: "fnDef", label, sourceIdentifier: d.identifier, content });
2479
+ return SKIP;
2480
+ } else if (n.type === "definition") {
2481
+ const d = n;
2482
+ if (d.url === SENTINEL_LINK_URL) return;
2483
+ out.push({ kind: "linkDef", label: normalizeId(d.identifier), url: d.url, title: d.title });
2484
+ }
2485
+ });
2486
+ for (const c of out) yield c;
2487
+ }
2488
+
2489
+ // src/components/extractDefBodiesFromHast.ts
2490
+ import { SKIP as SKIP2, visit as visit7 } from "unist-util-visit";
2491
+ var FN_LI_ID_RE = /(?:^|-)user-content-fn-(.+)$/;
2492
+ function sourceIdFromFootnoteLiId(idProp, clobberPrefix) {
2493
+ let raw = null;
2494
+ if (clobberPrefix !== void 0) {
2495
+ const exactPrefix = `${clobberPrefix}fn-`;
2496
+ if (idProp.startsWith(exactPrefix)) raw = idProp.slice(exactPrefix.length);
2497
+ }
2498
+ if (raw === null) {
2499
+ const m = idProp.match(FN_LI_ID_RE);
2500
+ raw = m ? m[1] : null;
2501
+ }
2502
+ if (raw === null) return null;
2503
+ try {
2504
+ return decodeURIComponent(raw);
2505
+ } catch {
2506
+ return raw;
2507
+ }
2508
+ }
2509
+ function isBackrefAnchor(c) {
2510
+ if (c.type !== "element") return false;
2511
+ const el = c;
2512
+ if (el.tagName !== "a") return false;
2513
+ return Boolean(el.properties && "dataFootnoteBackref" in el.properties);
2514
+ }
2515
+ function isWhitespaceText(c) {
2516
+ if (c.type !== "text") return false;
2517
+ return /^\s*$/.test(c.value);
2518
+ }
2519
+ function lastMeaningfulIdx(children) {
2520
+ for (let i = children.length - 1; i >= 0; i--) {
2521
+ if (!isWhitespaceText(children[i])) return i;
2522
+ }
2523
+ return -1;
2524
+ }
2525
+ function dropTrailingBackrefs(children) {
2526
+ let trailingWsStart = children.length;
2527
+ while (trailingWsStart > 0 && isWhitespaceText(children[trailingWsStart - 1])) {
2528
+ trailingWsStart--;
2529
+ }
2530
+ let scan = trailingWsStart;
2531
+ let peeledAny = false;
2532
+ while (scan > 0) {
2533
+ const t = children[scan - 1];
2534
+ if (!isBackrefAnchor(t)) break;
2535
+ peeledAny = true;
2536
+ scan -= 1;
2537
+ if (scan > 0 && children[scan - 1].type === "text" && children[scan - 1].value === " ") {
2538
+ scan -= 1;
2539
+ }
2540
+ }
2541
+ if (!peeledAny) return children;
2542
+ const trailing = children.slice(trailingWsStart);
2543
+ if (scan > 0) {
2544
+ const last = children[scan - 1];
2545
+ if (last.type === "text") {
2546
+ const v = last.value;
2547
+ if (v.endsWith(" ") && !/^\s*$/.test(v)) {
2548
+ return [...children.slice(0, scan - 1), { ...last, value: v.slice(0, -1) }, ...trailing];
2549
+ }
2550
+ }
2551
+ }
2552
+ return [...children.slice(0, scan), ...trailing];
2553
+ }
2554
+ function stripBackrefs(liChildren) {
2555
+ if (liChildren.length === 0) return liChildren;
2556
+ const lastIdx = lastMeaningfulIdx(liChildren);
2557
+ if (lastIdx < 0) return liChildren;
2558
+ const last = liChildren[lastIdx];
2559
+ if (isBackrefAnchor(last)) {
2560
+ return dropTrailingBackrefs(liChildren);
2561
+ }
2562
+ if (last.type === "element" && last.tagName === "p") {
2563
+ const p = last;
2564
+ const newPChildren = dropTrailingBackrefs(p.children);
2565
+ if (newPChildren === p.children) return liChildren;
2566
+ return liChildren.map((c, i) => i === lastIdx ? { ...p, children: newPChildren } : c);
2567
+ }
2568
+ return liChildren;
2569
+ }
2570
+ function stripLocalOccurrenceFromFootnoteSups(children) {
2571
+ let changed = false;
2572
+ const out = [];
2573
+ for (const c of children) {
2574
+ if (c.type === "element") {
2575
+ const el = c;
2576
+ let nextEl = el;
2577
+ if (el.tagName === "footnote-sup" && el.properties && "localOccurrence" in el.properties) {
2578
+ const { localOccurrence: _drop, ...rest } = el.properties;
2579
+ nextEl = { ...el, properties: rest };
2580
+ changed = true;
2581
+ }
2582
+ const newChildren = stripLocalOccurrenceFromFootnoteSups(nextEl.children);
2583
+ if (newChildren !== nextEl.children) {
2584
+ nextEl = { ...nextEl, children: newChildren };
2585
+ changed = true;
2586
+ }
2587
+ out.push(nextEl);
2588
+ } else {
2589
+ out.push(c);
2590
+ }
2591
+ }
2592
+ return changed ? out : children;
2593
+ }
2594
+ function extractDefBodiesFromHast(hast, clobberPrefix) {
2595
+ const out = /* @__PURE__ */ new Map();
2596
+ visit7(hast, "element", (sectionNode) => {
2597
+ const sec = sectionNode;
2598
+ if (sec.tagName !== "section") return;
2599
+ if (!(sec.properties && "dataFootnotes" in sec.properties)) return;
2600
+ visit7(sec, "element", (liNode) => {
2601
+ const li = liNode;
2602
+ if (li.tagName !== "li") return;
2603
+ const idProp = li.properties?.id;
2604
+ if (typeof idProp !== "string") return;
2605
+ const sourceId = sourceIdFromFootnoteLiId(idProp, clobberPrefix);
2606
+ if (sourceId === null) return;
2607
+ const normalized = normalizeId(sourceId);
2608
+ const stripped = stripBackrefs(li.children);
2609
+ out.set(normalized, stripLocalOccurrenceFromFootnoteSups(stripped));
2610
+ });
2611
+ return SKIP2;
2612
+ });
2613
+ return out;
2614
+ }
2615
+
2616
+ // src/components/aggregateFootnotesIfLast.tsx
2617
+ import { memo, useMemo as useMemo3 } from "react";
2618
+ import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
2619
+ function cloneHast(node) {
2620
+ return JSON.parse(JSON.stringify(node));
2621
+ }
2622
+ function isWhitespaceText2(c) {
2623
+ return c.type === "text" && /^\s*$/.test(c.value);
2624
+ }
2625
+ function lastMeaningfulIdx2(children) {
2626
+ for (let i = children.length - 1; i >= 0; i--) {
2627
+ if (!isWhitespaceText2(children[i])) return i;
2628
+ }
2629
+ return -1;
2630
+ }
2631
+ function buildBackref(href, occurrence, globalNumber) {
2632
+ const children = [{ type: "text", value: "\u21A9" }];
2633
+ if (occurrence > 1) {
2634
+ children.push({
2635
+ type: "element",
2636
+ tagName: "sup",
2637
+ properties: {},
2638
+ children: [{ type: "text", value: String(occurrence) }]
2639
+ });
2640
+ }
2641
+ const ariaLabel = occurrence === 1 ? `Back to reference ${globalNumber}` : `Back to reference ${globalNumber}-${occurrence}`;
2642
+ return {
2643
+ type: "element",
2644
+ tagName: "a",
2645
+ properties: {
2646
+ href,
2647
+ dataFootnoteBackref: "",
2648
+ className: ["data-footnote-backref"],
2649
+ ariaLabel
2650
+ },
2651
+ children
2652
+ };
2653
+ }
2654
+ function buildAggregateTree(registry, clobberPrefix, preserveOrphanReferences = false) {
2655
+ const seen = /* @__PURE__ */ new Set();
2656
+ const ordered = [];
2657
+ for (const sym of registry.chunkOrder) {
2658
+ const data = registry.chunkData.get(sym);
2659
+ if (!data) continue;
2660
+ for (const ref of data.refs) {
2661
+ if (ref.kind !== "footnote") continue;
2662
+ if (seen.has(ref.label)) continue;
2663
+ seen.add(ref.label);
2664
+ const canonicalSym = registry.canonicalFootnoteFor(ref.label);
2665
+ if (!canonicalSym) continue;
2666
+ const def = registry.chunkData.get(canonicalSym)?.defs.get(ref.label);
2667
+ if (!def) continue;
2668
+ const n = registry.globalNumber(ref.label);
2669
+ if (n === null) continue;
2670
+ const sourceIdentifier = def.sourceIdentifier ?? ref.label;
2671
+ ordered.push({
2672
+ normalizedLabel: ref.label,
2673
+ sourceIdentifier,
2674
+ bodyHast: def.bodyHast ?? [],
2675
+ n,
2676
+ withBackref: true
2677
+ });
2678
+ }
2679
+ }
2680
+ if (preserveOrphanReferences) {
2681
+ for (const sym of registry.chunkOrder) {
2682
+ const data = registry.chunkData.get(sym);
2683
+ if (!data) continue;
2684
+ for (const [label, def] of data.defs) {
2685
+ if (seen.has(label)) continue;
2686
+ if (registry.canonicalFootnoteFor(label) !== sym) continue;
2687
+ seen.add(label);
2688
+ ordered.push({
2689
+ normalizedLabel: label,
2690
+ sourceIdentifier: def.sourceIdentifier ?? label,
2691
+ bodyHast: def.bodyHast ?? [],
2692
+ n: null,
2693
+ withBackref: false
2694
+ });
2695
+ }
2696
+ }
2697
+ }
2698
+ if (ordered.length === 0) return null;
2699
+ const liElements = ordered.map(({ normalizedLabel, sourceIdentifier, bodyHast, n, withBackref }) => {
2700
+ const liChildren = bodyHast.map((c) => cloneHast(c));
2701
+ if (withBackref) {
2702
+ const totalRefs = registry.getRefsForLabel(normalizedLabel);
2703
+ const tailIdx = lastMeaningfulIdx2(liChildren);
2704
+ const tail = tailIdx !== -1 ? liChildren[tailIdx] : null;
2705
+ const dest = tail && tail.type === "element" && tail.tagName === "p" ? tail : null;
2706
+ const appended = [];
2707
+ for (let i = 1; i <= Math.max(totalRefs, 1); i++) {
2708
+ appended.push({ type: "text", value: " " });
2709
+ const href = i === 1 ? `#${clobberPrefix}fnref-${sourceIdentifier}` : `#${clobberPrefix}fnref-${sourceIdentifier}-${i}`;
2710
+ appended.push(buildBackref(href, i, n ?? 0));
2711
+ }
2712
+ if (dest) {
2713
+ dest.children = [...dest.children, ...appended];
2714
+ } else if (tailIdx !== -1) {
2715
+ liChildren.splice(tailIdx + 1, 0, ...appended);
2716
+ } else {
2717
+ liChildren.push(...appended);
2718
+ }
2719
+ }
2720
+ return {
2721
+ type: "element",
2722
+ tagName: "li",
2723
+ properties: {
2724
+ id: `${clobberPrefix}fn-${sourceIdentifier}`,
2725
+ ...n !== null ? { value: n } : {}
2726
+ },
2727
+ children: liChildren
2728
+ };
2729
+ });
2730
+ return {
2731
+ type: "element",
2732
+ tagName: "section",
2733
+ properties: {
2734
+ className: ["footnotes"],
2735
+ dataFootnotes: true,
2736
+ // a11y: name the section landmark so screen readers still announce it
2737
+ // even though the visible "Footnotes" h2 is omitted in coordinated mode.
2738
+ ariaLabel: "Footnotes"
2739
+ },
2740
+ children: [
2741
+ // Conventional separator above the footnote list. The default
2742
+ // mdast-util-to-hast footer doesn't emit one (GitHub renders the
2743
+ // separation via CSS `border-top` on `.footnotes`); we render a literal
2744
+ // `<hr>` so the visual break is preserved without sanitize-friendly CSS.
2745
+ {
2746
+ type: "element",
2747
+ tagName: "hr",
2748
+ properties: {},
2749
+ children: []
2750
+ },
2751
+ {
2752
+ type: "element",
2753
+ tagName: "ol",
2754
+ properties: {},
2755
+ children: liElements
2756
+ }
2757
+ ]
2758
+ };
2759
+ }
2760
+ var AggregateFootnotesIfLastImpl = ({
2761
+ registry,
2762
+ thisChunkSym,
2763
+ clobberPrefix,
2764
+ postOptions,
2765
+ preserveOrphanReferences = false
2766
+ }) => {
2767
+ const tree = useMemo3(
2768
+ () => buildAggregateTree(registry, clobberPrefix, preserveOrphanReferences),
2769
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2770
+ [registry, registry.version, clobberPrefix, preserveOrphanReferences]
2771
+ );
2772
+ const order = registry.chunkOrder;
2773
+ if (order.length === 0) return null;
2774
+ if (order[order.length - 1] !== thisChunkSym) return null;
2775
+ if (!tree) return null;
2776
+ return /* @__PURE__ */ jsx5(Fragment2, { children: renderHastSubtree(cloneHast(tree), postOptions) });
2777
+ };
2778
+ var AggregateFootnotesIfLast = memo(AggregateFootnotesIfLastImpl);
2779
+ AggregateFootnotesIfLast.displayName = "AggregateFootnotesIfLast";
2780
+
1854
2781
  // src/components/MarkdownContent.tsx
1855
- import { Fragment as Fragment3, jsx as jsx3 } from "react/jsx-runtime";
2782
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
2783
+ var REGISTRY_SSR_SNAPSHOT = () => 0;
1856
2784
  var DisplayOptimizeRemarkPluginMap = {
1857
2785
  ["REMOVE_COMMENTS" /* REMOVE_COMMENTS */]: remarkRemoveComments,
1858
2786
  ["SMARTYPANTS" /* SMARTYPANTS */]: remarkSmartypants,
@@ -1863,7 +2791,7 @@ var ExtraSyntaxRemarkPluginMap = {
1863
2791
  ["DEFINITION_LIST" /* DEFINITION_LIST */]: remarkDefinitionList
1864
2792
  };
1865
2793
  var DefaultCustomComponents = {};
1866
- var BlockMemoizedRenderer = memo(
2794
+ var BlockMemoizedRenderer = memo2(
1867
2795
  ({ content, usedComponents, remarkPlugins, rehypePlugins, remarkRehypeOptions }) => {
1868
2796
  const urlTransform = void 0;
1869
2797
  const allowedElements = void 0;
@@ -1871,8 +2799,30 @@ var BlockMemoizedRenderer = memo(
1871
2799
  const allowElement = void 0;
1872
2800
  const skipHtml = void 0;
1873
2801
  const unwrapDisallowed = void 0;
1874
- const cacheRef = useRef(createCache());
1875
- const depsRef = useRef({
2802
+ const { documentId, clobberPrefix, config } = useAIMarkdownRenderState();
2803
+ const reactId = useId2();
2804
+ const registry = useDocumentRegistry(documentId);
2805
+ const [allocation, setAllocation] = useState(null);
2806
+ const sym = allocation && allocation.registry === registry ? allocation.sym : null;
2807
+ const subscribeRegistry = useCallback2(
2808
+ (cb) => registry ? registry.subscribe(cb) : () => {
2809
+ },
2810
+ [registry]
2811
+ );
2812
+ const getRegistryVersion = useCallback2(() => registry?.version ?? 0, [registry]);
2813
+ useSyncExternalStore2(subscribeRegistry, getRegistryVersion, REGISTRY_SSR_SNAPSHOT);
2814
+ const ownLabels = useMemo4(() => collectDefLabels(content ?? ""), [content]);
2815
+ useEffect(() => {
2816
+ if (!registry) return;
2817
+ const s = registry.registerChunk(reactId, ownLabels.footnoteLabels, ownLabels.linkLabels);
2818
+ setAllocation({ registry, sym: s });
2819
+ return () => {
2820
+ registry.releaseSymbol(reactId);
2821
+ setAllocation(null);
2822
+ };
2823
+ }, [reactId, registry, ownLabels]);
2824
+ const cacheRef = useRef2(createCache());
2825
+ const depsRef = useRef2({
1876
2826
  usedComponents,
1877
2827
  remarkPlugins,
1878
2828
  rehypePlugins,
@@ -1882,9 +2832,11 @@ var BlockMemoizedRenderer = memo(
1882
2832
  disallowedElements,
1883
2833
  allowElement,
1884
2834
  skipHtml,
1885
- unwrapDisallowed
2835
+ unwrapDisallowed,
2836
+ registry,
2837
+ symbol: sym
1886
2838
  });
1887
- if (depsRef.current.usedComponents !== usedComponents || depsRef.current.remarkPlugins !== remarkPlugins || depsRef.current.rehypePlugins !== rehypePlugins || depsRef.current.remarkRehypeOptions !== remarkRehypeOptions || depsRef.current.urlTransform !== urlTransform || depsRef.current.allowedElements !== allowedElements || depsRef.current.disallowedElements !== disallowedElements || depsRef.current.allowElement !== allowElement || depsRef.current.skipHtml !== skipHtml || depsRef.current.unwrapDisallowed !== unwrapDisallowed) {
2839
+ if (depsRef.current.usedComponents !== usedComponents || depsRef.current.remarkPlugins !== remarkPlugins || depsRef.current.rehypePlugins !== rehypePlugins || depsRef.current.remarkRehypeOptions !== remarkRehypeOptions || depsRef.current.urlTransform !== urlTransform || depsRef.current.allowedElements !== allowedElements || depsRef.current.disallowedElements !== disallowedElements || depsRef.current.allowElement !== allowElement || depsRef.current.skipHtml !== skipHtml || depsRef.current.unwrapDisallowed !== unwrapDisallowed || depsRef.current.registry !== registry || depsRef.current.symbol !== sym) {
1888
2840
  cacheRef.current = createCache();
1889
2841
  depsRef.current = {
1890
2842
  usedComponents,
@@ -1896,39 +2848,201 @@ var BlockMemoizedRenderer = memo(
1896
2848
  disallowedElements,
1897
2849
  allowElement,
1898
2850
  skipHtml,
1899
- unwrapDisallowed
2851
+ unwrapDisallowed,
2852
+ registry,
2853
+ symbol: sym
1900
2854
  };
1901
2855
  }
1902
- const parsed = useMemo2(
1903
- () => parseStage({
1904
- children: content,
2856
+ const targetPhantomsRef = useRef2({
2857
+ missingFootnotes: /* @__PURE__ */ new Set(),
2858
+ missingLinks: /* @__PURE__ */ new Set()
2859
+ });
2860
+ const targetPhantoms = useMemo4(() => {
2861
+ let nextFootnotes;
2862
+ let nextLinks;
2863
+ if (!registry) {
2864
+ nextFootnotes = /* @__PURE__ */ new Set();
2865
+ nextLinks = /* @__PURE__ */ new Set();
2866
+ } else {
2867
+ const normalized = normalizeForMatch(content ?? "");
2868
+ nextFootnotes = /* @__PURE__ */ new Set();
2869
+ nextLinks = /* @__PURE__ */ new Set();
2870
+ for (const label of registry.labelSet.footnoteLabels) {
2871
+ if (ownLabels.footnoteLabels.has(label)) continue;
2872
+ if (normalized.includes(label)) nextFootnotes.add(label);
2873
+ }
2874
+ for (const label of registry.labelSet.linkLabels) {
2875
+ if (ownLabels.linkLabels.has(label)) continue;
2876
+ if (normalized.includes(label)) nextLinks.add(label);
2877
+ }
2878
+ }
2879
+ const prev = targetPhantomsRef.current;
2880
+ if (nextFootnotes.size === prev.missingFootnotes.size && nextLinks.size === prev.missingLinks.size && [...nextFootnotes].every((l) => prev.missingFootnotes.has(l)) && [...nextLinks].every((l) => prev.missingLinks.has(l))) {
2881
+ return prev;
2882
+ }
2883
+ const next = { missingFootnotes: nextFootnotes, missingLinks: nextLinks };
2884
+ targetPhantomsRef.current = next;
2885
+ return next;
2886
+ }, [registry, registry?.version, content, ownLabels]);
2887
+ const effectivePreserveOrphan = usePreserveOrphanReferences(config.preserveOrphanReferences);
2888
+ const preserveForBodyHarvest = effectivePreserveOrphan || Boolean(registry && sym);
2889
+ const handlers = useMemo4(() => {
2890
+ if (registry) return buildCrossChunkHandlers();
2891
+ if (effectivePreserveOrphan) {
2892
+ const { footnoteDefinition } = buildCrossChunkHandlers();
2893
+ return { footnoteDefinition };
2894
+ }
2895
+ return void 0;
2896
+ }, [registry, effectivePreserveOrphan]);
2897
+ const parsed = useMemo4(() => {
2898
+ const augmented = augmentSourceWithPhantoms(content ?? "", targetPhantoms);
2899
+ const baseHandlers = remarkRehypeOptions?.handlers ?? {};
2900
+ const mergedRemarkRehypeOptions = handlers ? {
2901
+ ...remarkRehypeOptions,
2902
+ handlers: { ...baseHandlers, ...handlers },
2903
+ // Phantom label sets are empty in standalone mode (no PASS 0.5
2904
+ // injection happened); the footnoteDefinition handler still reads
2905
+ // them via `state.options.phantomFootnoteLabels.has(id)`, which
2906
+ // returns false for every id → orphan-protect path proceeds.
2907
+ phantomFootnoteLabels: targetPhantoms.missingFootnotes,
2908
+ phantomLinkLabels: targetPhantoms.missingLinks,
2909
+ preserveOrphan: preserveForBodyHarvest,
2910
+ documentId
2911
+ } : {
2912
+ ...remarkRehypeOptions
2913
+ };
2914
+ return parseStage({
2915
+ children: augmented,
1905
2916
  remarkPlugins,
1906
2917
  rehypePlugins,
1907
- remarkRehypeOptions
1908
- }),
1909
- [content, remarkPlugins, rehypePlugins, remarkRehypeOptions]
1910
- );
1911
- const hast = useMemo2(() => transformStage(parsed), [parsed]);
1912
- const built = useMemo2(() => buildBlocks(parsed.mdast, hast, content), [parsed.mdast, hast, content]);
1913
- const postOptions = useMemo2(
2918
+ remarkRehypeOptions: mergedRemarkRehypeOptions
2919
+ });
2920
+ }, [
2921
+ content,
2922
+ targetPhantoms,
2923
+ remarkPlugins,
2924
+ rehypePlugins,
2925
+ remarkRehypeOptions,
2926
+ handlers,
2927
+ preserveForBodyHarvest,
2928
+ documentId
2929
+ ]);
2930
+ const hast = useMemo4(() => transformStage(parsed), [parsed]);
2931
+ const built = useMemo4(() => buildBlocks(parsed.mdast, hast, content ?? ""), [parsed.mdast, hast, content]);
2932
+ const postOptions = useMemo4(
1914
2933
  () => ({
1915
- components: usedComponents,
2934
+ components: { ...crossChunkComponents, ...usedComponents },
1916
2935
  urlTransform,
1917
2936
  allowedElements,
1918
2937
  disallowedElements,
1919
2938
  allowElement,
1920
2939
  skipHtml,
1921
- unwrapDisallowed
2940
+ unwrapDisallowed,
2941
+ // v6 fingerprint cache fields:
2942
+ registry: registry ?? void 0,
2943
+ thisChunkSymbol: sym ?? void 0,
2944
+ clobberPrefix
1922
2945
  }),
1923
- [usedComponents, urlTransform, allowedElements, disallowedElements, allowElement, skipHtml, unwrapDisallowed]
2946
+ // `sym` is now real state (setSym after allocateSymbol), so it's a
2947
+ // proper dep and postOptions refreshes when allocation completes.
2948
+ // `registry?.version` stays in deps so the per-block fingerprint cache
2949
+ // path sees the latest registry version on every coordinated update.
2950
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2951
+ [
2952
+ usedComponents,
2953
+ urlTransform,
2954
+ allowedElements,
2955
+ disallowedElements,
2956
+ allowElement,
2957
+ skipHtml,
2958
+ unwrapDisallowed,
2959
+ registry,
2960
+ registry?.version,
2961
+ sym,
2962
+ clobberPrefix
2963
+ ]
1924
2964
  );
2965
+ const lastContributionRef = useRef2(null);
2966
+ useEffect(() => {
2967
+ if (!registry || !sym) return;
2968
+ const refs = [];
2969
+ const defMeta = /* @__PURE__ */ new Map();
2970
+ const linkDefs = /* @__PURE__ */ new Map();
2971
+ for (const node of extractContributions(parsed.mdast, {
2972
+ phantomFootnoteLabels: targetPhantoms.missingFootnotes
2973
+ })) {
2974
+ if (node.kind === "ref") {
2975
+ refs.push({ label: node.label, kind: node.refKind, referenceType: node.referenceType });
2976
+ } else if (node.kind === "fnDef") {
2977
+ defMeta.set(node.label, {
2978
+ identifier: node.label,
2979
+ sourceIdentifier: node.sourceIdentifier,
2980
+ contentSource: node.content
2981
+ });
2982
+ } else if (node.kind === "linkDef") {
2983
+ linkDefs.set(node.label, { identifier: node.label, url: node.url, title: node.title });
2984
+ }
2985
+ }
2986
+ const fp = JSON.stringify({
2987
+ r: refs,
2988
+ d: Array.from(defMeta.entries()).map(([k, v]) => [k, v.sourceIdentifier, v.contentSource]),
2989
+ l: Array.from(linkDefs.entries()).map(([k, v]) => [k, v.url, v.title ?? ""]),
2990
+ ofn: Array.from(ownLabels.footnoteLabels).sort(),
2991
+ ol: Array.from(ownLabels.linkLabels).sort(),
2992
+ // Include targetPhantoms in the fingerprint: a phantom→resolved
2993
+ // transition (another chunk publishes a def for a label this chunk
2994
+ // references inside one of its OWN def bodies) changes the rendered
2995
+ // hast — the `<cross-chunk-link>` / `<cross-chunk-image>` placeholder
2996
+ // disappears and a real `<a>` / `<img>` takes its place — without
2997
+ // touching this chunk's refs / defMeta / linkDefs / ownLabels. Without
2998
+ // including the phantom snapshot in the fingerprint, the fp check
2999
+ // would short-circuit and the registry would keep stale bodyHast
3000
+ // forever, leaving the aggregate footer rendering the placeholder
3001
+ // long after the label was resolved.
3002
+ tpfn: Array.from(targetPhantoms.missingFootnotes).sort(),
3003
+ tpl: Array.from(targetPhantoms.missingLinks).sort()
3004
+ });
3005
+ if (lastContributionRef.current?.registry === registry && lastContributionRef.current.symbol === sym && lastContributionRef.current.fp === fp) {
3006
+ return;
3007
+ }
3008
+ const bodiesByLabel = extractDefBodiesFromHast(hast, clobberPrefix);
3009
+ const defs = /* @__PURE__ */ new Map();
3010
+ for (const [label, meta] of defMeta) {
3011
+ defs.set(label, {
3012
+ identifier: meta.identifier,
3013
+ sourceIdentifier: meta.sourceIdentifier,
3014
+ contentSource: meta.contentSource,
3015
+ bodyHast: bodiesByLabel.get(label) ?? []
3016
+ });
3017
+ }
3018
+ lastContributionRef.current = { registry, symbol: sym, fp };
3019
+ registry.contributeChunkData(sym, {
3020
+ refs,
3021
+ defs,
3022
+ linkDefs,
3023
+ ownFootnoteLabels: ownLabels.footnoteLabels,
3024
+ ownLinkLabels: ownLabels.linkLabels
3025
+ });
3026
+ }, [parsed, ownLabels, registry, targetPhantoms, sym, hast, clobberPrefix]);
1925
3027
  const rendered = renderBlocksWithCache(cacheRef, built.plan, built.globalCtx, postOptions);
1926
- return /* @__PURE__ */ jsx3(Fragment3, { children: rendered.map(({ node, reactKey }) => /* @__PURE__ */ jsx3(Fragment2, { children: node }, reactKey)) });
3028
+ return /* @__PURE__ */ jsxs2(ChunkSymbolContext.Provider, { value: sym, children: [
3029
+ rendered.map(({ node, reactKey }) => /* @__PURE__ */ jsx6(Fragment3, { children: node }, reactKey)),
3030
+ registry && sym ? /* @__PURE__ */ jsx6(
3031
+ AggregateFootnotesIfLast,
3032
+ {
3033
+ registry,
3034
+ thisChunkSym: sym,
3035
+ clobberPrefix,
3036
+ postOptions,
3037
+ preserveOrphanReferences: effectivePreserveOrphan
3038
+ }
3039
+ ) : null
3040
+ ] });
1927
3041
  }
1928
3042
  );
1929
3043
  BlockMemoizedRenderer.displayName = "BlockMemoizedRenderer";
1930
- var LegacyRenderer = memo(
1931
- ({ content, usedComponents, remarkPlugins, rehypePlugins, remarkRehypeOptions }) => /* @__PURE__ */ jsx3(
3044
+ var LegacyRenderer = memo2(
3045
+ ({ content, usedComponents, remarkPlugins, rehypePlugins, remarkRehypeOptions }) => /* @__PURE__ */ jsx6(
1932
3046
  Markdown_default,
1933
3047
  {
1934
3048
  remarkPlugins,
@@ -1940,26 +3054,25 @@ var LegacyRenderer = memo(
1940
3054
  )
1941
3055
  );
1942
3056
  LegacyRenderer.displayName = "LegacyRenderer";
1943
- var AIMarkdownContent = memo(({ content, customComponents }) => {
1944
- const { config, documentId } = useAIMarkdownRenderState();
1945
- const clobberPrefix = `${encodeURIComponent(documentId)}-user-content-`;
1946
- const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo2(
3057
+ var AIMarkdownContent = memo2(({ content, customComponents }) => {
3058
+ const { config, clobberPrefix } = useAIMarkdownRenderState();
3059
+ const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo4(
1947
3060
  () => ({
1948
3061
  extraSyntaxRemarkPlugins: config.extraSyntaxSupported.map((syntax) => ExtraSyntaxRemarkPluginMap[syntax]),
1949
3062
  enableDefinitionList: config.extraSyntaxSupported.includes("DEFINITION_LIST" /* DEFINITION_LIST */)
1950
3063
  }),
1951
3064
  [config.extraSyntaxSupported]
1952
3065
  );
1953
- const displayOptimizeRemarkPlugins = useMemo2(() => {
3066
+ const displayOptimizeRemarkPlugins = useMemo4(() => {
1954
3067
  return config.displayOptimizeAbilities.map((ability) => DisplayOptimizeRemarkPluginMap[ability]);
1955
3068
  }, [config.displayOptimizeAbilities]);
1956
- const usedComponents = useMemo2(() => {
3069
+ const usedComponents = useMemo4(() => {
1957
3070
  return customComponents ? { ...DefaultCustomComponents, ...customComponents } : DefaultCustomComponents;
1958
3071
  }, [customComponents]);
1959
- const remarkPlugins = useMemo2(
3072
+ const remarkPlugins = useMemo4(
1960
3073
  () => [
1961
3074
  // --- Core plugins (always active) ---
1962
- remarkGfm,
3075
+ remarkGfm2,
1963
3076
  [
1964
3077
  remarkMath,
1965
3078
  {
@@ -1981,7 +3094,7 @@ var AIMarkdownContent = memo(({ content, customComponents }) => {
1981
3094
  ],
1982
3095
  [extraSyntaxRemarkPlugins, displayOptimizeRemarkPlugins]
1983
3096
  );
1984
- const rehypePlugins = useMemo2(
3097
+ const rehypePlugins = useMemo4(
1985
3098
  () => [
1986
3099
  // Allow raw HTML through so rehype-sanitize can handle it.
1987
3100
  [rehypeRaw, { passThrough: [] }],
@@ -1989,6 +3102,11 @@ var AIMarkdownContent = memo(({ content, customComponents }) => {
1989
3102
  // Override `clobberPrefix` with the instance-scoped value so every id
1990
3103
  // and clobberable attribute is namespaced to this `<AIMarkdown>` instance.
1991
3104
  [rehypeSanitize, { ...sanitizeSchema, clobberPrefix }],
3105
+ // Normalize the auto-generated `<section data-footnotes>`: strip the
3106
+ // sr-only `<h2>Footnotes</h2>` label and prepend `<hr>`. Keeps standalone
3107
+ // single-doc rendering visually consistent with the cross-chunk aggregate
3108
+ // footer (which builds the same shape from scratch).
3109
+ rehypeFooterAdorn,
1992
3110
  // Re-prefix intra-document hash hrefs so they match the ids that
1993
3111
  // rehype-sanitize just clobbered. Must use the SAME prefix as the schema
1994
3112
  // above — that's why both read from `clobberPrefix`.
@@ -1998,7 +3116,7 @@ var AIMarkdownContent = memo(({ content, customComponents }) => {
1998
3116
  ],
1999
3117
  [clobberPrefix]
2000
3118
  );
2001
- const remarkRehypeOptions = useMemo2(
3119
+ const remarkRehypeOptions = useMemo4(
2002
3120
  () => ({
2003
3121
  allowDangerousHtml: true,
2004
3122
  // Suppress mdast-util-to-hast's `user-content-` prefix on footnote
@@ -2015,7 +3133,7 @@ var AIMarkdownContent = memo(({ content, customComponents }) => {
2015
3133
  [enableDefinitionList]
2016
3134
  );
2017
3135
  const Renderer = config.blockMemoEnabled ? BlockMemoizedRenderer : LegacyRenderer;
2018
- return /* @__PURE__ */ jsx3(
3136
+ return /* @__PURE__ */ jsx6(
2019
3137
  Renderer,
2020
3138
  {
2021
3139
  content,
@@ -2030,7 +3148,7 @@ AIMarkdownContent.displayName = "AIMarkdownContent";
2030
3148
  var MarkdownContent_default = AIMarkdownContent;
2031
3149
 
2032
3150
  // src/hooks/useStableValue.ts
2033
- import { useRef as useRef2, useLayoutEffect, useEffect } from "react";
3151
+ import { useRef as useRef3, useLayoutEffect, useEffect as useEffect2 } from "react";
2034
3152
 
2035
3153
  // ../../node_modules/.pnpm/lodash-es@4.17.23/node_modules/lodash-es/_setCacheAdd.js
2036
3154
  var HASH_UNDEFINED3 = "__lodash_hash_undefined__";
@@ -2450,9 +3568,9 @@ function isEqual(value, other) {
2450
3568
  var isEqual_default = isEqual;
2451
3569
 
2452
3570
  // src/hooks/useStableValue.ts
2453
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
3571
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect2;
2454
3572
  function useStableValue(value) {
2455
- const ref = useRef2(value);
3573
+ const ref = useRef3(value);
2456
3574
  const prev = ref.current;
2457
3575
  const stableValue = isEqual_default(prev, value) ? prev : value;
2458
3576
  useIsomorphicLayoutEffect(() => {
@@ -2462,21 +3580,21 @@ function useStableValue(value) {
2462
3580
  }
2463
3581
 
2464
3582
  // src/components/typography/Default.tsx
2465
- import { memo as memo2, useMemo as useMemo3 } from "react";
2466
- import { jsx as jsx4 } from "react/jsx-runtime";
2467
- var DefaultTypography = memo2(({ children, fontSize, variant, colorScheme, style }) => {
2468
- const className = useMemo3(
3583
+ import { memo as memo3, useMemo as useMemo5 } from "react";
3584
+ import { jsx as jsx7 } from "react/jsx-runtime";
3585
+ var DefaultTypography = memo3(({ children, fontSize, variant, colorScheme, style }) => {
3586
+ const className = useMemo5(
2469
3587
  () => ["aim-typography-root", variant, colorScheme].filter(Boolean).join(" "),
2470
3588
  [variant, colorScheme]
2471
3589
  );
2472
- const mergedStyle = useMemo3(() => ({ width: "100%", fontSize, ...style }), [fontSize, style]);
2473
- return /* @__PURE__ */ jsx4("div", { className, style: mergedStyle, children });
3590
+ const mergedStyle = useMemo5(() => ({ width: "100%", fontSize, ...style }), [fontSize, style]);
3591
+ return /* @__PURE__ */ jsx7("div", { className, style: mergedStyle, children });
2474
3592
  });
2475
3593
  DefaultTypography.displayName = "DefaultTypography";
2476
3594
  var Default_default = DefaultTypography;
2477
3595
 
2478
3596
  // src/index.tsx
2479
- import { jsx as jsx5 } from "react/jsx-runtime";
3597
+ import { jsx as jsx8 } from "react/jsx-runtime";
2480
3598
  var AIMarkdownComponent = ({
2481
3599
  streaming = false,
2482
3600
  content,
@@ -2493,18 +3611,18 @@ var AIMarkdownComponent = ({
2493
3611
  documentId
2494
3612
  }) => {
2495
3613
  const usedFontSize = fontSize === void 0 ? "0.9375rem" : typeof fontSize === "number" ? `${fontSize}px` : fontSize;
2496
- const generatedId = useId2();
3614
+ const generatedId = useId3();
2497
3615
  const usedDocumentId = documentId && documentId.length > 0 ? documentId : generatedId;
2498
3616
  const stableDefaultConfig = useStableValue(defaultConfig);
2499
3617
  const stableConfig = useStableValue(config);
2500
3618
  const stablePreprocessors = useStableValue(contentPreprocessors);
2501
3619
  const stableCustomComponents = useStableValue(customComponents);
2502
- const usedContent = useMemo4(
3620
+ const usedContent = useMemo6(
2503
3621
  () => content ? preprocessAIMDContent(content, stablePreprocessors) : content,
2504
3622
  [content, stablePreprocessors]
2505
3623
  );
2506
- const typographyStyle = useMemo4(() => ({ "--aim-font-size-root": usedFontSize }), [usedFontSize]);
2507
- return /* @__PURE__ */ jsx5(AIMarkdownMetadataProvider, { metadata, children: /* @__PURE__ */ jsx5(
3624
+ const typographyStyle = useMemo6(() => ({ "--aim-font-size-root": usedFontSize }), [usedFontSize]);
3625
+ return /* @__PURE__ */ jsx8(AIMarkdownMetadataProvider, { metadata, children: /* @__PURE__ */ jsx8(
2508
3626
  context_default,
2509
3627
  {
2510
3628
  streaming,
@@ -2514,29 +3632,31 @@ var AIMarkdownComponent = ({
2514
3632
  documentId: usedDocumentId,
2515
3633
  defaultConfig: stableDefaultConfig,
2516
3634
  config: stableConfig,
2517
- children: /* @__PURE__ */ jsx5(
3635
+ children: /* @__PURE__ */ jsx8(
2518
3636
  Typography,
2519
3637
  {
2520
3638
  fontSize: usedFontSize,
2521
3639
  variant,
2522
3640
  colorScheme,
2523
3641
  style: typographyStyle,
2524
- children: ExtraStyles ? /* @__PURE__ */ jsx5(ExtraStyles, { children: /* @__PURE__ */ jsx5(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents }) }) : /* @__PURE__ */ jsx5(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents })
3642
+ children: ExtraStyles ? /* @__PURE__ */ jsx8(ExtraStyles, { children: /* @__PURE__ */ jsx8(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents }) }) : /* @__PURE__ */ jsx8(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents })
2525
3643
  }
2526
3644
  )
2527
3645
  }
2528
3646
  ) });
2529
3647
  };
2530
- var AIMarkdown = memo3(AIMarkdownComponent);
3648
+ var AIMarkdown = memo4(AIMarkdownComponent);
2531
3649
  AIMarkdown.displayName = "AIMarkdown";
2532
3650
  var index_default = AIMarkdown;
2533
3651
  export {
3652
+ AIMarkdownDocuments,
2534
3653
  AIMarkdownRenderDisplayOptimizeAbility,
2535
3654
  AIMarkdownRenderExtraSyntax,
2536
3655
  index_default as default,
2537
3656
  defaultAIMarkdownRenderConfig,
2538
3657
  useAIMarkdownMetadata,
2539
3658
  useAIMarkdownRenderState,
3659
+ useDocumentRegistry,
2540
3660
  useStableValue
2541
3661
  };
2542
3662
  //# sourceMappingURL=index.js.map