@copilotkit/markdown 2.0.0-rc.19 → 2.0.0-rc.21
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/README.md +24 -7
- package/dist/dom/index.cjs +255 -0
- package/dist/dom/index.cjs.map +1 -0
- package/dist/dom/index.d.cts +93 -0
- package/dist/dom/index.d.cts.map +1 -0
- package/dist/dom/index.d.mts +93 -0
- package/dist/dom/index.d.mts.map +1 -0
- package/dist/dom/index.mjs +254 -0
- package/dist/dom/index.mjs.map +1 -0
- package/dist/index.cjs +522 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +271 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +512 -12
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.cjs +66 -458
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +13 -22
- package/dist/react/index.d.cts.map +1 -1
- package/dist/react/index.d.mts +13 -22
- package/dist/react/index.d.mts.map +1 -1
- package/dist/react/index.mjs +67 -459
- package/dist/react/index.mjs.map +1 -1
- package/dist/react-native/index.cjs.map +1 -1
- package/dist/react-native/index.d.cts.map +1 -1
- package/dist/react-native/index.d.mts.map +1 -1
- package/dist/react-native/index.mjs.map +1 -1
- package/dist/vue/index.cjs +86 -199
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +46 -16
- package/dist/vue/index.d.cts.map +1 -1
- package/dist/vue/index.d.mts +46 -16
- package/dist/vue/index.d.mts.map +1 -1
- package/dist/vue/index.mjs +88 -201
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +6 -2
package/dist/react/index.mjs
CHANGED
|
@@ -1,67 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { STREAMING_MARKDOWN_CARET_ATTRS, STREAMING_MARKDOWN_CARET_STYLE, STREAMING_MARKDOWN_ROOT_CLASS, buildStreamingMarkdownRenderTree, findStreamingMarkdownOpenNode, getStreamingMarkdownStyles, updateStreamingMarkdownParserSession } from "@copilotkit/markdown";
|
|
2
|
+
import { Fragment, createElement, useMemo, useRef } from "react";
|
|
3
3
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/react/use-streaming-markdown-parser.tsx
|
|
6
|
-
const DEFAULT_OPTIONS = {
|
|
7
|
-
segmenter: true,
|
|
8
|
-
enableTables: true,
|
|
9
|
-
enableAutolinks: true
|
|
10
|
-
};
|
|
11
|
-
function normalizeOptions(options) {
|
|
12
|
-
return {
|
|
13
|
-
segmenter: options?.segmenter ?? DEFAULT_OPTIONS.segmenter,
|
|
14
|
-
enableTables: options?.enableTables ?? DEFAULT_OPTIONS.enableTables,
|
|
15
|
-
enableAutolinks: options?.enableAutolinks ?? DEFAULT_OPTIONS.enableAutolinks
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function getSegmenterKey(segmenter) {
|
|
19
|
-
if (segmenter === true || segmenter === false) return String(segmenter);
|
|
20
|
-
return `object:${segmenter.locale ?? ""}:${segmenter.granularity ?? "word"}`;
|
|
21
|
-
}
|
|
22
|
-
function getOptionsKey(options) {
|
|
23
|
-
return `${getSegmenterKey(options.segmenter)}|tables:${String(options.enableTables)}|autolinks:${String(options.enableAutolinks)}`;
|
|
24
|
-
}
|
|
25
|
-
function parseFullText(text, options, isCompleteInput) {
|
|
26
|
-
const initialState = createStreamingMarkdownParserState(options);
|
|
27
|
-
const parsedState = text.length > 0 ? parseStreamingMarkdownChunk(initialState, text) : initialState;
|
|
28
|
-
return isCompleteInput ? finalizeStreamingMarkdown(parsedState) : parsedState;
|
|
29
|
-
}
|
|
30
|
-
function createSession(text, options, optionsKey, isCompleteInput) {
|
|
31
|
-
return {
|
|
32
|
-
parserState: parseFullText(text, options, isCompleteInput),
|
|
33
|
-
text,
|
|
34
|
-
optionsKey,
|
|
35
|
-
isCompleteInput
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function resolveNextSession(previous, text, options, optionsKey, isCompleteInput) {
|
|
39
|
-
const optionsChanged = previous.optionsKey !== optionsKey;
|
|
40
|
-
const completionChanged = previous.isCompleteInput !== isCompleteInput;
|
|
41
|
-
if (optionsChanged) return createSession(text, options, optionsKey, isCompleteInput);
|
|
42
|
-
const textChanged = text !== previous.text;
|
|
43
|
-
if (!textChanged && !completionChanged) return previous;
|
|
44
|
-
if (!textChanged && completionChanged) {
|
|
45
|
-
const parserState = isCompleteInput ? finalizeStreamingMarkdown(previous.parserState) : parseStreamingMarkdownChunk(previous.parserState, "");
|
|
46
|
-
return {
|
|
47
|
-
...previous,
|
|
48
|
-
parserState,
|
|
49
|
-
isCompleteInput
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
let nextParserState;
|
|
53
|
-
if (text.startsWith(previous.text)) {
|
|
54
|
-
const suffix = text.slice(previous.text.length);
|
|
55
|
-
nextParserState = suffix.length > 0 ? parseStreamingMarkdownChunk(previous.parserState, suffix) : previous.parserState;
|
|
56
|
-
} else nextParserState = parseFullText(text, options, false);
|
|
57
|
-
if (isCompleteInput) nextParserState = finalizeStreamingMarkdown(nextParserState);
|
|
58
|
-
return {
|
|
59
|
-
parserState: nextParserState,
|
|
60
|
-
text,
|
|
61
|
-
optionsKey,
|
|
62
|
-
isCompleteInput
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
6
|
/**
|
|
66
7
|
* Internal prop-driven hook for streaming markdown parsing in React.
|
|
67
8
|
*
|
|
@@ -72,42 +13,19 @@ function resolveNextSession(previous, text, options, optionsKey, isCompleteInput
|
|
|
72
13
|
*/
|
|
73
14
|
function useStreamingMarkdownParser(text, options, isCompleteInput = false) {
|
|
74
15
|
const sessionRef = useRef(null);
|
|
75
|
-
const segmenter = options?.segmenter;
|
|
76
|
-
const segmenterKind = typeof segmenter === "object" && segmenter !== null ? "object" : String(segmenter ?? true);
|
|
77
|
-
const segmenterLocale = typeof segmenter === "object" && segmenter !== null ? segmenter.locale ?? "" : "";
|
|
78
|
-
const segmenterGranularity = typeof segmenter === "object" && segmenter !== null ? segmenter.granularity ?? "word" : "";
|
|
79
|
-
const normalizedOptions = useMemo(() => normalizeOptions(options), [
|
|
80
|
-
options?.enableAutolinks,
|
|
81
|
-
options?.enableTables,
|
|
82
|
-
segmenterKind,
|
|
83
|
-
segmenterLocale,
|
|
84
|
-
segmenterGranularity
|
|
85
|
-
]);
|
|
86
|
-
const optionsKey = getOptionsKey(normalizedOptions);
|
|
87
16
|
return useMemo(() => {
|
|
88
|
-
const next =
|
|
17
|
+
const next = updateStreamingMarkdownParserSession(sessionRef.current, text, options, isCompleteInput);
|
|
89
18
|
sessionRef.current = next;
|
|
90
19
|
return next;
|
|
91
20
|
}, [
|
|
92
21
|
text,
|
|
93
|
-
|
|
94
|
-
optionsKey,
|
|
22
|
+
options,
|
|
95
23
|
isCompleteInput
|
|
96
24
|
]).parserState;
|
|
97
25
|
}
|
|
98
26
|
|
|
99
27
|
//#endregion
|
|
100
28
|
//#region src/react/streaming-markdown-renderer.tsx
|
|
101
|
-
const SAFE_HREF = /^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
|
|
102
|
-
const SAFE_IMG_SRC = /^(https?:|data:image\/(?!svg)|\/|\.\/|\.\.\/)/i;
|
|
103
|
-
function sanitizeHref(href) {
|
|
104
|
-
if (!href) return void 0;
|
|
105
|
-
return SAFE_HREF.test(href.trim()) ? href : void 0;
|
|
106
|
-
}
|
|
107
|
-
function sanitizeImgSrc(src) {
|
|
108
|
-
if (!src) return void 0;
|
|
109
|
-
return SAFE_IMG_SRC.test(src.trim()) ? src : void 0;
|
|
110
|
-
}
|
|
111
29
|
/**
|
|
112
30
|
* Helper for creating typed streaming markdown node renderer maps.
|
|
113
31
|
*
|
|
@@ -116,128 +34,10 @@ function sanitizeImgSrc(src) {
|
|
|
116
34
|
function createStreamingMarkdownNodeRenderers(renderers) {
|
|
117
35
|
return renderers;
|
|
118
36
|
}
|
|
119
|
-
const WORD_JOINER = "";
|
|
120
|
-
const DEFAULT_ROOT_CLASS = "cpk-streaming-markdown-root";
|
|
121
|
-
const DEFAULT_CITATION_CLASS = "cpk-streaming-markdown-citation";
|
|
122
|
-
const DEFAULT_CITATION_LABEL_CLASS = "cpk-streaming-markdown-citation-label";
|
|
123
|
-
const DEFAULT_STYLES = `
|
|
124
|
-
.${DEFAULT_ROOT_CLASS} .cpk-streaming-markdown-segment {
|
|
125
|
-
opacity: 1;
|
|
126
|
-
transition: opacity 400ms ease-out;
|
|
127
|
-
@starting-style {
|
|
128
|
-
opacity: 0;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_CLASS} {
|
|
133
|
-
vertical-align: baseline;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_LABEL_CLASS} {
|
|
137
|
-
display: inline-flex;
|
|
138
|
-
align-items: center;
|
|
139
|
-
justify-content: center;
|
|
140
|
-
inline-size: 1.4em;
|
|
141
|
-
block-size: 1.4em;
|
|
142
|
-
border-radius: 999px;
|
|
143
|
-
border: 1px solid hsl(0 0% 50% / 0.35);
|
|
144
|
-
background-color: hsl(0 0% 50% / 0.16);
|
|
145
|
-
color: inherit;
|
|
146
|
-
font-size: 0.7em;
|
|
147
|
-
line-height: 1;
|
|
148
|
-
font-variant-numeric: tabular-nums;
|
|
149
|
-
text-decoration: none;
|
|
150
|
-
transform: translateY(-0.15em);
|
|
151
|
-
}
|
|
152
|
-
`;
|
|
153
|
-
const DEFAULT_STYLES_FALLBACK = `
|
|
154
|
-
.${DEFAULT_ROOT_CLASS} .cpk-streaming-markdown-segment {
|
|
155
|
-
opacity: 1;
|
|
156
|
-
transition: opacity 400ms ease-out;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_CLASS} {
|
|
160
|
-
vertical-align: baseline;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_LABEL_CLASS} {
|
|
164
|
-
display: inline-flex;
|
|
165
|
-
align-items: center;
|
|
166
|
-
justify-content: center;
|
|
167
|
-
inline-size: 1.4em;
|
|
168
|
-
block-size: 1.4em;
|
|
169
|
-
border-radius: 999px;
|
|
170
|
-
border: 1px solid hsl(0 0% 50% / 0.35);
|
|
171
|
-
background-color: hsl(0 0% 50% / 0.16);
|
|
172
|
-
color: inherit;
|
|
173
|
-
font-size: 0.7em;
|
|
174
|
-
line-height: 1;
|
|
175
|
-
font-variant-numeric: tabular-nums;
|
|
176
|
-
text-decoration: none;
|
|
177
|
-
transform: translateY(-0.15em);
|
|
178
|
-
}
|
|
179
|
-
`;
|
|
180
|
-
const NODE_TYPE_TO_RENDERER_KEY = {
|
|
181
|
-
document: "document",
|
|
182
|
-
paragraph: "paragraph",
|
|
183
|
-
heading: "heading",
|
|
184
|
-
blockquote: "blockquote",
|
|
185
|
-
list: "list",
|
|
186
|
-
"list-item": "listItem",
|
|
187
|
-
"code-block": "codeBlock",
|
|
188
|
-
table: "table",
|
|
189
|
-
"table-row": "tableRow",
|
|
190
|
-
"table-cell": "tableCell",
|
|
191
|
-
"thematic-break": "thematicBreak",
|
|
192
|
-
text: "text",
|
|
193
|
-
em: "em",
|
|
194
|
-
strong: "strong",
|
|
195
|
-
strikethrough: "strikethrough",
|
|
196
|
-
"inline-code": "inlineCode",
|
|
197
|
-
"soft-break": "softBreak",
|
|
198
|
-
"hard-break": "hardBreak",
|
|
199
|
-
image: "image",
|
|
200
|
-
link: "link",
|
|
201
|
-
autolink: "autolink",
|
|
202
|
-
citation: "citation"
|
|
203
|
-
};
|
|
204
|
-
function renderWithOverride(node, context, defaultNode, children = null, citation) {
|
|
205
|
-
const rendererKey = NODE_TYPE_TO_RENDERER_KEY[node.type];
|
|
206
|
-
const renderer = context.nodeRenderers?.[rendererKey] ?? context.nodeRenderers?.node;
|
|
207
|
-
if (!renderer) return defaultNode;
|
|
208
|
-
return renderer({
|
|
209
|
-
node,
|
|
210
|
-
children,
|
|
211
|
-
defaultNode,
|
|
212
|
-
citation
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
function renderChildren(node, context) {
|
|
216
|
-
return node.children.map((childId) => {
|
|
217
|
-
const childNode = context.nodeById.get(childId);
|
|
218
|
-
if (!childNode) return null;
|
|
219
|
-
return /* @__PURE__ */ jsx(Fragment, { children: renderNode(childNode, context) }, childId);
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
function renderCaret(nodeId, context) {
|
|
223
|
-
if (context.caretTargetNodeId !== nodeId) return null;
|
|
224
|
-
return /* @__PURE__ */ jsx(Fragment, { children: context.caretNode }, `caret-${nodeId}`);
|
|
225
|
-
}
|
|
226
37
|
function renderDefaultCaret() {
|
|
227
38
|
return /* @__PURE__ */ jsx("span", {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"data-streaming-markdown-caret": true,
|
|
231
|
-
style: {
|
|
232
|
-
display: "inline-block",
|
|
233
|
-
width: "0.48em",
|
|
234
|
-
height: "0.48em",
|
|
235
|
-
marginInlineStart: "0.08em",
|
|
236
|
-
verticalAlign: "-0.08em",
|
|
237
|
-
borderRadius: "999px",
|
|
238
|
-
backgroundColor: "currentColor",
|
|
239
|
-
opacity: .55
|
|
240
|
-
}
|
|
39
|
+
...STREAMING_MARKDOWN_CARET_ATTRS,
|
|
40
|
+
style: STREAMING_MARKDOWN_CARET_STYLE
|
|
241
41
|
});
|
|
242
42
|
}
|
|
243
43
|
function resolveCaretNode(caret, props) {
|
|
@@ -246,289 +46,97 @@ function resolveCaretNode(caret, props) {
|
|
|
246
46
|
if (typeof caret === "function") return caret(props);
|
|
247
47
|
return caret;
|
|
248
48
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return node.segments.map((segment) => /* @__PURE__ */ jsx("span", {
|
|
258
|
-
className: "cpk-streaming-markdown-segment",
|
|
259
|
-
"data-streaming-markdown-segment-kind": segment.kind,
|
|
260
|
-
"data-streaming-markdown-whitespace": String(segment.isWhitespace),
|
|
261
|
-
children: segment.noBreakBefore ? `${WORD_JOINER}${segment.text}` : segment.text
|
|
262
|
-
}, `segment-${node.id}-${segment.start}-${segment.kind}`));
|
|
263
|
-
}
|
|
264
|
-
function handleLinkClick(context, node, url) {
|
|
265
|
-
return (event) => {
|
|
266
|
-
context.onLinkClickRef.current?.(event, url, node);
|
|
49
|
+
function toReactProps(element, context) {
|
|
50
|
+
const props = { key: element.key };
|
|
51
|
+
const entries = Object.entries(element.attrs);
|
|
52
|
+
for (const [name, value] of entries) props[name === "class" ? "className" : name] = value;
|
|
53
|
+
if (element.tag === "input") props.readOnly = true;
|
|
54
|
+
const action = element.action;
|
|
55
|
+
if (action?.type === "link") props.onClick = (event) => {
|
|
56
|
+
context.onLinkClickRef.current?.(event, action.url, action.node);
|
|
267
57
|
};
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const number = node.number ?? context.citations.numbers[node.idRef] ?? node.idRef;
|
|
271
|
-
const definition = context.citations.definitions[node.idRef];
|
|
272
|
-
return {
|
|
273
|
-
id: node.idRef,
|
|
274
|
-
number,
|
|
275
|
-
definition
|
|
58
|
+
if (action?.type === "citation") props.onClick = (event) => {
|
|
59
|
+
context.onCitationClickRef.current?.(event, action.citation, action.node);
|
|
276
60
|
};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
"
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
},
|
|
304
|
-
children: label
|
|
305
|
-
})
|
|
306
|
-
}, node.id);
|
|
307
|
-
}
|
|
308
|
-
function renderContainerNode(node, context) {
|
|
309
|
-
const children = /* @__PURE__ */ jsxs(Fragment$1, { children: [renderChildren(node, context), renderCaret(node.id, context)] });
|
|
310
|
-
if (node.type === "document") return renderWithOverride(node, context, /* @__PURE__ */ jsx(Fragment, { children }, node.id), children);
|
|
311
|
-
if (node.type === "paragraph") return renderWithOverride(node, context, /* @__PURE__ */ jsx("p", {
|
|
312
|
-
"data-streaming-markdown-node": node.type,
|
|
313
|
-
"data-node-open": String(!node.closed),
|
|
314
|
-
children
|
|
315
|
-
}, node.id), children);
|
|
316
|
-
if (node.type === "heading") {
|
|
317
|
-
const headingTag = `h${node.level}`;
|
|
318
|
-
return renderWithOverride(node, context, headingTag === "h1" ? /* @__PURE__ */ jsx("h1", {
|
|
319
|
-
"data-streaming-markdown-node": node.type,
|
|
320
|
-
"data-node-open": String(!node.closed),
|
|
321
|
-
children
|
|
322
|
-
}, node.id) : headingTag === "h2" ? /* @__PURE__ */ jsx("h2", {
|
|
323
|
-
"data-streaming-markdown-node": node.type,
|
|
324
|
-
"data-node-open": String(!node.closed),
|
|
325
|
-
children
|
|
326
|
-
}, node.id) : headingTag === "h3" ? /* @__PURE__ */ jsx("h3", {
|
|
327
|
-
"data-streaming-markdown-node": node.type,
|
|
328
|
-
"data-node-open": String(!node.closed),
|
|
329
|
-
children
|
|
330
|
-
}, node.id) : headingTag === "h4" ? /* @__PURE__ */ jsx("h4", {
|
|
331
|
-
"data-streaming-markdown-node": node.type,
|
|
332
|
-
"data-node-open": String(!node.closed),
|
|
333
|
-
children
|
|
334
|
-
}, node.id) : headingTag === "h5" ? /* @__PURE__ */ jsx("h5", {
|
|
335
|
-
"data-streaming-markdown-node": node.type,
|
|
336
|
-
"data-node-open": String(!node.closed),
|
|
337
|
-
children
|
|
338
|
-
}, node.id) : /* @__PURE__ */ jsx("h6", {
|
|
339
|
-
"data-streaming-markdown-node": node.type,
|
|
340
|
-
"data-node-open": String(!node.closed),
|
|
341
|
-
children
|
|
342
|
-
}, node.id), children);
|
|
343
|
-
}
|
|
344
|
-
if (node.type === "blockquote") return renderWithOverride(node, context, /* @__PURE__ */ jsx("blockquote", {
|
|
345
|
-
"data-streaming-markdown-node": node.type,
|
|
346
|
-
"data-node-open": String(!node.closed),
|
|
347
|
-
children
|
|
348
|
-
}, node.id), children);
|
|
349
|
-
if (node.type === "list") {
|
|
350
|
-
if (node.ordered) return renderWithOverride(node, context, /* @__PURE__ */ jsx("ol", {
|
|
351
|
-
start: node.start ?? void 0,
|
|
352
|
-
"data-streaming-markdown-node": node.type,
|
|
353
|
-
"data-node-open": String(!node.closed),
|
|
354
|
-
"data-list-tight": String(node.tight),
|
|
355
|
-
children
|
|
356
|
-
}, node.id), children);
|
|
357
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx("ul", {
|
|
358
|
-
"data-streaming-markdown-node": node.type,
|
|
359
|
-
"data-node-open": String(!node.closed),
|
|
360
|
-
"data-list-tight": String(node.tight),
|
|
361
|
-
children
|
|
362
|
-
}, node.id), children);
|
|
363
|
-
}
|
|
364
|
-
if (node.type === "list-item") return renderWithOverride(node, context, /* @__PURE__ */ jsx("li", {
|
|
365
|
-
"data-streaming-markdown-node": node.type,
|
|
366
|
-
"data-node-open": String(!node.closed),
|
|
367
|
-
children
|
|
368
|
-
}, node.id), children);
|
|
369
|
-
if (node.type === "table") return renderWithOverride(node, context, /* @__PURE__ */ jsx("table", {
|
|
370
|
-
"data-streaming-markdown-node": node.type,
|
|
371
|
-
"data-node-open": String(!node.closed),
|
|
372
|
-
children: /* @__PURE__ */ jsx("tbody", { children })
|
|
373
|
-
}, node.id), children);
|
|
374
|
-
if (node.type === "table-row") return renderWithOverride(node, context, /* @__PURE__ */ jsx("tr", {
|
|
375
|
-
"data-streaming-markdown-node": node.type,
|
|
376
|
-
"data-node-open": String(!node.closed),
|
|
377
|
-
children
|
|
378
|
-
}, node.id), children);
|
|
379
|
-
if (node.type === "table-cell") {
|
|
380
|
-
const parent = node.parentId == null ? void 0 : context.nodeById.get(node.parentId);
|
|
381
|
-
if (parent?.type === "table-row" && parent.isHeader) return renderWithOverride(node, context, /* @__PURE__ */ jsx("th", {
|
|
382
|
-
"data-streaming-markdown-node": node.type,
|
|
383
|
-
"data-node-open": String(!node.closed),
|
|
384
|
-
children
|
|
385
|
-
}, node.id), children);
|
|
386
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx("td", {
|
|
387
|
-
"data-streaming-markdown-node": node.type,
|
|
388
|
-
"data-node-open": String(!node.closed),
|
|
389
|
-
children
|
|
390
|
-
}, node.id), children);
|
|
61
|
+
return props;
|
|
62
|
+
}
|
|
63
|
+
const VOID_TAGS = new Set([
|
|
64
|
+
"br",
|
|
65
|
+
"hr",
|
|
66
|
+
"img",
|
|
67
|
+
"input"
|
|
68
|
+
]);
|
|
69
|
+
function renderList(children, context, slot) {
|
|
70
|
+
return children.map((child) => renderChild(child, context, slot));
|
|
71
|
+
}
|
|
72
|
+
/** Unwraps single-entry lists so overrides see `"text"` rather than `["text"]`. */
|
|
73
|
+
function collapse(nodes) {
|
|
74
|
+
return nodes.length === 1 ? nodes[0] : nodes;
|
|
75
|
+
}
|
|
76
|
+
function renderChild(child, context, slot) {
|
|
77
|
+
switch (child.kind) {
|
|
78
|
+
case "node": return renderRenderNode(child, context);
|
|
79
|
+
case "element": {
|
|
80
|
+
const props = toReactProps(child, context);
|
|
81
|
+
return VOID_TAGS.has(child.tag) ? createElement(child.tag, props) : createElement(child.tag, props, ...renderList(child.children, context, slot));
|
|
82
|
+
}
|
|
83
|
+
case "text": return child.text;
|
|
84
|
+
case "caret": return /* @__PURE__ */ jsx(Fragment, { children: context.caretNode }, child.key);
|
|
85
|
+
case "slot": return /* @__PURE__ */ jsx(Fragment, { children: slot }, child.key);
|
|
86
|
+
default: return child;
|
|
391
87
|
}
|
|
392
|
-
if (node.type === "em") return renderWithOverride(node, context, /* @__PURE__ */ jsx("em", {
|
|
393
|
-
"data-streaming-markdown-node": node.type,
|
|
394
|
-
"data-node-open": String(!node.closed),
|
|
395
|
-
children
|
|
396
|
-
}, node.id), children);
|
|
397
|
-
if (node.type === "strong") return renderWithOverride(node, context, /* @__PURE__ */ jsx("strong", {
|
|
398
|
-
"data-streaming-markdown-node": node.type,
|
|
399
|
-
"data-node-open": String(!node.closed),
|
|
400
|
-
children
|
|
401
|
-
}, node.id), children);
|
|
402
|
-
if (node.type === "strikethrough") return renderWithOverride(node, context, /* @__PURE__ */ jsx("s", {
|
|
403
|
-
"data-streaming-markdown-node": node.type,
|
|
404
|
-
"data-node-open": String(!node.closed),
|
|
405
|
-
children
|
|
406
|
-
}, node.id), children);
|
|
407
|
-
const href = sanitizeHref(node.url);
|
|
408
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx("a", {
|
|
409
|
-
href,
|
|
410
|
-
target: "_blank",
|
|
411
|
-
rel: "noopener noreferrer",
|
|
412
|
-
title: node.title,
|
|
413
|
-
onClick: href ? handleLinkClick(context, node, href) : void 0,
|
|
414
|
-
"data-streaming-markdown-node": node.type,
|
|
415
|
-
"data-node-open": String(!node.closed),
|
|
416
|
-
children
|
|
417
|
-
}, node.id), children);
|
|
418
88
|
}
|
|
419
|
-
function renderNode
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
},
|
|
429
|
-
if (node.type === "thematic-break") return renderWithOverride(node, context, /* @__PURE__ */ jsx("hr", {
|
|
430
|
-
"data-streaming-markdown-node": node.type,
|
|
431
|
-
"data-node-open": String(!node.closed)
|
|
432
|
-
}, node.id));
|
|
433
|
-
if (node.type === "text") {
|
|
434
|
-
const children = renderTextSegments(node);
|
|
435
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx(Fragment, { children }, node.id), children);
|
|
436
|
-
}
|
|
437
|
-
if (node.type === "inline-code") return renderWithOverride(node, context, /* @__PURE__ */ jsx("code", {
|
|
438
|
-
"data-streaming-markdown-node": node.type,
|
|
439
|
-
"data-node-open": String(!node.closed),
|
|
440
|
-
children: node.text
|
|
441
|
-
}, node.id), node.text);
|
|
442
|
-
if (node.type === "soft-break") {
|
|
443
|
-
const children = "\n";
|
|
444
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx(Fragment, { children }, node.id), children);
|
|
445
|
-
}
|
|
446
|
-
if (node.type === "hard-break") return renderWithOverride(node, context, /* @__PURE__ */ jsx("br", {
|
|
447
|
-
"data-streaming-markdown-node": node.type,
|
|
448
|
-
"data-node-open": String(!node.closed)
|
|
449
|
-
}, node.id));
|
|
450
|
-
if (node.type === "image") return renderWithOverride(node, context, /* @__PURE__ */ jsx("img", {
|
|
451
|
-
src: sanitizeImgSrc(node.url),
|
|
452
|
-
alt: node.alt,
|
|
453
|
-
title: node.title,
|
|
454
|
-
"data-streaming-markdown-node": node.type,
|
|
455
|
-
"data-node-open": String(!node.closed)
|
|
456
|
-
}, node.id));
|
|
457
|
-
if (node.type === "autolink") {
|
|
458
|
-
const href = sanitizeHref(node.url);
|
|
459
|
-
return renderWithOverride(node, context, /* @__PURE__ */ jsx("a", {
|
|
460
|
-
href,
|
|
461
|
-
target: "_blank",
|
|
462
|
-
rel: "noopener noreferrer",
|
|
463
|
-
onClick: href ? handleLinkClick(context, node, href) : void 0,
|
|
464
|
-
"data-streaming-markdown-node": node.type,
|
|
465
|
-
"data-node-open": String(!node.closed),
|
|
466
|
-
children: node.text
|
|
467
|
-
}, node.id), node.text);
|
|
468
|
-
}
|
|
469
|
-
const citation = getCitationRenderData(node, context);
|
|
470
|
-
return renderWithOverride(node, context, renderDefaultCitation(node, context, citation), null, citation);
|
|
89
|
+
function renderRenderNode(renderNode, context) {
|
|
90
|
+
const children = renderNode.children === null ? null : collapse(renderList(renderNode.children, context, null));
|
|
91
|
+
const defaultNode = collapse(renderList(renderNode.output, context, children));
|
|
92
|
+
const renderer = context.nodeRenderers?.[renderNode.rendererKey] ?? context.nodeRenderers?.node;
|
|
93
|
+
return /* @__PURE__ */ jsx(Fragment, { children: renderer ? renderer({
|
|
94
|
+
node: renderNode.node,
|
|
95
|
+
children,
|
|
96
|
+
defaultNode,
|
|
97
|
+
citation: renderNode.citation
|
|
98
|
+
}) : defaultNode }, renderNode.key);
|
|
471
99
|
}
|
|
472
100
|
/**
|
|
473
101
|
* React renderer for streaming markdown parser output.
|
|
474
102
|
*
|
|
475
103
|
* @public
|
|
476
104
|
*/
|
|
477
|
-
function StreamingMarkdownRenderer({ children, options, isComplete = false, caret, className, onLinkClick, onCitationClick, nodeRenderers }) {
|
|
105
|
+
function StreamingMarkdownRenderer({ children, options, isComplete = false, caret, className, onLinkClick, onCitationClick, nodeRenderers, nodeClassNames }) {
|
|
478
106
|
const parserState = useStreamingMarkdownParser(children ?? "", options, isComplete);
|
|
479
107
|
const onLinkClickRef = useRef(onLinkClick);
|
|
480
108
|
const onCitationClickRef = useRef(onCitationClick);
|
|
481
109
|
onLinkClickRef.current = onLinkClick;
|
|
482
110
|
onCitationClickRef.current = onCitationClick;
|
|
483
|
-
const
|
|
484
|
-
const map = /* @__PURE__ */ new Map();
|
|
485
|
-
for (const node of parserState.nodes) map.set(node.id, node);
|
|
486
|
-
return map;
|
|
487
|
-
}, [parserState.nodes]);
|
|
488
|
-
const context = useMemo(() => {
|
|
489
|
-
const openNode = findDeepestOpenRenderableNode(parserState.stack, nodeById);
|
|
111
|
+
const content = useMemo(() => {
|
|
490
112
|
const caretNode = resolveCaretNode(caret, {
|
|
491
113
|
isComplete: parserState.isComplete,
|
|
492
|
-
openNode
|
|
114
|
+
openNode: findStreamingMarkdownOpenNode(parserState)
|
|
115
|
+
});
|
|
116
|
+
const tree = buildStreamingMarkdownRenderTree(parserState, {
|
|
117
|
+
caret: caretNode != null,
|
|
118
|
+
nodeClassNames
|
|
493
119
|
});
|
|
494
|
-
|
|
495
|
-
return {
|
|
496
|
-
nodeById,
|
|
497
|
-
citations: parserState.citations,
|
|
120
|
+
if (!tree.root) return null;
|
|
121
|
+
return renderRenderNode(tree.root, {
|
|
498
122
|
onLinkClickRef,
|
|
499
123
|
onCitationClickRef,
|
|
500
124
|
nodeRenderers,
|
|
501
|
-
caretTargetNodeId,
|
|
502
125
|
caretNode
|
|
503
|
-
};
|
|
126
|
+
});
|
|
504
127
|
}, [
|
|
505
|
-
|
|
506
|
-
parserState.citations,
|
|
507
|
-
parserState.isComplete,
|
|
508
|
-
parserState.stack,
|
|
128
|
+
parserState,
|
|
509
129
|
nodeRenderers,
|
|
130
|
+
nodeClassNames,
|
|
510
131
|
caret
|
|
511
132
|
]);
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
const styleText = typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent) ? DEFAULT_STYLES_FALLBACK : DEFAULT_STYLES;
|
|
515
|
-
if (!rootNode) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("style", { children: styleText }), /* @__PURE__ */ jsx("div", {
|
|
516
|
-
className: rootClassName,
|
|
517
|
-
"data-streaming-markdown-root": true
|
|
518
|
-
})] });
|
|
519
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("style", { children: styleText }), /* @__PURE__ */ jsx("div", {
|
|
133
|
+
const rootClassName = className ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${className}` : STREAMING_MARKDOWN_ROOT_CLASS;
|
|
134
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("style", { children: getStreamingMarkdownStyles() }), /* @__PURE__ */ jsx("div", {
|
|
520
135
|
className: rootClassName,
|
|
521
136
|
"data-streaming-markdown-root": true,
|
|
522
|
-
children:
|
|
137
|
+
children: content
|
|
523
138
|
})] });
|
|
524
139
|
}
|
|
525
|
-
function findDeepestOpenRenderableNode(stack, nodeById) {
|
|
526
|
-
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
527
|
-
const candidate = nodeById.get(stack[index]) ?? null;
|
|
528
|
-
if (candidate && candidate.type !== "document") return candidate;
|
|
529
|
-
}
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
532
140
|
|
|
533
141
|
//#endregion
|
|
534
142
|
export { StreamingMarkdownRenderer, createStreamingMarkdownNodeRenderers, useStreamingMarkdownParser };
|