@copilotkit/markdown 0.0.1 → 2.0.0-rc.12
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/LICENSE +21 -0
- package/README.md +23 -2
- package/dist/index.cjs +2023 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +361 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +361 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2018 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.cjs +538 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +190 -0
- package/dist/react/index.d.cts.map +1 -0
- package/dist/react/index.d.mts +190 -0
- package/dist/react/index.d.mts.map +1 -0
- package/dist/react/index.mjs +535 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/react-native/index.cjs +330 -0
- package/dist/react-native/index.cjs.map +1 -0
- package/dist/react-native/index.d.cts +74 -0
- package/dist/react-native/index.d.cts.map +1 -0
- package/dist/react-native/index.d.mts +74 -0
- package/dist/react-native/index.d.mts.map +1 -0
- package/dist/react-native/index.mjs +300 -0
- package/dist/react-native/index.mjs.map +1 -0
- package/dist/vue/index.cjs +256 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.cts +116 -0
- package/dist/vue/index.d.cts.map +1 -0
- package/dist/vue/index.d.mts +116 -0
- package/dist/vue/index.d.mts.map +1 -0
- package/dist/vue/index.mjs +255 -0
- package/dist/vue/index.mjs.map +1 -0
- package/package.json +75 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as vue from "vue";
|
|
2
|
+
import { PropType, VNode } from "vue";
|
|
3
|
+
import { StreamingMarkdownAstNode, StreamingMarkdownParserOptions } from "@copilotkit/markdown";
|
|
4
|
+
|
|
5
|
+
//#region src/vue/streaming-markdown-renderer.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Custom Vue render function for a specific AST node.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
type VueStreamingMarkdownNodeRenderer = (node: StreamingMarkdownAstNode, defaultVNode: VNode | string | null) => VNode | string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Supported key names for Vue `nodeRenderers`.
|
|
13
|
+
* CamelCase, matching `StreamingMarkdownNodeRendererKey` on the React renderer.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
type VueStreamingMarkdownNodeRendererKey = "paragraph" | "heading" | "blockquote" | "list" | "listItem" | "codeBlock" | "table" | "tableRow" | "tableCell" | "thematicBreak" | "em" | "strong" | "strikethrough" | "inlineCode" | "hardBreak" | "image" | "link" | "autolink" | "citation";
|
|
18
|
+
/**
|
|
19
|
+
* Optional map of node-type renderer keys to custom Vue render functions.
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
type VueStreamingMarkdownNodeRenderers = Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
|
|
23
|
+
/**
|
|
24
|
+
* Vue 3 streaming markdown renderer.
|
|
25
|
+
*
|
|
26
|
+
* Drives the `@copilotkit/markdown` zero-dep parser and walks the
|
|
27
|
+
* resulting AST into VNodes via `h()`.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
32
|
+
/** The full markdown source string, which may grow over time during streaming. */content: {
|
|
33
|
+
type: PropType<string>;
|
|
34
|
+
required: true;
|
|
35
|
+
}; /** When true, finalizes the parser state (end of stream). */
|
|
36
|
+
isComplete: {
|
|
37
|
+
type: PropType<boolean>;
|
|
38
|
+
default: boolean;
|
|
39
|
+
}; /** Optional parser option overrides. */
|
|
40
|
+
options: {
|
|
41
|
+
type: PropType<Partial<StreamingMarkdownParserOptions>>;
|
|
42
|
+
default: undefined;
|
|
43
|
+
}; /** Optional CSS class applied to the root wrapper element. */
|
|
44
|
+
class: {
|
|
45
|
+
type: PropType<string>;
|
|
46
|
+
default: undefined;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
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.
|
|
51
|
+
*/
|
|
52
|
+
caret: {
|
|
53
|
+
type: PropType<boolean>;
|
|
54
|
+
default: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Optional map of node-type keys to custom Vue render functions.
|
|
58
|
+
* Each function receives the AST node and the default VNode and can return
|
|
59
|
+
* 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
|
+
*/
|
|
64
|
+
nodeRenderers: {
|
|
65
|
+
type: PropType<VueStreamingMarkdownNodeRenderers>;
|
|
66
|
+
default: undefined;
|
|
67
|
+
};
|
|
68
|
+
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
69
|
+
[key: string]: any;
|
|
70
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
71
|
+
/** The full markdown source string, which may grow over time during streaming. */content: {
|
|
72
|
+
type: PropType<string>;
|
|
73
|
+
required: true;
|
|
74
|
+
}; /** When true, finalizes the parser state (end of stream). */
|
|
75
|
+
isComplete: {
|
|
76
|
+
type: PropType<boolean>;
|
|
77
|
+
default: boolean;
|
|
78
|
+
}; /** Optional parser option overrides. */
|
|
79
|
+
options: {
|
|
80
|
+
type: PropType<Partial<StreamingMarkdownParserOptions>>;
|
|
81
|
+
default: undefined;
|
|
82
|
+
}; /** Optional CSS class applied to the root wrapper element. */
|
|
83
|
+
class: {
|
|
84
|
+
type: PropType<string>;
|
|
85
|
+
default: undefined;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
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.
|
|
90
|
+
*/
|
|
91
|
+
caret: {
|
|
92
|
+
type: PropType<boolean>;
|
|
93
|
+
default: boolean;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Optional map of node-type keys to custom Vue render functions.
|
|
97
|
+
* Each function receives the AST node and the default VNode and can return
|
|
98
|
+
* 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
|
+
*/
|
|
103
|
+
nodeRenderers: {
|
|
104
|
+
type: PropType<VueStreamingMarkdownNodeRenderers>;
|
|
105
|
+
default: undefined;
|
|
106
|
+
};
|
|
107
|
+
}>> & Readonly<{}>, {
|
|
108
|
+
isComplete: boolean;
|
|
109
|
+
options: StreamingMarkdownParserOptions;
|
|
110
|
+
class: string;
|
|
111
|
+
caret: boolean;
|
|
112
|
+
nodeRenderers: Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
|
|
113
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
114
|
+
//#endregion
|
|
115
|
+
export { StreamingMarkdownRenderer, type VueStreamingMarkdownNodeRenderer, type VueStreamingMarkdownNodeRendererKey, type VueStreamingMarkdownNodeRenderers };
|
|
116
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as vue from "vue";
|
|
2
|
+
import { PropType, VNode } from "vue";
|
|
3
|
+
import { StreamingMarkdownAstNode, StreamingMarkdownParserOptions } from "@copilotkit/markdown";
|
|
4
|
+
|
|
5
|
+
//#region src/vue/streaming-markdown-renderer.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Custom Vue render function for a specific AST node.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
type VueStreamingMarkdownNodeRenderer = (node: StreamingMarkdownAstNode, defaultVNode: VNode | string | null) => VNode | string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Supported key names for Vue `nodeRenderers`.
|
|
13
|
+
* CamelCase, matching `StreamingMarkdownNodeRendererKey` on the React renderer.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
type VueStreamingMarkdownNodeRendererKey = "paragraph" | "heading" | "blockquote" | "list" | "listItem" | "codeBlock" | "table" | "tableRow" | "tableCell" | "thematicBreak" | "em" | "strong" | "strikethrough" | "inlineCode" | "hardBreak" | "image" | "link" | "autolink" | "citation";
|
|
18
|
+
/**
|
|
19
|
+
* Optional map of node-type renderer keys to custom Vue render functions.
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
type VueStreamingMarkdownNodeRenderers = Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
|
|
23
|
+
/**
|
|
24
|
+
* Vue 3 streaming markdown renderer.
|
|
25
|
+
*
|
|
26
|
+
* Drives the `@copilotkit/markdown` zero-dep parser and walks the
|
|
27
|
+
* resulting AST into VNodes via `h()`.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
declare const StreamingMarkdownRenderer: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
32
|
+
/** The full markdown source string, which may grow over time during streaming. */content: {
|
|
33
|
+
type: PropType<string>;
|
|
34
|
+
required: true;
|
|
35
|
+
}; /** When true, finalizes the parser state (end of stream). */
|
|
36
|
+
isComplete: {
|
|
37
|
+
type: PropType<boolean>;
|
|
38
|
+
default: boolean;
|
|
39
|
+
}; /** Optional parser option overrides. */
|
|
40
|
+
options: {
|
|
41
|
+
type: PropType<Partial<StreamingMarkdownParserOptions>>;
|
|
42
|
+
default: undefined;
|
|
43
|
+
}; /** Optional CSS class applied to the root wrapper element. */
|
|
44
|
+
class: {
|
|
45
|
+
type: PropType<string>;
|
|
46
|
+
default: undefined;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
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.
|
|
51
|
+
*/
|
|
52
|
+
caret: {
|
|
53
|
+
type: PropType<boolean>;
|
|
54
|
+
default: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Optional map of node-type keys to custom Vue render functions.
|
|
58
|
+
* Each function receives the AST node and the default VNode and can return
|
|
59
|
+
* 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
|
+
*/
|
|
64
|
+
nodeRenderers: {
|
|
65
|
+
type: PropType<VueStreamingMarkdownNodeRenderers>;
|
|
66
|
+
default: undefined;
|
|
67
|
+
};
|
|
68
|
+
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
69
|
+
[key: string]: any;
|
|
70
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
71
|
+
/** The full markdown source string, which may grow over time during streaming. */content: {
|
|
72
|
+
type: PropType<string>;
|
|
73
|
+
required: true;
|
|
74
|
+
}; /** When true, finalizes the parser state (end of stream). */
|
|
75
|
+
isComplete: {
|
|
76
|
+
type: PropType<boolean>;
|
|
77
|
+
default: boolean;
|
|
78
|
+
}; /** Optional parser option overrides. */
|
|
79
|
+
options: {
|
|
80
|
+
type: PropType<Partial<StreamingMarkdownParserOptions>>;
|
|
81
|
+
default: undefined;
|
|
82
|
+
}; /** Optional CSS class applied to the root wrapper element. */
|
|
83
|
+
class: {
|
|
84
|
+
type: PropType<string>;
|
|
85
|
+
default: undefined;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
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.
|
|
90
|
+
*/
|
|
91
|
+
caret: {
|
|
92
|
+
type: PropType<boolean>;
|
|
93
|
+
default: boolean;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Optional map of node-type keys to custom Vue render functions.
|
|
97
|
+
* Each function receives the AST node and the default VNode and can return
|
|
98
|
+
* 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
|
+
*/
|
|
103
|
+
nodeRenderers: {
|
|
104
|
+
type: PropType<VueStreamingMarkdownNodeRenderers>;
|
|
105
|
+
default: undefined;
|
|
106
|
+
};
|
|
107
|
+
}>> & Readonly<{}>, {
|
|
108
|
+
isComplete: boolean;
|
|
109
|
+
options: StreamingMarkdownParserOptions;
|
|
110
|
+
class: string;
|
|
111
|
+
caret: boolean;
|
|
112
|
+
nodeRenderers: Partial<Record<VueStreamingMarkdownNodeRendererKey, VueStreamingMarkdownNodeRenderer>>;
|
|
113
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
114
|
+
//#endregion
|
|
115
|
+
export { StreamingMarkdownRenderer, type VueStreamingMarkdownNodeRenderer, type VueStreamingMarkdownNodeRendererKey, type VueStreamingMarkdownNodeRenderers };
|
|
116
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","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"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { computed, defineComponent, h } from "vue";
|
|
2
|
+
import { createStreamingMarkdownParserState, finalizeStreamingMarkdown, parseStreamingMarkdownChunk } from "@copilotkit/markdown";
|
|
3
|
+
|
|
4
|
+
//#region src/vue/streaming-markdown-renderer.ts
|
|
5
|
+
const SAFE_HREF = /^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
|
|
6
|
+
const SAFE_IMG_SRC = /^(https?:|data:image\/(?!svg)|\/|\.\/|\.\.\/)/i;
|
|
7
|
+
function sanitizeHref(href) {
|
|
8
|
+
if (!href) return void 0;
|
|
9
|
+
return SAFE_HREF.test(href.trim()) ? href : void 0;
|
|
10
|
+
}
|
|
11
|
+
function sanitizeImgSrc(src) {
|
|
12
|
+
if (!src) return void 0;
|
|
13
|
+
return SAFE_IMG_SRC.test(src.trim()) ? src : void 0;
|
|
14
|
+
}
|
|
15
|
+
const DEFAULT_OPTIONS = {
|
|
16
|
+
segmenter: true,
|
|
17
|
+
enableTables: true,
|
|
18
|
+
enableAutolinks: true
|
|
19
|
+
};
|
|
20
|
+
function normalizeOptions(options) {
|
|
21
|
+
return {
|
|
22
|
+
segmenter: options?.segmenter ?? DEFAULT_OPTIONS.segmenter,
|
|
23
|
+
enableTables: options?.enableTables ?? DEFAULT_OPTIONS.enableTables,
|
|
24
|
+
enableAutolinks: options?.enableAutolinks ?? DEFAULT_OPTIONS.enableAutolinks
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function buildParserState(content, options, isComplete) {
|
|
28
|
+
const state = createStreamingMarkdownParserState(options);
|
|
29
|
+
const parsed = content.length > 0 ? parseStreamingMarkdownChunk(state, content) : state;
|
|
30
|
+
return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;
|
|
31
|
+
}
|
|
32
|
+
function renderTextSegments(node) {
|
|
33
|
+
if (node.text.length === 0) return [];
|
|
34
|
+
if (node.segments.length === 0) return [h("span", { class: "cpk-streaming-markdown-segment" }, node.text)];
|
|
35
|
+
return node.segments.map((segment) => h("span", {
|
|
36
|
+
class: "cpk-streaming-markdown-segment",
|
|
37
|
+
"data-streaming-markdown-segment-kind": segment.kind,
|
|
38
|
+
"data-streaming-markdown-whitespace": String(segment.isWhitespace)
|
|
39
|
+
}, segment.noBreakBefore ? `${segment.text}` : segment.text));
|
|
40
|
+
}
|
|
41
|
+
function renderChildren(childIds, nodeById, nodeRenderers) {
|
|
42
|
+
return childIds.map((id) => {
|
|
43
|
+
const child = nodeById.get(id);
|
|
44
|
+
return child ? renderNode(child, nodeById, nodeRenderers) : null;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function renderNode(node, nodeById, nodeRenderers) {
|
|
48
|
+
function withOverride(key, defaultVNode) {
|
|
49
|
+
const override = nodeRenderers?.[key];
|
|
50
|
+
return override ? override(node, defaultVNode) : defaultVNode;
|
|
51
|
+
}
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case "document": return h("span", { style: "display:contents" }, renderChildren(node.children, nodeById, nodeRenderers));
|
|
54
|
+
case "paragraph": return withOverride("paragraph", h("p", {
|
|
55
|
+
"data-streaming-markdown-node": "paragraph",
|
|
56
|
+
"data-node-open": String(!node.closed)
|
|
57
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
58
|
+
case "heading": return withOverride("heading", h(`h${node.level}`, {
|
|
59
|
+
"data-streaming-markdown-node": "heading",
|
|
60
|
+
"data-node-open": String(!node.closed)
|
|
61
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
62
|
+
case "blockquote": return withOverride("blockquote", h("blockquote", {
|
|
63
|
+
"data-streaming-markdown-node": "blockquote",
|
|
64
|
+
"data-node-open": String(!node.closed)
|
|
65
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
66
|
+
case "list": {
|
|
67
|
+
const tag = node.ordered ? "ol" : "ul";
|
|
68
|
+
const attrs = {
|
|
69
|
+
"data-streaming-markdown-node": "list",
|
|
70
|
+
"data-node-open": String(!node.closed),
|
|
71
|
+
"data-list-tight": String(node.tight)
|
|
72
|
+
};
|
|
73
|
+
if (node.ordered && node.start != null) attrs.start = node.start;
|
|
74
|
+
return withOverride("list", h(tag, attrs, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
75
|
+
}
|
|
76
|
+
case "list-item": return withOverride("listItem", h("li", {
|
|
77
|
+
"data-streaming-markdown-node": "list-item",
|
|
78
|
+
"data-node-open": String(!node.closed)
|
|
79
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
80
|
+
case "code-block": return withOverride("codeBlock", h("pre", {
|
|
81
|
+
"data-streaming-markdown-node": "code-block",
|
|
82
|
+
"data-node-open": String(!node.closed)
|
|
83
|
+
}, [h("code", { "data-code-info": node.info ?? void 0 }, node.text)]));
|
|
84
|
+
case "table": return withOverride("table", h("table", {
|
|
85
|
+
"data-streaming-markdown-node": "table",
|
|
86
|
+
"data-node-open": String(!node.closed)
|
|
87
|
+
}, [h("tbody", renderChildren(node.children, nodeById, nodeRenderers))]));
|
|
88
|
+
case "table-row": return withOverride("tableRow", h("tr", {
|
|
89
|
+
"data-streaming-markdown-node": "table-row",
|
|
90
|
+
"data-node-open": String(!node.closed)
|
|
91
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
92
|
+
case "table-cell": {
|
|
93
|
+
const parent = node.parentId != null ? nodeById.get(node.parentId) : void 0;
|
|
94
|
+
return withOverride("tableCell", h(parent?.type === "table-row" && parent.isHeader ? "th" : "td", {
|
|
95
|
+
"data-streaming-markdown-node": "table-cell",
|
|
96
|
+
"data-node-open": String(!node.closed)
|
|
97
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
98
|
+
}
|
|
99
|
+
case "thematic-break": return withOverride("thematicBreak", h("hr", {
|
|
100
|
+
"data-streaming-markdown-node": "thematic-break",
|
|
101
|
+
"data-node-open": String(!node.closed)
|
|
102
|
+
}));
|
|
103
|
+
case "text": return h("span", { style: "display:contents" }, renderTextSegments(node));
|
|
104
|
+
case "em": return withOverride("em", h("em", {
|
|
105
|
+
"data-streaming-markdown-node": "em",
|
|
106
|
+
"data-node-open": String(!node.closed)
|
|
107
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
108
|
+
case "strong": return withOverride("strong", h("strong", {
|
|
109
|
+
"data-streaming-markdown-node": "strong",
|
|
110
|
+
"data-node-open": String(!node.closed)
|
|
111
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
112
|
+
case "strikethrough": return withOverride("strikethrough", h("del", {
|
|
113
|
+
"data-streaming-markdown-node": "strikethrough",
|
|
114
|
+
"data-node-open": String(!node.closed)
|
|
115
|
+
}, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
116
|
+
case "inline-code": return withOverride("inlineCode", h("code", {
|
|
117
|
+
"data-streaming-markdown-node": "inline-code",
|
|
118
|
+
"data-node-open": String(!node.closed)
|
|
119
|
+
}, node.text));
|
|
120
|
+
case "soft-break": return "\n";
|
|
121
|
+
case "hard-break": return withOverride("hardBreak", h("br", {
|
|
122
|
+
"data-streaming-markdown-node": "hard-break",
|
|
123
|
+
"data-node-open": String(!node.closed)
|
|
124
|
+
}));
|
|
125
|
+
case "link": {
|
|
126
|
+
const safeHref = sanitizeHref(node.url);
|
|
127
|
+
const attrs = {
|
|
128
|
+
"data-streaming-markdown-node": "link",
|
|
129
|
+
"data-node-open": String(!node.closed),
|
|
130
|
+
target: "_blank",
|
|
131
|
+
rel: "noopener noreferrer"
|
|
132
|
+
};
|
|
133
|
+
if (safeHref != null) attrs.href = safeHref;
|
|
134
|
+
if (node.title) attrs.title = node.title;
|
|
135
|
+
return withOverride("link", h("a", attrs, renderChildren(node.children, nodeById, nodeRenderers)));
|
|
136
|
+
}
|
|
137
|
+
case "image": {
|
|
138
|
+
const safeSrc = sanitizeImgSrc(node.url);
|
|
139
|
+
const attrs = {
|
|
140
|
+
"data-streaming-markdown-node": "image",
|
|
141
|
+
"data-node-open": String(!node.closed),
|
|
142
|
+
alt: node.alt
|
|
143
|
+
};
|
|
144
|
+
if (safeSrc != null) attrs.src = safeSrc;
|
|
145
|
+
if (node.title) attrs.title = node.title;
|
|
146
|
+
return withOverride("image", h("img", attrs));
|
|
147
|
+
}
|
|
148
|
+
case "autolink": {
|
|
149
|
+
const safeHref = sanitizeHref(node.url);
|
|
150
|
+
const attrs = {
|
|
151
|
+
"data-streaming-markdown-node": "autolink",
|
|
152
|
+
"data-node-open": String(!node.closed),
|
|
153
|
+
target: "_blank",
|
|
154
|
+
rel: "noopener noreferrer"
|
|
155
|
+
};
|
|
156
|
+
if (safeHref != null) attrs.href = safeHref;
|
|
157
|
+
return withOverride("autolink", h("a", attrs, node.text));
|
|
158
|
+
}
|
|
159
|
+
case "citation": return withOverride("citation", h("sup", {
|
|
160
|
+
"data-streaming-markdown-node": "citation",
|
|
161
|
+
"data-node-open": String(!node.closed),
|
|
162
|
+
class: "cpk-streaming-markdown-citation"
|
|
163
|
+
}, [h("span", {
|
|
164
|
+
role: "doc-noteref",
|
|
165
|
+
class: "cpk-streaming-markdown-citation-label"
|
|
166
|
+
}, String(node.number ?? node.idRef))]));
|
|
167
|
+
default: return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Vue 3 streaming markdown renderer.
|
|
172
|
+
*
|
|
173
|
+
* Drives the `@copilotkit/markdown` zero-dep parser and walks the
|
|
174
|
+
* resulting AST into VNodes via `h()`.
|
|
175
|
+
*
|
|
176
|
+
* @public
|
|
177
|
+
*/
|
|
178
|
+
const StreamingMarkdownRenderer = defineComponent({
|
|
179
|
+
name: "StreamingMarkdownRenderer",
|
|
180
|
+
props: {
|
|
181
|
+
content: {
|
|
182
|
+
type: String,
|
|
183
|
+
required: true
|
|
184
|
+
},
|
|
185
|
+
isComplete: {
|
|
186
|
+
type: Boolean,
|
|
187
|
+
default: false
|
|
188
|
+
},
|
|
189
|
+
options: {
|
|
190
|
+
type: Object,
|
|
191
|
+
default: void 0
|
|
192
|
+
},
|
|
193
|
+
class: {
|
|
194
|
+
type: String,
|
|
195
|
+
default: void 0
|
|
196
|
+
},
|
|
197
|
+
caret: {
|
|
198
|
+
type: Boolean,
|
|
199
|
+
default: false
|
|
200
|
+
},
|
|
201
|
+
nodeRenderers: {
|
|
202
|
+
type: Object,
|
|
203
|
+
default: void 0
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
setup(props) {
|
|
207
|
+
const parserState = computed(() => {
|
|
208
|
+
const opts = normalizeOptions(props.options);
|
|
209
|
+
return buildParserState(props.content ?? "", opts, props.isComplete);
|
|
210
|
+
});
|
|
211
|
+
const nodeById = computed(() => {
|
|
212
|
+
const map = /* @__PURE__ */ new Map();
|
|
213
|
+
for (const node of parserState.value.nodes) map.set(node.id, node);
|
|
214
|
+
return map;
|
|
215
|
+
});
|
|
216
|
+
return () => {
|
|
217
|
+
const state = parserState.value;
|
|
218
|
+
const byId = nodeById.value;
|
|
219
|
+
const rootNode = state.rootId != null ? byId.get(state.rootId) : void 0;
|
|
220
|
+
const renderers = props.nodeRenderers;
|
|
221
|
+
const rootClass = props.class ? `cpk-streaming-markdown-root ${props.class}` : "cpk-streaming-markdown-root";
|
|
222
|
+
if (!rootNode) return h("div", {
|
|
223
|
+
class: rootClass,
|
|
224
|
+
"data-streaming-markdown-root": true
|
|
225
|
+
});
|
|
226
|
+
const children = rootNode.type === "document" ? rootNode.children.map((id) => {
|
|
227
|
+
const child = byId.get(id);
|
|
228
|
+
return child ? renderNode(child, byId, renderers) : null;
|
|
229
|
+
}) : [renderNode(rootNode, byId, renderers)];
|
|
230
|
+
if (props.caret && !state.isComplete) children.push(h("span", {
|
|
231
|
+
"aria-hidden": true,
|
|
232
|
+
class: "cpk-streaming-markdown-caret",
|
|
233
|
+
"data-streaming-markdown-caret": true,
|
|
234
|
+
style: {
|
|
235
|
+
display: "inline-block",
|
|
236
|
+
width: "0.48em",
|
|
237
|
+
height: "0.48em",
|
|
238
|
+
marginInlineStart: "0.08em",
|
|
239
|
+
verticalAlign: "-0.08em",
|
|
240
|
+
borderRadius: "999px",
|
|
241
|
+
backgroundColor: "currentColor",
|
|
242
|
+
opacity: "0.55"
|
|
243
|
+
}
|
|
244
|
+
}));
|
|
245
|
+
return h("div", {
|
|
246
|
+
class: rootClass,
|
|
247
|
+
"data-streaming-markdown-root": true
|
|
248
|
+
}, children);
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
export { StreamingMarkdownRenderer };
|
|
255
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","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":";;;;AAkBA,MAAM,YAAY;AAClB,MAAM,eAAe;AAErB,SAAS,aAAa,MAA8C;AAClE,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,UAAU,KAAK,KAAK,MAAM,CAAC,GAAG,OAAO;;AAG9C,SAAS,eAAe,KAA6C;AACnE,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO,aAAa,KAAK,IAAI,MAAM,CAAC,GAAG,MAAM;;AAO/C,MAAM,kBAAkD;CACtD,WAAW;CACX,cAAc;CACd,iBAAiB;CAClB;AAED,SAAS,iBACP,SACgC;AAChC,QAAO;EACL,WAAW,SAAS,aAAa,gBAAgB;EACjD,cAAc,SAAS,gBAAgB,gBAAgB;EACvD,iBACE,SAAS,mBAAmB,gBAAgB;EAC/C;;AAGH,SAAS,iBACP,SACA,SACA,YAC8B;CAC9B,MAAM,QAAQ,mCAAmC,QAAQ;CACzD,MAAM,SACJ,QAAQ,SAAS,IAAI,4BAA4B,OAAO,QAAQ,GAAG;AACrE,QAAO,aAAa,0BAA0B,OAAO,GAAG;;AAW1D,SAAS,mBAAmB,MAAoC;AAC9D,KAAI,KAAK,KAAK,WAAW,EAAG,QAAO,EAAE;AACrC,KAAI,KAAK,SAAS,WAAW,EAC3B,QAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,kCAAkC,EAAE,KAAK,KAAK,CAAC;AAE5E,QAAO,KAAK,SAAS,KAAK,YACxB,EACE,QACA;EACE,OAAO;EACP,wCAAwC,QAAQ;EAChD,sCAAsC,OAAO,QAAQ,aAAa;EACnE,EACD,QAAQ,gBAAgB,IAAI,QAAQ,SAAS,QAAQ,KACtD,CACF;;AAGH,SAAS,eACP,UACA,UACA,eAC2B;AAC3B,QAAO,SAAS,KAAK,OAAO;EAC1B,MAAM,QAAQ,SAAS,IAAI,GAAG;AAC9B,SAAO,QAAQ,WAAW,OAAO,UAAU,cAAc,GAAG;GAC5D;;AAGJ,SAAS,WACP,MACA,UACA,eACuB;CAEvB,SAAS,aACP,KACA,cACuB;EACvB,MAAM,WAAW,gBAAgB;AACjC,SAAO,WAAW,SAAS,MAAM,aAAa,GAAG;;AAGnD,SAAQ,KAAK,MAAb;EACE,KAAK,WAIH,QAAO,EACL,QACA,EAAE,OAAO,oBAAoB,EAC7B,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD;EAGH,KAAK,YASH,QAAO,aAAa,aARC,EACnB,KACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC6C;EAGhD,KAAK,UAUH,QAAO,aAAa,WARC,EADT,IAAI,KAAK,SAGnB;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC2C;EAG9C,KAAK,aASH,QAAO,aAAa,cARC,EACnB,cACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC8C;EAGjD,KAAK,QAAQ;GACX,MAAM,MAAM,KAAK,UAAU,OAAO;GAClC,MAAM,QAAiC;IACrC,gCAAgC;IAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;IACtC,mBAAmB,OAAO,KAAK,MAAM;IACtC;AACD,OAAI,KAAK,WAAW,KAAK,SAAS,KAChC,OAAM,QAAQ,KAAK;AAOrB,UAAO,aAAa,QALC,EACnB,KACA,OACA,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CACwC;;EAG3C,KAAK,YASH,QAAO,aAAa,YARC,EACnB,MACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC4C;EAG/C,KAAK,aASH,QAAO,aAAa,aARC,EACnB,OACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,CAAC,EAAE,QAAQ,EAAE,kBAAkB,KAAK,QAAQ,QAAW,EAAE,KAAK,KAAK,CAAC,CACrE,CAC6C;EAGhD,KAAK,QASH,QAAO,aAAa,SARC,EACnB,SACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,CAAC,EAAE,SAAS,eAAe,KAAK,UAAU,UAAU,cAAc,CAAC,CAAC,CACrE,CACyC;EAG5C,KAAK,YASH,QAAO,aAAa,YARC,EACnB,MACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC4C;EAG/C,KAAK,cAAc;GACjB,MAAM,SACJ,KAAK,YAAY,OAAO,SAAS,IAAI,KAAK,SAAS,GAAG;AAWxD,UAAO,aAAa,aARC,EAFJ,QAAQ,SAAS,eAAe,OAAO,WACjC,OAAO,MAG5B;IACE,gCAAgC;IAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;IACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC6C;;EAGhD,KAAK,iBAKH,QAAO,aAAa,iBAJC,EAAE,MAAM;GAC3B,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,CAAC,CACgD;EAGpD,KAAK,OACH,QAAO,EAAE,QAAQ,EAAE,OAAO,oBAAoB,EAAE,mBAAmB,KAAK,CAAC;EAG3E,KAAK,KASH,QAAO,aAAa,MARC,EACnB,MACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CACsC;EAGzC,KAAK,SASH,QAAO,aAAa,UARC,EACnB,UACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CAC0C;EAG7C,KAAK,gBASH,QAAO,aAAa,iBARC,EACnB,OACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CACiD;EAGpD,KAAK,cASH,QAAO,aAAa,cARC,EACnB,QACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,EACD,KAAK,KACN,CAC8C;EAGjD,KAAK,aACH,QAAO;EAGT,KAAK,aAKH,QAAO,aAAa,aAJC,EAAE,MAAM;GAC3B,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACvC,CAAC,CAC4C;EAGhD,KAAK,QAAQ;GACX,MAAM,WAAW,aAAa,KAAK,IAAI;GACvC,MAAM,QAAiC;IACrC,gCAAgC;IAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;IACtC,QAAQ;IACR,KAAK;IACN;AACD,OAAI,YAAY,KACd,OAAM,OAAO;AAEf,OAAI,KAAK,MACP,OAAM,QAAQ,KAAK;AAOrB,UAAO,aAAa,QALC,EACnB,KACA,OACA,eAAe,KAAK,UAAU,UAAU,cAAc,CACvD,CACwC;;EAG3C,KAAK,SAAS;GACZ,MAAM,UAAU,eAAe,KAAK,IAAI;GACxC,MAAM,QAAiC;IACrC,gCAAgC;IAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;IACtC,KAAK,KAAK;IACX;AACD,OAAI,WAAW,KACb,OAAM,MAAM;AAEd,OAAI,KAAK,MACP,OAAM,QAAQ,KAAK;AAGrB,UAAO,aAAa,SADC,EAAE,OAAO,MAAM,CACM;;EAG5C,KAAK,YAAY;GACf,MAAM,WAAW,aAAa,KAAK,IAAI;GACvC,MAAM,QAAiC;IACrC,gCAAgC;IAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;IACtC,QAAQ;IACR,KAAK;IACN;AACD,OAAI,YAAY,KACd,OAAM,OAAO;AAGf,UAAO,aAAa,YADC,EAAE,KAAK,OAAO,KAAK,KAAK,CACA;;EAG/C,KAAK,WAoBH,QAAO,aAAa,YAlBC,EACnB,OACA;GACE,gCAAgC;GAChC,kBAAkB,OAAO,CAAC,KAAK,OAAO;GACtC,OAAO;GACR,EACD,CACE,EACE,QACA;GACE,MAAM;GACN,OAAO;GACR,EACD,OAAO,KAAK,UAAU,KAAK,MAAM,CAClC,CACF,CACF,CAC4C;EAG/C,QACE,QAAO;;;;;;;;;;;AAiEb,MAAa,4BAA4B,gBAAgB;CACvD,MAAM;CAEN,OAAO;EAEL,SAAS;GACP,MAAM;GACN,UAAU;GACX;EAED,YAAY;GACV,MAAM;GACN,SAAS;GACV;EAED,SAAS;GACP,MAAM;GACN,SAAS;GACV;EAED,OAAO;GACL,MAAM;GACN,SAAS;GACV;EAKD,OAAO;GACL,MAAM;GACN,SAAS;GACV;EASD,eAAe;GACb,MAAM;GACN,SAAS;GACV;EACF;CAED,MAAM,OAAO;EACX,MAAM,cAAc,eAA6C;GAC/D,MAAM,OAAO,iBAAiB,MAAM,QAAQ;AAC5C,UAAO,iBAAiB,MAAM,WAAW,IAAI,MAAM,MAAM,WAAW;IACpE;EAEF,MAAM,WAAW,eAAyB;GACxC,MAAM,sBAAM,IAAI,KAAuC;AACvD,QAAK,MAAM,QAAQ,YAAY,MAAM,MACnC,KAAI,IAAI,KAAK,IAAI,KAAK;AAExB,UAAO;IACP;AAEF,eAAa;GACX,MAAM,QAAQ,YAAY;GAC1B,MAAM,OAAO,SAAS;GACtB,MAAM,WACJ,MAAM,UAAU,OAAO,KAAK,IAAI,MAAM,OAAO,GAAG;GAClD,MAAM,YAAY,MAAM;GAExB,MAAM,YAAY,MAAM,QACpB,+BAA+B,MAAM,UACrC;AAEJ,OAAI,CAAC,SACH,QAAO,EAAE,OAAO;IACd,OAAO;IACP,gCAAgC;IACjC,CAAC;GAIJ,MAAM,WACJ,SAAS,SAAS,aACd,SAAS,SAAS,KAAK,OAAO;IAC5B,MAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,WAAO,QAAQ,WAAW,OAAO,MAAM,UAAU,GAAG;KACpD,GACF,CAAC,WAAW,UAAU,MAAM,UAAU,CAAC;AAG7C,OAAI,MAAM,SAAS,CAAC,MAAM,WACxB,UAAS,KACP,EAAE,QAAQ;IACR,eAAe;IACf,OAAO;IACP,iCAAiC;IACjC,OAAO;KACL,SAAS;KACT,OAAO;KACP,QAAQ;KACR,mBAAmB;KACnB,eAAe;KACf,cAAc;KACd,iBAAiB;KACjB,SAAS;KACV;IACF,CAAC,CACH;AAGH,UAAO,EACL,OACA;IACE,OAAO;IACP,gCAAgC;IACjC,EACD,SACD;;;CAGN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/markdown",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "2.0.0-rc.12",
|
|
4
4
|
"description": "Zero-dependency streaming markdown parser and renderer for CopilotKit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/CopilotKit/CopilotKit.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.mjs",
|
|
13
|
+
"types": "./dist/index.d.cts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.mjs",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./react": {
|
|
20
|
+
"import": "./dist/react/index.mjs",
|
|
21
|
+
"require": "./dist/react/index.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./react-native": {
|
|
24
|
+
"import": "./dist/react-native/index.mjs",
|
|
25
|
+
"require": "./dist/react-native/index.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./vue": {
|
|
28
|
+
"import": "./dist/vue/index.mjs",
|
|
29
|
+
"require": "./dist/vue/index.cjs"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18",
|
|
43
|
+
"react-native": ">=0.70",
|
|
44
|
+
"vue": ">=3"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"react": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"react-native": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"vue": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@testing-library/react": "^16.1.0",
|
|
59
|
+
"@types/react": "^19",
|
|
60
|
+
"@types/node": "^22.15.3",
|
|
61
|
+
"@vue/test-utils": "^2",
|
|
62
|
+
"jsdom": "^25.0.1",
|
|
63
|
+
"react": "^19",
|
|
64
|
+
"react-dom": "^19",
|
|
65
|
+
"tsdown": "^0.20.3",
|
|
66
|
+
"typescript": "5.8.2",
|
|
67
|
+
"vitest": "^3.2.4",
|
|
68
|
+
"vue": "^3",
|
|
69
|
+
"@copilotkit/typescript-config": "1.55.0-next.8"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsdown",
|
|
76
|
+
"dev": "tsdown --watch",
|
|
77
|
+
"check-types": "tsc --noEmit",
|
|
78
|
+
"test": "vitest run",
|
|
79
|
+
"test:watch": "vitest",
|
|
80
|
+
"publint": "publint .",
|
|
81
|
+
"attw": "attw --pack . --profile node16 --ignore-rules internal-resolution-error"
|
|
9
82
|
}
|
|
10
|
-
}
|
|
83
|
+
}
|