@stream-mdx/core 0.1.0 → 0.2.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
@@ -67,12 +67,14 @@ __export(index_exports, {
67
67
  findClosingHtmlTag: () => findClosingHtmlTag,
68
68
  generateBlockId: () => generateBlockId,
69
69
  getBlockKey: () => getBlockKey,
70
+ getDefaultCodeWrapperAttributes: () => getDefaultCodeWrapperAttributes,
70
71
  initializeSecurity: () => initializeSecurity,
71
72
  initializeTrustedTypesPolicy: () => initializeTrustedTypesPolicy,
72
73
  inlineNodesToPlainText: () => inlineNodesToPlainText,
73
74
  isLikelyMdxComponent: () => isLikelyMdxComponent,
74
75
  normalizeBlockquoteText: () => normalizeBlockquoteText,
75
76
  normalizeFormatAnticipation: () => normalizeFormatAnticipation,
77
+ normalizeHighlightedLines: () => normalizeHighlightedLines,
76
78
  normalizeLang: () => normalizeLang,
77
79
  parseCodeFenceInfo: () => parseCodeFenceInfo,
78
80
  prepareInlineStreamingContent: () => prepareInlineStreamingContent,
@@ -230,6 +232,22 @@ function manualExtractHighlightedLines(html, fallbackLength) {
230
232
  }
231
233
  return normalizeHighlightedLines(lines, fallbackLength);
232
234
  }
235
+ function getDefaultCodeWrapperAttributes(lang, themes = { dark: "github-dark", light: "github-light" }) {
236
+ const language = lang && typeof lang === "string" && lang.length > 0 ? lang : "text";
237
+ const themeLabel = `${themes.dark} ${themes.light}`;
238
+ return {
239
+ preAttrs: {
240
+ class: `shiki shiki-themes ${themeLabel}`,
241
+ "data-language": language,
242
+ style: "--shiki-dark-bg: transparent; --shiki-light-bg: transparent"
243
+ },
244
+ codeAttrs: {
245
+ "data-language": language,
246
+ "data-theme": themeLabel,
247
+ style: "display: grid;"
248
+ }
249
+ };
250
+ }
233
251
  function dedentIndentedCode(raw) {
234
252
  if (!raw) return "";
235
253
  const normalized = normalizeNewlines(raw);
@@ -636,6 +654,130 @@ function splitTextByRegexWithPrecedence(text, regex, toNode) {
636
654
  return result;
637
655
  }
638
656
 
657
+ // src/streaming/inline-streaming.ts
658
+ var DEFAULT_FORMAT_ANTICIPATION = {
659
+ inline: false,
660
+ mathInline: false,
661
+ mathBlock: false,
662
+ html: false,
663
+ mdx: false,
664
+ regex: false
665
+ };
666
+ function normalizeFormatAnticipation(input) {
667
+ if (input === true) {
668
+ return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
669
+ }
670
+ if (!input) {
671
+ return { ...DEFAULT_FORMAT_ANTICIPATION };
672
+ }
673
+ return {
674
+ inline: input.inline ?? false,
675
+ mathInline: input.mathInline ?? false,
676
+ mathBlock: input.mathBlock ?? false,
677
+ html: input.html ?? false,
678
+ mdx: input.mdx ?? false,
679
+ regex: input.regex ?? false
680
+ };
681
+ }
682
+ function prepareInlineStreamingContent(content, options) {
683
+ const enableMath = options?.math !== false;
684
+ const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
685
+ const enableInlineAnticipation = anticipation.inline;
686
+ const enableMathInlineAnticipation = anticipation.mathInline;
687
+ const enableMathBlockAnticipation = anticipation.mathBlock;
688
+ const stack = [];
689
+ const toggleToken = (token) => {
690
+ const last = stack[stack.length - 1];
691
+ if (last === token) {
692
+ stack.pop();
693
+ } else {
694
+ stack.push(token);
695
+ }
696
+ };
697
+ let mathDisplayOpen = false;
698
+ let mathDisplayCrossedNewline = false;
699
+ for (let i = 0; i < content.length; i++) {
700
+ const code = content.charCodeAt(i);
701
+ if (code === 10 || code === 13) {
702
+ if (mathDisplayOpen) {
703
+ mathDisplayCrossedNewline = true;
704
+ }
705
+ continue;
706
+ }
707
+ if (code === 96) {
708
+ toggleToken("code");
709
+ continue;
710
+ }
711
+ if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
712
+ toggleToken("strike");
713
+ i += 1;
714
+ continue;
715
+ }
716
+ if (code === 42) {
717
+ if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
718
+ toggleToken("strong");
719
+ i += 1;
720
+ } else {
721
+ toggleToken("em");
722
+ }
723
+ continue;
724
+ }
725
+ if (enableMath && code === 36) {
726
+ if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
727
+ toggleToken("math-display");
728
+ if (mathDisplayOpen) {
729
+ mathDisplayOpen = false;
730
+ mathDisplayCrossedNewline = false;
731
+ } else {
732
+ mathDisplayOpen = true;
733
+ mathDisplayCrossedNewline = false;
734
+ }
735
+ i += 1;
736
+ } else {
737
+ toggleToken("math-inline");
738
+ }
739
+ }
740
+ }
741
+ const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
742
+ const hasIncompleteMathInline = stack.includes("math-inline");
743
+ const hasIncompleteMathDisplay = stack.includes("math-display");
744
+ const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
745
+ if (enableMath && hasIncompleteMath) {
746
+ if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
747
+ return { kind: "raw", status: "raw", reason: "incomplete-math" };
748
+ }
749
+ if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
750
+ return { kind: "raw", status: "raw", reason: "incomplete-math" };
751
+ }
752
+ }
753
+ if (hasIncompleteFormatting && !enableInlineAnticipation) {
754
+ return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
755
+ }
756
+ if (!hasIncompleteFormatting && !hasIncompleteMath) {
757
+ return { kind: "parse", status: "complete", content, appended: "" };
758
+ }
759
+ const appendForToken = (token) => {
760
+ switch (token) {
761
+ case "code":
762
+ return "`";
763
+ case "strike":
764
+ return "~~";
765
+ case "strong":
766
+ return "**";
767
+ case "em":
768
+ return "*";
769
+ case "math-inline":
770
+ return "$";
771
+ case "math-display":
772
+ return "$$";
773
+ default:
774
+ return "";
775
+ }
776
+ };
777
+ const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
778
+ return { kind: "parse", status: "anticipated", content: content + appended, appended };
779
+ }
780
+
639
781
  // src/worker-html-sanitizer.ts
640
782
  var rehypeParse = __toESM(require("rehype-parse"), 1);
641
783
  var rehypeSanitize = __toESM(require("rehype-sanitize"), 1);
@@ -817,6 +959,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
817
959
  const mdxAutoClose = options?.mdx?.autoClose === true;
818
960
  const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
819
961
  const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
962
+ const protectedRanges = options?.protectedRanges ?? [];
963
+ const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
820
964
  while (match !== null) {
821
965
  const start = match.index;
822
966
  const tagName = match[1];
@@ -831,6 +975,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
831
975
  continue;
832
976
  }
833
977
  let end = tagPattern.lastIndex;
978
+ if (protectedKinds && protectedRanges.length > 0) {
979
+ const absoluteStart = baseIsFinite ? baseOffset + start : start;
980
+ const absoluteEnd = baseIsFinite ? baseOffset + end : end;
981
+ const covered = protectedRanges.some(
982
+ (range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
983
+ );
984
+ if (covered) {
985
+ tagPattern.lastIndex = start + 1;
986
+ match = tagPattern.exec(source);
987
+ continue;
988
+ }
989
+ }
834
990
  if (!isSelfClosing && !mdxAllowed) {
835
991
  const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
836
992
  if (closingIndex === -1) {
@@ -1128,23 +1284,33 @@ function getBlockKey(block) {
1128
1284
  }
1129
1285
  function detectMDX(content, options) {
1130
1286
  const inlineCodeRanges = collectInlineCodeRanges(content);
1287
+ const protectedRanges = options?.protectedRanges ?? [];
1288
+ const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
1289
+ const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block", "html-inline", "html-block", "autolink"]) : null;
1131
1290
  const componentPattern = /<([A-Z][\w-]*)(\s|\/?>)/g;
1132
1291
  let componentMatch = componentPattern.exec(content);
1133
1292
  while (componentMatch !== null) {
1134
1293
  const start = componentMatch.index;
1135
1294
  const end = start + componentMatch[0].length;
1136
- if (!isWithinRanges(start, end, inlineCodeRanges)) {
1137
- return true;
1295
+ if (isWithinRanges(start, end, inlineCodeRanges)) {
1296
+ componentMatch = componentPattern.exec(content);
1297
+ continue;
1298
+ }
1299
+ if (protectedKinds) {
1300
+ const absoluteStart = baseOffset + start;
1301
+ const absoluteEnd = baseOffset + end;
1302
+ const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);
1303
+ if (covered) {
1304
+ componentMatch = componentPattern.exec(content);
1305
+ continue;
1306
+ }
1138
1307
  }
1139
- componentMatch = componentPattern.exec(content);
1308
+ return true;
1140
1309
  }
1141
1310
  if (/(^|\n)\s*(import|export)\s/.test(content)) {
1142
1311
  return true;
1143
1312
  }
1144
1313
  const expressionPattern = /\{[^{}]+\}/g;
1145
- const protectedRanges = options?.protectedRanges ?? [];
1146
- const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
1147
- const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block"]) : null;
1148
1314
  for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {
1149
1315
  const index = match.index;
1150
1316
  const prev = index > 0 ? content[index - 1] : "";
@@ -1392,7 +1558,12 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1392
1558
  }
1393
1559
  if (name === "Paragraph") {
1394
1560
  const paragraphRaw = raw.slice(cursor.from, cursor.to);
1395
- const paragraphData = processListItemParagraph(paragraphRaw);
1561
+ const meta = block.payload.meta ?? {};
1562
+ const paragraphData = processListItemParagraph(paragraphRaw, {
1563
+ formatAnticipation: meta.formatAnticipation,
1564
+ math: meta.mathEnabled,
1565
+ streaming: !block.isFinalized
1566
+ });
1396
1567
  const parsedInline = paragraphData.inline;
1397
1568
  if (!paragraphHandled) {
1398
1569
  inlineNodes = parsedInline;
@@ -1424,7 +1595,10 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1424
1595
  } else if (name === "BulletList" || name === "OrderedList") {
1425
1596
  const nestedId = `${id}::list:${subListIndex++}`;
1426
1597
  const nestedOrdered = name === "OrderedList";
1427
- childSnapshots.push(buildListNodeSnapshot(block, cursor.node, nestedOrdered, nestedId, baseOffset, raw));
1598
+ const nestedSnapshot = buildListNodeSnapshot(block, cursor.node, nestedOrdered, nestedId, baseOffset, raw);
1599
+ if (Array.isArray(nestedSnapshot.children) && nestedSnapshot.children.length > 0) {
1600
+ childSnapshots.push(nestedSnapshot);
1601
+ }
1428
1602
  } else if (name === "Blockquote") {
1429
1603
  const quoteId = `${id}::blockquote:${blockquoteIndex++}`;
1430
1604
  childSnapshots.push(buildBlockquoteSnapshot(block, cursor.node, quoteId, baseOffset, raw));
@@ -1460,11 +1634,31 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1460
1634
  };
1461
1635
  return itemSnapshot;
1462
1636
  }
1463
- function processListItemParagraph(raw) {
1637
+ function parseListInline(raw, options) {
1638
+ if (!options?.streaming || !options.formatAnticipation) {
1639
+ return listInlineParser.parse(raw);
1640
+ }
1641
+ const prepared = prepareInlineStreamingContent(raw, { formatAnticipation: options.formatAnticipation, math: options.math });
1642
+ if (prepared.kind === "raw") {
1643
+ return [{ kind: "text", text: raw }];
1644
+ }
1645
+ let preparedContent = prepared.content;
1646
+ let appended = prepared.appended;
1647
+ const normalized = normalizeFormatAnticipation(options.formatAnticipation);
1648
+ if (normalized.regex) {
1649
+ const regexAppend = listInlineParser.getRegexAnticipationAppend(raw);
1650
+ if (regexAppend) {
1651
+ preparedContent += regexAppend;
1652
+ appended += regexAppend;
1653
+ }
1654
+ }
1655
+ return listInlineParser.parse(preparedContent, { cache: false });
1656
+ }
1657
+ function processListItemParagraph(raw, options) {
1464
1658
  const normalized = normalizeParagraphText(raw);
1465
1659
  const { content, task } = stripTaskMarker(normalized);
1466
- const inline = listInlineParser.parse(content);
1467
- const segments = extractMixedContentSegments(content, void 0, (value) => listInlineParser.parse(value));
1660
+ const inline = parseListInline(content, options);
1661
+ const segments = extractMixedContentSegments(content, void 0, (value) => parseListInline(value, options));
1468
1662
  return {
1469
1663
  inline,
1470
1664
  segments,
@@ -1749,28 +1943,82 @@ function enrichTableSnapshot(block, snapshot) {
1749
1943
  function enrichCodeSnapshot(block, snapshot) {
1750
1944
  const source = typeof block.payload.meta?.code === "string" ? block.payload.meta?.code : block.payload.raw ?? "";
1751
1945
  const lines = extractCodeLines(source);
1946
+ const meta = block.payload.meta;
1752
1947
  const highlightedHtml = block.payload.highlightedHtml ?? "";
1753
- const highlightedLines = extractHighlightedLines(highlightedHtml, lines.length);
1754
- const { preAttrs, codeAttrs } = extractCodeWrapperAttributes(highlightedHtml);
1755
- const lang = typeof block.payload.meta?.lang === "string" ? String(block.payload.meta?.lang) : void 0;
1948
+ const hasBlockHighlight = typeof block.payload.highlightedHtml === "string" && block.payload.highlightedHtml.length > 0;
1949
+ const metaLines = Array.isArray(meta?.highlightedLines) ? meta?.highlightedLines : null;
1950
+ const includeLineHtml = metaLines ? true : !hasBlockHighlight || lines.length >= 200;
1951
+ const highlightedLines = metaLines ? normalizeHighlightedLines(metaLines, lines.length) : extractHighlightedLines(highlightedHtml, lines.length);
1952
+ const metaTokenLines = Array.isArray(meta?.tokenLines) ? meta?.tokenLines : null;
1953
+ const tokenLines = metaTokenLines ? normalizeTokenLines(metaTokenLines, lines.length) : null;
1954
+ const metaDiffKind = Array.isArray(meta?.diffKind) ? meta?.diffKind : null;
1955
+ const diffKindLines = metaDiffKind ? normalizeOptionalArray(metaDiffKind, lines.length) : null;
1956
+ const metaOldNo = Array.isArray(meta?.oldNo) ? meta?.oldNo : null;
1957
+ const oldNoLines = metaOldNo ? normalizeOptionalArray(metaOldNo, lines.length) : null;
1958
+ const metaNewNo = Array.isArray(meta?.newNo) ? meta?.newNo : null;
1959
+ const newNoLines = metaNewNo ? normalizeOptionalArray(metaNewNo, lines.length) : null;
1960
+ const lang = typeof meta?.lang === "string" ? String(meta.lang) : void 0;
1961
+ let { preAttrs, codeAttrs } = extractCodeWrapperAttributes(highlightedHtml);
1962
+ if (!preAttrs || !codeAttrs) {
1963
+ const defaults = getDefaultCodeWrapperAttributes(lang);
1964
+ preAttrs = preAttrs ?? defaults.preAttrs;
1965
+ codeAttrs = codeAttrs ?? defaults.codeAttrs;
1966
+ }
1756
1967
  snapshot.props = {
1757
1968
  ...snapshot.props ?? {},
1758
1969
  lang,
1759
1970
  preAttrs,
1760
1971
  codeAttrs
1761
1972
  };
1762
- snapshot.children = lines.map((line, index) => ({
1763
- id: `${block.id}::line:${index}`,
1764
- type: "code-line",
1765
- props: {
1973
+ snapshot.children = lines.map((line, index) => {
1974
+ const props = {
1766
1975
  index,
1767
1976
  text: line,
1768
- html: highlightedLines[index] ?? null
1769
- },
1770
- children: []
1771
- }));
1977
+ html: includeLineHtml ? highlightedLines[index] ?? null : null
1978
+ };
1979
+ if (tokenLines) {
1980
+ props.tokens = tokenLines[index] ?? null;
1981
+ }
1982
+ if (diffKindLines) {
1983
+ props.diffKind = diffKindLines[index] ?? null;
1984
+ }
1985
+ if (oldNoLines) {
1986
+ props.oldNo = oldNoLines[index] ?? null;
1987
+ }
1988
+ if (newNoLines) {
1989
+ props.newNo = newNoLines[index] ?? null;
1990
+ }
1991
+ return {
1992
+ id: `${block.id}::line:${index}`,
1993
+ type: "code-line",
1994
+ props,
1995
+ children: []
1996
+ };
1997
+ });
1772
1998
  return snapshot;
1773
1999
  }
2000
+ function normalizeTokenLines(lines, fallbackLength) {
2001
+ if (!lines || lines.length === 0) {
2002
+ return new Array(Math.max(0, fallbackLength)).fill(null);
2003
+ }
2004
+ const length = Math.max(fallbackLength, lines.length);
2005
+ const result = new Array(length).fill(null);
2006
+ for (let i = 0; i < lines.length; i++) {
2007
+ result[i] = lines[i] ?? null;
2008
+ }
2009
+ return result;
2010
+ }
2011
+ function normalizeOptionalArray(lines, fallbackLength) {
2012
+ if (!lines || lines.length === 0) {
2013
+ return new Array(Math.max(0, fallbackLength)).fill(null);
2014
+ }
2015
+ const length = Math.max(fallbackLength, lines.length);
2016
+ const result = new Array(length).fill(null);
2017
+ for (let i = 0; i < lines.length; i++) {
2018
+ result[i] = lines[i] ?? null;
2019
+ }
2020
+ return result;
2021
+ }
1774
2022
  function cloneInlineNodes(nodes) {
1775
2023
  return nodes.map((node) => {
1776
2024
  if ("children" in node && Array.isArray(node.children)) {
@@ -1939,7 +2187,17 @@ function applyPatchBatch(snapshot, patches) {
1939
2187
  case "appendLines": {
1940
2188
  const parent = resolveTargetNode(snapshot, patch.at);
1941
2189
  if (!parent) break;
1942
- appendLinesToCodeNode(snapshot, parent, patch.startIndex, patch.lines ?? [], patch.highlight ?? []);
2190
+ appendLinesToCodeNode(
2191
+ snapshot,
2192
+ parent,
2193
+ patch.startIndex,
2194
+ patch.lines ?? [],
2195
+ patch.highlight ?? [],
2196
+ patch.tokens,
2197
+ patch.diffKind,
2198
+ patch.oldNo,
2199
+ patch.newNo
2200
+ );
1943
2201
  break;
1944
2202
  }
1945
2203
  case "setHTML": {
@@ -2061,7 +2319,7 @@ function applyPropsToNode(node, props) {
2061
2319
  }
2062
2320
  node.props = next;
2063
2321
  }
2064
- function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights) {
2322
+ function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights, tokens, diffKind, oldNo, newNo) {
2065
2323
  if (parent.type !== "code") return;
2066
2324
  const insertionIndex = Math.max(0, Math.min(startIndex, parent.children.length));
2067
2325
  let currentIndex = insertionIndex;
@@ -2079,6 +2337,18 @@ function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights)
2079
2337
  html: highlights[i] ?? null
2080
2338
  }
2081
2339
  };
2340
+ if (tokens && Object.prototype.hasOwnProperty.call(tokens, i)) {
2341
+ child.props.tokens = tokens[i] ?? null;
2342
+ }
2343
+ if (diffKind && Object.prototype.hasOwnProperty.call(diffKind, i)) {
2344
+ child.props.diffKind = diffKind[i] ?? null;
2345
+ }
2346
+ if (oldNo && Object.prototype.hasOwnProperty.call(oldNo, i)) {
2347
+ child.props.oldNo = oldNo[i] ?? null;
2348
+ }
2349
+ if (newNo && Object.prototype.hasOwnProperty.call(newNo, i)) {
2350
+ child.props.newNo = newNo[i] ?? null;
2351
+ }
2082
2352
  snapshot.nodes.set(child.id, child);
2083
2353
  parent.children.splice(currentIndex, 0, child.id);
2084
2354
  currentIndex++;
@@ -2634,6 +2904,14 @@ function mergeAppendLines(window2, startIndex) {
2634
2904
  }
2635
2905
  const lines = [...base.lines ?? []];
2636
2906
  const highlight = Array.isArray(base.highlight) ? [...base.highlight] : [];
2907
+ const tokens = Array.isArray(base.tokens) ? [...base.tokens] : [];
2908
+ const diffKind = Array.isArray(base.diffKind) ? [...base.diffKind] : [];
2909
+ const oldNo = Array.isArray(base.oldNo) ? [...base.oldNo] : [];
2910
+ const newNo = Array.isArray(base.newNo) ? [...base.newNo] : [];
2911
+ let includeTokens = Array.isArray(base.tokens);
2912
+ let includeDiffKind = Array.isArray(base.diffKind);
2913
+ let includeOldNo = Array.isArray(base.oldNo);
2914
+ let includeNewNo = Array.isArray(base.newNo);
2637
2915
  const baseStart = base.startIndex;
2638
2916
  let expectedStart = baseStart + lines.length;
2639
2917
  let j = startIndex + 1;
@@ -2641,6 +2919,7 @@ function mergeAppendLines(window2, startIndex) {
2641
2919
  while (j < window2.length && mergedCount < APPEND_MERGE_LIMIT) {
2642
2920
  const next = window2[j];
2643
2921
  if (next.op === "appendLines" && next.at.blockId === base.at.blockId && next.at.nodeId === base.at.nodeId && typeof next.startIndex === "number" && next.startIndex === expectedStart) {
2922
+ const priorLineCount = lines.length;
2644
2923
  lines.push(...next.lines ?? []);
2645
2924
  const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];
2646
2925
  const appendedCount = next.lines?.length ?? 0;
@@ -2654,6 +2933,50 @@ function mergeAppendLines(window2, startIndex) {
2654
2933
  highlight.push(null);
2655
2934
  }
2656
2935
  }
2936
+ if (Array.isArray(next.tokens)) {
2937
+ if (!includeTokens) {
2938
+ tokens.push(...new Array(priorLineCount).fill(null));
2939
+ includeTokens = true;
2940
+ }
2941
+ for (let idx = 0; idx < appendedCount; idx++) {
2942
+ tokens.push(idx < next.tokens.length ? next.tokens[idx] ?? null : null);
2943
+ }
2944
+ } else if (includeTokens) {
2945
+ tokens.push(...new Array(appendedCount).fill(null));
2946
+ }
2947
+ if (Array.isArray(next.diffKind)) {
2948
+ if (!includeDiffKind) {
2949
+ diffKind.push(...new Array(priorLineCount).fill(null));
2950
+ includeDiffKind = true;
2951
+ }
2952
+ for (let idx = 0; idx < appendedCount; idx++) {
2953
+ diffKind.push(idx < next.diffKind.length ? next.diffKind[idx] ?? null : null);
2954
+ }
2955
+ } else if (includeDiffKind) {
2956
+ diffKind.push(...new Array(appendedCount).fill(null));
2957
+ }
2958
+ if (Array.isArray(next.oldNo)) {
2959
+ if (!includeOldNo) {
2960
+ oldNo.push(...new Array(priorLineCount).fill(null));
2961
+ includeOldNo = true;
2962
+ }
2963
+ for (let idx = 0; idx < appendedCount; idx++) {
2964
+ oldNo.push(idx < next.oldNo.length ? next.oldNo[idx] ?? null : null);
2965
+ }
2966
+ } else if (includeOldNo) {
2967
+ oldNo.push(...new Array(appendedCount).fill(null));
2968
+ }
2969
+ if (Array.isArray(next.newNo)) {
2970
+ if (!includeNewNo) {
2971
+ newNo.push(...new Array(priorLineCount).fill(null));
2972
+ includeNewNo = true;
2973
+ }
2974
+ for (let idx = 0; idx < appendedCount; idx++) {
2975
+ newNo.push(idx < next.newNo.length ? next.newNo[idx] ?? null : null);
2976
+ }
2977
+ } else if (includeNewNo) {
2978
+ newNo.push(...new Array(appendedCount).fill(null));
2979
+ }
2657
2980
  expectedStart = baseStart + lines.length;
2658
2981
  mergedCount++;
2659
2982
  j++;
@@ -2664,7 +2987,11 @@ function mergeAppendLines(window2, startIndex) {
2664
2987
  const combined = {
2665
2988
  ...base,
2666
2989
  lines,
2667
- highlight: highlight.length > 0 ? highlight : void 0
2990
+ highlight: highlight.length > 0 ? highlight : void 0,
2991
+ tokens: includeTokens && tokens.length > 0 ? tokens : void 0,
2992
+ diffKind: includeDiffKind && diffKind.length > 0 ? diffKind : void 0,
2993
+ oldNo: includeOldNo && oldNo.length > 0 ? oldNo : void 0,
2994
+ newNo: includeNewNo && newNo.length > 0 ? newNo : void 0
2668
2995
  };
2669
2996
  return { patch: combined, nextIndex: j };
2670
2997
  }
@@ -3004,130 +3331,6 @@ var CustomStreamingMatcher = class {
3004
3331
  return false;
3005
3332
  }
3006
3333
  };
3007
-
3008
- // src/streaming/inline-streaming.ts
3009
- var DEFAULT_FORMAT_ANTICIPATION = {
3010
- inline: false,
3011
- mathInline: false,
3012
- mathBlock: false,
3013
- html: false,
3014
- mdx: false,
3015
- regex: false
3016
- };
3017
- function normalizeFormatAnticipation(input) {
3018
- if (input === true) {
3019
- return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
3020
- }
3021
- if (!input) {
3022
- return { ...DEFAULT_FORMAT_ANTICIPATION };
3023
- }
3024
- return {
3025
- inline: input.inline ?? false,
3026
- mathInline: input.mathInline ?? false,
3027
- mathBlock: input.mathBlock ?? false,
3028
- html: input.html ?? false,
3029
- mdx: input.mdx ?? false,
3030
- regex: input.regex ?? false
3031
- };
3032
- }
3033
- function prepareInlineStreamingContent(content, options) {
3034
- const enableMath = options?.math !== false;
3035
- const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
3036
- const enableInlineAnticipation = anticipation.inline;
3037
- const enableMathInlineAnticipation = anticipation.mathInline;
3038
- const enableMathBlockAnticipation = anticipation.mathBlock;
3039
- const stack = [];
3040
- const toggleToken = (token) => {
3041
- const last = stack[stack.length - 1];
3042
- if (last === token) {
3043
- stack.pop();
3044
- } else {
3045
- stack.push(token);
3046
- }
3047
- };
3048
- let mathDisplayOpen = false;
3049
- let mathDisplayCrossedNewline = false;
3050
- for (let i = 0; i < content.length; i++) {
3051
- const code = content.charCodeAt(i);
3052
- if (code === 10 || code === 13) {
3053
- if (mathDisplayOpen) {
3054
- mathDisplayCrossedNewline = true;
3055
- }
3056
- continue;
3057
- }
3058
- if (code === 96) {
3059
- toggleToken("code");
3060
- continue;
3061
- }
3062
- if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
3063
- toggleToken("strike");
3064
- i += 1;
3065
- continue;
3066
- }
3067
- if (code === 42) {
3068
- if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
3069
- toggleToken("strong");
3070
- i += 1;
3071
- } else {
3072
- toggleToken("em");
3073
- }
3074
- continue;
3075
- }
3076
- if (enableMath && code === 36) {
3077
- if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
3078
- toggleToken("math-display");
3079
- if (mathDisplayOpen) {
3080
- mathDisplayOpen = false;
3081
- mathDisplayCrossedNewline = false;
3082
- } else {
3083
- mathDisplayOpen = true;
3084
- mathDisplayCrossedNewline = false;
3085
- }
3086
- i += 1;
3087
- } else {
3088
- toggleToken("math-inline");
3089
- }
3090
- }
3091
- }
3092
- const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
3093
- const hasIncompleteMathInline = stack.includes("math-inline");
3094
- const hasIncompleteMathDisplay = stack.includes("math-display");
3095
- const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
3096
- if (enableMath && hasIncompleteMath) {
3097
- if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
3098
- return { kind: "raw", status: "raw", reason: "incomplete-math" };
3099
- }
3100
- if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
3101
- return { kind: "raw", status: "raw", reason: "incomplete-math" };
3102
- }
3103
- }
3104
- if (hasIncompleteFormatting && !enableInlineAnticipation) {
3105
- return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
3106
- }
3107
- if (!hasIncompleteFormatting && !hasIncompleteMath) {
3108
- return { kind: "parse", status: "complete", content, appended: "" };
3109
- }
3110
- const appendForToken = (token) => {
3111
- switch (token) {
3112
- case "code":
3113
- return "`";
3114
- case "strike":
3115
- return "~~";
3116
- case "strong":
3117
- return "**";
3118
- case "em":
3119
- return "*";
3120
- case "math-inline":
3121
- return "$";
3122
- case "math-display":
3123
- return "$$";
3124
- default:
3125
- return "";
3126
- }
3127
- };
3128
- const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
3129
- return { kind: "parse", status: "anticipated", content: content + appended, appended };
3130
- }
3131
3334
  // Annotate the CommonJS export names for ESM import in node:
3132
3335
  0 && (module.exports = {
3133
3336
  CSP_HEADERS,
@@ -3167,12 +3370,14 @@ function prepareInlineStreamingContent(content, options) {
3167
3370
  findClosingHtmlTag,
3168
3371
  generateBlockId,
3169
3372
  getBlockKey,
3373
+ getDefaultCodeWrapperAttributes,
3170
3374
  initializeSecurity,
3171
3375
  initializeTrustedTypesPolicy,
3172
3376
  inlineNodesToPlainText,
3173
3377
  isLikelyMdxComponent,
3174
3378
  normalizeBlockquoteText,
3175
3379
  normalizeFormatAnticipation,
3380
+ normalizeHighlightedLines,
3176
3381
  normalizeLang,
3177
3382
  parseCodeFenceInfo,
3178
3383
  prepareInlineStreamingContent,
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Block, NodeSnapshot, InlineNode, Patch } from './types.cjs';
2
- export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.cjs';
3
- export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, stripCodeFence } from './code-highlighting.cjs';
2
+ export { ASTInlinePlugin, CoalescingMetrics, CodeHighlightOutputMode, CodeHighlightingMode, CompiledMdxModule, DiffKind, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, LazyTokenizationPriority, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, TokenLineV1, TokenSpan, TokenStyle, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.cjs';
3
+ export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, getDefaultCodeWrapperAttributes, normalizeHighlightedLines, stripCodeFence } from './code-highlighting.cjs';
4
4
  export { PerformanceTimer, StringBuffer, applyUpdate, debounce, detectMDX, generateBlockId, getBlockKey, normalizeBlockquoteText, normalizeLang, parseCodeFenceInfo, removeHeadingMarkers } from './utils.cjs';
5
5
  export { MixedContentAutoCloseHtmlOptions, MixedContentAutoCloseMdxOptions, MixedContentOptions, extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.cjs';
6
6
  export { CSP_HEADERS, SanitizationPolicy, createSanitizationConfig, createTrustedHTML, initializeSecurity, initializeTrustedTypesPolicy, sanitizeCodeHTML, sanitizeHTML, sanitizeMathHTML, sanitizeURL } from './security.cjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Block, NodeSnapshot, InlineNode, Patch } from './types.js';
2
- export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.js';
3
- export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, stripCodeFence } from './code-highlighting.js';
2
+ export { ASTInlinePlugin, CoalescingMetrics, CodeHighlightOutputMode, CodeHighlightingMode, CompiledMdxModule, DiffKind, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, LazyTokenizationPriority, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, TokenLineV1, TokenSpan, TokenStyle, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.js';
3
+ export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, getDefaultCodeWrapperAttributes, normalizeHighlightedLines, stripCodeFence } from './code-highlighting.js';
4
4
  export { PerformanceTimer, StringBuffer, applyUpdate, debounce, detectMDX, generateBlockId, getBlockKey, normalizeBlockquoteText, normalizeLang, parseCodeFenceInfo, removeHeadingMarkers } from './utils.js';
5
5
  export { MixedContentAutoCloseHtmlOptions, MixedContentAutoCloseMdxOptions, MixedContentOptions, extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.js';
6
6
  export { CSP_HEADERS, SanitizationPolicy, createSanitizationConfig, createTrustedHTML, initializeSecurity, initializeTrustedTypesPolicy, sanitizeCodeHTML, sanitizeHTML, sanitizeMathHTML, sanitizeURL } from './security.js';