@ai-react-markdown/engine 2.12.0 → 2.13.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.
@@ -265,8 +265,51 @@ var defaultUrlTransform = (value) => {
265
265
  return "";
266
266
  };
267
267
 
268
- // src/components/incrementalParse/computeFreezeBoundary.ts
269
- var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
268
+ // src/components/incrementalParse/freezeScanState.ts
269
+ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
270
+ return {
271
+ defListEnabled,
272
+ mathFlow,
273
+ referenceTaint,
274
+ confirmedOffset: 0,
275
+ candidates: [],
276
+ defs: /* @__PURE__ */ new Map(),
277
+ footnoteDefs: /* @__PURE__ */ new Map(),
278
+ unresolvedRefs: [],
279
+ tagBalance: /* @__PURE__ */ new Map(),
280
+ openTotal: 0,
281
+ p5Tok: { kind: "data" },
282
+ formPointerMaybeSet: false,
283
+ openStack: [],
284
+ mdBlock: { kind: "none" },
285
+ blankRun: 0,
286
+ lastBlankStart: -1,
287
+ hazardVerdict: false,
288
+ prevLineBlank: true,
289
+ // doc start counts as a block start
290
+ prevLineWasText: false,
291
+ prevLineWasValidDef: false,
292
+ prevLineOpenContent: false,
293
+ tableMaybeOpen: false,
294
+ containerMaybeOpen: false,
295
+ paragraphHasUnpairedRun: false,
296
+ openBracket: null,
297
+ p5SealPending: false,
298
+ defBlockMaybeOpen: false,
299
+ fnDefResumable: false,
300
+ phasePoisonedAt: Infinity,
301
+ pendingTruncatedTags: [],
302
+ pendingTruncatedCloses: [],
303
+ pendingTag: null
304
+ };
305
+ }
306
+ function pendingFenceCloser(checkpoint) {
307
+ const cp = checkpoint;
308
+ if (cp.phasePoisonedAt !== Infinity) return "";
309
+ if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
310
+ if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
311
+ return "";
312
+ }
270
313
 
271
314
  // src/components/incrementalParse/mdLineText.ts
272
315
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -274,6 +317,9 @@ var isMdBlank = (text) => MD_BLANK_RE.test(text);
274
317
  var mdTrim = (text) => text.replace(/^[ \t\r]+|[ \t\r]+$/g, "");
275
318
  var mdTrimStart = (text) => text.replace(/^[ \t\r]+/, "");
276
319
 
320
+ // src/components/incrementalParse/freezeLineSyntax.ts
321
+ var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
322
+
277
323
  // src/components/incrementalParse/referenceTaint.ts
278
324
  var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
279
325
  var FOOTNOTE_DEF_RE = /^ {0,3}\[\^[^\]]*\]:/;
@@ -459,7 +505,7 @@ function settleRefsAndEarliestUnresolved(cp) {
459
505
  return earliestUnresolved;
460
506
  }
461
507
 
462
- // src/components/incrementalParse/computeFreezeBoundary.ts
508
+ // src/components/incrementalParse/freezeLineSyntax.ts
463
509
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
464
510
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
465
511
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
@@ -711,43 +757,6 @@ function maskIntraLineCodeSpans(text, carryOpen) {
711
757
  out += text.slice(cursor);
712
758
  return { masked: out, unpaired: false };
713
759
  }
714
- function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
715
- return {
716
- defListEnabled,
717
- mathFlow,
718
- referenceTaint,
719
- confirmedOffset: 0,
720
- candidates: [],
721
- defs: /* @__PURE__ */ new Map(),
722
- footnoteDefs: /* @__PURE__ */ new Map(),
723
- unresolvedRefs: [],
724
- tagBalance: /* @__PURE__ */ new Map(),
725
- openTotal: 0,
726
- p5Tok: { kind: "data" },
727
- formPointerMaybeSet: false,
728
- openStack: [],
729
- mdBlock: { kind: "none" },
730
- blankRun: 0,
731
- lastBlankStart: -1,
732
- hazardVerdict: false,
733
- prevLineBlank: true,
734
- // doc start counts as a block start
735
- prevLineWasText: false,
736
- prevLineWasValidDef: false,
737
- prevLineOpenContent: false,
738
- tableMaybeOpen: false,
739
- containerMaybeOpen: false,
740
- paragraphHasUnpairedRun: false,
741
- openBracket: null,
742
- p5SealPending: false,
743
- defBlockMaybeOpen: false,
744
- fnDefResumable: false,
745
- phasePoisonedAt: Infinity,
746
- pendingTruncatedTags: [],
747
- pendingTruncatedCloses: [],
748
- pendingTag: null
749
- };
750
- }
751
760
  function classifyBlockStart(text, indent, defListEnabled) {
752
761
  if (indent >= 4) return true;
753
762
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -755,71 +764,23 @@ function classifyBlockStart(text, indent, defListEnabled) {
755
764
  if (indent === 0) return false;
756
765
  return null;
757
766
  }
758
- function computeFreezeBoundary(text, options, resume) {
759
- const mathFlow = options.mathFlow ?? true;
760
- const referenceTaint = options.referenceTaint ?? true;
761
- const prev = resume;
762
- const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
763
- let start = cp.confirmedOffset;
764
- let tailLine = null;
765
- while (start < text.length) {
766
- let end = text.length;
767
- let textEnd = text.length;
768
- {
769
- const nl = text.indexOf("\n", start);
770
- const cr = text.indexOf("\r", start);
771
- if (cr !== -1 && (nl === -1 || cr < nl)) {
772
- textEnd = cr;
773
- end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
774
- } else if (nl !== -1) {
775
- textEnd = nl;
776
- end = nl;
777
- }
778
- }
779
- const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
780
- const lineText = text.slice(start, textEnd);
781
- const ln = {
782
- start,
783
- end,
784
- text: lineText,
785
- blank: confirmed && isMdBlank(lineText),
786
- indent: computeIndent(lineText)
787
- };
788
- if (!confirmed) {
789
- tailLine = ln;
790
- break;
791
- }
792
- processConfirmedLine(cp, ln, text);
793
- cp.confirmedOffset = end + 1;
794
- start = end + 1;
795
- }
796
- const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
797
- const defListSettled = (c) => {
798
- if (!options.defListEnabled || c.blankRun >= 2) return true;
799
- if (c.defListSettled !== null) return c.defListSettled;
800
- if (tailLine) return !canBecomeDdLine(tailLine.text, false);
801
- return false;
802
- };
803
- const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
804
- let boundary = 0;
805
- for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
806
- const c = cp.candidates[i];
807
- if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
808
- if (c.offset > cp.phasePoisonedAt) continue;
809
- if (c.offset > earliestUnresolved) continue;
810
- if (!defListSettled(c)) continue;
811
- boundary = c.offset;
812
- break;
813
- }
814
- return { boundary, checkpoint: cp };
815
- }
816
- function pendingFenceCloser(checkpoint) {
817
- const cp = checkpoint;
818
- if (cp.phasePoisonedAt !== Infinity) return "";
819
- if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
820
- if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
821
- return "";
822
- }
767
+ var SCANNER_NAME_LISTS = [
768
+ ["type1", TYPE1_NAMES],
769
+ ["rawText", RAW_TEXT_ELEMENTS],
770
+ ["type6", TYPE6_NAMES],
771
+ ["void", VOID_TAGS],
772
+ ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
773
+ // Added with F28, and it is the list that would have made F28 visible: the
774
+ // derived census alphabet partitions names by their membership across THIS
775
+ // table, so before it existed `frame` shared one 39-name class with `div`
776
+ // and could never be sampled apart from it.
777
+ ["noElement", NO_ELEMENT_NAMES],
778
+ ["tablePart", TABLE_PART_NAMES],
779
+ ["scopeBarrier", SCOPE_BARRIER_NAMES],
780
+ ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
781
+ ];
782
+
783
+ // src/components/incrementalParse/freezeLineTransition.ts
823
784
  function floatingResidue(text, commentOpenAtStart) {
824
785
  let out = "";
825
786
  let open = commentOpenAtStart;
@@ -1469,24 +1430,66 @@ function processConfirmedLine(cp, ln, text) {
1469
1430
  }
1470
1431
  }
1471
1432
  }
1472
- var SCANNER_NAME_LISTS = [
1473
- ["type1", TYPE1_NAMES],
1474
- ["rawText", RAW_TEXT_ELEMENTS],
1475
- ["type6", TYPE6_NAMES],
1476
- ["void", VOID_TAGS],
1477
- ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1478
- // Added with F28, and it is the list that would have made F28 visible: the
1479
- // derived census alphabet partitions names by their membership across THIS
1480
- // table, so before it existed `frame` shared one 39-name class with `div`
1481
- // and could never be sampled apart from it.
1482
- ["noElement", NO_ELEMENT_NAMES],
1483
- ["tablePart", TABLE_PART_NAMES],
1484
- ["scopeBarrier", SCOPE_BARRIER_NAMES],
1485
- ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
1486
- ];
1487
1433
 
1488
- // src/components/incrementalParse/spliceParse.ts
1489
- var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1434
+ // src/components/incrementalParse/computeFreezeBoundary.ts
1435
+ function computeFreezeBoundary(text, options, resume) {
1436
+ const mathFlow = options.mathFlow ?? true;
1437
+ const referenceTaint = options.referenceTaint ?? true;
1438
+ const prev = resume;
1439
+ const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
1440
+ let start = cp.confirmedOffset;
1441
+ let tailLine = null;
1442
+ while (start < text.length) {
1443
+ let end = text.length;
1444
+ let textEnd = text.length;
1445
+ {
1446
+ const nl = text.indexOf("\n", start);
1447
+ const cr = text.indexOf("\r", start);
1448
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
1449
+ textEnd = cr;
1450
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
1451
+ } else if (nl !== -1) {
1452
+ textEnd = nl;
1453
+ end = nl;
1454
+ }
1455
+ }
1456
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
1457
+ const lineText = text.slice(start, textEnd);
1458
+ const ln = {
1459
+ start,
1460
+ end,
1461
+ text: lineText,
1462
+ blank: confirmed && isMdBlank(lineText),
1463
+ indent: computeIndent(lineText)
1464
+ };
1465
+ if (!confirmed) {
1466
+ tailLine = ln;
1467
+ break;
1468
+ }
1469
+ processConfirmedLine(cp, ln, text);
1470
+ cp.confirmedOffset = end + 1;
1471
+ start = end + 1;
1472
+ }
1473
+ const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
1474
+ const defListSettled = (c) => {
1475
+ if (!options.defListEnabled || c.blankRun >= 2) return true;
1476
+ if (c.defListSettled !== null) return c.defListSettled;
1477
+ if (tailLine) return !canBecomeDdLine(tailLine.text, false);
1478
+ return false;
1479
+ };
1480
+ const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
1481
+ let boundary = 0;
1482
+ for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
1483
+ const c = cp.candidates[i];
1484
+ if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
1485
+ if (c.offset > cp.phasePoisonedAt) continue;
1486
+ if (c.offset > earliestUnresolved) continue;
1487
+ if (!defListSettled(c)) continue;
1488
+ boundary = c.offset;
1489
+ break;
1490
+ }
1491
+ return { boundary, checkpoint: cp };
1492
+ }
1490
1493
 
1491
1494
  // src/components/hastPredicates.ts
1492
1495
  function isFootnoteSection(node) {
@@ -1534,121 +1537,7 @@ function attributeHastChildren(mdast, hast, stopAt = Infinity) {
1534
1537
  }
1535
1538
  }
1536
1539
 
1537
- // src/components/incrementalParse/spliceParse.ts
1538
- function collectPrefixInjection(mdast, content, boundary, resume) {
1539
- const resumeValid = resume != null && resume.boundary <= boundary;
1540
- if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1541
- const resumeAt = resumeValid ? resume.boundary : 0;
1542
- const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1543
- let uninjectable = false;
1544
- let cacheable = true;
1545
- const pushRef = (token) => {
1546
- const last = events[events.length - 1];
1547
- if (last && last.kind === "refs") last.tokens.push(token);
1548
- else events.push({ kind: "refs", tokens: [token] });
1549
- };
1550
- const visit7 = (node, nested) => {
1551
- const type = node.type;
1552
- if (type === "definition") {
1553
- const start = node.position?.start?.offset;
1554
- const end = node.position?.end?.offset;
1555
- if (start !== void 0 && end !== void 0 && start < boundary) {
1556
- const slice = content.slice(start, end);
1557
- if (nested && slice.includes("\n")) uninjectable = true;
1558
- else events.push({ kind: "def", source: slice });
1559
- }
1560
- return;
1561
- }
1562
- if (type === "footnoteDefinition") {
1563
- const start = node.position?.start;
1564
- const end = node.position?.end?.offset;
1565
- if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1566
- if (nested) {
1567
- uninjectable = true;
1568
- return;
1569
- }
1570
- const lineStart = start.offset - (start.column - 1);
1571
- events.push({
1572
- kind: "footnoteDef",
1573
- source: content.slice(lineStart, end),
1574
- origStart: lineStart,
1575
- origLine: start.line
1576
- });
1577
- return;
1578
- }
1579
- if (type === "footnoteReference") {
1580
- const start = node.position?.start?.offset;
1581
- const end = node.position?.end?.offset;
1582
- if (start === void 0 || end === void 0) {
1583
- uninjectable = true;
1584
- return;
1585
- }
1586
- pushRef(content.slice(start, end));
1587
- return;
1588
- }
1589
- const children = node.children;
1590
- if (children) {
1591
- for (const child of children) visit7(child, true);
1592
- }
1593
- };
1594
- let lastStart = -1;
1595
- for (const child of mdast.children) {
1596
- const start = child.position?.start?.offset;
1597
- if (start !== void 0) {
1598
- if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1599
- lastStart = start;
1600
- }
1601
- if (start === void 0) {
1602
- if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1603
- cacheable = false;
1604
- visit7(child, false);
1605
- continue;
1606
- }
1607
- if (start >= boundary || start < resumeAt) continue;
1608
- visit7(child, false);
1609
- }
1610
- return { events, uninjectable, cacheable };
1611
- }
1612
- function cloneEventsForAppend(events) {
1613
- const out = events.slice();
1614
- const last = out[out.length - 1];
1615
- if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1616
- return out;
1617
- }
1618
- var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1619
- var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1620
- function tailMentionsTerminator(tailSource) {
1621
- return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1622
- }
1623
- function buildInjectionPrefix(events) {
1624
- if (events.length === 0) return { text: "", segments: [] };
1625
- let text = "";
1626
- let line = 1;
1627
- const segments = [];
1628
- for (const event of events) {
1629
- if (text.length > 0) {
1630
- text += "\n\n";
1631
- line += 2;
1632
- }
1633
- const injStart = text.length;
1634
- const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1635
- if (event.kind === "footnoteDef") {
1636
- segments.push({
1637
- injStart,
1638
- injEnd: injStart + source.length,
1639
- offsetDelta: event.origStart - injStart,
1640
- lineDelta: event.origLine - line
1641
- });
1642
- }
1643
- text += source;
1644
- line += countNewlines(source);
1645
- }
1646
- return { text: `${text}
1647
-
1648
- ${INJECTION_TERMINATOR}
1649
-
1650
- `, segments };
1651
- }
1540
+ // src/components/incrementalParse/spliceCoordinates.ts
1652
1541
  function rebaseTree(node, offsetDelta, lineDelta) {
1653
1542
  rebaseTreeDual(node, EMPTY_SEGMENTS, offsetDelta, lineDelta);
1654
1543
  }
@@ -1677,6 +1566,23 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1677
1566
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1678
1567
  }
1679
1568
  }
1569
+ function rebasePoint(point, offsetDelta, lineDelta) {
1570
+ return {
1571
+ line: point.line + lineDelta,
1572
+ column: point.column,
1573
+ offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1574
+ };
1575
+ }
1576
+ function countNewlines(text, end = text.length) {
1577
+ let count = 0;
1578
+ for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1579
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1580
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1581
+ }
1582
+ return count;
1583
+ }
1584
+
1585
+ // src/components/incrementalParse/spliceHtmlGuards.ts
1680
1586
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1681
1587
  var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1682
1588
  function hasStrayTablePart(values) {
@@ -1758,127 +1664,26 @@ function rawTextRegionCrossesOut(values) {
1758
1664
  }
1759
1665
  return false;
1760
1666
  }
1761
- function spliceTrees(input) {
1762
- const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1763
- const injectedLen = injectionPrefix.length;
1764
- const injectedLines = countNewlines(injectionPrefix);
1765
- const prefixLines = countNewlines(content, boundary);
1766
- const offsetDelta = boundary - injectedLen;
1767
- const lineDelta = prefixLines - injectedLines;
1768
- const prefixMdast = [];
1769
- for (const child of prevMdast.children) {
1770
- const start = child.position?.start?.offset;
1771
- if (start !== void 0 && start < boundary) prefixMdast.push(child);
1772
- }
1773
- const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1774
- const cutRegion = [];
1775
- for (let i = 0; i < prevHast.children.length; i++) {
1776
- if (attrs[i] < boundary) {
1777
- const node2 = prevHast.children[i];
1778
- const end = node2.position?.end?.offset;
1779
- if (end !== void 0 && end > boundary) return null;
1780
- if (isTrailingLiteralText(node2)) {
1781
- const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1782
- if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1783
- if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1784
- return null;
1785
- }
1786
- cutRegion.push(node2);
1787
- continue;
1788
- }
1789
- const node = prevHast.children[i];
1790
- if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node) && // …and only when that element really IS an html block's output: a
1791
- // raw literal can only trail an `html` mdast node. A position-less
1792
- // text after a `<p>` is the NEXT block's remnant — a stray end tag
1793
- // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1794
- // separator — owned by the tail, which re-parses it; freezing it
1795
- // here duplicated it (v2.4.0 review P3). Falls through to the
1796
- // remnant look-ahead below, which bails to a full parse. And the
1797
- // literal must really be THAT block's trailing text: the block's raw
1798
- // source ends with it. A dropped-tag block right after a frozen html
1799
- // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1800
- // position, and freezing it duplicated it (release soak of the fix).
1801
- ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1802
- cutRegion.push(node);
1803
- break;
1804
- }
1805
- for (let j = i; j < prevHast.children.length; j++) {
1806
- const look = prevHast.children[j];
1807
- if (look.type === "element" || look.position !== void 0) break;
1808
- if (isTrailingLiteralText(look)) return null;
1809
- }
1810
- break;
1811
- }
1812
- const tailMdastChildren = tailMdast.children.filter((child) => {
1813
- const start = child.position?.start?.offset;
1814
- return !(start !== void 0 && start < injectedLen);
1815
- });
1816
- const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1817
- const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1818
- if (hasStrayTablePart(prefixHtmlValues)) return null;
1819
- if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1820
- const leadingHtml = [];
1821
- for (const child of tailMdastChildren) {
1822
- if (isWrapInvisible(child)) continue;
1823
- if (child.type !== "html") break;
1824
- if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1825
- leadingHtml.push(child.value);
1667
+ function isSanitizeStrippedConstruct(value) {
1668
+ const v = value.trim();
1669
+ return v.startsWith("<!--") && v.endsWith("-->") || v.startsWith("<?") && v.endsWith("?>") || v.startsWith("<![CDATA[") && v.endsWith("]]>") || /^<![A-Za-z]/.test(v) && !/^<!doctype/i.test(v) && v.endsWith(">");
1670
+ }
1671
+ function isExactSanitizeStrippedConstruct(value) {
1672
+ if (value.startsWith("<!--")) {
1673
+ if (value.length < 7 || !value.endsWith("-->")) return false;
1674
+ const close = value.indexOf("-->", 4);
1675
+ const bangClose = value.indexOf("--!>", 4);
1676
+ if (close !== value.length - 3) return false;
1677
+ return bangClose === -1 || bangClose > close;
1826
1678
  }
1827
- if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1828
- const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1829
- if (aligned === null) return null;
1830
- const hastChildren = aligned.children;
1831
- const interiorFinalLiteral = aligned.interiorFinalLiteral;
1832
- const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1833
- if (stripResult === null) return null;
1834
- const strippedTailHast = stripResult.rest;
1835
- for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1836
- for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1837
- const mdastChildren = prefixMdast.concat(tailMdastChildren);
1838
- const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1839
- if (seamMergeVerdict === null) return null;
1840
- const seamMergeAllowed = seamMergeVerdict;
1841
- let firstTailChild = true;
1842
- for (const child of strippedTailHast) {
1843
- const tailEnd = hastChildren[hastChildren.length - 1];
1844
- if (firstTailChild && child.type === "text" && child.position !== void 0) {
1845
- return null;
1846
- }
1847
- if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1848
- if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
1849
- if (seamMergeAllowed) return null;
1850
- firstTailChild = false;
1851
- hastChildren.push(child);
1852
- continue;
1853
- }
1854
- const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
1855
- if (seamMergeAllowed || literalSeam) {
1856
- hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
1857
- firstTailChild = false;
1858
- continue;
1859
- }
1860
- }
1861
- firstTailChild = false;
1862
- hastChildren.push(child);
1679
+ if (/^<!doctype/i.test(value)) return false;
1680
+ if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1681
+ return value.indexOf(">") === value.length - 1;
1863
1682
  }
1864
- if (!hastChildren.some((child) => child.position !== void 0)) return null;
1865
- const docStart = { line: 1, column: 1, offset: 0 };
1866
- const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
1867
- const hastRootPosition = mdastRootPosition;
1868
- const mdast = { type: "root", children: mdastChildren };
1869
- if (mdastRootPosition) mdast.position = mdastRootPosition;
1870
- const hast = { type: "root", children: hastChildren };
1871
- if (hastRootPosition) hast.position = hastRootPosition;
1872
- if (prevHast.data !== void 0) hast.data = prevHast.data;
1873
- return { mdast, hast };
1874
- }
1875
- function rebasePoint(point, offsetDelta, lineDelta) {
1876
- return {
1877
- line: point.line + lineDelta,
1878
- column: point.column,
1879
- offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1880
- };
1683
+ return false;
1881
1684
  }
1685
+
1686
+ // src/components/incrementalParse/prefixAlignment.ts
1882
1687
  function isWrapInvisible(node) {
1883
1688
  return node.type === "definition" || node.type === "footnoteDefinition";
1884
1689
  }
@@ -2020,96 +1825,303 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
2020
1825
  idx += 1;
2021
1826
  continue;
2022
1827
  }
2023
- const start = node.position?.start?.offset;
2024
- if (start === void 0 || start >= injectedLen) return null;
2025
- consumed += 1;
2026
- idx += 1;
2027
- }
2028
- if (consumed < injectedVisibleCount) return null;
2029
- let remnant = "";
2030
- let remnantMerged = null;
2031
- if (tailWrapVisible) {
2032
- const gap = children[idx];
2033
- if (!gap || !isSeparatorText(gap)) return null;
2034
- remnant = gap.value.slice(1);
2035
- remnantMerged = remnant !== "";
2036
- idx += 1;
1828
+ const start = node.position?.start?.offset;
1829
+ if (start === void 0 || start >= injectedLen) return null;
1830
+ consumed += 1;
1831
+ idx += 1;
1832
+ }
1833
+ if (consumed < injectedVisibleCount) return null;
1834
+ let remnant = "";
1835
+ let remnantMerged = null;
1836
+ if (tailWrapVisible) {
1837
+ const gap = children[idx];
1838
+ if (!gap || !isSeparatorText(gap)) return null;
1839
+ remnant = gap.value.slice(1);
1840
+ remnantMerged = remnant !== "";
1841
+ idx += 1;
1842
+ }
1843
+ const rest = children.slice(idx);
1844
+ if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1845
+ return { rest, remnantMerged };
1846
+ }
1847
+ function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1848
+ const firstText = tailHastChildren[0];
1849
+ const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1850
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1851
+ if (!firstText) return false;
1852
+ if (!isSeparatorText(firstText)) {
1853
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1854
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1855
+ if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1856
+ return null;
1857
+ }
1858
+ return false;
1859
+ }
1860
+ if (!firstVisible) return false;
1861
+ const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1862
+ if (firstContent) {
1863
+ const start = firstContent.position?.start?.offset;
1864
+ const vStart = firstVisible.position?.start?.offset;
1865
+ const vEnd = firstVisible.position?.end?.offset;
1866
+ if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1867
+ if (start >= vStart && start < vEnd) return true;
1868
+ }
1869
+ }
1870
+ if (firstVisible.type === "math") return false;
1871
+ if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1872
+ return null;
1873
+ }
1874
+ function isSeparatorText(node) {
1875
+ return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1876
+ }
1877
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1878
+ const start = el.position?.start?.offset;
1879
+ if (start === void 0) return false;
1880
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1881
+ if (!owner || owner.type !== "html") return false;
1882
+ const text = literal.value.trim();
1883
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1884
+ }
1885
+ function isTrailingLiteralText(node) {
1886
+ return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1887
+ }
1888
+ function countTrailingNewlines(value) {
1889
+ let n = 0;
1890
+ for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
1891
+ return n;
1892
+ }
1893
+
1894
+ // src/components/incrementalParse/prefixInjection.ts
1895
+ var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1896
+ function collectPrefixInjection(mdast, content, boundary, resume) {
1897
+ const resumeValid = resume != null && resume.boundary <= boundary;
1898
+ if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1899
+ const resumeAt = resumeValid ? resume.boundary : 0;
1900
+ const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1901
+ let uninjectable = false;
1902
+ let cacheable = true;
1903
+ const pushRef = (token) => {
1904
+ const last = events[events.length - 1];
1905
+ if (last && last.kind === "refs") last.tokens.push(token);
1906
+ else events.push({ kind: "refs", tokens: [token] });
1907
+ };
1908
+ const visit7 = (node, nested) => {
1909
+ const type = node.type;
1910
+ if (type === "definition") {
1911
+ const start = node.position?.start?.offset;
1912
+ const end = node.position?.end?.offset;
1913
+ if (start !== void 0 && end !== void 0 && start < boundary) {
1914
+ const slice = content.slice(start, end);
1915
+ if (nested && slice.includes("\n")) uninjectable = true;
1916
+ else events.push({ kind: "def", source: slice });
1917
+ }
1918
+ return;
1919
+ }
1920
+ if (type === "footnoteDefinition") {
1921
+ const start = node.position?.start;
1922
+ const end = node.position?.end?.offset;
1923
+ if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1924
+ if (nested) {
1925
+ uninjectable = true;
1926
+ return;
1927
+ }
1928
+ const lineStart = start.offset - (start.column - 1);
1929
+ events.push({
1930
+ kind: "footnoteDef",
1931
+ source: content.slice(lineStart, end),
1932
+ origStart: lineStart,
1933
+ origLine: start.line
1934
+ });
1935
+ return;
1936
+ }
1937
+ if (type === "footnoteReference") {
1938
+ const start = node.position?.start?.offset;
1939
+ const end = node.position?.end?.offset;
1940
+ if (start === void 0 || end === void 0) {
1941
+ uninjectable = true;
1942
+ return;
1943
+ }
1944
+ pushRef(content.slice(start, end));
1945
+ return;
1946
+ }
1947
+ const children = node.children;
1948
+ if (children) {
1949
+ for (const child of children) visit7(child, true);
1950
+ }
1951
+ };
1952
+ let lastStart = -1;
1953
+ for (const child of mdast.children) {
1954
+ const start = child.position?.start?.offset;
1955
+ if (start !== void 0) {
1956
+ if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1957
+ lastStart = start;
1958
+ }
1959
+ if (start === void 0) {
1960
+ if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1961
+ cacheable = false;
1962
+ visit7(child, false);
1963
+ continue;
1964
+ }
1965
+ if (start >= boundary || start < resumeAt) continue;
1966
+ visit7(child, false);
2037
1967
  }
2038
- const rest = children.slice(idx);
2039
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
2040
- return { rest, remnantMerged };
1968
+ return { events, uninjectable, cacheable };
2041
1969
  }
2042
- function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
2043
- const firstText = tailHastChildren[0];
2044
- const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
2045
- if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
2046
- if (!firstText) return false;
2047
- if (!isSeparatorText(firstText)) {
2048
- if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
2049
- if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
2050
- if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
2051
- return null;
1970
+ function cloneEventsForAppend(events) {
1971
+ const out = events.slice();
1972
+ const last = out[out.length - 1];
1973
+ if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1974
+ return out;
1975
+ }
1976
+ var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1977
+ var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1978
+ function tailMentionsTerminator(tailSource) {
1979
+ return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1980
+ }
1981
+ function buildInjectionPrefix(events) {
1982
+ if (events.length === 0) return { text: "", segments: [] };
1983
+ let text = "";
1984
+ let line = 1;
1985
+ const segments = [];
1986
+ for (const event of events) {
1987
+ if (text.length > 0) {
1988
+ text += "\n\n";
1989
+ line += 2;
2052
1990
  }
2053
- return false;
2054
- }
2055
- if (!firstVisible) return false;
2056
- const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
2057
- if (firstContent) {
2058
- const start = firstContent.position?.start?.offset;
2059
- const vStart = firstVisible.position?.start?.offset;
2060
- const vEnd = firstVisible.position?.end?.offset;
2061
- if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
2062
- if (start >= vStart && start < vEnd) return true;
1991
+ const injStart = text.length;
1992
+ const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1993
+ if (event.kind === "footnoteDef") {
1994
+ segments.push({
1995
+ injStart,
1996
+ injEnd: injStart + source.length,
1997
+ offsetDelta: event.origStart - injStart,
1998
+ lineDelta: event.origLine - line
1999
+ });
2063
2000
  }
2001
+ text += source;
2002
+ line += countNewlines(source);
2064
2003
  }
2065
- if (firstVisible.type === "math") return false;
2066
- if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
2067
- return null;
2068
- }
2069
- function isSanitizeStrippedConstruct(value) {
2070
- const v = value.trim();
2071
- return v.startsWith("<!--") && v.endsWith("-->") || v.startsWith("<?") && v.endsWith("?>") || v.startsWith("<![CDATA[") && v.endsWith("]]>") || /^<![A-Za-z]/.test(v) && !/^<!doctype/i.test(v) && v.endsWith(">");
2004
+ return { text: `${text}
2005
+
2006
+ ${INJECTION_TERMINATOR}
2007
+
2008
+ `, segments };
2072
2009
  }
2073
- function isExactSanitizeStrippedConstruct(value) {
2074
- if (value.startsWith("<!--")) {
2075
- if (value.length < 7 || !value.endsWith("-->")) return false;
2076
- const close = value.indexOf("-->", 4);
2077
- const bangClose = value.indexOf("--!>", 4);
2078
- if (close !== value.length - 3) return false;
2079
- return bangClose === -1 || bangClose > close;
2010
+
2011
+ // src/components/incrementalParse/spliceParse.ts
2012
+ function spliceTrees(input) {
2013
+ const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
2014
+ const injectedLen = injectionPrefix.length;
2015
+ const injectedLines = countNewlines(injectionPrefix);
2016
+ const prefixLines = countNewlines(content, boundary);
2017
+ const offsetDelta = boundary - injectedLen;
2018
+ const lineDelta = prefixLines - injectedLines;
2019
+ const prefixMdast = [];
2020
+ for (const child of prevMdast.children) {
2021
+ const start = child.position?.start?.offset;
2022
+ if (start !== void 0 && start < boundary) prefixMdast.push(child);
2080
2023
  }
2081
- if (/^<!doctype/i.test(value)) return false;
2082
- if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
2083
- return value.indexOf(">") === value.length - 1;
2024
+ const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
2025
+ const cutRegion = [];
2026
+ for (let i = 0; i < prevHast.children.length; i++) {
2027
+ if (attrs[i] < boundary) {
2028
+ const node2 = prevHast.children[i];
2029
+ const end = node2.position?.end?.offset;
2030
+ if (end !== void 0 && end > boundary) return null;
2031
+ if (isTrailingLiteralText(node2)) {
2032
+ const prev = i > 0 ? prevHast.children[i - 1] : void 0;
2033
+ if (!prev || prev.type !== "element" || prev.position === void 0) return null;
2034
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
2035
+ return null;
2036
+ }
2037
+ cutRegion.push(node2);
2038
+ continue;
2039
+ }
2040
+ const node = prevHast.children[i];
2041
+ if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node) && // …and only when that element really IS an html block's output: a
2042
+ // raw literal can only trail an `html` mdast node. A position-less
2043
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
2044
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
2045
+ // separator — owned by the tail, which re-parses it; freezing it
2046
+ // here duplicated it (v2.4.0 review P3). Falls through to the
2047
+ // remnant look-ahead below, which bails to a full parse. And the
2048
+ // literal must really be THAT block's trailing text: the block's raw
2049
+ // source ends with it. A dropped-tag block right after a frozen html
2050
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
2051
+ // position, and freezing it duplicated it (release soak of the fix).
2052
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
2053
+ cutRegion.push(node);
2054
+ break;
2055
+ }
2056
+ for (let j = i; j < prevHast.children.length; j++) {
2057
+ const look = prevHast.children[j];
2058
+ if (look.type === "element" || look.position !== void 0) break;
2059
+ if (isTrailingLiteralText(look)) return null;
2060
+ }
2061
+ break;
2084
2062
  }
2085
- return false;
2086
- }
2087
- function isSeparatorText(node) {
2088
- return node.type === "text" && node.position === void 0 && node.value.trim() === "";
2089
- }
2090
- function ownsTrailingLiteral(el, literal, prefixMdast) {
2091
- const start = el.position?.start?.offset;
2092
- if (start === void 0) return false;
2093
- const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
2094
- if (!owner || owner.type !== "html") return false;
2095
- const text = literal.value.trim();
2096
- return text !== "" && owner.value.trimEnd().endsWith(text);
2097
- }
2098
- function isTrailingLiteralText(node) {
2099
- return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
2100
- }
2101
- function countTrailingNewlines(value) {
2102
- let n = 0;
2103
- for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
2104
- return n;
2105
- }
2106
- function countNewlines(text, end = text.length) {
2107
- let count = 0;
2108
- for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
2109
- for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
2110
- if (text.charCodeAt(i + 1) !== 10) count += 1;
2063
+ const tailMdastChildren = tailMdast.children.filter((child) => {
2064
+ const start = child.position?.start?.offset;
2065
+ return !(start !== void 0 && start < injectedLen);
2066
+ });
2067
+ const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
2068
+ const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
2069
+ if (hasStrayTablePart(prefixHtmlValues)) return null;
2070
+ if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
2071
+ const leadingHtml = [];
2072
+ for (const child of tailMdastChildren) {
2073
+ if (isWrapInvisible(child)) continue;
2074
+ if (child.type !== "html") break;
2075
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
2076
+ leadingHtml.push(child.value);
2111
2077
  }
2112
- return count;
2078
+ if (headRoutedCaptureUnclosed(leadingHtml)) return null;
2079
+ const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
2080
+ if (aligned === null) return null;
2081
+ const hastChildren = aligned.children;
2082
+ const interiorFinalLiteral = aligned.interiorFinalLiteral;
2083
+ const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
2084
+ if (stripResult === null) return null;
2085
+ const strippedTailHast = stripResult.rest;
2086
+ for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
2087
+ for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
2088
+ const mdastChildren = prefixMdast.concat(tailMdastChildren);
2089
+ const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
2090
+ if (seamMergeVerdict === null) return null;
2091
+ const seamMergeAllowed = seamMergeVerdict;
2092
+ let firstTailChild = true;
2093
+ for (const child of strippedTailHast) {
2094
+ const tailEnd = hastChildren[hastChildren.length - 1];
2095
+ if (firstTailChild && child.type === "text" && child.position !== void 0) {
2096
+ return null;
2097
+ }
2098
+ if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
2099
+ if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
2100
+ if (seamMergeAllowed) return null;
2101
+ firstTailChild = false;
2102
+ hastChildren.push(child);
2103
+ continue;
2104
+ }
2105
+ const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
2106
+ if (seamMergeAllowed || literalSeam) {
2107
+ hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
2108
+ firstTailChild = false;
2109
+ continue;
2110
+ }
2111
+ }
2112
+ firstTailChild = false;
2113
+ hastChildren.push(child);
2114
+ }
2115
+ if (!hastChildren.some((child) => child.position !== void 0)) return null;
2116
+ const docStart = { line: 1, column: 1, offset: 0 };
2117
+ const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
2118
+ const hastRootPosition = mdastRootPosition;
2119
+ const mdast = { type: "root", children: mdastChildren };
2120
+ if (mdastRootPosition) mdast.position = mdastRootPosition;
2121
+ const hast = { type: "root", children: hastChildren };
2122
+ if (hastRootPosition) hast.position = hastRootPosition;
2123
+ if (prevHast.data !== void 0) hast.data = prevHast.data;
2124
+ return { mdast, hast };
2113
2125
  }
2114
2126
 
2115
2127
  // src/components/incrementalParse/advanceIncrementalParse.ts
@@ -2561,7 +2573,8 @@ function buildRegistryIndex(registry) {
2561
2573
  links: /* @__PURE__ */ new Map(),
2562
2574
  numbers: /* @__PURE__ */ new Map(),
2563
2575
  counts: /* @__PURE__ */ new Map(),
2564
- occurrences: /* @__PURE__ */ new Map()
2576
+ occurrences: /* @__PURE__ */ new Map(),
2577
+ labelOccurrences: /* @__PURE__ */ new Map()
2565
2578
  };
2566
2579
  for (const sym of registry.chunkOrder) {
2567
2580
  const data = registry.chunkData.get(sym);
@@ -2578,12 +2591,78 @@ function buildRegistryIndex(registry) {
2578
2591
  index.counts.set(label, total);
2579
2592
  const prior = local.get(label);
2580
2593
  if (prior) prior.count++;
2581
- else local.set(label, { start: total, count: 1 });
2594
+ else {
2595
+ const range = { start: total, count: 1 };
2596
+ local.set(label, range);
2597
+ let byChunk = index.labelOccurrences.get(label);
2598
+ if (!byChunk) index.labelOccurrences.set(label, byChunk = /* @__PURE__ */ new Map());
2599
+ byChunk.set(sym, range);
2600
+ }
2582
2601
  }
2583
2602
  }
2584
2603
  return index;
2585
2604
  }
2586
2605
 
2606
+ // src/components/registrySubscriptions.ts
2607
+ function equal(a, b) {
2608
+ if (a.owner !== b.owner || a.url !== b.url || a.title !== b.title || a.number !== b.number || a.count !== b.count)
2609
+ return false;
2610
+ if (a.occurrences?.size !== b.occurrences?.size) return false;
2611
+ if (a.occurrences) {
2612
+ for (const [chunk, range] of a.occurrences) {
2613
+ const next = b.occurrences?.get(chunk);
2614
+ if (!next || next.start !== range.start || next.count !== range.count) return false;
2615
+ }
2616
+ }
2617
+ return true;
2618
+ }
2619
+ function createRegistrySubscriptions(read) {
2620
+ const groups = { link: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() };
2621
+ const snapshot = (kind, label, { registry, index }) => {
2622
+ if (kind === "link") {
2623
+ const owner = index.links.get(label);
2624
+ const def = owner ? registry.chunkData.get(owner)?.linkDefs.get(label) : void 0;
2625
+ return { owner, url: def?.url, title: def?.title };
2626
+ }
2627
+ return {
2628
+ owner: index.footnotes.get(label),
2629
+ number: index.numbers.get(label),
2630
+ count: index.counts.get(label),
2631
+ occurrences: index.labelOccurrences.get(label)
2632
+ };
2633
+ };
2634
+ return {
2635
+ subscribe(kind, rawLabel, cb) {
2636
+ const label = normalizeId(rawLabel);
2637
+ const map = groups[kind];
2638
+ let group = map.get(label);
2639
+ if (!group) {
2640
+ group = { snapshot: snapshot(kind, label, read()), listeners: /* @__PURE__ */ new Set() };
2641
+ map.set(label, group);
2642
+ }
2643
+ const listener = () => cb();
2644
+ group.listeners.add(listener);
2645
+ return () => {
2646
+ group.listeners.delete(listener);
2647
+ if (group.listeners.size === 0 && map.get(label) === group) map.delete(label);
2648
+ };
2649
+ },
2650
+ collect() {
2651
+ if (groups.link.size === 0 && groups.footnote.size === 0) return [];
2652
+ const state = read();
2653
+ const callbacks = [];
2654
+ for (const kind of ["link", "footnote"]) {
2655
+ for (const [label, group] of groups[kind]) {
2656
+ const next = snapshot(kind, label, state);
2657
+ if (!equal(group.snapshot, next)) callbacks.push(...group.listeners);
2658
+ group.snapshot = next;
2659
+ }
2660
+ }
2661
+ return callbacks;
2662
+ }
2663
+ };
2664
+ }
2665
+
2587
2666
  // src/components/documentRegistry.ts
2588
2667
  function createRegistry(onEmpty) {
2589
2668
  let index;
@@ -2595,6 +2674,7 @@ function createRegistry(onEmpty) {
2595
2674
  }
2596
2675
  return index;
2597
2676
  };
2677
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2598
2678
  const reg = {
2599
2679
  chunkOrder: [],
2600
2680
  chunkData: /* @__PURE__ */ new Map(),
@@ -2745,6 +2825,9 @@ function createRegistry(onEmpty) {
2745
2825
  this._subscribers.delete(cb);
2746
2826
  };
2747
2827
  },
2828
+ subscribeLabel(kind, label, cb) {
2829
+ return labels.subscribe(kind, label, cb);
2830
+ },
2748
2831
  canonicalFootnoteFor(label) {
2749
2832
  return getIndex().footnotes.get(normalizeId(label)) ?? null;
2750
2833
  },
@@ -2772,7 +2855,9 @@ function createRegistry(onEmpty) {
2772
2855
  this._notifyScheduled = true;
2773
2856
  queueMicrotask(() => {
2774
2857
  this._notifyScheduled = false;
2858
+ const labelCallbacks = labels.collect();
2775
2859
  for (const cb of [...this._subscribers]) cb();
2860
+ for (const cb of labelCallbacks) cb();
2776
2861
  });
2777
2862
  }
2778
2863
  };