@copilotkit/markdown 2.0.0-rc.20 → 2.0.0-rc.22

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.
@@ -3,181 +3,77 @@ let vue = require("vue");
3
3
  let _copilotkit_markdown = require("@copilotkit/markdown");
4
4
 
5
5
  //#region src/vue/streaming-markdown-renderer.ts
6
- const SAFE_HREF = /^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
7
- const SAFE_IMG_SRC = /^(https?:|data:image\/(?!svg)|\/|\.\/|\.\.\/)/i;
8
- function sanitizeHref(href) {
9
- if (!href) return void 0;
10
- return SAFE_HREF.test(href.trim()) ? href : void 0;
6
+ const VUE_RENDERER_KEYS = new Set([
7
+ "paragraph",
8
+ "heading",
9
+ "blockquote",
10
+ "list",
11
+ "listItem",
12
+ "codeBlock",
13
+ "table",
14
+ "tableRow",
15
+ "tableCell",
16
+ "thematicBreak",
17
+ "em",
18
+ "strong",
19
+ "strikethrough",
20
+ "inlineCode",
21
+ "hardBreak",
22
+ "image",
23
+ "link",
24
+ "autolink",
25
+ "citation",
26
+ "math"
27
+ ]);
28
+ function isVueRendererKey(key) {
29
+ return VUE_RENDERER_KEYS.has(key);
11
30
  }
12
- function sanitizeImgSrc(src) {
13
- if (!src) return void 0;
14
- return SAFE_IMG_SRC.test(src.trim()) ? src : void 0;
15
- }
16
- const DEFAULT_OPTIONS = {
17
- segmenter: true,
18
- enableTables: true,
19
- enableAutolinks: true
20
- };
21
- function normalizeOptions(options) {
22
- return {
23
- segmenter: options?.segmenter ?? DEFAULT_OPTIONS.segmenter,
24
- enableTables: options?.enableTables ?? DEFAULT_OPTIONS.enableTables,
25
- enableAutolinks: options?.enableAutolinks ?? DEFAULT_OPTIONS.enableAutolinks
31
+ function elementProps(element, context) {
32
+ const props = {
33
+ key: element.key,
34
+ ...element.attrs
26
35
  };
36
+ const action = element.action;
37
+ if (action?.type === "link") props.onClick = (event) => context.onLinkClick?.(event, action.url, action.node);
38
+ if (action?.type === "citation") props.onClick = (event) => context.onCitationClick?.(event, action.citation, action.node);
39
+ return props;
27
40
  }
28
- function buildParserState(content, options, isComplete) {
29
- const state = (0, _copilotkit_markdown.createStreamingMarkdownParserState)(options);
30
- const parsed = content.length > 0 ? (0, _copilotkit_markdown.parseStreamingMarkdownChunk)(state, content) : state;
31
- return isComplete ? (0, _copilotkit_markdown.finalizeStreamingMarkdown)(parsed) : parsed;
32
- }
33
- function renderTextSegments(node) {
34
- if (node.text.length === 0) return [];
35
- if (node.segments.length === 0) return [(0, vue.h)("span", { class: "cpk-streaming-markdown-segment" }, node.text)];
36
- return node.segments.map((segment) => (0, vue.h)("span", {
37
- class: "cpk-streaming-markdown-segment",
38
- "data-streaming-markdown-segment-kind": segment.kind,
39
- "data-streaming-markdown-whitespace": String(segment.isWhitespace)
40
- }, segment.noBreakBefore ? `⁠${segment.text}` : segment.text));
41
- }
42
- function renderChildren(childIds, nodeById, nodeRenderers) {
43
- return childIds.map((id) => {
44
- const child = nodeById.get(id);
45
- return child ? renderNode(child, nodeById, nodeRenderers) : null;
46
- });
47
- }
48
- function renderNode(node, nodeById, nodeRenderers) {
49
- function withOverride(key, defaultVNode) {
50
- const override = nodeRenderers?.[key];
51
- return override ? override(node, defaultVNode) : defaultVNode;
52
- }
53
- switch (node.type) {
54
- case "document": return (0, vue.h)("span", { style: "display:contents" }, renderChildren(node.children, nodeById, nodeRenderers));
55
- case "paragraph": return withOverride("paragraph", (0, vue.h)("p", {
56
- "data-streaming-markdown-node": "paragraph",
57
- "data-node-open": String(!node.closed)
58
- }, renderChildren(node.children, nodeById, nodeRenderers)));
59
- case "heading": return withOverride("heading", (0, vue.h)(`h${node.level}`, {
60
- "data-streaming-markdown-node": "heading",
61
- "data-node-open": String(!node.closed)
62
- }, renderChildren(node.children, nodeById, nodeRenderers)));
63
- case "blockquote": return withOverride("blockquote", (0, vue.h)("blockquote", {
64
- "data-streaming-markdown-node": "blockquote",
65
- "data-node-open": String(!node.closed)
66
- }, renderChildren(node.children, nodeById, nodeRenderers)));
67
- case "list": {
68
- const tag = node.ordered ? "ol" : "ul";
69
- const attrs = {
70
- "data-streaming-markdown-node": "list",
71
- "data-node-open": String(!node.closed),
72
- "data-list-tight": String(node.tight)
73
- };
74
- if (node.ordered && node.start != null) attrs.start = node.start;
75
- return withOverride("list", (0, vue.h)(tag, attrs, renderChildren(node.children, nodeById, nodeRenderers)));
76
- }
77
- case "list-item": return withOverride("listItem", (0, vue.h)("li", {
78
- "data-streaming-markdown-node": "list-item",
79
- "data-node-open": String(!node.closed)
80
- }, renderChildren(node.children, nodeById, nodeRenderers)));
81
- case "code-block": return withOverride("codeBlock", (0, vue.h)("pre", {
82
- "data-streaming-markdown-node": "code-block",
83
- "data-node-open": String(!node.closed)
84
- }, [(0, vue.h)("code", { "data-code-info": node.info ?? void 0 }, node.text)]));
85
- case "table": return withOverride("table", (0, vue.h)("table", {
86
- "data-streaming-markdown-node": "table",
87
- "data-node-open": String(!node.closed)
88
- }, [(0, vue.h)("tbody", renderChildren(node.children, nodeById, nodeRenderers))]));
89
- case "table-row": return withOverride("tableRow", (0, vue.h)("tr", {
90
- "data-streaming-markdown-node": "table-row",
91
- "data-node-open": String(!node.closed)
92
- }, renderChildren(node.children, nodeById, nodeRenderers)));
93
- case "table-cell": {
94
- const parent = node.parentId != null ? nodeById.get(node.parentId) : void 0;
95
- return withOverride("tableCell", (0, vue.h)(parent?.type === "table-row" && parent.isHeader ? "th" : "td", {
96
- "data-streaming-markdown-node": "table-cell",
97
- "data-node-open": String(!node.closed)
98
- }, renderChildren(node.children, nodeById, nodeRenderers)));
99
- }
100
- case "thematic-break": return withOverride("thematicBreak", (0, vue.h)("hr", {
101
- "data-streaming-markdown-node": "thematic-break",
102
- "data-node-open": String(!node.closed)
103
- }));
104
- case "text": return (0, vue.h)("span", { style: "display:contents" }, renderTextSegments(node));
105
- case "em": return withOverride("em", (0, vue.h)("em", {
106
- "data-streaming-markdown-node": "em",
107
- "data-node-open": String(!node.closed)
108
- }, renderChildren(node.children, nodeById, nodeRenderers)));
109
- case "strong": return withOverride("strong", (0, vue.h)("strong", {
110
- "data-streaming-markdown-node": "strong",
111
- "data-node-open": String(!node.closed)
112
- }, renderChildren(node.children, nodeById, nodeRenderers)));
113
- case "strikethrough": return withOverride("strikethrough", (0, vue.h)("del", {
114
- "data-streaming-markdown-node": "strikethrough",
115
- "data-node-open": String(!node.closed)
116
- }, renderChildren(node.children, nodeById, nodeRenderers)));
117
- case "inline-code": return withOverride("inlineCode", (0, vue.h)("code", {
118
- "data-streaming-markdown-node": "inline-code",
119
- "data-node-open": String(!node.closed)
120
- }, node.text));
121
- case "soft-break": return "\n";
122
- case "hard-break": return withOverride("hardBreak", (0, vue.h)("br", {
123
- "data-streaming-markdown-node": "hard-break",
124
- "data-node-open": String(!node.closed)
125
- }));
126
- case "link": {
127
- const safeHref = sanitizeHref(node.url);
128
- const attrs = {
129
- "data-streaming-markdown-node": "link",
130
- "data-node-open": String(!node.closed),
131
- target: "_blank",
132
- rel: "noopener noreferrer"
133
- };
134
- if (safeHref != null) attrs.href = safeHref;
135
- if (node.title) attrs.title = node.title;
136
- return withOverride("link", (0, vue.h)("a", attrs, renderChildren(node.children, nodeById, nodeRenderers)));
137
- }
138
- case "image": {
139
- const safeSrc = sanitizeImgSrc(node.url);
140
- const attrs = {
141
- "data-streaming-markdown-node": "image",
142
- "data-node-open": String(!node.closed),
143
- alt: node.alt
144
- };
145
- if (safeSrc != null) attrs.src = safeSrc;
146
- if (node.title) attrs.title = node.title;
147
- return withOverride("image", (0, vue.h)("img", attrs));
148
- }
149
- case "autolink": {
150
- const safeHref = sanitizeHref(node.url);
151
- const attrs = {
152
- "data-streaming-markdown-node": "autolink",
153
- "data-node-open": String(!node.closed),
154
- target: "_blank",
155
- rel: "noopener noreferrer"
156
- };
157
- if (safeHref != null) attrs.href = safeHref;
158
- return withOverride("autolink", (0, vue.h)("a", attrs, node.text));
159
- }
160
- case "citation": return withOverride("citation", (0, vue.h)("sup", {
161
- "data-streaming-markdown-node": "citation",
162
- "data-node-open": String(!node.closed),
163
- class: "cpk-streaming-markdown-citation"
164
- }, [(0, vue.h)("span", {
165
- role: "doc-noteref",
166
- class: "cpk-streaming-markdown-citation-label"
167
- }, String(node.number ?? node.idRef))]));
168
- default: return null;
41
+ function renderChild(child, context, slot) {
42
+ switch (child.kind) {
43
+ case "node": return renderRenderNode(child, context);
44
+ case "element": return (0, vue.h)(child.tag, elementProps(child, context), child.children.flatMap((entry) => renderChild(entry, context, slot)));
45
+ case "text": return child.text;
46
+ case "caret": return (0, vue.h)("span", {
47
+ key: child.key,
48
+ ..._copilotkit_markdown.STREAMING_MARKDOWN_CARET_ATTRS,
49
+ style: _copilotkit_markdown.STREAMING_MARKDOWN_CARET_STYLE
50
+ });
51
+ case "slot": return slot;
52
+ default: return child;
169
53
  }
170
54
  }
55
+ function renderRenderNode(renderNode, context) {
56
+ const children = (renderNode.children ?? []).flatMap((entry) => renderChild(entry, context, []));
57
+ const output = renderNode.output.flatMap((entry) => renderChild(entry, context, children));
58
+ const key = renderNode.rendererKey;
59
+ const override = isVueRendererKey(key) ? context.nodeRenderers?.[key] : void 0;
60
+ if (!override) return output;
61
+ const [first] = output;
62
+ const defaultVNode = (0, vue.isVNode)(first) || typeof first === "string" ? first : null;
63
+ return [override(renderNode.node, defaultVNode)];
64
+ }
171
65
  /**
172
66
  * Vue 3 streaming markdown renderer.
173
67
  *
174
- * Drives the `@copilotkit/markdown` zero-dep parser and walks the
175
- * resulting AST into VNodes via `h()`.
68
+ * Parses with `@copilotkit/markdown` and renders the shared render tree with
69
+ * `h()`, so markup, sanitization, caret placement, and the per-token fade-in
70
+ * match the React and DOM renderers.
176
71
  *
177
72
  * @public
178
73
  */
179
74
  const StreamingMarkdownRenderer = (0, vue.defineComponent)({
180
75
  name: "StreamingMarkdownRenderer",
76
+ inheritAttrs: false,
181
77
  props: {
182
78
  content: {
183
79
  type: String,
@@ -202,51 +98,42 @@ const StreamingMarkdownRenderer = (0, vue.defineComponent)({
202
98
  nodeRenderers: {
203
99
  type: Object,
204
100
  default: void 0
101
+ },
102
+ nodeClassNames: {
103
+ type: Object,
104
+ default: void 0
105
+ },
106
+ onLinkClick: {
107
+ type: Function,
108
+ default: void 0
109
+ },
110
+ onCitationClick: {
111
+ type: Function,
112
+ default: void 0
205
113
  }
206
114
  },
207
- setup(props) {
115
+ setup(props, { attrs }) {
116
+ let session = null;
208
117
  const parserState = (0, vue.computed)(() => {
209
- const opts = normalizeOptions(props.options);
210
- return buildParserState(props.content ?? "", opts, props.isComplete);
211
- });
212
- const nodeById = (0, vue.computed)(() => {
213
- const map = /* @__PURE__ */ new Map();
214
- for (const node of parserState.value.nodes) map.set(node.id, node);
215
- return map;
118
+ session = (0, _copilotkit_markdown.updateStreamingMarkdownParserSession)(session, props.content ?? "", props.options, props.isComplete);
119
+ return session.parserState;
216
120
  });
217
121
  return () => {
218
- const state = parserState.value;
219
- const byId = nodeById.value;
220
- const rootNode = state.rootId != null ? byId.get(state.rootId) : void 0;
221
- const renderers = props.nodeRenderers;
222
- const rootClass = props.class ? `cpk-streaming-markdown-root ${props.class}` : "cpk-streaming-markdown-root";
223
- if (!rootNode) return (0, vue.h)("div", {
224
- class: rootClass,
225
- "data-streaming-markdown-root": true
122
+ const tree = (0, _copilotkit_markdown.buildStreamingMarkdownRenderTree)(parserState.value, {
123
+ caret: props.caret,
124
+ nodeClassNames: props.nodeClassNames
226
125
  });
227
- const children = rootNode.type === "document" ? rootNode.children.map((id) => {
228
- const child = byId.get(id);
229
- return child ? renderNode(child, byId, renderers) : null;
230
- }) : [renderNode(rootNode, byId, renderers)];
231
- if (props.caret && !state.isComplete) children.push((0, vue.h)("span", {
232
- "aria-hidden": true,
233
- class: "cpk-streaming-markdown-caret",
234
- "data-streaming-markdown-caret": true,
235
- style: {
236
- display: "inline-block",
237
- width: "0.48em",
238
- height: "0.48em",
239
- marginInlineStart: "0.08em",
240
- verticalAlign: "-0.08em",
241
- borderRadius: "999px",
242
- backgroundColor: "currentColor",
243
- opacity: "0.55"
244
- }
245
- }));
246
- return (0, vue.h)("div", {
126
+ const content = tree.root ? renderRenderNode(tree.root, {
127
+ nodeRenderers: props.nodeRenderers,
128
+ onLinkClick: props.onLinkClick,
129
+ onCitationClick: props.onCitationClick
130
+ }) : [];
131
+ const rootClass = props.class ? `${_copilotkit_markdown.STREAMING_MARKDOWN_ROOT_CLASS} ${props.class}` : _copilotkit_markdown.STREAMING_MARKDOWN_ROOT_CLASS;
132
+ return [(0, vue.h)("style", (0, _copilotkit_markdown.getStreamingMarkdownStyles)()), (0, vue.h)("div", {
133
+ ...attrs,
247
134
  class: rootClass,
248
135
  "data-streaming-markdown-root": true
249
- }, children);
136
+ }, content)];
250
137
  };
251
138
  }
252
139
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/vue/streaming-markdown-renderer.ts"],"sourcesContent":["import { computed, defineComponent, h } from \"vue\";\nimport type { PropType, VNode } from \"vue\";\nimport {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserState,\n TextSegment,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// URL sanitization (allowlisted href and image src schemes)\n// ---------------------------------------------------------------------------\n\nconst SAFE_HREF = /^(https?:|mailto:|tel:|#|\\/|\\.\\/|\\.\\.\\/)/i;\nconst SAFE_IMG_SRC = /^(https?:|data:image\\/(?!svg)|\\/|\\.\\/|\\.\\.\\/)/i;\n\nfunction sanitizeHref(href: string | undefined): string | undefined {\n if (!href) return undefined;\n return SAFE_HREF.test(href.trim()) ? href : undefined;\n}\n\nfunction sanitizeImgSrc(src: string | undefined): string | undefined {\n if (!src) return undefined;\n return SAFE_IMG_SRC.test(src.trim()) ? src : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Parser helpers (full re-parse approach — simple and correct)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_OPTIONS: StreamingMarkdownParserOptions = {\n segmenter: true,\n enableTables: true,\n enableAutolinks: true,\n};\n\nfunction normalizeOptions(\n options?: Partial<StreamingMarkdownParserOptions>,\n): StreamingMarkdownParserOptions {\n return {\n segmenter: options?.segmenter ?? DEFAULT_OPTIONS.segmenter,\n enableTables: options?.enableTables ?? DEFAULT_OPTIONS.enableTables,\n enableAutolinks:\n options?.enableAutolinks ?? DEFAULT_OPTIONS.enableAutolinks,\n };\n}\n\nfunction buildParserState(\n content: string,\n options: StreamingMarkdownParserOptions,\n isComplete: boolean,\n): StreamingMarkdownParserState {\n const state = createStreamingMarkdownParserState(options);\n const parsed =\n content.length > 0 ? parseStreamingMarkdownChunk(state, content) : state;\n return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;\n}\n\n// ---------------------------------------------------------------------------\n// AST → VNode walker\n// ---------------------------------------------------------------------------\n\ntype NodeById = Map<number, StreamingMarkdownAstNode>;\n\ntype TextNode = Extract<StreamingMarkdownAstNode, { type: \"text\" }>;\n\nfunction renderTextSegments(node: TextNode): (VNode | string)[] {\n if (node.text.length === 0) return [];\n if (node.segments.length === 0) {\n return [h(\"span\", { class: \"cpk-streaming-markdown-segment\" }, node.text)];\n }\n return node.segments.map((segment: TextSegment) =>\n h(\n \"span\",\n {\n class: \"cpk-streaming-markdown-segment\",\n \"data-streaming-markdown-segment-kind\": segment.kind,\n \"data-streaming-markdown-whitespace\": String(segment.isWhitespace),\n },\n segment.noBreakBefore ? `⁠${segment.text}` : segment.text,\n ),\n );\n}\n\nfunction renderChildren(\n childIds: number[],\n nodeById: NodeById,\n nodeRenderers?: VueStreamingMarkdownNodeRenderers,\n): (VNode | string | null)[] {\n return childIds.map((id) => {\n const child = nodeById.get(id);\n return child ? renderNode(child, nodeById, nodeRenderers) : null;\n });\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n nodeById: NodeById,\n nodeRenderers?: VueStreamingMarkdownNodeRenderers,\n): VNode | string | null {\n // Helper: apply a nodeRenderers override if one is registered for this node type.\n function withOverride(\n key: VueStreamingMarkdownNodeRendererKey,\n defaultVNode: VNode | string | null,\n ): VNode | string | null {\n const override = nodeRenderers?.[key];\n return override ? override(node, defaultVNode) : defaultVNode;\n }\n\n switch (node.type) {\n case \"document\": {\n // Document is transparent — render children directly as a Fragment via array\n // We wrap in a div for the root element, handled at the top level.\n // Here just return children inlined (will be wrapped by root div).\n return h(\n \"span\",\n { style: \"display:contents\" },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n }\n\n case \"paragraph\": {\n const defaultVNode = h(\n \"p\",\n {\n \"data-streaming-markdown-node\": \"paragraph\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"paragraph\", defaultVNode);\n }\n\n case \"heading\": {\n const tag = `h${node.level}` as \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n const defaultVNode = h(\n tag,\n {\n \"data-streaming-markdown-node\": \"heading\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"heading\", defaultVNode);\n }\n\n case \"blockquote\": {\n const defaultVNode = h(\n \"blockquote\",\n {\n \"data-streaming-markdown-node\": \"blockquote\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"blockquote\", defaultVNode);\n }\n\n case \"list\": {\n const tag = node.ordered ? \"ol\" : \"ul\";\n const attrs: Record<string, unknown> = {\n \"data-streaming-markdown-node\": \"list\",\n \"data-node-open\": String(!node.closed),\n \"data-list-tight\": String(node.tight),\n };\n if (node.ordered && node.start != null) {\n attrs.start = node.start;\n }\n const defaultVNode = h(\n tag,\n attrs,\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"list\", defaultVNode);\n }\n\n case \"list-item\": {\n const defaultVNode = h(\n \"li\",\n {\n \"data-streaming-markdown-node\": \"list-item\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"listItem\", defaultVNode);\n }\n\n case \"code-block\": {\n const defaultVNode = h(\n \"pre\",\n {\n \"data-streaming-markdown-node\": \"code-block\",\n \"data-node-open\": String(!node.closed),\n },\n [h(\"code\", { \"data-code-info\": node.info ?? undefined }, node.text)],\n );\n return withOverride(\"codeBlock\", defaultVNode);\n }\n\n case \"table\": {\n const defaultVNode = h(\n \"table\",\n {\n \"data-streaming-markdown-node\": \"table\",\n \"data-node-open\": String(!node.closed),\n },\n [h(\"tbody\", renderChildren(node.children, nodeById, nodeRenderers))],\n );\n return withOverride(\"table\", defaultVNode);\n }\n\n case \"table-row\": {\n const defaultVNode = h(\n \"tr\",\n {\n \"data-streaming-markdown-node\": \"table-row\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"tableRow\", defaultVNode);\n }\n\n case \"table-cell\": {\n const parent =\n node.parentId != null ? nodeById.get(node.parentId) : undefined;\n const isHeader = parent?.type === \"table-row\" && parent.isHeader;\n const tag = isHeader ? \"th\" : \"td\";\n const defaultVNode = h(\n tag,\n {\n \"data-streaming-markdown-node\": \"table-cell\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"tableCell\", defaultVNode);\n }\n\n case \"thematic-break\": {\n const defaultVNode = h(\"hr\", {\n \"data-streaming-markdown-node\": \"thematic-break\",\n \"data-node-open\": String(!node.closed),\n });\n return withOverride(\"thematicBreak\", defaultVNode);\n }\n\n case \"text\": {\n return h(\"span\", { style: \"display:contents\" }, renderTextSegments(node));\n }\n\n case \"em\": {\n const defaultVNode = h(\n \"em\",\n {\n \"data-streaming-markdown-node\": \"em\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"em\", defaultVNode);\n }\n\n case \"strong\": {\n const defaultVNode = h(\n \"strong\",\n {\n \"data-streaming-markdown-node\": \"strong\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"strong\", defaultVNode);\n }\n\n case \"strikethrough\": {\n const defaultVNode = h(\n \"del\",\n {\n \"data-streaming-markdown-node\": \"strikethrough\",\n \"data-node-open\": String(!node.closed),\n },\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"strikethrough\", defaultVNode);\n }\n\n case \"inline-code\": {\n const defaultVNode = h(\n \"code\",\n {\n \"data-streaming-markdown-node\": \"inline-code\",\n \"data-node-open\": String(!node.closed),\n },\n node.text,\n );\n return withOverride(\"inlineCode\", defaultVNode);\n }\n\n case \"soft-break\": {\n return \"\\n\";\n }\n\n case \"hard-break\": {\n const defaultVNode = h(\"br\", {\n \"data-streaming-markdown-node\": \"hard-break\",\n \"data-node-open\": String(!node.closed),\n });\n return withOverride(\"hardBreak\", defaultVNode);\n }\n\n case \"link\": {\n const safeHref = sanitizeHref(node.url);\n const attrs: Record<string, unknown> = {\n \"data-streaming-markdown-node\": \"link\",\n \"data-node-open\": String(!node.closed),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n };\n if (safeHref != null) {\n attrs.href = safeHref;\n }\n if (node.title) {\n attrs.title = node.title;\n }\n const defaultVNode = h(\n \"a\",\n attrs,\n renderChildren(node.children, nodeById, nodeRenderers),\n );\n return withOverride(\"link\", defaultVNode);\n }\n\n case \"image\": {\n const safeSrc = sanitizeImgSrc(node.url);\n const attrs: Record<string, unknown> = {\n \"data-streaming-markdown-node\": \"image\",\n \"data-node-open\": String(!node.closed),\n alt: node.alt,\n };\n if (safeSrc != null) {\n attrs.src = safeSrc;\n }\n if (node.title) {\n attrs.title = node.title;\n }\n const defaultVNode = h(\"img\", attrs);\n return withOverride(\"image\", defaultVNode);\n }\n\n case \"autolink\": {\n const safeHref = sanitizeHref(node.url);\n const attrs: Record<string, unknown> = {\n \"data-streaming-markdown-node\": \"autolink\",\n \"data-node-open\": String(!node.closed),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n };\n if (safeHref != null) {\n attrs.href = safeHref;\n }\n const defaultVNode = h(\"a\", attrs, node.text);\n return withOverride(\"autolink\", defaultVNode);\n }\n\n case \"citation\": {\n // Render as a superscript with the citation reference\n const defaultVNode = h(\n \"sup\",\n {\n \"data-streaming-markdown-node\": \"citation\",\n \"data-node-open\": String(!node.closed),\n class: \"cpk-streaming-markdown-citation\",\n },\n [\n h(\n \"span\",\n {\n role: \"doc-noteref\",\n class: \"cpk-streaming-markdown-citation-label\",\n },\n String(node.number ?? node.idRef),\n ),\n ],\n );\n return withOverride(\"citation\", defaultVNode);\n }\n\n default: {\n return null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Vue component props types (exported for consumer use)\n// ---------------------------------------------------------------------------\n\n/**\n * Custom Vue render function for a specific AST node.\n * @public\n */\nexport type VueStreamingMarkdownNodeRenderer = (\n node: StreamingMarkdownAstNode,\n defaultVNode: VNode | string | null,\n) => VNode | string | null;\n\n/**\n * Supported key names for Vue `nodeRenderers`.\n * CamelCase, matching `StreamingMarkdownNodeRendererKey` on the React renderer.\n *\n * @public\n */\nexport type VueStreamingMarkdownNodeRendererKey =\n | \"paragraph\"\n | \"heading\"\n | \"blockquote\"\n | \"list\"\n | \"listItem\"\n | \"codeBlock\"\n | \"table\"\n | \"tableRow\"\n | \"tableCell\"\n | \"thematicBreak\"\n | \"em\"\n | \"strong\"\n | \"strikethrough\"\n | \"inlineCode\"\n | \"hardBreak\"\n | \"image\"\n | \"link\"\n | \"autolink\"\n | \"citation\";\n\n/**\n * Optional map of node-type renderer keys to custom Vue render functions.\n * @public\n */\nexport type VueStreamingMarkdownNodeRenderers = Partial<\n Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>\n>;\n\n// ---------------------------------------------------------------------------\n// StreamingMarkdownRenderer — Vue 3 Composition API component (.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Vue 3 streaming markdown renderer.\n *\n * Drives the `@copilotkit/markdown` zero-dep parser and walks the\n * resulting AST into VNodes via `h()`.\n *\n * @public\n */\nexport const StreamingMarkdownRenderer = defineComponent({\n name: \"StreamingMarkdownRenderer\",\n\n props: {\n /** The full markdown source string, which may grow over time during streaming. */\n content: {\n type: String as PropType<string>,\n required: true,\n },\n /** When true, finalizes the parser state (end of stream). */\n isComplete: {\n type: Boolean as PropType<boolean>,\n default: false,\n },\n /** Optional parser option overrides. */\n options: {\n type: Object as PropType<Partial<StreamingMarkdownParserOptions>>,\n default: undefined,\n },\n /** Optional CSS class applied to the root wrapper element. */\n class: {\n type: String as PropType<string>,\n default: undefined,\n },\n /**\n * When true, renders a trailing cursor span after the last open node.\n * Animation niceties are left to CSS; this just inserts the element.\n */\n caret: {\n type: Boolean as PropType<boolean>,\n default: false,\n },\n /**\n * Optional map of node-type keys to custom Vue render functions.\n * Each function receives the AST node and the default VNode and can return\n * a replacement VNode (or the defaultVNode to keep the default).\n * Supported keys: paragraph, heading, blockquote, list, listItem, codeBlock,\n * table, tableRow, tableCell, thematicBreak, em, strong, strikethrough,\n * inlineCode, hardBreak, link, image, autolink, citation.\n */\n nodeRenderers: {\n type: Object as PropType<VueStreamingMarkdownNodeRenderers>,\n default: undefined,\n },\n },\n\n setup(props) {\n const parserState = computed<StreamingMarkdownParserState>(() => {\n const opts = normalizeOptions(props.options);\n return buildParserState(props.content ?? \"\", opts, props.isComplete);\n });\n\n const nodeById = computed<NodeById>(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n for (const node of parserState.value.nodes) {\n map.set(node.id, node);\n }\n return map;\n });\n\n return () => {\n const state = parserState.value;\n const byId = nodeById.value;\n const rootNode =\n state.rootId != null ? byId.get(state.rootId) : undefined;\n const renderers = props.nodeRenderers;\n\n const rootClass = props.class\n ? `cpk-streaming-markdown-root ${props.class}`\n : \"cpk-streaming-markdown-root\";\n\n if (!rootNode) {\n return h(\"div\", {\n class: rootClass,\n \"data-streaming-markdown-root\": true,\n });\n }\n\n // Render children of document directly (skip the transparent document wrapper)\n const children: (VNode | string | null)[] =\n rootNode.type === \"document\"\n ? rootNode.children.map((id) => {\n const child = byId.get(id);\n return child ? renderNode(child, byId, renderers) : null;\n })\n : [renderNode(rootNode, byId, renderers)];\n\n // Append caret if requested and not complete\n if (props.caret && !state.isComplete) {\n children.push(\n h(\"span\", {\n \"aria-hidden\": true,\n class: \"cpk-streaming-markdown-caret\",\n \"data-streaming-markdown-caret\": true,\n style: {\n display: \"inline-block\",\n width: \"0.48em\",\n height: \"0.48em\",\n marginInlineStart: \"0.08em\",\n verticalAlign: \"-0.08em\",\n borderRadius: \"999px\",\n backgroundColor: \"currentColor\",\n opacity: \"0.55\",\n },\n }),\n );\n }\n\n return h(\n \"div\",\n {\n class: rootClass,\n \"data-streaming-markdown-root\": true,\n },\n children,\n );\n };\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["STREAMING_MARKDOWN_CARET_ATTRS","STREAMING_MARKDOWN_CARET_STYLE","STREAMING_MARKDOWN_ROOT_CLASS"],"sources":["../../src/vue/streaming-markdown-renderer.ts"],"sourcesContent":["import { computed, defineComponent, h, isVNode } from \"vue\";\nimport type { PropType, VNode, VNodeChild } from \"vue\";\nimport {\n STREAMING_MARKDOWN_CARET_ATTRS,\n STREAMING_MARKDOWN_CARET_STYLE,\n STREAMING_MARKDOWN_ROOT_CLASS,\n buildStreamingMarkdownRenderTree,\n getStreamingMarkdownStyles,\n updateStreamingMarkdownParserSession,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeClassNames,\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserSession,\n StreamingMarkdownRenderChild,\n StreamingMarkdownRenderElement,\n StreamingMarkdownRenderNode,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// Vue component props types (exported for consumer use)\n// ---------------------------------------------------------------------------\n\n/**\n * Custom Vue render function for a specific AST node.\n * @public\n */\nexport type VueStreamingMarkdownNodeRenderer = (\n node: StreamingMarkdownAstNode,\n defaultVNode: VNode | string | null,\n) => VNode | string | null;\n\n/**\n * Supported key names for Vue `nodeRenderers`.\n * CamelCase, matching `StreamingMarkdownNodeRendererKey` on the React renderer.\n *\n * @public\n */\nexport type VueStreamingMarkdownNodeRendererKey =\n | \"paragraph\"\n | \"heading\"\n | \"blockquote\"\n | \"list\"\n | \"listItem\"\n | \"codeBlock\"\n | \"table\"\n | \"tableRow\"\n | \"tableCell\"\n | \"thematicBreak\"\n | \"em\"\n | \"strong\"\n | \"strikethrough\"\n | \"inlineCode\"\n | \"hardBreak\"\n | \"image\"\n | \"link\"\n | \"autolink\"\n | \"citation\"\n | \"math\";\n\n/**\n * Optional map of node-type renderer keys to custom Vue render functions.\n * @public\n */\nexport type VueStreamingMarkdownNodeRenderers = Partial<\n Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>\n>;\n\n/**\n * Called when a link or autolink is clicked.\n * @public\n */\nexport type VueStreamingMarkdownLinkClickHandler = (\n event: MouseEvent,\n url: string,\n node: StreamingMarkdownAstNode,\n) => void;\n\n/**\n * Called when a citation reference is clicked.\n * @public\n */\nexport type VueStreamingMarkdownCitationClickHandler = (\n event: MouseEvent,\n citation: StreamingMarkdownCitationRenderData,\n node: StreamingMarkdownAstNode,\n) => void;\n\n// ---------------------------------------------------------------------------\n// Render tree → VNodes\n// ---------------------------------------------------------------------------\n\ntype RenderContext = {\n nodeRenderers: VueStreamingMarkdownNodeRenderers | undefined;\n onLinkClick: VueStreamingMarkdownLinkClickHandler | undefined;\n onCitationClick: VueStreamingMarkdownCitationClickHandler | undefined;\n};\n\nconst VUE_RENDERER_KEYS = new Set<string>([\n \"paragraph\",\n \"heading\",\n \"blockquote\",\n \"list\",\n \"listItem\",\n \"codeBlock\",\n \"table\",\n \"tableRow\",\n \"tableCell\",\n \"thematicBreak\",\n \"em\",\n \"strong\",\n \"strikethrough\",\n \"inlineCode\",\n \"hardBreak\",\n \"image\",\n \"link\",\n \"autolink\",\n \"citation\",\n \"math\",\n] satisfies VueStreamingMarkdownNodeRendererKey[]);\n\nfunction isVueRendererKey(\n key: string,\n): key is VueStreamingMarkdownNodeRendererKey {\n return VUE_RENDERER_KEYS.has(key);\n}\n\nfunction elementProps(\n element: StreamingMarkdownRenderElement,\n context: RenderContext,\n) {\n const props: Record<string, unknown> = { key: element.key, ...element.attrs };\n const action = element.action;\n if (action?.type === \"link\") {\n props.onClick = (event: MouseEvent) =>\n context.onLinkClick?.(event, action.url, action.node);\n }\n if (action?.type === \"citation\") {\n props.onClick = (event: MouseEvent) =>\n context.onCitationClick?.(event, action.citation, action.node);\n }\n return props;\n}\n\nfunction renderChild(\n child: StreamingMarkdownRenderChild,\n context: RenderContext,\n slot: VNodeChild[],\n): VNodeChild | VNodeChild[] {\n switch (child.kind) {\n case \"node\":\n return renderRenderNode(child, context);\n case \"element\":\n return h(\n child.tag,\n elementProps(child, context),\n child.children.flatMap((entry) => renderChild(entry, context, slot)),\n );\n case \"text\":\n return child.text;\n case \"caret\":\n return h(\"span\", {\n key: child.key,\n ...STREAMING_MARKDOWN_CARET_ATTRS,\n style: STREAMING_MARKDOWN_CARET_STYLE,\n });\n case \"slot\":\n return slot;\n default: {\n const unreachable: never = child;\n return unreachable;\n }\n }\n}\n\nfunction renderRenderNode(\n renderNode: StreamingMarkdownRenderNode,\n context: RenderContext,\n): VNodeChild[] {\n const children = (renderNode.children ?? []).flatMap((entry) =>\n renderChild(entry, context, []),\n );\n const output = renderNode.output.flatMap((entry) =>\n renderChild(entry, context, children),\n );\n const key = renderNode.rendererKey;\n const override = isVueRendererKey(key)\n ? context.nodeRenderers?.[key]\n : undefined;\n if (!override) {\n return output;\n }\n const [first] = output;\n const defaultVNode =\n isVNode(first) || typeof first === \"string\" ? first : null;\n return [override(renderNode.node, defaultVNode)];\n}\n\n// ---------------------------------------------------------------------------\n// StreamingMarkdownRenderer — Vue 3 Composition API component (.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Vue 3 streaming markdown renderer.\n *\n * Parses with `@copilotkit/markdown` and renders the shared render tree with\n * `h()`, so markup, sanitization, caret placement, and the per-token fade-in\n * match the React and DOM renderers.\n *\n * @public\n */\nexport const StreamingMarkdownRenderer = defineComponent({\n name: \"StreamingMarkdownRenderer\",\n inheritAttrs: false,\n\n props: {\n /** The full markdown source string, which may grow over time during streaming. */\n content: {\n type: String as PropType<string>,\n required: true,\n },\n /** When true, finalizes the parser state (end of stream). */\n isComplete: {\n type: Boolean as PropType<boolean>,\n default: false,\n },\n /** Optional parser option overrides. */\n options: {\n type: Object as PropType<Partial<StreamingMarkdownParserOptions>>,\n default: undefined,\n },\n /** Optional CSS class applied to the root wrapper element. */\n class: {\n type: String as PropType<string>,\n default: undefined,\n },\n /**\n * When true, renders a caret inside the deepest open node while streaming.\n */\n caret: {\n type: Boolean as PropType<boolean>,\n default: false,\n },\n /**\n * Optional map of node-type keys to custom Vue render functions.\n * Each function receives the AST node and the default VNode and can return\n * a replacement VNode (or the defaultVNode to keep the default).\n */\n nodeRenderers: {\n type: Object as PropType<VueStreamingMarkdownNodeRenderers>,\n default: undefined,\n },\n /** Extra classes for the default element of a node type. */\n nodeClassNames: {\n type: Object as PropType<StreamingMarkdownNodeClassNames>,\n default: undefined,\n },\n /** Called when a link or autolink is clicked. */\n onLinkClick: {\n type: Function as PropType<VueStreamingMarkdownLinkClickHandler>,\n default: undefined,\n },\n /** Called when a citation reference with a URL is clicked. */\n onCitationClick: {\n type: Function as PropType<VueStreamingMarkdownCitationClickHandler>,\n default: undefined,\n },\n },\n\n setup(props, { attrs }) {\n // Not reactive on purpose: the previous session only seeds the next parse.\n let session: StreamingMarkdownParserSession | null = null;\n const parserState = computed(() => {\n session = updateStreamingMarkdownParserSession(\n session,\n props.content ?? \"\",\n props.options,\n props.isComplete,\n );\n return session.parserState;\n });\n\n return () => {\n const tree = buildStreamingMarkdownRenderTree(parserState.value, {\n caret: props.caret,\n nodeClassNames: props.nodeClassNames,\n });\n const content = tree.root\n ? renderRenderNode(tree.root, {\n nodeRenderers: props.nodeRenderers,\n onLinkClick: props.onLinkClick,\n onCitationClick: props.onCitationClick,\n })\n : [];\n\n const rootClass = props.class\n ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${props.class}`\n : STREAMING_MARKDOWN_ROOT_CLASS;\n\n return [\n h(\"style\", getStreamingMarkdownStyles()),\n h(\n \"div\",\n { ...attrs, class: rootClass, \"data-streaming-markdown-root\": true },\n content,\n ),\n ];\n };\n },\n});\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import * as vue from "vue";
2
2
  import { PropType, VNode } from "vue";
3
- import { StreamingMarkdownAstNode, StreamingMarkdownParserOptions } from "@copilotkit/markdown";
3
+ import { StreamingMarkdownAstNode, StreamingMarkdownCitationRenderData, StreamingMarkdownNodeClassNames, StreamingMarkdownParserOptions } from "@copilotkit/markdown";
4
4
 
5
5
  //#region src/vue/streaming-markdown-renderer.d.ts
6
6
  /**
@@ -14,17 +14,28 @@ type VueStreamingMarkdownNodeRenderer = (node: StreamingMarkdownAstNode, default
14
14
  *
15
15
  * @public
16
16
  */
17
- type VueStreamingMarkdownNodeRendererKey = "paragraph" | "heading" | "blockquote" | "list" | "listItem" | "codeBlock" | "table" | "tableRow" | "tableCell" | "thematicBreak" | "em" | "strong" | "strikethrough" | "inlineCode" | "hardBreak" | "image" | "link" | "autolink" | "citation";
17
+ type VueStreamingMarkdownNodeRendererKey = "paragraph" | "heading" | "blockquote" | "list" | "listItem" | "codeBlock" | "table" | "tableRow" | "tableCell" | "thematicBreak" | "em" | "strong" | "strikethrough" | "inlineCode" | "hardBreak" | "image" | "link" | "autolink" | "citation" | "math";
18
18
  /**
19
19
  * Optional map of node-type renderer keys to custom Vue render functions.
20
20
  * @public
21
21
  */
22
22
  type VueStreamingMarkdownNodeRenderers = Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
23
+ /**
24
+ * Called when a link or autolink is clicked.
25
+ * @public
26
+ */
27
+ type VueStreamingMarkdownLinkClickHandler = (event: MouseEvent, url: string, node: StreamingMarkdownAstNode) => void;
28
+ /**
29
+ * Called when a citation reference is clicked.
30
+ * @public
31
+ */
32
+ type VueStreamingMarkdownCitationClickHandler = (event: MouseEvent, citation: StreamingMarkdownCitationRenderData, node: StreamingMarkdownAstNode) => void;
23
33
  /**
24
34
  * Vue 3 streaming markdown renderer.
25
35
  *
26
- * Drives the `@copilotkit/markdown` zero-dep parser and walks the
27
- * resulting AST into VNodes via `h()`.
36
+ * Parses with `@copilotkit/markdown` and renders the shared render tree with
37
+ * `h()`, so markup, sanitization, caret placement, and the per-token fade-in
38
+ * match the React and DOM renderers.
28
39
  *
29
40
  * @public
30
41
  */
@@ -46,8 +57,7 @@ declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropType
46
57
  default: undefined;
47
58
  };
48
59
  /**
49
- * When true, renders a trailing cursor span after the last open node.
50
- * Animation niceties are left to CSS; this just inserts the element.
60
+ * When true, renders a caret inside the deepest open node while streaming.
51
61
  */
52
62
  caret: {
53
63
  type: PropType<boolean>;
@@ -57,17 +67,26 @@ declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropType
57
67
  * Optional map of node-type keys to custom Vue render functions.
58
68
  * Each function receives the AST node and the default VNode and can return
59
69
  * a replacement VNode (or the defaultVNode to keep the default).
60
- * Supported keys: paragraph, heading, blockquote, list, listItem, codeBlock,
61
- * table, tableRow, tableCell, thematicBreak, em, strong, strikethrough,
62
- * inlineCode, hardBreak, link, image, autolink, citation.
63
70
  */
64
71
  nodeRenderers: {
65
72
  type: PropType<VueStreamingMarkdownNodeRenderers>;
66
73
  default: undefined;
74
+ }; /** Extra classes for the default element of a node type. */
75
+ nodeClassNames: {
76
+ type: PropType<StreamingMarkdownNodeClassNames>;
77
+ default: undefined;
78
+ }; /** Called when a link or autolink is clicked. */
79
+ onLinkClick: {
80
+ type: PropType<VueStreamingMarkdownLinkClickHandler>;
81
+ default: undefined;
82
+ }; /** Called when a citation reference with a URL is clicked. */
83
+ onCitationClick: {
84
+ type: PropType<VueStreamingMarkdownCitationClickHandler>;
85
+ default: undefined;
67
86
  };
68
87
  }>, () => VNode<vue.RendererNode, vue.RendererElement, {
69
88
  [key: string]: any;
70
- }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
89
+ }>[], {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
71
90
  /** The full markdown source string, which may grow over time during streaming. */content: {
72
91
  type: PropType<string>;
73
92
  required: true;
@@ -85,8 +104,7 @@ declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropType
85
104
  default: undefined;
86
105
  };
87
106
  /**
88
- * When true, renders a trailing cursor span after the last open node.
89
- * Animation niceties are left to CSS; this just inserts the element.
107
+ * When true, renders a caret inside the deepest open node while streaming.
90
108
  */
91
109
  caret: {
92
110
  type: PropType<boolean>;
@@ -96,13 +114,22 @@ declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropType
96
114
  * Optional map of node-type keys to custom Vue render functions.
97
115
  * Each function receives the AST node and the default VNode and can return
98
116
  * a replacement VNode (or the defaultVNode to keep the default).
99
- * Supported keys: paragraph, heading, blockquote, list, listItem, codeBlock,
100
- * table, tableRow, tableCell, thematicBreak, em, strong, strikethrough,
101
- * inlineCode, hardBreak, link, image, autolink, citation.
102
117
  */
103
118
  nodeRenderers: {
104
119
  type: PropType<VueStreamingMarkdownNodeRenderers>;
105
120
  default: undefined;
121
+ }; /** Extra classes for the default element of a node type. */
122
+ nodeClassNames: {
123
+ type: PropType<StreamingMarkdownNodeClassNames>;
124
+ default: undefined;
125
+ }; /** Called when a link or autolink is clicked. */
126
+ onLinkClick: {
127
+ type: PropType<VueStreamingMarkdownLinkClickHandler>;
128
+ default: undefined;
129
+ }; /** Called when a citation reference with a URL is clicked. */
130
+ onCitationClick: {
131
+ type: PropType<VueStreamingMarkdownCitationClickHandler>;
132
+ default: undefined;
106
133
  };
107
134
  }>> & Readonly<{}>, {
108
135
  isComplete: boolean;
@@ -110,7 +137,10 @@ declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropType
110
137
  class: string;
111
138
  caret: boolean;
112
139
  nodeRenderers: Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
140
+ nodeClassNames: StreamingMarkdownNodeClassNames;
141
+ onLinkClick: VueStreamingMarkdownLinkClickHandler;
142
+ onCitationClick: VueStreamingMarkdownCitationClickHandler;
113
143
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
114
144
  //#endregion
115
- export { StreamingMarkdownRenderer, type VueStreamingMarkdownNodeRenderer, type VueStreamingMarkdownNodeRendererKey, type VueStreamingMarkdownNodeRenderers };
145
+ export { StreamingMarkdownRenderer, type VueStreamingMarkdownCitationClickHandler, type VueStreamingMarkdownLinkClickHandler, type VueStreamingMarkdownNodeRenderer, type VueStreamingMarkdownNodeRendererKey, type VueStreamingMarkdownNodeRenderers };
116
146
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vue/streaming-markdown-renderer.ts"],"mappings":";;;;;;;;AAwZA;KAAY,gCAAA,IACV,IAAA,EAAM,wBAAA,EACN,YAAA,EAAc,KAAA,qBACX,KAAA;;;;;;;KAQO,mCAAA;;;;;KAyBA,iCAAA,GAAoC,OAAA,CAC9C,MAAA,CAAO,mCAAA,EAAqC,gCAAA;;AA1B9C;;;;;AAyBA;;cAgBa,yBAAA,MAAyB,eAAA,CAyCR,GAAA,CAzCQ,gBAAA;EAf7B;UAqBa,QAAA;;KAtBiC;;UA2BhC,QAAA;;KA1BuD;;UA+BxD,QAAA,CAAS,OAAA,CAAQ,8BAAA;;KAVjB;;UAeA,QAAA;;;EAAA;;;;;UAQC,QAAA;;;;;;;;;;;;UAYD,QAAA,CAAS,iCAAA;;;gBAzCO,GAAA,CAAA,YAAA;;4GAyCR,GAAA,CAAA,gBAAA;;UAnCR,QAAA;;;;UAKC,QAAA;;KAXe;;UAgBhB,QAAA,CAAS,OAAA,CAAQ,8BAAA;;;;UAKjB,QAAA;;;;;;;;UAQC,QAAA;;;;;;;;;;;;UAYD,QAAA,CAAS,iCAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vue/streaming-markdown-renderer.ts"],"mappings":";;;;;;;;AA6BA;KAAY,gCAAA,IACV,IAAA,EAAM,wBAAA,EACN,YAAA,EAAc,KAAA,qBACX,KAAA;;;;;;;KAQO,mCAAA;;;;;KA0BA,iCAAA,GAAoC,OAAA,CAC9C,MAAA,CAAO,mCAAA,EAAqC,gCAAA;;AA3B9C;;;KAkCY,oCAAA,IACV,KAAA,EAAO,UAAA,EACP,GAAA,UACA,IAAA,EAAM,wBAAA;;AAXR;;;KAkBY,wCAAA,IACV,KAAA,EAAO,UAAA,EACP,QAAA,EAAU,mCAAA,EACV,IAAA,EAAM,wBAAA;;;;;;;;;;cA8HK,yBAAA,MAAyB,eAAA,CAqDN,GAAA,CArDM,gBAAA;EAlJwC;UAyJxD,QAAA;;KAjJb;;UAsJc,QAAA;;KApJS;;UAyJV,QAAA,CAAS,OAAA,CAAQ,8BAAA;;KAjJ9B;;UAsJa,QAAA;;;EAtJb;;;;UA6Jc,QAAA;;;EA3JS;AA8HhC;;;;;UAsCsB,QAAA,CAAS,iCAAA;;KAhBT;;UAqBA,QAAA,CAAS,+BAAA;;KAAT;;UAKE,QAAA,CAAS,oCAAA;;KAKD;;UAAR,QAAA,CAAS,wCAAA;;;gBArDK,GAAA,CAAA,YAAA;;8GAqDN,GAAA,CAAA,gBAAA;EAzCT;UALD,QAAA;;KAsBC;;UAjBA,QAAA;;KAoCU;;UA/BX,QAAA,CAAS,OAAA,CAAQ,8BAAA;;;;UAKjB,QAAA;;;;;;;UAOC,QAAA;;;EA7Be;;;;;;UAsChB,QAAA,CAAS,iCAAA;;;;UAKT,QAAA,CAAS,+BAAA;;KA1BT;;UA+BE,QAAA,CAAS,oCAAA;;;;UAKT,QAAA,CAAS,wCAAA"}