@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.dev.js CHANGED
@@ -172,8 +172,51 @@ var defaultUrlTransform = (value) => {
172
172
  return "";
173
173
  };
174
174
 
175
- // src/components/incrementalParse/computeFreezeBoundary.ts
176
- import { htmlBlockNames } from "micromark-util-html-tag-name";
175
+ // src/components/incrementalParse/freezeScanState.ts
176
+ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
177
+ return {
178
+ defListEnabled,
179
+ mathFlow,
180
+ referenceTaint,
181
+ confirmedOffset: 0,
182
+ candidates: [],
183
+ defs: /* @__PURE__ */ new Map(),
184
+ footnoteDefs: /* @__PURE__ */ new Map(),
185
+ unresolvedRefs: [],
186
+ tagBalance: /* @__PURE__ */ new Map(),
187
+ openTotal: 0,
188
+ p5Tok: { kind: "data" },
189
+ formPointerMaybeSet: false,
190
+ openStack: [],
191
+ mdBlock: { kind: "none" },
192
+ blankRun: 0,
193
+ lastBlankStart: -1,
194
+ hazardVerdict: false,
195
+ prevLineBlank: true,
196
+ // doc start counts as a block start
197
+ prevLineWasText: false,
198
+ prevLineWasValidDef: false,
199
+ prevLineOpenContent: false,
200
+ tableMaybeOpen: false,
201
+ containerMaybeOpen: false,
202
+ paragraphHasUnpairedRun: false,
203
+ openBracket: null,
204
+ p5SealPending: false,
205
+ defBlockMaybeOpen: false,
206
+ fnDefResumable: false,
207
+ phasePoisonedAt: Infinity,
208
+ pendingTruncatedTags: [],
209
+ pendingTruncatedCloses: [],
210
+ pendingTag: null
211
+ };
212
+ }
213
+ function pendingFenceCloser(checkpoint) {
214
+ const cp = checkpoint;
215
+ if (cp.phasePoisonedAt !== Infinity) return "";
216
+ if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
217
+ if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
218
+ return "";
219
+ }
177
220
 
178
221
  // src/components/incrementalParse/mdLineText.ts
179
222
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -181,6 +224,9 @@ var isMdBlank = (text) => MD_BLANK_RE.test(text);
181
224
  var mdTrim = (text) => text.replace(/^[ \t\r]+|[ \t\r]+$/g, "");
182
225
  var mdTrimStart = (text) => text.replace(/^[ \t\r]+/, "");
183
226
 
227
+ // src/components/incrementalParse/freezeLineSyntax.ts
228
+ import { htmlBlockNames } from "micromark-util-html-tag-name";
229
+
184
230
  // src/components/incrementalParse/referenceTaint.ts
185
231
  import { normalizeIdentifier } from "micromark-util-normalize-identifier";
186
232
  var FOOTNOTE_DEF_RE = /^ {0,3}\[\^[^\]]*\]:/;
@@ -366,7 +412,7 @@ function settleRefsAndEarliestUnresolved(cp) {
366
412
  return earliestUnresolved;
367
413
  }
368
414
 
369
- // src/components/incrementalParse/computeFreezeBoundary.ts
415
+ // src/components/incrementalParse/freezeLineSyntax.ts
370
416
  var TYPE6_NAMES = new Set(htmlBlockNames);
371
417
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
372
418
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
@@ -618,43 +664,6 @@ function maskIntraLineCodeSpans(text, carryOpen) {
618
664
  out += text.slice(cursor);
619
665
  return { masked: out, unpaired: false };
620
666
  }
621
- function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
622
- return {
623
- defListEnabled,
624
- mathFlow,
625
- referenceTaint,
626
- confirmedOffset: 0,
627
- candidates: [],
628
- defs: /* @__PURE__ */ new Map(),
629
- footnoteDefs: /* @__PURE__ */ new Map(),
630
- unresolvedRefs: [],
631
- tagBalance: /* @__PURE__ */ new Map(),
632
- openTotal: 0,
633
- p5Tok: { kind: "data" },
634
- formPointerMaybeSet: false,
635
- openStack: [],
636
- mdBlock: { kind: "none" },
637
- blankRun: 0,
638
- lastBlankStart: -1,
639
- hazardVerdict: false,
640
- prevLineBlank: true,
641
- // doc start counts as a block start
642
- prevLineWasText: false,
643
- prevLineWasValidDef: false,
644
- prevLineOpenContent: false,
645
- tableMaybeOpen: false,
646
- containerMaybeOpen: false,
647
- paragraphHasUnpairedRun: false,
648
- openBracket: null,
649
- p5SealPending: false,
650
- defBlockMaybeOpen: false,
651
- fnDefResumable: false,
652
- phasePoisonedAt: Infinity,
653
- pendingTruncatedTags: [],
654
- pendingTruncatedCloses: [],
655
- pendingTag: null
656
- };
657
- }
658
667
  function classifyBlockStart(text, indent, defListEnabled) {
659
668
  if (indent >= 4) return true;
660
669
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -662,71 +671,23 @@ function classifyBlockStart(text, indent, defListEnabled) {
662
671
  if (indent === 0) return false;
663
672
  return null;
664
673
  }
665
- function computeFreezeBoundary(text, options, resume) {
666
- const mathFlow = options.mathFlow ?? true;
667
- const referenceTaint = options.referenceTaint ?? true;
668
- const prev = resume;
669
- const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
670
- let start = cp.confirmedOffset;
671
- let tailLine = null;
672
- while (start < text.length) {
673
- let end = text.length;
674
- let textEnd = text.length;
675
- {
676
- const nl = text.indexOf("\n", start);
677
- const cr = text.indexOf("\r", start);
678
- if (cr !== -1 && (nl === -1 || cr < nl)) {
679
- textEnd = cr;
680
- end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
681
- } else if (nl !== -1) {
682
- textEnd = nl;
683
- end = nl;
684
- }
685
- }
686
- const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
687
- const lineText = text.slice(start, textEnd);
688
- const ln = {
689
- start,
690
- end,
691
- text: lineText,
692
- blank: confirmed && isMdBlank(lineText),
693
- indent: computeIndent(lineText)
694
- };
695
- if (!confirmed) {
696
- tailLine = ln;
697
- break;
698
- }
699
- processConfirmedLine(cp, ln, text);
700
- cp.confirmedOffset = end + 1;
701
- start = end + 1;
702
- }
703
- const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
704
- const defListSettled = (c) => {
705
- if (!options.defListEnabled || c.blankRun >= 2) return true;
706
- if (c.defListSettled !== null) return c.defListSettled;
707
- if (tailLine) return !canBecomeDdLine(tailLine.text, false);
708
- return false;
709
- };
710
- const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
711
- let boundary = 0;
712
- for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
713
- const c = cp.candidates[i];
714
- if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
715
- if (c.offset > cp.phasePoisonedAt) continue;
716
- if (c.offset > earliestUnresolved) continue;
717
- if (!defListSettled(c)) continue;
718
- boundary = c.offset;
719
- break;
720
- }
721
- return { boundary, checkpoint: cp };
722
- }
723
- function pendingFenceCloser(checkpoint) {
724
- const cp = checkpoint;
725
- if (cp.phasePoisonedAt !== Infinity) return "";
726
- if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
727
- if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
728
- return "";
729
- }
674
+ var SCANNER_NAME_LISTS = [
675
+ ["type1", TYPE1_NAMES],
676
+ ["rawText", RAW_TEXT_ELEMENTS],
677
+ ["type6", TYPE6_NAMES],
678
+ ["void", VOID_TAGS],
679
+ ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
680
+ // Added with F28, and it is the list that would have made F28 visible: the
681
+ // derived census alphabet partitions names by their membership across THIS
682
+ // table, so before it existed `frame` shared one 39-name class with `div`
683
+ // and could never be sampled apart from it.
684
+ ["noElement", NO_ELEMENT_NAMES],
685
+ ["tablePart", TABLE_PART_NAMES],
686
+ ["scopeBarrier", SCOPE_BARRIER_NAMES],
687
+ ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
688
+ ];
689
+
690
+ // src/components/incrementalParse/freezeLineTransition.ts
730
691
  function floatingResidue(text, commentOpenAtStart) {
731
692
  let out = "";
732
693
  let open = commentOpenAtStart;
@@ -1376,24 +1337,66 @@ function processConfirmedLine(cp, ln, text) {
1376
1337
  }
1377
1338
  }
1378
1339
  }
1379
- var SCANNER_NAME_LISTS = [
1380
- ["type1", TYPE1_NAMES],
1381
- ["rawText", RAW_TEXT_ELEMENTS],
1382
- ["type6", TYPE6_NAMES],
1383
- ["void", VOID_TAGS],
1384
- ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1385
- // Added with F28, and it is the list that would have made F28 visible: the
1386
- // derived census alphabet partitions names by their membership across THIS
1387
- // table, so before it existed `frame` shared one 39-name class with `div`
1388
- // and could never be sampled apart from it.
1389
- ["noElement", NO_ELEMENT_NAMES],
1390
- ["tablePart", TABLE_PART_NAMES],
1391
- ["scopeBarrier", SCOPE_BARRIER_NAMES],
1392
- ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
1393
- ];
1394
1340
 
1395
- // src/components/incrementalParse/spliceParse.ts
1396
- import { normalizeIdentifier as normalizeIdentifier2 } from "micromark-util-normalize-identifier";
1341
+ // src/components/incrementalParse/computeFreezeBoundary.ts
1342
+ function computeFreezeBoundary(text, options, resume) {
1343
+ const mathFlow = options.mathFlow ?? true;
1344
+ const referenceTaint = options.referenceTaint ?? true;
1345
+ const prev = resume;
1346
+ const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
1347
+ let start = cp.confirmedOffset;
1348
+ let tailLine = null;
1349
+ while (start < text.length) {
1350
+ let end = text.length;
1351
+ let textEnd = text.length;
1352
+ {
1353
+ const nl = text.indexOf("\n", start);
1354
+ const cr = text.indexOf("\r", start);
1355
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
1356
+ textEnd = cr;
1357
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
1358
+ } else if (nl !== -1) {
1359
+ textEnd = nl;
1360
+ end = nl;
1361
+ }
1362
+ }
1363
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
1364
+ const lineText = text.slice(start, textEnd);
1365
+ const ln = {
1366
+ start,
1367
+ end,
1368
+ text: lineText,
1369
+ blank: confirmed && isMdBlank(lineText),
1370
+ indent: computeIndent(lineText)
1371
+ };
1372
+ if (!confirmed) {
1373
+ tailLine = ln;
1374
+ break;
1375
+ }
1376
+ processConfirmedLine(cp, ln, text);
1377
+ cp.confirmedOffset = end + 1;
1378
+ start = end + 1;
1379
+ }
1380
+ const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
1381
+ const defListSettled = (c) => {
1382
+ if (!options.defListEnabled || c.blankRun >= 2) return true;
1383
+ if (c.defListSettled !== null) return c.defListSettled;
1384
+ if (tailLine) return !canBecomeDdLine(tailLine.text, false);
1385
+ return false;
1386
+ };
1387
+ const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
1388
+ let boundary = 0;
1389
+ for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
1390
+ const c = cp.candidates[i];
1391
+ if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
1392
+ if (c.offset > cp.phasePoisonedAt) continue;
1393
+ if (c.offset > earliestUnresolved) continue;
1394
+ if (!defListSettled(c)) continue;
1395
+ boundary = c.offset;
1396
+ break;
1397
+ }
1398
+ return { boundary, checkpoint: cp };
1399
+ }
1397
1400
 
1398
1401
  // src/components/hastPredicates.ts
1399
1402
  function isFootnoteSection(node) {
@@ -1441,121 +1444,7 @@ function attributeHastChildren(mdast, hast, stopAt = Infinity) {
1441
1444
  }
1442
1445
  }
1443
1446
 
1444
- // src/components/incrementalParse/spliceParse.ts
1445
- function collectPrefixInjection(mdast, content, boundary, resume) {
1446
- const resumeValid = resume != null && resume.boundary <= boundary;
1447
- if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1448
- const resumeAt = resumeValid ? resume.boundary : 0;
1449
- const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1450
- let uninjectable = false;
1451
- let cacheable = true;
1452
- const pushRef = (token) => {
1453
- const last = events[events.length - 1];
1454
- if (last && last.kind === "refs") last.tokens.push(token);
1455
- else events.push({ kind: "refs", tokens: [token] });
1456
- };
1457
- const visit7 = (node, nested) => {
1458
- const type = node.type;
1459
- if (type === "definition") {
1460
- const start = node.position?.start?.offset;
1461
- const end = node.position?.end?.offset;
1462
- if (start !== void 0 && end !== void 0 && start < boundary) {
1463
- const slice = content.slice(start, end);
1464
- if (nested && slice.includes("\n")) uninjectable = true;
1465
- else events.push({ kind: "def", source: slice });
1466
- }
1467
- return;
1468
- }
1469
- if (type === "footnoteDefinition") {
1470
- const start = node.position?.start;
1471
- const end = node.position?.end?.offset;
1472
- if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1473
- if (nested) {
1474
- uninjectable = true;
1475
- return;
1476
- }
1477
- const lineStart = start.offset - (start.column - 1);
1478
- events.push({
1479
- kind: "footnoteDef",
1480
- source: content.slice(lineStart, end),
1481
- origStart: lineStart,
1482
- origLine: start.line
1483
- });
1484
- return;
1485
- }
1486
- if (type === "footnoteReference") {
1487
- const start = node.position?.start?.offset;
1488
- const end = node.position?.end?.offset;
1489
- if (start === void 0 || end === void 0) {
1490
- uninjectable = true;
1491
- return;
1492
- }
1493
- pushRef(content.slice(start, end));
1494
- return;
1495
- }
1496
- const children = node.children;
1497
- if (children) {
1498
- for (const child of children) visit7(child, true);
1499
- }
1500
- };
1501
- let lastStart = -1;
1502
- for (const child of mdast.children) {
1503
- const start = child.position?.start?.offset;
1504
- if (start !== void 0) {
1505
- if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1506
- lastStart = start;
1507
- }
1508
- if (start === void 0) {
1509
- if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1510
- cacheable = false;
1511
- visit7(child, false);
1512
- continue;
1513
- }
1514
- if (start >= boundary || start < resumeAt) continue;
1515
- visit7(child, false);
1516
- }
1517
- return { events, uninjectable, cacheable };
1518
- }
1519
- function cloneEventsForAppend(events) {
1520
- const out = events.slice();
1521
- const last = out[out.length - 1];
1522
- if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1523
- return out;
1524
- }
1525
- var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1526
- var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1527
- function tailMentionsTerminator(tailSource) {
1528
- return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
1529
- }
1530
- function buildInjectionPrefix(events) {
1531
- if (events.length === 0) return { text: "", segments: [] };
1532
- let text = "";
1533
- let line = 1;
1534
- const segments = [];
1535
- for (const event of events) {
1536
- if (text.length > 0) {
1537
- text += "\n\n";
1538
- line += 2;
1539
- }
1540
- const injStart = text.length;
1541
- const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1542
- if (event.kind === "footnoteDef") {
1543
- segments.push({
1544
- injStart,
1545
- injEnd: injStart + source.length,
1546
- offsetDelta: event.origStart - injStart,
1547
- lineDelta: event.origLine - line
1548
- });
1549
- }
1550
- text += source;
1551
- line += countNewlines(source);
1552
- }
1553
- return { text: `${text}
1554
-
1555
- ${INJECTION_TERMINATOR}
1556
-
1557
- `, segments };
1558
- }
1447
+ // src/components/incrementalParse/spliceCoordinates.ts
1559
1448
  function rebaseTree(node, offsetDelta, lineDelta) {
1560
1449
  rebaseTreeDual(node, EMPTY_SEGMENTS, offsetDelta, lineDelta);
1561
1450
  }
@@ -1584,6 +1473,23 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1584
1473
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1585
1474
  }
1586
1475
  }
1476
+ function rebasePoint(point, offsetDelta, lineDelta) {
1477
+ return {
1478
+ line: point.line + lineDelta,
1479
+ column: point.column,
1480
+ offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1481
+ };
1482
+ }
1483
+ function countNewlines(text, end = text.length) {
1484
+ let count = 0;
1485
+ for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1486
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1487
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1488
+ }
1489
+ return count;
1490
+ }
1491
+
1492
+ // src/components/incrementalParse/spliceHtmlGuards.ts
1587
1493
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1588
1494
  var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1589
1495
  function hasStrayTablePart(values) {
@@ -1665,127 +1571,26 @@ function rawTextRegionCrossesOut(values) {
1665
1571
  }
1666
1572
  return false;
1667
1573
  }
1668
- function spliceTrees(input) {
1669
- const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1670
- const injectedLen = injectionPrefix.length;
1671
- const injectedLines = countNewlines(injectionPrefix);
1672
- const prefixLines = countNewlines(content, boundary);
1673
- const offsetDelta = boundary - injectedLen;
1674
- const lineDelta = prefixLines - injectedLines;
1675
- const prefixMdast = [];
1676
- for (const child of prevMdast.children) {
1677
- const start = child.position?.start?.offset;
1678
- if (start !== void 0 && start < boundary) prefixMdast.push(child);
1679
- }
1680
- const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1681
- const cutRegion = [];
1682
- for (let i = 0; i < prevHast.children.length; i++) {
1683
- if (attrs[i] < boundary) {
1684
- const node2 = prevHast.children[i];
1685
- const end = node2.position?.end?.offset;
1686
- if (end !== void 0 && end > boundary) return null;
1687
- if (isTrailingLiteralText(node2)) {
1688
- const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1689
- if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1690
- if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1691
- return null;
1692
- }
1693
- cutRegion.push(node2);
1694
- continue;
1695
- }
1696
- const node = prevHast.children[i];
1697
- 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
1698
- // raw literal can only trail an `html` mdast node. A position-less
1699
- // text after a `<p>` is the NEXT block's remnant — a stray end tag
1700
- // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1701
- // separator — owned by the tail, which re-parses it; freezing it
1702
- // here duplicated it (v2.4.0 review P3). Falls through to the
1703
- // remnant look-ahead below, which bails to a full parse. And the
1704
- // literal must really be THAT block's trailing text: the block's raw
1705
- // source ends with it. A dropped-tag block right after a frozen html
1706
- // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1707
- // position, and freezing it duplicated it (release soak of the fix).
1708
- ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1709
- cutRegion.push(node);
1710
- break;
1711
- }
1712
- for (let j = i; j < prevHast.children.length; j++) {
1713
- const look = prevHast.children[j];
1714
- if (look.type === "element" || look.position !== void 0) break;
1715
- if (isTrailingLiteralText(look)) return null;
1716
- }
1717
- break;
1718
- }
1719
- const tailMdastChildren = tailMdast.children.filter((child) => {
1720
- const start = child.position?.start?.offset;
1721
- return !(start !== void 0 && start < injectedLen);
1722
- });
1723
- const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1724
- const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1725
- if (hasStrayTablePart(prefixHtmlValues)) return null;
1726
- if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1727
- const leadingHtml = [];
1728
- for (const child of tailMdastChildren) {
1729
- if (isWrapInvisible(child)) continue;
1730
- if (child.type !== "html") break;
1731
- if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1732
- leadingHtml.push(child.value);
1574
+ function isSanitizeStrippedConstruct(value) {
1575
+ const v = value.trim();
1576
+ 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(">");
1577
+ }
1578
+ function isExactSanitizeStrippedConstruct(value) {
1579
+ if (value.startsWith("<!--")) {
1580
+ if (value.length < 7 || !value.endsWith("-->")) return false;
1581
+ const close = value.indexOf("-->", 4);
1582
+ const bangClose = value.indexOf("--!>", 4);
1583
+ if (close !== value.length - 3) return false;
1584
+ return bangClose === -1 || bangClose > close;
1733
1585
  }
1734
- if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1735
- const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1736
- if (aligned === null) return null;
1737
- const hastChildren = aligned.children;
1738
- const interiorFinalLiteral = aligned.interiorFinalLiteral;
1739
- const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1740
- if (stripResult === null) return null;
1741
- const strippedTailHast = stripResult.rest;
1742
- for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1743
- for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1744
- const mdastChildren = prefixMdast.concat(tailMdastChildren);
1745
- const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1746
- if (seamMergeVerdict === null) return null;
1747
- const seamMergeAllowed = seamMergeVerdict;
1748
- let firstTailChild = true;
1749
- for (const child of strippedTailHast) {
1750
- const tailEnd = hastChildren[hastChildren.length - 1];
1751
- if (firstTailChild && child.type === "text" && child.position !== void 0) {
1752
- return null;
1753
- }
1754
- if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1755
- if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
1756
- if (seamMergeAllowed) return null;
1757
- firstTailChild = false;
1758
- hastChildren.push(child);
1759
- continue;
1760
- }
1761
- const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
1762
- if (seamMergeAllowed || literalSeam) {
1763
- hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
1764
- firstTailChild = false;
1765
- continue;
1766
- }
1767
- }
1768
- firstTailChild = false;
1769
- hastChildren.push(child);
1586
+ if (/^<!doctype/i.test(value)) return false;
1587
+ if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1588
+ return value.indexOf(">") === value.length - 1;
1770
1589
  }
1771
- if (!hastChildren.some((child) => child.position !== void 0)) return null;
1772
- const docStart = { line: 1, column: 1, offset: 0 };
1773
- const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
1774
- const hastRootPosition = mdastRootPosition;
1775
- const mdast = { type: "root", children: mdastChildren };
1776
- if (mdastRootPosition) mdast.position = mdastRootPosition;
1777
- const hast = { type: "root", children: hastChildren };
1778
- if (hastRootPosition) hast.position = hastRootPosition;
1779
- if (prevHast.data !== void 0) hast.data = prevHast.data;
1780
- return { mdast, hast };
1781
- }
1782
- function rebasePoint(point, offsetDelta, lineDelta) {
1783
- return {
1784
- line: point.line + lineDelta,
1785
- column: point.column,
1786
- offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1787
- };
1590
+ return false;
1788
1591
  }
1592
+
1593
+ // src/components/incrementalParse/prefixAlignment.ts
1789
1594
  function isWrapInvisible(node) {
1790
1595
  return node.type === "definition" || node.type === "footnoteDefinition";
1791
1596
  }
@@ -1927,96 +1732,303 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
1927
1732
  idx += 1;
1928
1733
  continue;
1929
1734
  }
1930
- const start = node.position?.start?.offset;
1931
- if (start === void 0 || start >= injectedLen) return null;
1932
- consumed += 1;
1933
- idx += 1;
1934
- }
1935
- if (consumed < injectedVisibleCount) return null;
1936
- let remnant = "";
1937
- let remnantMerged = null;
1938
- if (tailWrapVisible) {
1939
- const gap = children[idx];
1940
- if (!gap || !isSeparatorText(gap)) return null;
1941
- remnant = gap.value.slice(1);
1942
- remnantMerged = remnant !== "";
1943
- idx += 1;
1735
+ const start = node.position?.start?.offset;
1736
+ if (start === void 0 || start >= injectedLen) return null;
1737
+ consumed += 1;
1738
+ idx += 1;
1739
+ }
1740
+ if (consumed < injectedVisibleCount) return null;
1741
+ let remnant = "";
1742
+ let remnantMerged = null;
1743
+ if (tailWrapVisible) {
1744
+ const gap = children[idx];
1745
+ if (!gap || !isSeparatorText(gap)) return null;
1746
+ remnant = gap.value.slice(1);
1747
+ remnantMerged = remnant !== "";
1748
+ idx += 1;
1749
+ }
1750
+ const rest = children.slice(idx);
1751
+ if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1752
+ return { rest, remnantMerged };
1753
+ }
1754
+ function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1755
+ const firstText = tailHastChildren[0];
1756
+ const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1757
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1758
+ if (!firstText) return false;
1759
+ if (!isSeparatorText(firstText)) {
1760
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1761
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1762
+ if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1763
+ return null;
1764
+ }
1765
+ return false;
1766
+ }
1767
+ if (!firstVisible) return false;
1768
+ const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1769
+ if (firstContent) {
1770
+ const start = firstContent.position?.start?.offset;
1771
+ const vStart = firstVisible.position?.start?.offset;
1772
+ const vEnd = firstVisible.position?.end?.offset;
1773
+ if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1774
+ if (start >= vStart && start < vEnd) return true;
1775
+ }
1776
+ }
1777
+ if (firstVisible.type === "math") return false;
1778
+ if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1779
+ return null;
1780
+ }
1781
+ function isSeparatorText(node) {
1782
+ return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1783
+ }
1784
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1785
+ const start = el.position?.start?.offset;
1786
+ if (start === void 0) return false;
1787
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1788
+ if (!owner || owner.type !== "html") return false;
1789
+ const text = literal.value.trim();
1790
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1791
+ }
1792
+ function isTrailingLiteralText(node) {
1793
+ return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1794
+ }
1795
+ function countTrailingNewlines(value) {
1796
+ let n = 0;
1797
+ for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
1798
+ return n;
1799
+ }
1800
+
1801
+ // src/components/incrementalParse/prefixInjection.ts
1802
+ import { normalizeIdentifier as normalizeIdentifier2 } from "micromark-util-normalize-identifier";
1803
+ function collectPrefixInjection(mdast, content, boundary, resume) {
1804
+ const resumeValid = resume != null && resume.boundary <= boundary;
1805
+ if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1806
+ const resumeAt = resumeValid ? resume.boundary : 0;
1807
+ const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1808
+ let uninjectable = false;
1809
+ let cacheable = true;
1810
+ const pushRef = (token) => {
1811
+ const last = events[events.length - 1];
1812
+ if (last && last.kind === "refs") last.tokens.push(token);
1813
+ else events.push({ kind: "refs", tokens: [token] });
1814
+ };
1815
+ const visit7 = (node, nested) => {
1816
+ const type = node.type;
1817
+ if (type === "definition") {
1818
+ const start = node.position?.start?.offset;
1819
+ const end = node.position?.end?.offset;
1820
+ if (start !== void 0 && end !== void 0 && start < boundary) {
1821
+ const slice = content.slice(start, end);
1822
+ if (nested && slice.includes("\n")) uninjectable = true;
1823
+ else events.push({ kind: "def", source: slice });
1824
+ }
1825
+ return;
1826
+ }
1827
+ if (type === "footnoteDefinition") {
1828
+ const start = node.position?.start;
1829
+ const end = node.position?.end?.offset;
1830
+ if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1831
+ if (nested) {
1832
+ uninjectable = true;
1833
+ return;
1834
+ }
1835
+ const lineStart = start.offset - (start.column - 1);
1836
+ events.push({
1837
+ kind: "footnoteDef",
1838
+ source: content.slice(lineStart, end),
1839
+ origStart: lineStart,
1840
+ origLine: start.line
1841
+ });
1842
+ return;
1843
+ }
1844
+ if (type === "footnoteReference") {
1845
+ const start = node.position?.start?.offset;
1846
+ const end = node.position?.end?.offset;
1847
+ if (start === void 0 || end === void 0) {
1848
+ uninjectable = true;
1849
+ return;
1850
+ }
1851
+ pushRef(content.slice(start, end));
1852
+ return;
1853
+ }
1854
+ const children = node.children;
1855
+ if (children) {
1856
+ for (const child of children) visit7(child, true);
1857
+ }
1858
+ };
1859
+ let lastStart = -1;
1860
+ for (const child of mdast.children) {
1861
+ const start = child.position?.start?.offset;
1862
+ if (start !== void 0) {
1863
+ if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1864
+ lastStart = start;
1865
+ }
1866
+ if (start === void 0) {
1867
+ if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1868
+ cacheable = false;
1869
+ visit7(child, false);
1870
+ continue;
1871
+ }
1872
+ if (start >= boundary || start < resumeAt) continue;
1873
+ visit7(child, false);
1944
1874
  }
1945
- const rest = children.slice(idx);
1946
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1947
- return { rest, remnantMerged };
1875
+ return { events, uninjectable, cacheable };
1948
1876
  }
1949
- function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1950
- const firstText = tailHastChildren[0];
1951
- const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1952
- if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1953
- if (!firstText) return false;
1954
- if (!isSeparatorText(firstText)) {
1955
- if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1956
- if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1957
- if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1958
- return null;
1877
+ function cloneEventsForAppend(events) {
1878
+ const out = events.slice();
1879
+ const last = out[out.length - 1];
1880
+ if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1881
+ return out;
1882
+ }
1883
+ var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1884
+ var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1885
+ function tailMentionsTerminator(tailSource) {
1886
+ return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
1887
+ }
1888
+ function buildInjectionPrefix(events) {
1889
+ if (events.length === 0) return { text: "", segments: [] };
1890
+ let text = "";
1891
+ let line = 1;
1892
+ const segments = [];
1893
+ for (const event of events) {
1894
+ if (text.length > 0) {
1895
+ text += "\n\n";
1896
+ line += 2;
1959
1897
  }
1960
- return false;
1961
- }
1962
- if (!firstVisible) return false;
1963
- const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1964
- if (firstContent) {
1965
- const start = firstContent.position?.start?.offset;
1966
- const vStart = firstVisible.position?.start?.offset;
1967
- const vEnd = firstVisible.position?.end?.offset;
1968
- if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1969
- if (start >= vStart && start < vEnd) return true;
1898
+ const injStart = text.length;
1899
+ const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1900
+ if (event.kind === "footnoteDef") {
1901
+ segments.push({
1902
+ injStart,
1903
+ injEnd: injStart + source.length,
1904
+ offsetDelta: event.origStart - injStart,
1905
+ lineDelta: event.origLine - line
1906
+ });
1970
1907
  }
1908
+ text += source;
1909
+ line += countNewlines(source);
1971
1910
  }
1972
- if (firstVisible.type === "math") return false;
1973
- if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1974
- return null;
1975
- }
1976
- function isSanitizeStrippedConstruct(value) {
1977
- const v = value.trim();
1978
- 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(">");
1911
+ return { text: `${text}
1912
+
1913
+ ${INJECTION_TERMINATOR}
1914
+
1915
+ `, segments };
1979
1916
  }
1980
- function isExactSanitizeStrippedConstruct(value) {
1981
- if (value.startsWith("<!--")) {
1982
- if (value.length < 7 || !value.endsWith("-->")) return false;
1983
- const close = value.indexOf("-->", 4);
1984
- const bangClose = value.indexOf("--!>", 4);
1985
- if (close !== value.length - 3) return false;
1986
- return bangClose === -1 || bangClose > close;
1917
+
1918
+ // src/components/incrementalParse/spliceParse.ts
1919
+ function spliceTrees(input) {
1920
+ const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1921
+ const injectedLen = injectionPrefix.length;
1922
+ const injectedLines = countNewlines(injectionPrefix);
1923
+ const prefixLines = countNewlines(content, boundary);
1924
+ const offsetDelta = boundary - injectedLen;
1925
+ const lineDelta = prefixLines - injectedLines;
1926
+ const prefixMdast = [];
1927
+ for (const child of prevMdast.children) {
1928
+ const start = child.position?.start?.offset;
1929
+ if (start !== void 0 && start < boundary) prefixMdast.push(child);
1987
1930
  }
1988
- if (/^<!doctype/i.test(value)) return false;
1989
- if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1990
- return value.indexOf(">") === value.length - 1;
1931
+ const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1932
+ const cutRegion = [];
1933
+ for (let i = 0; i < prevHast.children.length; i++) {
1934
+ if (attrs[i] < boundary) {
1935
+ const node2 = prevHast.children[i];
1936
+ const end = node2.position?.end?.offset;
1937
+ if (end !== void 0 && end > boundary) return null;
1938
+ if (isTrailingLiteralText(node2)) {
1939
+ const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1940
+ if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1941
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1942
+ return null;
1943
+ }
1944
+ cutRegion.push(node2);
1945
+ continue;
1946
+ }
1947
+ const node = prevHast.children[i];
1948
+ 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
1949
+ // raw literal can only trail an `html` mdast node. A position-less
1950
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
1951
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1952
+ // separator — owned by the tail, which re-parses it; freezing it
1953
+ // here duplicated it (v2.4.0 review P3). Falls through to the
1954
+ // remnant look-ahead below, which bails to a full parse. And the
1955
+ // literal must really be THAT block's trailing text: the block's raw
1956
+ // source ends with it. A dropped-tag block right after a frozen html
1957
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1958
+ // position, and freezing it duplicated it (release soak of the fix).
1959
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1960
+ cutRegion.push(node);
1961
+ break;
1962
+ }
1963
+ for (let j = i; j < prevHast.children.length; j++) {
1964
+ const look = prevHast.children[j];
1965
+ if (look.type === "element" || look.position !== void 0) break;
1966
+ if (isTrailingLiteralText(look)) return null;
1967
+ }
1968
+ break;
1991
1969
  }
1992
- return false;
1993
- }
1994
- function isSeparatorText(node) {
1995
- return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1996
- }
1997
- function ownsTrailingLiteral(el, literal, prefixMdast) {
1998
- const start = el.position?.start?.offset;
1999
- if (start === void 0) return false;
2000
- const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
2001
- if (!owner || owner.type !== "html") return false;
2002
- const text = literal.value.trim();
2003
- return text !== "" && owner.value.trimEnd().endsWith(text);
2004
- }
2005
- function isTrailingLiteralText(node) {
2006
- return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
2007
- }
2008
- function countTrailingNewlines(value) {
2009
- let n = 0;
2010
- for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
2011
- return n;
2012
- }
2013
- function countNewlines(text, end = text.length) {
2014
- let count = 0;
2015
- for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
2016
- for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
2017
- if (text.charCodeAt(i + 1) !== 10) count += 1;
1970
+ const tailMdastChildren = tailMdast.children.filter((child) => {
1971
+ const start = child.position?.start?.offset;
1972
+ return !(start !== void 0 && start < injectedLen);
1973
+ });
1974
+ const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1975
+ const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1976
+ if (hasStrayTablePart(prefixHtmlValues)) return null;
1977
+ if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1978
+ const leadingHtml = [];
1979
+ for (const child of tailMdastChildren) {
1980
+ if (isWrapInvisible(child)) continue;
1981
+ if (child.type !== "html") break;
1982
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1983
+ leadingHtml.push(child.value);
2018
1984
  }
2019
- return count;
1985
+ if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1986
+ const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1987
+ if (aligned === null) return null;
1988
+ const hastChildren = aligned.children;
1989
+ const interiorFinalLiteral = aligned.interiorFinalLiteral;
1990
+ const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1991
+ if (stripResult === null) return null;
1992
+ const strippedTailHast = stripResult.rest;
1993
+ for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1994
+ for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1995
+ const mdastChildren = prefixMdast.concat(tailMdastChildren);
1996
+ const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1997
+ if (seamMergeVerdict === null) return null;
1998
+ const seamMergeAllowed = seamMergeVerdict;
1999
+ let firstTailChild = true;
2000
+ for (const child of strippedTailHast) {
2001
+ const tailEnd = hastChildren[hastChildren.length - 1];
2002
+ if (firstTailChild && child.type === "text" && child.position !== void 0) {
2003
+ return null;
2004
+ }
2005
+ if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
2006
+ if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
2007
+ if (seamMergeAllowed) return null;
2008
+ firstTailChild = false;
2009
+ hastChildren.push(child);
2010
+ continue;
2011
+ }
2012
+ const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
2013
+ if (seamMergeAllowed || literalSeam) {
2014
+ hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
2015
+ firstTailChild = false;
2016
+ continue;
2017
+ }
2018
+ }
2019
+ firstTailChild = false;
2020
+ hastChildren.push(child);
2021
+ }
2022
+ if (!hastChildren.some((child) => child.position !== void 0)) return null;
2023
+ const docStart = { line: 1, column: 1, offset: 0 };
2024
+ const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
2025
+ const hastRootPosition = mdastRootPosition;
2026
+ const mdast = { type: "root", children: mdastChildren };
2027
+ if (mdastRootPosition) mdast.position = mdastRootPosition;
2028
+ const hast = { type: "root", children: hastChildren };
2029
+ if (hastRootPosition) hast.position = hastRootPosition;
2030
+ if (prevHast.data !== void 0) hast.data = prevHast.data;
2031
+ return { mdast, hast };
2020
2032
  }
2021
2033
 
2022
2034
  // src/components/incrementalParse/advanceIncrementalParse.ts
@@ -2468,7 +2480,8 @@ function buildRegistryIndex(registry) {
2468
2480
  links: /* @__PURE__ */ new Map(),
2469
2481
  numbers: /* @__PURE__ */ new Map(),
2470
2482
  counts: /* @__PURE__ */ new Map(),
2471
- occurrences: /* @__PURE__ */ new Map()
2483
+ occurrences: /* @__PURE__ */ new Map(),
2484
+ labelOccurrences: /* @__PURE__ */ new Map()
2472
2485
  };
2473
2486
  for (const sym of registry.chunkOrder) {
2474
2487
  const data = registry.chunkData.get(sym);
@@ -2485,12 +2498,78 @@ function buildRegistryIndex(registry) {
2485
2498
  index.counts.set(label, total);
2486
2499
  const prior = local.get(label);
2487
2500
  if (prior) prior.count++;
2488
- else local.set(label, { start: total, count: 1 });
2501
+ else {
2502
+ const range = { start: total, count: 1 };
2503
+ local.set(label, range);
2504
+ let byChunk = index.labelOccurrences.get(label);
2505
+ if (!byChunk) index.labelOccurrences.set(label, byChunk = /* @__PURE__ */ new Map());
2506
+ byChunk.set(sym, range);
2507
+ }
2489
2508
  }
2490
2509
  }
2491
2510
  return index;
2492
2511
  }
2493
2512
 
2513
+ // src/components/registrySubscriptions.ts
2514
+ function equal(a, b) {
2515
+ if (a.owner !== b.owner || a.url !== b.url || a.title !== b.title || a.number !== b.number || a.count !== b.count)
2516
+ return false;
2517
+ if (a.occurrences?.size !== b.occurrences?.size) return false;
2518
+ if (a.occurrences) {
2519
+ for (const [chunk, range] of a.occurrences) {
2520
+ const next = b.occurrences?.get(chunk);
2521
+ if (!next || next.start !== range.start || next.count !== range.count) return false;
2522
+ }
2523
+ }
2524
+ return true;
2525
+ }
2526
+ function createRegistrySubscriptions(read) {
2527
+ const groups = { link: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() };
2528
+ const snapshot = (kind, label, { registry, index }) => {
2529
+ if (kind === "link") {
2530
+ const owner = index.links.get(label);
2531
+ const def = owner ? registry.chunkData.get(owner)?.linkDefs.get(label) : void 0;
2532
+ return { owner, url: def?.url, title: def?.title };
2533
+ }
2534
+ return {
2535
+ owner: index.footnotes.get(label),
2536
+ number: index.numbers.get(label),
2537
+ count: index.counts.get(label),
2538
+ occurrences: index.labelOccurrences.get(label)
2539
+ };
2540
+ };
2541
+ return {
2542
+ subscribe(kind, rawLabel, cb) {
2543
+ const label = normalizeId(rawLabel);
2544
+ const map = groups[kind];
2545
+ let group = map.get(label);
2546
+ if (!group) {
2547
+ group = { snapshot: snapshot(kind, label, read()), listeners: /* @__PURE__ */ new Set() };
2548
+ map.set(label, group);
2549
+ }
2550
+ const listener = () => cb();
2551
+ group.listeners.add(listener);
2552
+ return () => {
2553
+ group.listeners.delete(listener);
2554
+ if (group.listeners.size === 0 && map.get(label) === group) map.delete(label);
2555
+ };
2556
+ },
2557
+ collect() {
2558
+ if (groups.link.size === 0 && groups.footnote.size === 0) return [];
2559
+ const state = read();
2560
+ const callbacks = [];
2561
+ for (const kind of ["link", "footnote"]) {
2562
+ for (const [label, group] of groups[kind]) {
2563
+ const next = snapshot(kind, label, state);
2564
+ if (!equal(group.snapshot, next)) callbacks.push(...group.listeners);
2565
+ group.snapshot = next;
2566
+ }
2567
+ }
2568
+ return callbacks;
2569
+ }
2570
+ };
2571
+ }
2572
+
2494
2573
  // src/components/documentRegistry.ts
2495
2574
  function createRegistry(onEmpty) {
2496
2575
  let index;
@@ -2502,6 +2581,7 @@ function createRegistry(onEmpty) {
2502
2581
  }
2503
2582
  return index;
2504
2583
  };
2584
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2505
2585
  const reg = {
2506
2586
  chunkOrder: [],
2507
2587
  chunkData: /* @__PURE__ */ new Map(),
@@ -2652,6 +2732,9 @@ function createRegistry(onEmpty) {
2652
2732
  this._subscribers.delete(cb);
2653
2733
  };
2654
2734
  },
2735
+ subscribeLabel(kind, label, cb) {
2736
+ return labels.subscribe(kind, label, cb);
2737
+ },
2655
2738
  canonicalFootnoteFor(label) {
2656
2739
  return getIndex().footnotes.get(normalizeId(label)) ?? null;
2657
2740
  },
@@ -2679,7 +2762,9 @@ function createRegistry(onEmpty) {
2679
2762
  this._notifyScheduled = true;
2680
2763
  queueMicrotask(() => {
2681
2764
  this._notifyScheduled = false;
2765
+ const labelCallbacks = labels.collect();
2682
2766
  for (const cb of [...this._subscribers]) cb();
2767
+ for (const cb of labelCallbacks) cb();
2683
2768
  });
2684
2769
  }
2685
2770
  };