@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/CHANGELOG.md +6 -0
- package/README.md +6 -0
- package/dist/code-highlighting.cjs +20 -0
- package/dist/code-highlighting.d.cts +9 -1
- package/dist/code-highlighting.d.ts +9 -1
- package/dist/code-highlighting.mjs +18 -0
- package/dist/index.cjs +354 -149
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +352 -149
- package/dist/mixed-content.cjs +14 -0
- package/dist/mixed-content.d.cts +3 -1
- package/dist/mixed-content.d.ts +3 -1
- package/dist/mixed-content.mjs +14 -0
- package/dist/perf/patch-coalescing.cjs +58 -1
- package/dist/perf/patch-coalescing.mjs +58 -1
- package/dist/types.d.cts +64 -1
- package/dist/types.d.ts +64 -1
- package/dist/utils.cjs +16 -6
- package/dist/utils.mjs +16 -6
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -141,6 +141,22 @@ function manualExtractHighlightedLines(html, fallbackLength) {
|
|
|
141
141
|
}
|
|
142
142
|
return normalizeHighlightedLines(lines, fallbackLength);
|
|
143
143
|
}
|
|
144
|
+
function getDefaultCodeWrapperAttributes(lang, themes = { dark: "github-dark", light: "github-light" }) {
|
|
145
|
+
const language = lang && typeof lang === "string" && lang.length > 0 ? lang : "text";
|
|
146
|
+
const themeLabel = `${themes.dark} ${themes.light}`;
|
|
147
|
+
return {
|
|
148
|
+
preAttrs: {
|
|
149
|
+
class: `shiki shiki-themes ${themeLabel}`,
|
|
150
|
+
"data-language": language,
|
|
151
|
+
style: "--shiki-dark-bg: transparent; --shiki-light-bg: transparent"
|
|
152
|
+
},
|
|
153
|
+
codeAttrs: {
|
|
154
|
+
"data-language": language,
|
|
155
|
+
"data-theme": themeLabel,
|
|
156
|
+
style: "display: grid;"
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
144
160
|
function dedentIndentedCode(raw) {
|
|
145
161
|
if (!raw) return "";
|
|
146
162
|
const normalized = normalizeNewlines(raw);
|
|
@@ -547,6 +563,130 @@ function splitTextByRegexWithPrecedence(text, regex, toNode) {
|
|
|
547
563
|
return result;
|
|
548
564
|
}
|
|
549
565
|
|
|
566
|
+
// src/streaming/inline-streaming.ts
|
|
567
|
+
var DEFAULT_FORMAT_ANTICIPATION = {
|
|
568
|
+
inline: false,
|
|
569
|
+
mathInline: false,
|
|
570
|
+
mathBlock: false,
|
|
571
|
+
html: false,
|
|
572
|
+
mdx: false,
|
|
573
|
+
regex: false
|
|
574
|
+
};
|
|
575
|
+
function normalizeFormatAnticipation(input) {
|
|
576
|
+
if (input === true) {
|
|
577
|
+
return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
|
|
578
|
+
}
|
|
579
|
+
if (!input) {
|
|
580
|
+
return { ...DEFAULT_FORMAT_ANTICIPATION };
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
inline: input.inline ?? false,
|
|
584
|
+
mathInline: input.mathInline ?? false,
|
|
585
|
+
mathBlock: input.mathBlock ?? false,
|
|
586
|
+
html: input.html ?? false,
|
|
587
|
+
mdx: input.mdx ?? false,
|
|
588
|
+
regex: input.regex ?? false
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function prepareInlineStreamingContent(content, options) {
|
|
592
|
+
const enableMath = options?.math !== false;
|
|
593
|
+
const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
|
|
594
|
+
const enableInlineAnticipation = anticipation.inline;
|
|
595
|
+
const enableMathInlineAnticipation = anticipation.mathInline;
|
|
596
|
+
const enableMathBlockAnticipation = anticipation.mathBlock;
|
|
597
|
+
const stack = [];
|
|
598
|
+
const toggleToken = (token) => {
|
|
599
|
+
const last = stack[stack.length - 1];
|
|
600
|
+
if (last === token) {
|
|
601
|
+
stack.pop();
|
|
602
|
+
} else {
|
|
603
|
+
stack.push(token);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
let mathDisplayOpen = false;
|
|
607
|
+
let mathDisplayCrossedNewline = false;
|
|
608
|
+
for (let i = 0; i < content.length; i++) {
|
|
609
|
+
const code = content.charCodeAt(i);
|
|
610
|
+
if (code === 10 || code === 13) {
|
|
611
|
+
if (mathDisplayOpen) {
|
|
612
|
+
mathDisplayCrossedNewline = true;
|
|
613
|
+
}
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
if (code === 96) {
|
|
617
|
+
toggleToken("code");
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
|
|
621
|
+
toggleToken("strike");
|
|
622
|
+
i += 1;
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if (code === 42) {
|
|
626
|
+
if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
|
|
627
|
+
toggleToken("strong");
|
|
628
|
+
i += 1;
|
|
629
|
+
} else {
|
|
630
|
+
toggleToken("em");
|
|
631
|
+
}
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
if (enableMath && code === 36) {
|
|
635
|
+
if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
|
|
636
|
+
toggleToken("math-display");
|
|
637
|
+
if (mathDisplayOpen) {
|
|
638
|
+
mathDisplayOpen = false;
|
|
639
|
+
mathDisplayCrossedNewline = false;
|
|
640
|
+
} else {
|
|
641
|
+
mathDisplayOpen = true;
|
|
642
|
+
mathDisplayCrossedNewline = false;
|
|
643
|
+
}
|
|
644
|
+
i += 1;
|
|
645
|
+
} else {
|
|
646
|
+
toggleToken("math-inline");
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
|
|
651
|
+
const hasIncompleteMathInline = stack.includes("math-inline");
|
|
652
|
+
const hasIncompleteMathDisplay = stack.includes("math-display");
|
|
653
|
+
const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
|
|
654
|
+
if (enableMath && hasIncompleteMath) {
|
|
655
|
+
if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
|
|
656
|
+
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
657
|
+
}
|
|
658
|
+
if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
|
|
659
|
+
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (hasIncompleteFormatting && !enableInlineAnticipation) {
|
|
663
|
+
return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
|
|
664
|
+
}
|
|
665
|
+
if (!hasIncompleteFormatting && !hasIncompleteMath) {
|
|
666
|
+
return { kind: "parse", status: "complete", content, appended: "" };
|
|
667
|
+
}
|
|
668
|
+
const appendForToken = (token) => {
|
|
669
|
+
switch (token) {
|
|
670
|
+
case "code":
|
|
671
|
+
return "`";
|
|
672
|
+
case "strike":
|
|
673
|
+
return "~~";
|
|
674
|
+
case "strong":
|
|
675
|
+
return "**";
|
|
676
|
+
case "em":
|
|
677
|
+
return "*";
|
|
678
|
+
case "math-inline":
|
|
679
|
+
return "$";
|
|
680
|
+
case "math-display":
|
|
681
|
+
return "$$";
|
|
682
|
+
default:
|
|
683
|
+
return "";
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
|
|
687
|
+
return { kind: "parse", status: "anticipated", content: content + appended, appended };
|
|
688
|
+
}
|
|
689
|
+
|
|
550
690
|
// src/worker-html-sanitizer.ts
|
|
551
691
|
import * as rehypeParse from "rehype-parse";
|
|
552
692
|
import * as rehypeSanitize from "rehype-sanitize";
|
|
@@ -728,6 +868,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
|
|
|
728
868
|
const mdxAutoClose = options?.mdx?.autoClose === true;
|
|
729
869
|
const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
|
|
730
870
|
const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
|
|
871
|
+
const protectedRanges = options?.protectedRanges ?? [];
|
|
872
|
+
const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
|
|
731
873
|
while (match !== null) {
|
|
732
874
|
const start = match.index;
|
|
733
875
|
const tagName = match[1];
|
|
@@ -742,6 +884,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
|
|
|
742
884
|
continue;
|
|
743
885
|
}
|
|
744
886
|
let end = tagPattern.lastIndex;
|
|
887
|
+
if (protectedKinds && protectedRanges.length > 0) {
|
|
888
|
+
const absoluteStart = baseIsFinite ? baseOffset + start : start;
|
|
889
|
+
const absoluteEnd = baseIsFinite ? baseOffset + end : end;
|
|
890
|
+
const covered = protectedRanges.some(
|
|
891
|
+
(range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
|
|
892
|
+
);
|
|
893
|
+
if (covered) {
|
|
894
|
+
tagPattern.lastIndex = start + 1;
|
|
895
|
+
match = tagPattern.exec(source);
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
745
899
|
if (!isSelfClosing && !mdxAllowed) {
|
|
746
900
|
const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
|
|
747
901
|
if (closingIndex === -1) {
|
|
@@ -1039,23 +1193,33 @@ function getBlockKey(block) {
|
|
|
1039
1193
|
}
|
|
1040
1194
|
function detectMDX(content, options) {
|
|
1041
1195
|
const inlineCodeRanges = collectInlineCodeRanges(content);
|
|
1196
|
+
const protectedRanges = options?.protectedRanges ?? [];
|
|
1197
|
+
const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
|
|
1198
|
+
const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block", "html-inline", "html-block", "autolink"]) : null;
|
|
1042
1199
|
const componentPattern = /<([A-Z][\w-]*)(\s|\/?>)/g;
|
|
1043
1200
|
let componentMatch = componentPattern.exec(content);
|
|
1044
1201
|
while (componentMatch !== null) {
|
|
1045
1202
|
const start = componentMatch.index;
|
|
1046
1203
|
const end = start + componentMatch[0].length;
|
|
1047
|
-
if (
|
|
1048
|
-
|
|
1204
|
+
if (isWithinRanges(start, end, inlineCodeRanges)) {
|
|
1205
|
+
componentMatch = componentPattern.exec(content);
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
if (protectedKinds) {
|
|
1209
|
+
const absoluteStart = baseOffset + start;
|
|
1210
|
+
const absoluteEnd = baseOffset + end;
|
|
1211
|
+
const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);
|
|
1212
|
+
if (covered) {
|
|
1213
|
+
componentMatch = componentPattern.exec(content);
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1049
1216
|
}
|
|
1050
|
-
|
|
1217
|
+
return true;
|
|
1051
1218
|
}
|
|
1052
1219
|
if (/(^|\n)\s*(import|export)\s/.test(content)) {
|
|
1053
1220
|
return true;
|
|
1054
1221
|
}
|
|
1055
1222
|
const expressionPattern = /\{[^{}]+\}/g;
|
|
1056
|
-
const protectedRanges = options?.protectedRanges ?? [];
|
|
1057
|
-
const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
|
|
1058
|
-
const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block"]) : null;
|
|
1059
1223
|
for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {
|
|
1060
1224
|
const index = match.index;
|
|
1061
1225
|
const prev = index > 0 ? content[index - 1] : "";
|
|
@@ -1303,7 +1467,12 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
|
|
|
1303
1467
|
}
|
|
1304
1468
|
if (name === "Paragraph") {
|
|
1305
1469
|
const paragraphRaw = raw.slice(cursor.from, cursor.to);
|
|
1306
|
-
const
|
|
1470
|
+
const meta = block.payload.meta ?? {};
|
|
1471
|
+
const paragraphData = processListItemParagraph(paragraphRaw, {
|
|
1472
|
+
formatAnticipation: meta.formatAnticipation,
|
|
1473
|
+
math: meta.mathEnabled,
|
|
1474
|
+
streaming: !block.isFinalized
|
|
1475
|
+
});
|
|
1307
1476
|
const parsedInline = paragraphData.inline;
|
|
1308
1477
|
if (!paragraphHandled) {
|
|
1309
1478
|
inlineNodes = parsedInline;
|
|
@@ -1335,7 +1504,10 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
|
|
|
1335
1504
|
} else if (name === "BulletList" || name === "OrderedList") {
|
|
1336
1505
|
const nestedId = `${id}::list:${subListIndex++}`;
|
|
1337
1506
|
const nestedOrdered = name === "OrderedList";
|
|
1338
|
-
|
|
1507
|
+
const nestedSnapshot = buildListNodeSnapshot(block, cursor.node, nestedOrdered, nestedId, baseOffset, raw);
|
|
1508
|
+
if (Array.isArray(nestedSnapshot.children) && nestedSnapshot.children.length > 0) {
|
|
1509
|
+
childSnapshots.push(nestedSnapshot);
|
|
1510
|
+
}
|
|
1339
1511
|
} else if (name === "Blockquote") {
|
|
1340
1512
|
const quoteId = `${id}::blockquote:${blockquoteIndex++}`;
|
|
1341
1513
|
childSnapshots.push(buildBlockquoteSnapshot(block, cursor.node, quoteId, baseOffset, raw));
|
|
@@ -1371,11 +1543,31 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
|
|
|
1371
1543
|
};
|
|
1372
1544
|
return itemSnapshot;
|
|
1373
1545
|
}
|
|
1374
|
-
function
|
|
1546
|
+
function parseListInline(raw, options) {
|
|
1547
|
+
if (!options?.streaming || !options.formatAnticipation) {
|
|
1548
|
+
return listInlineParser.parse(raw);
|
|
1549
|
+
}
|
|
1550
|
+
const prepared = prepareInlineStreamingContent(raw, { formatAnticipation: options.formatAnticipation, math: options.math });
|
|
1551
|
+
if (prepared.kind === "raw") {
|
|
1552
|
+
return [{ kind: "text", text: raw }];
|
|
1553
|
+
}
|
|
1554
|
+
let preparedContent = prepared.content;
|
|
1555
|
+
let appended = prepared.appended;
|
|
1556
|
+
const normalized = normalizeFormatAnticipation(options.formatAnticipation);
|
|
1557
|
+
if (normalized.regex) {
|
|
1558
|
+
const regexAppend = listInlineParser.getRegexAnticipationAppend(raw);
|
|
1559
|
+
if (regexAppend) {
|
|
1560
|
+
preparedContent += regexAppend;
|
|
1561
|
+
appended += regexAppend;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return listInlineParser.parse(preparedContent, { cache: false });
|
|
1565
|
+
}
|
|
1566
|
+
function processListItemParagraph(raw, options) {
|
|
1375
1567
|
const normalized = normalizeParagraphText(raw);
|
|
1376
1568
|
const { content, task } = stripTaskMarker(normalized);
|
|
1377
|
-
const inline =
|
|
1378
|
-
const segments = extractMixedContentSegments(content, void 0, (value) =>
|
|
1569
|
+
const inline = parseListInline(content, options);
|
|
1570
|
+
const segments = extractMixedContentSegments(content, void 0, (value) => parseListInline(value, options));
|
|
1379
1571
|
return {
|
|
1380
1572
|
inline,
|
|
1381
1573
|
segments,
|
|
@@ -1660,28 +1852,82 @@ function enrichTableSnapshot(block, snapshot) {
|
|
|
1660
1852
|
function enrichCodeSnapshot(block, snapshot) {
|
|
1661
1853
|
const source = typeof block.payload.meta?.code === "string" ? block.payload.meta?.code : block.payload.raw ?? "";
|
|
1662
1854
|
const lines = extractCodeLines(source);
|
|
1855
|
+
const meta = block.payload.meta;
|
|
1663
1856
|
const highlightedHtml = block.payload.highlightedHtml ?? "";
|
|
1664
|
-
const
|
|
1665
|
-
const
|
|
1666
|
-
const
|
|
1857
|
+
const hasBlockHighlight = typeof block.payload.highlightedHtml === "string" && block.payload.highlightedHtml.length > 0;
|
|
1858
|
+
const metaLines = Array.isArray(meta?.highlightedLines) ? meta?.highlightedLines : null;
|
|
1859
|
+
const includeLineHtml = metaLines ? true : !hasBlockHighlight || lines.length >= 200;
|
|
1860
|
+
const highlightedLines = metaLines ? normalizeHighlightedLines(metaLines, lines.length) : extractHighlightedLines(highlightedHtml, lines.length);
|
|
1861
|
+
const metaTokenLines = Array.isArray(meta?.tokenLines) ? meta?.tokenLines : null;
|
|
1862
|
+
const tokenLines = metaTokenLines ? normalizeTokenLines(metaTokenLines, lines.length) : null;
|
|
1863
|
+
const metaDiffKind = Array.isArray(meta?.diffKind) ? meta?.diffKind : null;
|
|
1864
|
+
const diffKindLines = metaDiffKind ? normalizeOptionalArray(metaDiffKind, lines.length) : null;
|
|
1865
|
+
const metaOldNo = Array.isArray(meta?.oldNo) ? meta?.oldNo : null;
|
|
1866
|
+
const oldNoLines = metaOldNo ? normalizeOptionalArray(metaOldNo, lines.length) : null;
|
|
1867
|
+
const metaNewNo = Array.isArray(meta?.newNo) ? meta?.newNo : null;
|
|
1868
|
+
const newNoLines = metaNewNo ? normalizeOptionalArray(metaNewNo, lines.length) : null;
|
|
1869
|
+
const lang = typeof meta?.lang === "string" ? String(meta.lang) : void 0;
|
|
1870
|
+
let { preAttrs, codeAttrs } = extractCodeWrapperAttributes(highlightedHtml);
|
|
1871
|
+
if (!preAttrs || !codeAttrs) {
|
|
1872
|
+
const defaults = getDefaultCodeWrapperAttributes(lang);
|
|
1873
|
+
preAttrs = preAttrs ?? defaults.preAttrs;
|
|
1874
|
+
codeAttrs = codeAttrs ?? defaults.codeAttrs;
|
|
1875
|
+
}
|
|
1667
1876
|
snapshot.props = {
|
|
1668
1877
|
...snapshot.props ?? {},
|
|
1669
1878
|
lang,
|
|
1670
1879
|
preAttrs,
|
|
1671
1880
|
codeAttrs
|
|
1672
1881
|
};
|
|
1673
|
-
snapshot.children = lines.map((line, index) =>
|
|
1674
|
-
|
|
1675
|
-
type: "code-line",
|
|
1676
|
-
props: {
|
|
1882
|
+
snapshot.children = lines.map((line, index) => {
|
|
1883
|
+
const props = {
|
|
1677
1884
|
index,
|
|
1678
1885
|
text: line,
|
|
1679
|
-
html: highlightedLines[index] ?? null
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1886
|
+
html: includeLineHtml ? highlightedLines[index] ?? null : null
|
|
1887
|
+
};
|
|
1888
|
+
if (tokenLines) {
|
|
1889
|
+
props.tokens = tokenLines[index] ?? null;
|
|
1890
|
+
}
|
|
1891
|
+
if (diffKindLines) {
|
|
1892
|
+
props.diffKind = diffKindLines[index] ?? null;
|
|
1893
|
+
}
|
|
1894
|
+
if (oldNoLines) {
|
|
1895
|
+
props.oldNo = oldNoLines[index] ?? null;
|
|
1896
|
+
}
|
|
1897
|
+
if (newNoLines) {
|
|
1898
|
+
props.newNo = newNoLines[index] ?? null;
|
|
1899
|
+
}
|
|
1900
|
+
return {
|
|
1901
|
+
id: `${block.id}::line:${index}`,
|
|
1902
|
+
type: "code-line",
|
|
1903
|
+
props,
|
|
1904
|
+
children: []
|
|
1905
|
+
};
|
|
1906
|
+
});
|
|
1683
1907
|
return snapshot;
|
|
1684
1908
|
}
|
|
1909
|
+
function normalizeTokenLines(lines, fallbackLength) {
|
|
1910
|
+
if (!lines || lines.length === 0) {
|
|
1911
|
+
return new Array(Math.max(0, fallbackLength)).fill(null);
|
|
1912
|
+
}
|
|
1913
|
+
const length = Math.max(fallbackLength, lines.length);
|
|
1914
|
+
const result = new Array(length).fill(null);
|
|
1915
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1916
|
+
result[i] = lines[i] ?? null;
|
|
1917
|
+
}
|
|
1918
|
+
return result;
|
|
1919
|
+
}
|
|
1920
|
+
function normalizeOptionalArray(lines, fallbackLength) {
|
|
1921
|
+
if (!lines || lines.length === 0) {
|
|
1922
|
+
return new Array(Math.max(0, fallbackLength)).fill(null);
|
|
1923
|
+
}
|
|
1924
|
+
const length = Math.max(fallbackLength, lines.length);
|
|
1925
|
+
const result = new Array(length).fill(null);
|
|
1926
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1927
|
+
result[i] = lines[i] ?? null;
|
|
1928
|
+
}
|
|
1929
|
+
return result;
|
|
1930
|
+
}
|
|
1685
1931
|
function cloneInlineNodes(nodes) {
|
|
1686
1932
|
return nodes.map((node) => {
|
|
1687
1933
|
if ("children" in node && Array.isArray(node.children)) {
|
|
@@ -1850,7 +2096,17 @@ function applyPatchBatch(snapshot, patches) {
|
|
|
1850
2096
|
case "appendLines": {
|
|
1851
2097
|
const parent = resolveTargetNode(snapshot, patch.at);
|
|
1852
2098
|
if (!parent) break;
|
|
1853
|
-
appendLinesToCodeNode(
|
|
2099
|
+
appendLinesToCodeNode(
|
|
2100
|
+
snapshot,
|
|
2101
|
+
parent,
|
|
2102
|
+
patch.startIndex,
|
|
2103
|
+
patch.lines ?? [],
|
|
2104
|
+
patch.highlight ?? [],
|
|
2105
|
+
patch.tokens,
|
|
2106
|
+
patch.diffKind,
|
|
2107
|
+
patch.oldNo,
|
|
2108
|
+
patch.newNo
|
|
2109
|
+
);
|
|
1854
2110
|
break;
|
|
1855
2111
|
}
|
|
1856
2112
|
case "setHTML": {
|
|
@@ -1972,7 +2228,7 @@ function applyPropsToNode(node, props) {
|
|
|
1972
2228
|
}
|
|
1973
2229
|
node.props = next;
|
|
1974
2230
|
}
|
|
1975
|
-
function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights) {
|
|
2231
|
+
function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights, tokens, diffKind, oldNo, newNo) {
|
|
1976
2232
|
if (parent.type !== "code") return;
|
|
1977
2233
|
const insertionIndex = Math.max(0, Math.min(startIndex, parent.children.length));
|
|
1978
2234
|
let currentIndex = insertionIndex;
|
|
@@ -1990,6 +2246,18 @@ function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights)
|
|
|
1990
2246
|
html: highlights[i] ?? null
|
|
1991
2247
|
}
|
|
1992
2248
|
};
|
|
2249
|
+
if (tokens && Object.prototype.hasOwnProperty.call(tokens, i)) {
|
|
2250
|
+
child.props.tokens = tokens[i] ?? null;
|
|
2251
|
+
}
|
|
2252
|
+
if (diffKind && Object.prototype.hasOwnProperty.call(diffKind, i)) {
|
|
2253
|
+
child.props.diffKind = diffKind[i] ?? null;
|
|
2254
|
+
}
|
|
2255
|
+
if (oldNo && Object.prototype.hasOwnProperty.call(oldNo, i)) {
|
|
2256
|
+
child.props.oldNo = oldNo[i] ?? null;
|
|
2257
|
+
}
|
|
2258
|
+
if (newNo && Object.prototype.hasOwnProperty.call(newNo, i)) {
|
|
2259
|
+
child.props.newNo = newNo[i] ?? null;
|
|
2260
|
+
}
|
|
1993
2261
|
snapshot.nodes.set(child.id, child);
|
|
1994
2262
|
parent.children.splice(currentIndex, 0, child.id);
|
|
1995
2263
|
currentIndex++;
|
|
@@ -2545,6 +2813,14 @@ function mergeAppendLines(window2, startIndex) {
|
|
|
2545
2813
|
}
|
|
2546
2814
|
const lines = [...base.lines ?? []];
|
|
2547
2815
|
const highlight = Array.isArray(base.highlight) ? [...base.highlight] : [];
|
|
2816
|
+
const tokens = Array.isArray(base.tokens) ? [...base.tokens] : [];
|
|
2817
|
+
const diffKind = Array.isArray(base.diffKind) ? [...base.diffKind] : [];
|
|
2818
|
+
const oldNo = Array.isArray(base.oldNo) ? [...base.oldNo] : [];
|
|
2819
|
+
const newNo = Array.isArray(base.newNo) ? [...base.newNo] : [];
|
|
2820
|
+
let includeTokens = Array.isArray(base.tokens);
|
|
2821
|
+
let includeDiffKind = Array.isArray(base.diffKind);
|
|
2822
|
+
let includeOldNo = Array.isArray(base.oldNo);
|
|
2823
|
+
let includeNewNo = Array.isArray(base.newNo);
|
|
2548
2824
|
const baseStart = base.startIndex;
|
|
2549
2825
|
let expectedStart = baseStart + lines.length;
|
|
2550
2826
|
let j = startIndex + 1;
|
|
@@ -2552,6 +2828,7 @@ function mergeAppendLines(window2, startIndex) {
|
|
|
2552
2828
|
while (j < window2.length && mergedCount < APPEND_MERGE_LIMIT) {
|
|
2553
2829
|
const next = window2[j];
|
|
2554
2830
|
if (next.op === "appendLines" && next.at.blockId === base.at.blockId && next.at.nodeId === base.at.nodeId && typeof next.startIndex === "number" && next.startIndex === expectedStart) {
|
|
2831
|
+
const priorLineCount = lines.length;
|
|
2555
2832
|
lines.push(...next.lines ?? []);
|
|
2556
2833
|
const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];
|
|
2557
2834
|
const appendedCount = next.lines?.length ?? 0;
|
|
@@ -2565,6 +2842,50 @@ function mergeAppendLines(window2, startIndex) {
|
|
|
2565
2842
|
highlight.push(null);
|
|
2566
2843
|
}
|
|
2567
2844
|
}
|
|
2845
|
+
if (Array.isArray(next.tokens)) {
|
|
2846
|
+
if (!includeTokens) {
|
|
2847
|
+
tokens.push(...new Array(priorLineCount).fill(null));
|
|
2848
|
+
includeTokens = true;
|
|
2849
|
+
}
|
|
2850
|
+
for (let idx = 0; idx < appendedCount; idx++) {
|
|
2851
|
+
tokens.push(idx < next.tokens.length ? next.tokens[idx] ?? null : null);
|
|
2852
|
+
}
|
|
2853
|
+
} else if (includeTokens) {
|
|
2854
|
+
tokens.push(...new Array(appendedCount).fill(null));
|
|
2855
|
+
}
|
|
2856
|
+
if (Array.isArray(next.diffKind)) {
|
|
2857
|
+
if (!includeDiffKind) {
|
|
2858
|
+
diffKind.push(...new Array(priorLineCount).fill(null));
|
|
2859
|
+
includeDiffKind = true;
|
|
2860
|
+
}
|
|
2861
|
+
for (let idx = 0; idx < appendedCount; idx++) {
|
|
2862
|
+
diffKind.push(idx < next.diffKind.length ? next.diffKind[idx] ?? null : null);
|
|
2863
|
+
}
|
|
2864
|
+
} else if (includeDiffKind) {
|
|
2865
|
+
diffKind.push(...new Array(appendedCount).fill(null));
|
|
2866
|
+
}
|
|
2867
|
+
if (Array.isArray(next.oldNo)) {
|
|
2868
|
+
if (!includeOldNo) {
|
|
2869
|
+
oldNo.push(...new Array(priorLineCount).fill(null));
|
|
2870
|
+
includeOldNo = true;
|
|
2871
|
+
}
|
|
2872
|
+
for (let idx = 0; idx < appendedCount; idx++) {
|
|
2873
|
+
oldNo.push(idx < next.oldNo.length ? next.oldNo[idx] ?? null : null);
|
|
2874
|
+
}
|
|
2875
|
+
} else if (includeOldNo) {
|
|
2876
|
+
oldNo.push(...new Array(appendedCount).fill(null));
|
|
2877
|
+
}
|
|
2878
|
+
if (Array.isArray(next.newNo)) {
|
|
2879
|
+
if (!includeNewNo) {
|
|
2880
|
+
newNo.push(...new Array(priorLineCount).fill(null));
|
|
2881
|
+
includeNewNo = true;
|
|
2882
|
+
}
|
|
2883
|
+
for (let idx = 0; idx < appendedCount; idx++) {
|
|
2884
|
+
newNo.push(idx < next.newNo.length ? next.newNo[idx] ?? null : null);
|
|
2885
|
+
}
|
|
2886
|
+
} else if (includeNewNo) {
|
|
2887
|
+
newNo.push(...new Array(appendedCount).fill(null));
|
|
2888
|
+
}
|
|
2568
2889
|
expectedStart = baseStart + lines.length;
|
|
2569
2890
|
mergedCount++;
|
|
2570
2891
|
j++;
|
|
@@ -2575,7 +2896,11 @@ function mergeAppendLines(window2, startIndex) {
|
|
|
2575
2896
|
const combined = {
|
|
2576
2897
|
...base,
|
|
2577
2898
|
lines,
|
|
2578
|
-
highlight: highlight.length > 0 ? highlight : void 0
|
|
2899
|
+
highlight: highlight.length > 0 ? highlight : void 0,
|
|
2900
|
+
tokens: includeTokens && tokens.length > 0 ? tokens : void 0,
|
|
2901
|
+
diffKind: includeDiffKind && diffKind.length > 0 ? diffKind : void 0,
|
|
2902
|
+
oldNo: includeOldNo && oldNo.length > 0 ? oldNo : void 0,
|
|
2903
|
+
newNo: includeNewNo && newNo.length > 0 ? newNo : void 0
|
|
2579
2904
|
};
|
|
2580
2905
|
return { patch: combined, nextIndex: j };
|
|
2581
2906
|
}
|
|
@@ -2915,130 +3240,6 @@ var CustomStreamingMatcher = class {
|
|
|
2915
3240
|
return false;
|
|
2916
3241
|
}
|
|
2917
3242
|
};
|
|
2918
|
-
|
|
2919
|
-
// src/streaming/inline-streaming.ts
|
|
2920
|
-
var DEFAULT_FORMAT_ANTICIPATION = {
|
|
2921
|
-
inline: false,
|
|
2922
|
-
mathInline: false,
|
|
2923
|
-
mathBlock: false,
|
|
2924
|
-
html: false,
|
|
2925
|
-
mdx: false,
|
|
2926
|
-
regex: false
|
|
2927
|
-
};
|
|
2928
|
-
function normalizeFormatAnticipation(input) {
|
|
2929
|
-
if (input === true) {
|
|
2930
|
-
return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
|
|
2931
|
-
}
|
|
2932
|
-
if (!input) {
|
|
2933
|
-
return { ...DEFAULT_FORMAT_ANTICIPATION };
|
|
2934
|
-
}
|
|
2935
|
-
return {
|
|
2936
|
-
inline: input.inline ?? false,
|
|
2937
|
-
mathInline: input.mathInline ?? false,
|
|
2938
|
-
mathBlock: input.mathBlock ?? false,
|
|
2939
|
-
html: input.html ?? false,
|
|
2940
|
-
mdx: input.mdx ?? false,
|
|
2941
|
-
regex: input.regex ?? false
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
function prepareInlineStreamingContent(content, options) {
|
|
2945
|
-
const enableMath = options?.math !== false;
|
|
2946
|
-
const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
|
|
2947
|
-
const enableInlineAnticipation = anticipation.inline;
|
|
2948
|
-
const enableMathInlineAnticipation = anticipation.mathInline;
|
|
2949
|
-
const enableMathBlockAnticipation = anticipation.mathBlock;
|
|
2950
|
-
const stack = [];
|
|
2951
|
-
const toggleToken = (token) => {
|
|
2952
|
-
const last = stack[stack.length - 1];
|
|
2953
|
-
if (last === token) {
|
|
2954
|
-
stack.pop();
|
|
2955
|
-
} else {
|
|
2956
|
-
stack.push(token);
|
|
2957
|
-
}
|
|
2958
|
-
};
|
|
2959
|
-
let mathDisplayOpen = false;
|
|
2960
|
-
let mathDisplayCrossedNewline = false;
|
|
2961
|
-
for (let i = 0; i < content.length; i++) {
|
|
2962
|
-
const code = content.charCodeAt(i);
|
|
2963
|
-
if (code === 10 || code === 13) {
|
|
2964
|
-
if (mathDisplayOpen) {
|
|
2965
|
-
mathDisplayCrossedNewline = true;
|
|
2966
|
-
}
|
|
2967
|
-
continue;
|
|
2968
|
-
}
|
|
2969
|
-
if (code === 96) {
|
|
2970
|
-
toggleToken("code");
|
|
2971
|
-
continue;
|
|
2972
|
-
}
|
|
2973
|
-
if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
|
|
2974
|
-
toggleToken("strike");
|
|
2975
|
-
i += 1;
|
|
2976
|
-
continue;
|
|
2977
|
-
}
|
|
2978
|
-
if (code === 42) {
|
|
2979
|
-
if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
|
|
2980
|
-
toggleToken("strong");
|
|
2981
|
-
i += 1;
|
|
2982
|
-
} else {
|
|
2983
|
-
toggleToken("em");
|
|
2984
|
-
}
|
|
2985
|
-
continue;
|
|
2986
|
-
}
|
|
2987
|
-
if (enableMath && code === 36) {
|
|
2988
|
-
if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
|
|
2989
|
-
toggleToken("math-display");
|
|
2990
|
-
if (mathDisplayOpen) {
|
|
2991
|
-
mathDisplayOpen = false;
|
|
2992
|
-
mathDisplayCrossedNewline = false;
|
|
2993
|
-
} else {
|
|
2994
|
-
mathDisplayOpen = true;
|
|
2995
|
-
mathDisplayCrossedNewline = false;
|
|
2996
|
-
}
|
|
2997
|
-
i += 1;
|
|
2998
|
-
} else {
|
|
2999
|
-
toggleToken("math-inline");
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
|
|
3004
|
-
const hasIncompleteMathInline = stack.includes("math-inline");
|
|
3005
|
-
const hasIncompleteMathDisplay = stack.includes("math-display");
|
|
3006
|
-
const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
|
|
3007
|
-
if (enableMath && hasIncompleteMath) {
|
|
3008
|
-
if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
|
|
3009
|
-
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
3010
|
-
}
|
|
3011
|
-
if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
|
|
3012
|
-
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
if (hasIncompleteFormatting && !enableInlineAnticipation) {
|
|
3016
|
-
return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
|
|
3017
|
-
}
|
|
3018
|
-
if (!hasIncompleteFormatting && !hasIncompleteMath) {
|
|
3019
|
-
return { kind: "parse", status: "complete", content, appended: "" };
|
|
3020
|
-
}
|
|
3021
|
-
const appendForToken = (token) => {
|
|
3022
|
-
switch (token) {
|
|
3023
|
-
case "code":
|
|
3024
|
-
return "`";
|
|
3025
|
-
case "strike":
|
|
3026
|
-
return "~~";
|
|
3027
|
-
case "strong":
|
|
3028
|
-
return "**";
|
|
3029
|
-
case "em":
|
|
3030
|
-
return "*";
|
|
3031
|
-
case "math-inline":
|
|
3032
|
-
return "$";
|
|
3033
|
-
case "math-display":
|
|
3034
|
-
return "$$";
|
|
3035
|
-
default:
|
|
3036
|
-
return "";
|
|
3037
|
-
}
|
|
3038
|
-
};
|
|
3039
|
-
const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
|
|
3040
|
-
return { kind: "parse", status: "anticipated", content: content + appended, appended };
|
|
3041
|
-
}
|
|
3042
3243
|
export {
|
|
3043
3244
|
CSP_HEADERS,
|
|
3044
3245
|
CustomStreamingMatcher,
|
|
@@ -3077,12 +3278,14 @@ export {
|
|
|
3077
3278
|
findClosingHtmlTag,
|
|
3078
3279
|
generateBlockId,
|
|
3079
3280
|
getBlockKey,
|
|
3281
|
+
getDefaultCodeWrapperAttributes,
|
|
3080
3282
|
initializeSecurity,
|
|
3081
3283
|
initializeTrustedTypesPolicy,
|
|
3082
3284
|
inlineNodesToPlainText,
|
|
3083
3285
|
isLikelyMdxComponent,
|
|
3084
3286
|
normalizeBlockquoteText,
|
|
3085
3287
|
normalizeFormatAnticipation,
|
|
3288
|
+
normalizeHighlightedLines,
|
|
3086
3289
|
normalizeLang,
|
|
3087
3290
|
parseCodeFenceInfo,
|
|
3088
3291
|
prepareInlineStreamingContent,
|
package/dist/mixed-content.cjs
CHANGED
|
@@ -217,6 +217,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
|
|
|
217
217
|
const mdxAutoClose = options?.mdx?.autoClose === true;
|
|
218
218
|
const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
|
|
219
219
|
const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
|
|
220
|
+
const protectedRanges = options?.protectedRanges ?? [];
|
|
221
|
+
const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
|
|
220
222
|
while (match !== null) {
|
|
221
223
|
const start = match.index;
|
|
222
224
|
const tagName = match[1];
|
|
@@ -231,6 +233,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
|
|
|
231
233
|
continue;
|
|
232
234
|
}
|
|
233
235
|
let end = tagPattern.lastIndex;
|
|
236
|
+
if (protectedKinds && protectedRanges.length > 0) {
|
|
237
|
+
const absoluteStart = baseIsFinite ? baseOffset + start : start;
|
|
238
|
+
const absoluteEnd = baseIsFinite ? baseOffset + end : end;
|
|
239
|
+
const covered = protectedRanges.some(
|
|
240
|
+
(range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
|
|
241
|
+
);
|
|
242
|
+
if (covered) {
|
|
243
|
+
tagPattern.lastIndex = start + 1;
|
|
244
|
+
match = tagPattern.exec(source);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
234
248
|
if (!isSelfClosing && !mdxAllowed) {
|
|
235
249
|
const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
|
|
236
250
|
if (closingIndex === -1) {
|
package/dist/mixed-content.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InlineNode, MixedContentSegment } from './types.cjs';
|
|
1
|
+
import { ProtectedRange, InlineNode, MixedContentSegment } from './types.cjs';
|
|
2
2
|
|
|
3
3
|
interface MixedContentAutoCloseHtmlOptions {
|
|
4
4
|
autoClose?: boolean;
|
|
@@ -13,6 +13,8 @@ interface MixedContentAutoCloseMdxOptions {
|
|
|
13
13
|
interface MixedContentOptions {
|
|
14
14
|
html?: MixedContentAutoCloseHtmlOptions;
|
|
15
15
|
mdx?: MixedContentAutoCloseMdxOptions;
|
|
16
|
+
protectedRanges?: ReadonlyArray<ProtectedRange>;
|
|
17
|
+
protectedRangeKinds?: ReadonlyArray<ProtectedRange["kind"]>;
|
|
16
18
|
}
|
|
17
19
|
declare function extractMixedContentSegments(raw: string, baseOffset: number | undefined, parseInline: (content: string) => InlineNode[], options?: MixedContentOptions): MixedContentSegment[];
|
|
18
20
|
declare function isLikelyMdxComponent(tagName: string): boolean;
|