@ai-react-markdown/core 1.3.0 → 1.4.0

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