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