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