@copilotkit/markdown 2.0.0-rc.46 → 2.0.0-rc.47
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/dist/react/index.cjs
CHANGED
|
@@ -35,9 +35,11 @@ function useStreamingMarkdownParser(text, options, isCompleteInput = false) {
|
|
|
35
35
|
function createStreamingMarkdownNodeRenderers(renderers) {
|
|
36
36
|
return renderers;
|
|
37
37
|
}
|
|
38
|
+
const { class: CARET_CLASS_NAME, ...CARET_ATTRS } = _copilotkit_markdown.STREAMING_MARKDOWN_CARET_ATTRS;
|
|
38
39
|
function renderDefaultCaret() {
|
|
39
40
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
40
|
-
...
|
|
41
|
+
...CARET_ATTRS,
|
|
42
|
+
className: CARET_CLASS_NAME,
|
|
41
43
|
style: _copilotkit_markdown.STREAMING_MARKDOWN_CARET_STYLE
|
|
42
44
|
});
|
|
43
45
|
}
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["STREAMING_MARKDOWN_CARET_ATTRS","STREAMING_MARKDOWN_CARET_STYLE","Fragment","STREAMING_MARKDOWN_ROOT_CLASS"],"sources":["../../src/react/use-streaming-markdown-parser.tsx","../../src/react/streaming-markdown-renderer.tsx"],"sourcesContent":["import { updateStreamingMarkdownParserSession } from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserSession,\n} from \"@copilotkit/markdown\";\nimport { useMemo, useRef } from \"react\";\n\n/**\n * Internal prop-driven hook for streaming markdown parsing in React.\n *\n * @param text - Full markdown text that typically grows over time.\n * @param options - Optional parser option overrides.\n * @param isCompleteInput - When true, finalizes the parse state for the current text.\n * @returns The current immutable streaming markdown parser state.\n */\nexport function useStreamingMarkdownParser(\n text: string,\n options?: Partial<StreamingMarkdownParserOptions>,\n isCompleteInput = false,\n) {\n const sessionRef = useRef<StreamingMarkdownParserSession | null>(null);\n // The session compares options by value, so a new options object with the\n // same values keeps the parsed state (and node identity).\n const session = useMemo(() => {\n const next = updateStreamingMarkdownParserSession(\n sessionRef.current,\n text,\n options,\n isCompleteInput,\n );\n sessionRef.current = next;\n return next;\n }, [text, options, isCompleteInput]);\n\n return session.parserState;\n}\n","import {\n STREAMING_MARKDOWN_CARET_ATTRS,\n STREAMING_MARKDOWN_CARET_STYLE,\n STREAMING_MARKDOWN_ROOT_CLASS,\n buildStreamingMarkdownRenderTree,\n findStreamingMarkdownOpenNode,\n getStreamingMarkdownStyles,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeClassNames,\n StreamingMarkdownNodeRendererKey,\n StreamingMarkdownNodeType,\n StreamingMarkdownParserOptions,\n StreamingMarkdownRenderChild,\n StreamingMarkdownRenderElement,\n StreamingMarkdownRenderNode,\n} from \"@copilotkit/markdown\";\nimport { Fragment, createElement, useMemo, useRef } from \"react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useStreamingMarkdownParser } from \"./use-streaming-markdown-parser\";\n\nexport type {\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeRendererKey,\n} from \"@copilotkit/markdown\";\n\n/**\n * Props for the `StreamingMarkdownRenderer` component.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /**\n * Full markdown source that grows over time.\n */\n children: string;\n\n /**\n * Optional parser option overrides.\n */\n options?: Partial<StreamingMarkdownParserOptions>;\n\n /**\n * When `true`, finalizes the parser state after applying the latest text.\n */\n isComplete?: boolean;\n\n /**\n * Caret rendering behavior for streaming output.\n *\n * - `false`/`undefined`: no caret\n * - `true`: render the default caret\n * - `ReactNode`: render a custom caret node\n * - function: render a custom caret node from parser state\n */\n caret?: StreamingMarkdownCaret;\n\n /**\n * Optional class applied to the root element.\n */\n className?: string;\n\n /**\n * Called when a link or autolink is clicked.\n */\n onLinkClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n url: string,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Called when a citation reference is clicked.\n */\n onCitationClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n citation: StreamingMarkdownCitationRenderData,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Optional custom renderers keyed by streaming markdown node type.\n */\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n\n /**\n * Extra classes for the default element of a node type, for example\n * `{ codeBlock: \"my-code-block\" }`.\n */\n nodeClassNames?: StreamingMarkdownNodeClassNames;\n}\n\n/**\n * Props supplied to a custom node renderer.\n *\n * @public\n */\nexport interface StreamingMarkdownNodeRendererProps<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> {\n /**\n * The immutable AST node being rendered.\n */\n node: TNode;\n\n /**\n * Already-rendered child content for container nodes.\n */\n children: ReactNode;\n\n /**\n * Default element tree produced by `StreamingMarkdownRenderer` for this node.\n */\n defaultNode: ReactNode;\n\n /**\n * Citation metadata when rendering a citation node.\n */\n citation?: StreamingMarkdownCitationRenderData;\n}\n\n/**\n * Custom renderer callback for a specific streaming markdown node.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderer<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> = (props: StreamingMarkdownNodeRendererProps<TNode>) => ReactNode;\n\n/**\n * Render context supplied to caret renderer callbacks.\n *\n * @public\n */\nexport interface StreamingMarkdownCaretRenderProps {\n /**\n * Whether the parser state is complete for the current source text.\n */\n isComplete: boolean;\n\n /**\n * The deepest currently open AST node, if any.\n */\n openNode: StreamingMarkdownAstNode | null;\n}\n\n/**\n * Callback type for custom caret renderers.\n *\n * @public\n */\nexport type StreamingMarkdownCaretRenderer = (\n props: StreamingMarkdownCaretRenderProps,\n) => ReactNode;\n\n/**\n * Supported caret prop values.\n *\n * @public\n */\nexport type StreamingMarkdownCaret =\n | boolean\n | ReactNode\n | StreamingMarkdownCaretRenderer;\n\ntype StreamingMarkdownNodeOfType<TNodeType extends StreamingMarkdownNodeType> =\n Extract<StreamingMarkdownAstNode, { type: TNodeType }>;\n\ntype StreamingMarkdownNodeByRendererKey = {\n node: StreamingMarkdownAstNode;\n document: StreamingMarkdownNodeOfType<\"document\">;\n paragraph: StreamingMarkdownNodeOfType<\"paragraph\">;\n heading: StreamingMarkdownNodeOfType<\"heading\">;\n blockquote: StreamingMarkdownNodeOfType<\"blockquote\">;\n list: StreamingMarkdownNodeOfType<\"list\">;\n listItem: StreamingMarkdownNodeOfType<\"list-item\">;\n codeBlock: StreamingMarkdownNodeOfType<\"code-block\">;\n table: StreamingMarkdownNodeOfType<\"table\">;\n tableRow: StreamingMarkdownNodeOfType<\"table-row\">;\n tableCell: StreamingMarkdownNodeOfType<\"table-cell\">;\n thematicBreak: StreamingMarkdownNodeOfType<\"thematic-break\">;\n text: StreamingMarkdownNodeOfType<\"text\">;\n em: StreamingMarkdownNodeOfType<\"em\">;\n strong: StreamingMarkdownNodeOfType<\"strong\">;\n strikethrough: StreamingMarkdownNodeOfType<\"strikethrough\">;\n inlineCode: StreamingMarkdownNodeOfType<\"inline-code\">;\n softBreak: StreamingMarkdownNodeOfType<\"soft-break\">;\n hardBreak: StreamingMarkdownNodeOfType<\"hard-break\">;\n image: StreamingMarkdownNodeOfType<\"image\">;\n link: StreamingMarkdownNodeOfType<\"link\">;\n autolink: StreamingMarkdownNodeOfType<\"autolink\">;\n citation: StreamingMarkdownNodeOfType<\"citation\">;\n math: StreamingMarkdownNodeOfType<\"math\">;\n};\n\n/**\n * Custom renderers keyed by camelCase node names.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderers = Partial<{\n [TKey in StreamingMarkdownNodeRendererKey]: StreamingMarkdownNodeRenderer<\n StreamingMarkdownNodeByRendererKey[TKey]\n >;\n}>;\n\n/**\n * Helper for creating typed streaming markdown node renderer maps.\n *\n * @public\n */\nexport function createStreamingMarkdownNodeRenderers<\n T extends StreamingMarkdownNodeRenderers,\n>(renderers: T): T {\n return renderers;\n}\n\ntype RenderContext = {\n onLinkClickRef: { current: StreamingMarkdownRendererProps[\"onLinkClick\"] };\n onCitationClickRef: {\n current: StreamingMarkdownRendererProps[\"onCitationClick\"];\n };\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n caretNode: ReactNode;\n};\n\nfunction renderDefaultCaret() {\n return (\n <span\n {...STREAMING_MARKDOWN_CARET_ATTRS}\n style={STREAMING_MARKDOWN_CARET_STYLE}\n />\n );\n}\n\nfunction resolveCaretNode(\n caret: StreamingMarkdownCaret | undefined,\n props: StreamingMarkdownCaretRenderProps,\n): ReactNode {\n if (caret === undefined || caret === false || caret === null) {\n return null;\n }\n\n if (caret === true) {\n return renderDefaultCaret();\n }\n\n if (typeof caret === \"function\") {\n return caret(props);\n }\n\n return caret;\n}\n\nfunction toReactProps(\n element: StreamingMarkdownRenderElement,\n context: RenderContext,\n) {\n const props: Record<string, unknown> = { key: element.key };\n const entries = Object.entries(element.attrs);\n for (const [name, value] of entries) {\n props[name === \"class\" ? \"className\" : name] = value;\n }\n // A task-list checkbox is display-only; readOnly tells React it is\n // intentionally uncontrolled by user input.\n if (element.tag === \"input\") props.readOnly = true;\n\n const action = element.action;\n if (action?.type === \"link\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onLinkClickRef.current?.(event, action.url, action.node);\n };\n }\n if (action?.type === \"citation\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onCitationClickRef.current?.(event, action.citation, action.node);\n };\n }\n return props;\n}\n\nconst VOID_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\"]);\n\nfunction renderList(\n children: StreamingMarkdownRenderChild[],\n context: RenderContext,\n slot: ReactNode,\n): ReactNode[] {\n return children.map((child) => renderChild(child, context, slot));\n}\n\n/** Unwraps single-entry lists so overrides see `\"text\"` rather than `[\"text\"]`. */\nfunction collapse(nodes: ReactNode[]): ReactNode {\n return nodes.length === 1 ? nodes[0] : nodes;\n}\n\nfunction renderChild(\n child: StreamingMarkdownRenderChild,\n context: RenderContext,\n slot: ReactNode,\n): ReactNode {\n switch (child.kind) {\n case \"node\":\n return renderRenderNode(child, context);\n case \"element\": {\n const props = toReactProps(child, context);\n return VOID_TAGS.has(child.tag)\n ? createElement(child.tag, props)\n : createElement(\n child.tag,\n props,\n ...renderList(child.children, context, slot),\n );\n }\n case \"text\":\n return child.text;\n case \"caret\":\n return <Fragment key={child.key}>{context.caretNode}</Fragment>;\n case \"slot\":\n return <Fragment key={child.key}>{slot}</Fragment>;\n default: {\n const unreachable: never = child;\n return unreachable;\n }\n }\n}\n\nfunction renderRenderNode(\n renderNode: StreamingMarkdownRenderNode,\n context: RenderContext,\n): ReactNode {\n const children =\n renderNode.children === null\n ? null\n : collapse(renderList(renderNode.children, context, null));\n const defaultNode = collapse(\n renderList(renderNode.output, context, children),\n );\n const renderer = (context.nodeRenderers?.[renderNode.rendererKey] ??\n context.nodeRenderers?.node) as StreamingMarkdownNodeRenderer | undefined;\n\n const rendered = renderer\n ? renderer({\n node: renderNode.node,\n children,\n defaultNode,\n citation: renderNode.citation,\n })\n : defaultNode;\n return <Fragment key={renderNode.key}>{rendered}</Fragment>;\n}\n\n/**\n * React renderer for streaming markdown parser output.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n children,\n options,\n isComplete = false,\n caret,\n className,\n onLinkClick,\n onCitationClick,\n nodeRenderers,\n nodeClassNames,\n}: StreamingMarkdownRendererProps) {\n const text = children ?? \"\";\n const parserState = useStreamingMarkdownParser(text, options, isComplete);\n const onLinkClickRef =\n useRef<StreamingMarkdownRendererProps[\"onLinkClick\"]>(onLinkClick);\n const onCitationClickRef =\n useRef<StreamingMarkdownRendererProps[\"onCitationClick\"]>(onCitationClick);\n onLinkClickRef.current = onLinkClick;\n onCitationClickRef.current = onCitationClick;\n\n const content = useMemo(() => {\n const caretNode = resolveCaretNode(caret, {\n isComplete: parserState.isComplete,\n openNode: findStreamingMarkdownOpenNode(parserState),\n });\n const tree = buildStreamingMarkdownRenderTree(parserState, {\n caret: caretNode != null,\n nodeClassNames,\n });\n if (!tree.root) return null;\n return renderRenderNode(tree.root, {\n onLinkClickRef,\n onCitationClickRef,\n nodeRenderers,\n caretNode,\n });\n }, [parserState, nodeRenderers, nodeClassNames, caret]);\n\n const rootClassName = className\n ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${className}`\n : STREAMING_MARKDOWN_ROOT_CLASS;\n\n return (\n <>\n <style>{getStreamingMarkdownStyles()}</style>\n <div className={rootClassName} data-streaming-markdown-root>\n {content}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["STREAMING_MARKDOWN_CARET_ATTRS","STREAMING_MARKDOWN_CARET_STYLE","Fragment","STREAMING_MARKDOWN_ROOT_CLASS"],"sources":["../../src/react/use-streaming-markdown-parser.tsx","../../src/react/streaming-markdown-renderer.tsx"],"sourcesContent":["import { updateStreamingMarkdownParserSession } from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserSession,\n} from \"@copilotkit/markdown\";\nimport { useMemo, useRef } from \"react\";\n\n/**\n * Internal prop-driven hook for streaming markdown parsing in React.\n *\n * @param text - Full markdown text that typically grows over time.\n * @param options - Optional parser option overrides.\n * @param isCompleteInput - When true, finalizes the parse state for the current text.\n * @returns The current immutable streaming markdown parser state.\n */\nexport function useStreamingMarkdownParser(\n text: string,\n options?: Partial<StreamingMarkdownParserOptions>,\n isCompleteInput = false,\n) {\n const sessionRef = useRef<StreamingMarkdownParserSession | null>(null);\n // The session compares options by value, so a new options object with the\n // same values keeps the parsed state (and node identity).\n const session = useMemo(() => {\n const next = updateStreamingMarkdownParserSession(\n sessionRef.current,\n text,\n options,\n isCompleteInput,\n );\n sessionRef.current = next;\n return next;\n }, [text, options, isCompleteInput]);\n\n return session.parserState;\n}\n","import {\n STREAMING_MARKDOWN_CARET_ATTRS,\n STREAMING_MARKDOWN_CARET_STYLE,\n STREAMING_MARKDOWN_ROOT_CLASS,\n buildStreamingMarkdownRenderTree,\n findStreamingMarkdownOpenNode,\n getStreamingMarkdownStyles,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeClassNames,\n StreamingMarkdownNodeRendererKey,\n StreamingMarkdownNodeType,\n StreamingMarkdownParserOptions,\n StreamingMarkdownRenderChild,\n StreamingMarkdownRenderElement,\n StreamingMarkdownRenderNode,\n} from \"@copilotkit/markdown\";\nimport { Fragment, createElement, useMemo, useRef } from \"react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useStreamingMarkdownParser } from \"./use-streaming-markdown-parser\";\n\nexport type {\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeRendererKey,\n} from \"@copilotkit/markdown\";\n\n/**\n * Props for the `StreamingMarkdownRenderer` component.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /**\n * Full markdown source that grows over time.\n */\n children: string;\n\n /**\n * Optional parser option overrides.\n */\n options?: Partial<StreamingMarkdownParserOptions>;\n\n /**\n * When `true`, finalizes the parser state after applying the latest text.\n */\n isComplete?: boolean;\n\n /**\n * Caret rendering behavior for streaming output.\n *\n * - `false`/`undefined`: no caret\n * - `true`: render the default caret\n * - `ReactNode`: render a custom caret node\n * - function: render a custom caret node from parser state\n */\n caret?: StreamingMarkdownCaret;\n\n /**\n * Optional class applied to the root element.\n */\n className?: string;\n\n /**\n * Called when a link or autolink is clicked.\n */\n onLinkClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n url: string,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Called when a citation reference is clicked.\n */\n onCitationClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n citation: StreamingMarkdownCitationRenderData,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Optional custom renderers keyed by streaming markdown node type.\n */\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n\n /**\n * Extra classes for the default element of a node type, for example\n * `{ codeBlock: \"my-code-block\" }`.\n */\n nodeClassNames?: StreamingMarkdownNodeClassNames;\n}\n\n/**\n * Props supplied to a custom node renderer.\n *\n * @public\n */\nexport interface StreamingMarkdownNodeRendererProps<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> {\n /**\n * The immutable AST node being rendered.\n */\n node: TNode;\n\n /**\n * Already-rendered child content for container nodes.\n */\n children: ReactNode;\n\n /**\n * Default element tree produced by `StreamingMarkdownRenderer` for this node.\n */\n defaultNode: ReactNode;\n\n /**\n * Citation metadata when rendering a citation node.\n */\n citation?: StreamingMarkdownCitationRenderData;\n}\n\n/**\n * Custom renderer callback for a specific streaming markdown node.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderer<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> = (props: StreamingMarkdownNodeRendererProps<TNode>) => ReactNode;\n\n/**\n * Render context supplied to caret renderer callbacks.\n *\n * @public\n */\nexport interface StreamingMarkdownCaretRenderProps {\n /**\n * Whether the parser state is complete for the current source text.\n */\n isComplete: boolean;\n\n /**\n * The deepest currently open AST node, if any.\n */\n openNode: StreamingMarkdownAstNode | null;\n}\n\n/**\n * Callback type for custom caret renderers.\n *\n * @public\n */\nexport type StreamingMarkdownCaretRenderer = (\n props: StreamingMarkdownCaretRenderProps,\n) => ReactNode;\n\n/**\n * Supported caret prop values.\n *\n * @public\n */\nexport type StreamingMarkdownCaret =\n | boolean\n | ReactNode\n | StreamingMarkdownCaretRenderer;\n\ntype StreamingMarkdownNodeOfType<TNodeType extends StreamingMarkdownNodeType> =\n Extract<StreamingMarkdownAstNode, { type: TNodeType }>;\n\ntype StreamingMarkdownNodeByRendererKey = {\n node: StreamingMarkdownAstNode;\n document: StreamingMarkdownNodeOfType<\"document\">;\n paragraph: StreamingMarkdownNodeOfType<\"paragraph\">;\n heading: StreamingMarkdownNodeOfType<\"heading\">;\n blockquote: StreamingMarkdownNodeOfType<\"blockquote\">;\n list: StreamingMarkdownNodeOfType<\"list\">;\n listItem: StreamingMarkdownNodeOfType<\"list-item\">;\n codeBlock: StreamingMarkdownNodeOfType<\"code-block\">;\n table: StreamingMarkdownNodeOfType<\"table\">;\n tableRow: StreamingMarkdownNodeOfType<\"table-row\">;\n tableCell: StreamingMarkdownNodeOfType<\"table-cell\">;\n thematicBreak: StreamingMarkdownNodeOfType<\"thematic-break\">;\n text: StreamingMarkdownNodeOfType<\"text\">;\n em: StreamingMarkdownNodeOfType<\"em\">;\n strong: StreamingMarkdownNodeOfType<\"strong\">;\n strikethrough: StreamingMarkdownNodeOfType<\"strikethrough\">;\n inlineCode: StreamingMarkdownNodeOfType<\"inline-code\">;\n softBreak: StreamingMarkdownNodeOfType<\"soft-break\">;\n hardBreak: StreamingMarkdownNodeOfType<\"hard-break\">;\n image: StreamingMarkdownNodeOfType<\"image\">;\n link: StreamingMarkdownNodeOfType<\"link\">;\n autolink: StreamingMarkdownNodeOfType<\"autolink\">;\n citation: StreamingMarkdownNodeOfType<\"citation\">;\n math: StreamingMarkdownNodeOfType<\"math\">;\n};\n\n/**\n * Custom renderers keyed by camelCase node names.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderers = Partial<{\n [TKey in StreamingMarkdownNodeRendererKey]: StreamingMarkdownNodeRenderer<\n StreamingMarkdownNodeByRendererKey[TKey]\n >;\n}>;\n\n/**\n * Helper for creating typed streaming markdown node renderer maps.\n *\n * @public\n */\nexport function createStreamingMarkdownNodeRenderers<\n T extends StreamingMarkdownNodeRenderers,\n>(renderers: T): T {\n return renderers;\n}\n\ntype RenderContext = {\n onLinkClickRef: { current: StreamingMarkdownRendererProps[\"onLinkClick\"] };\n onCitationClickRef: {\n current: StreamingMarkdownRendererProps[\"onCitationClick\"];\n };\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n caretNode: ReactNode;\n};\n\n// The shared caret attrs use the DOM name `class`; React needs `className`.\nconst { class: CARET_CLASS_NAME, ...CARET_ATTRS } =\n STREAMING_MARKDOWN_CARET_ATTRS;\n\nfunction renderDefaultCaret() {\n return (\n <span\n {...CARET_ATTRS}\n className={CARET_CLASS_NAME}\n style={STREAMING_MARKDOWN_CARET_STYLE}\n />\n );\n}\n\nfunction resolveCaretNode(\n caret: StreamingMarkdownCaret | undefined,\n props: StreamingMarkdownCaretRenderProps,\n): ReactNode {\n if (caret === undefined || caret === false || caret === null) {\n return null;\n }\n\n if (caret === true) {\n return renderDefaultCaret();\n }\n\n if (typeof caret === \"function\") {\n return caret(props);\n }\n\n return caret;\n}\n\nfunction toReactProps(\n element: StreamingMarkdownRenderElement,\n context: RenderContext,\n) {\n const props: Record<string, unknown> = { key: element.key };\n const entries = Object.entries(element.attrs);\n for (const [name, value] of entries) {\n props[name === \"class\" ? \"className\" : name] = value;\n }\n // A task-list checkbox is display-only; readOnly tells React it is\n // intentionally uncontrolled by user input.\n if (element.tag === \"input\") props.readOnly = true;\n\n const action = element.action;\n if (action?.type === \"link\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onLinkClickRef.current?.(event, action.url, action.node);\n };\n }\n if (action?.type === \"citation\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onCitationClickRef.current?.(event, action.citation, action.node);\n };\n }\n return props;\n}\n\nconst VOID_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\"]);\n\nfunction renderList(\n children: StreamingMarkdownRenderChild[],\n context: RenderContext,\n slot: ReactNode,\n): ReactNode[] {\n return children.map((child) => renderChild(child, context, slot));\n}\n\n/** Unwraps single-entry lists so overrides see `\"text\"` rather than `[\"text\"]`. */\nfunction collapse(nodes: ReactNode[]): ReactNode {\n return nodes.length === 1 ? nodes[0] : nodes;\n}\n\nfunction renderChild(\n child: StreamingMarkdownRenderChild,\n context: RenderContext,\n slot: ReactNode,\n): ReactNode {\n switch (child.kind) {\n case \"node\":\n return renderRenderNode(child, context);\n case \"element\": {\n const props = toReactProps(child, context);\n return VOID_TAGS.has(child.tag)\n ? createElement(child.tag, props)\n : createElement(\n child.tag,\n props,\n ...renderList(child.children, context, slot),\n );\n }\n case \"text\":\n return child.text;\n case \"caret\":\n return <Fragment key={child.key}>{context.caretNode}</Fragment>;\n case \"slot\":\n return <Fragment key={child.key}>{slot}</Fragment>;\n default: {\n const unreachable: never = child;\n return unreachable;\n }\n }\n}\n\nfunction renderRenderNode(\n renderNode: StreamingMarkdownRenderNode,\n context: RenderContext,\n): ReactNode {\n const children =\n renderNode.children === null\n ? null\n : collapse(renderList(renderNode.children, context, null));\n const defaultNode = collapse(\n renderList(renderNode.output, context, children),\n );\n const renderer = (context.nodeRenderers?.[renderNode.rendererKey] ??\n context.nodeRenderers?.node) as StreamingMarkdownNodeRenderer | undefined;\n\n const rendered = renderer\n ? renderer({\n node: renderNode.node,\n children,\n defaultNode,\n citation: renderNode.citation,\n })\n : defaultNode;\n return <Fragment key={renderNode.key}>{rendered}</Fragment>;\n}\n\n/**\n * React renderer for streaming markdown parser output.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n children,\n options,\n isComplete = false,\n caret,\n className,\n onLinkClick,\n onCitationClick,\n nodeRenderers,\n nodeClassNames,\n}: StreamingMarkdownRendererProps) {\n const text = children ?? \"\";\n const parserState = useStreamingMarkdownParser(text, options, isComplete);\n const onLinkClickRef =\n useRef<StreamingMarkdownRendererProps[\"onLinkClick\"]>(onLinkClick);\n const onCitationClickRef =\n useRef<StreamingMarkdownRendererProps[\"onCitationClick\"]>(onCitationClick);\n onLinkClickRef.current = onLinkClick;\n onCitationClickRef.current = onCitationClick;\n\n const content = useMemo(() => {\n const caretNode = resolveCaretNode(caret, {\n isComplete: parserState.isComplete,\n openNode: findStreamingMarkdownOpenNode(parserState),\n });\n const tree = buildStreamingMarkdownRenderTree(parserState, {\n caret: caretNode != null,\n nodeClassNames,\n });\n if (!tree.root) return null;\n return renderRenderNode(tree.root, {\n onLinkClickRef,\n onCitationClickRef,\n nodeRenderers,\n caretNode,\n });\n }, [parserState, nodeRenderers, nodeClassNames, caret]);\n\n const rootClassName = className\n ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${className}`\n : STREAMING_MARKDOWN_ROOT_CLASS;\n\n return (\n <>\n <style>{getStreamingMarkdownStyles()}</style>\n <div className={rootClassName} data-streaming-markdown-root>\n {content}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/react/streaming-markdown-renderer.tsx","../../src/react/use-streaming-markdown-parser.tsx"],"mappings":";;;;;;;AAiCA;;;UAAiB,8BAAA;EASL;;;EALV,QAAA;EAiCQ;;;EA5BR,OAAA,GAAU,OAAA,CAAQ,8BAAA;EAqCV;;;EAhCR,UAAA;EA4CgD;;;;;;;;EAlChD,KAAA,GAAQ,sBAAA;EAUR;;;EALA,SAAA;EAOE;;;EAFF,WAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,GAAA,UACA,IAAA,EAAM,wBAAA;EAOC;;;EADT,eAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,QAAA,EAAU,qCAAA,EACV,IAAA,EAAM,wBAAA;EADN;;;EAOF,aAAA,GAAgB,8BAAA;EAAA;;;;EAMhB,cAAA,GAAiB,+BAAA;AAAA;;;;;;UAQF,kCAAA,eACD,wBAAA,GAA2B,wBAAA;EAe5B;;;EAVb,IAAA,EAAM,KAAA;EALN;;;EAUA,QAAA,EAAU,SAAA;EALJ;;;EAUN,WAAA,EAAa,SAAA;EAAA;;;EAKb,QAAA,GAAW,qCAAA;AAAA;AAQb;;;;;AAAA,KAAY,6BAAA,eACI,wBAAA,GAA2B,wBAAA,KACtC,KAAA,EAAO,kCAAA,CAAmC,KAAA,MAAW,SAAA;;;;;;UAOzC,iCAAA;EAR0B;;;EAYzC,UAAA;EAXwD;;;EAgBxD,QAAA,EAAU,wBAAA;AAAA;;;;;;KAQA,8BAAA,IACV,KAAA,EAAO,iCAAA,KACJ,SAAA;;AAFL;;;;KASY,sBAAA,aAER,SAAA,GACA,8BAAA;AAAA,KAEC,2BAAA,mBAA8C,yBAAA,IACjD,OAAA,CAAQ,wBAAA;EAA4B,IAAA,EAAM,SAAA;AAAA;AAAA,KAEvC,kCAAA;EACH,IAAA,EAAM,wBAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,OAAA,EAAS,2BAAA;EACT,UAAA,EAAY,2BAAA;EACZ,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,aAAA,EAAe,2BAAA;EACf,IAAA,EAAM,2BAAA;EACN,EAAA,EAAI,2BAAA;EACJ,MAAA,EAAQ,2BAAA;EACR,aAAA,EAAe,2BAAA;EACf,UAAA,EAAY,2BAAA;EACZ,SAAA,EAAW,2BAAA;EACX,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,QAAA,EAAU,2BAAA;EACV,IAAA,EAAM,2BAAA;AAAA;;;;;;KAQI,8BAAA,GAAiC,OAAA,YAClC,kCAAA,GAAmC,6BAAA,CAC1C,kCAAA,CAAmC,IAAA;;;;;;iBASvB,oCAAA,WACJ,8BAAA,CAAA,CACV,SAAA,EAAW,CAAA,GAAI,CAAA;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/react/streaming-markdown-renderer.tsx","../../src/react/use-streaming-markdown-parser.tsx"],"mappings":";;;;;;;AAiCA;;;UAAiB,8BAAA;EASL;;;EALV,QAAA;EAiCQ;;;EA5BR,OAAA,GAAU,OAAA,CAAQ,8BAAA;EAqCV;;;EAhCR,UAAA;EA4CgD;;;;;;;;EAlChD,KAAA,GAAQ,sBAAA;EAUR;;;EALA,SAAA;EAOE;;;EAFF,WAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,GAAA,UACA,IAAA,EAAM,wBAAA;EAOC;;;EADT,eAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,QAAA,EAAU,qCAAA,EACV,IAAA,EAAM,wBAAA;EADN;;;EAOF,aAAA,GAAgB,8BAAA;EAAA;;;;EAMhB,cAAA,GAAiB,+BAAA;AAAA;;;;;;UAQF,kCAAA,eACD,wBAAA,GAA2B,wBAAA;EAe5B;;;EAVb,IAAA,EAAM,KAAA;EALN;;;EAUA,QAAA,EAAU,SAAA;EALJ;;;EAUN,WAAA,EAAa,SAAA;EAAA;;;EAKb,QAAA,GAAW,qCAAA;AAAA;AAQb;;;;;AAAA,KAAY,6BAAA,eACI,wBAAA,GAA2B,wBAAA,KACtC,KAAA,EAAO,kCAAA,CAAmC,KAAA,MAAW,SAAA;;;;;;UAOzC,iCAAA;EAR0B;;;EAYzC,UAAA;EAXwD;;;EAgBxD,QAAA,EAAU,wBAAA;AAAA;;;;;;KAQA,8BAAA,IACV,KAAA,EAAO,iCAAA,KACJ,SAAA;;AAFL;;;;KASY,sBAAA,aAER,SAAA,GACA,8BAAA;AAAA,KAEC,2BAAA,mBAA8C,yBAAA,IACjD,OAAA,CAAQ,wBAAA;EAA4B,IAAA,EAAM,SAAA;AAAA;AAAA,KAEvC,kCAAA;EACH,IAAA,EAAM,wBAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,OAAA,EAAS,2BAAA;EACT,UAAA,EAAY,2BAAA;EACZ,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,aAAA,EAAe,2BAAA;EACf,IAAA,EAAM,2BAAA;EACN,EAAA,EAAI,2BAAA;EACJ,MAAA,EAAQ,2BAAA;EACR,aAAA,EAAe,2BAAA;EACf,UAAA,EAAY,2BAAA;EACZ,SAAA,EAAW,2BAAA;EACX,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,QAAA,EAAU,2BAAA;EACV,IAAA,EAAM,2BAAA;AAAA;;;;;;KAQI,8BAAA,GAAiC,OAAA,YAClC,kCAAA,GAAmC,6BAAA,CAC1C,kCAAA,CAAmC,IAAA;;;;;;iBASvB,oCAAA,WACJ,8BAAA,CAAA,CACV,SAAA,EAAW,CAAA,GAAI,CAAA;;;;;;iBAqJD,yBAAA,CAAA;EACd,QAAA;EACA,OAAA;EACA,UAAA;EACA,KAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,aAAA;EACA;AAAA,GACC,8BAAA,GAA8B,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;AAtVjC;;;;iBClBgB,0BAAA,CACd,IAAA,UACA,OAAA,GAAU,OAAA,CAAQ,8BAAA,GAClB,eAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react/streaming-markdown-renderer.tsx","../../src/react/use-streaming-markdown-parser.tsx"],"mappings":";;;;;;;AAiCA;;;UAAiB,8BAAA;EASL;;;EALV,QAAA;EAiCQ;;;EA5BR,OAAA,GAAU,OAAA,CAAQ,8BAAA;EAqCV;;;EAhCR,UAAA;EA4CgD;;;;;;;;EAlChD,KAAA,GAAQ,sBAAA;EAUR;;;EALA,SAAA;EAOE;;;EAFF,WAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,GAAA,UACA,IAAA,EAAM,wBAAA;EAOC;;;EADT,eAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,QAAA,EAAU,qCAAA,EACV,IAAA,EAAM,wBAAA;EADN;;;EAOF,aAAA,GAAgB,8BAAA;EAAA;;;;EAMhB,cAAA,GAAiB,+BAAA;AAAA;;;;;;UAQF,kCAAA,eACD,wBAAA,GAA2B,wBAAA;EAe5B;;;EAVb,IAAA,EAAM,KAAA;EALN;;;EAUA,QAAA,EAAU,SAAA;EALJ;;;EAUN,WAAA,EAAa,SAAA;EAAA;;;EAKb,QAAA,GAAW,qCAAA;AAAA;AAQb;;;;;AAAA,KAAY,6BAAA,eACI,wBAAA,GAA2B,wBAAA,KACtC,KAAA,EAAO,kCAAA,CAAmC,KAAA,MAAW,SAAA;;;;;;UAOzC,iCAAA;EAR0B;;;EAYzC,UAAA;EAXwD;;;EAgBxD,QAAA,EAAU,wBAAA;AAAA;;;;;;KAQA,8BAAA,IACV,KAAA,EAAO,iCAAA,KACJ,SAAA;;AAFL;;;;KASY,sBAAA,aAER,SAAA,GACA,8BAAA;AAAA,KAEC,2BAAA,mBAA8C,yBAAA,IACjD,OAAA,CAAQ,wBAAA;EAA4B,IAAA,EAAM,SAAA;AAAA;AAAA,KAEvC,kCAAA;EACH,IAAA,EAAM,wBAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,OAAA,EAAS,2BAAA;EACT,UAAA,EAAY,2BAAA;EACZ,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,aAAA,EAAe,2BAAA;EACf,IAAA,EAAM,2BAAA;EACN,EAAA,EAAI,2BAAA;EACJ,MAAA,EAAQ,2BAAA;EACR,aAAA,EAAe,2BAAA;EACf,UAAA,EAAY,2BAAA;EACZ,SAAA,EAAW,2BAAA;EACX,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,QAAA,EAAU,2BAAA;EACV,IAAA,EAAM,2BAAA;AAAA;;;;;;KAQI,8BAAA,GAAiC,OAAA,YAClC,kCAAA,GAAmC,6BAAA,CAC1C,kCAAA,CAAmC,IAAA;;;;;;iBASvB,oCAAA,WACJ,8BAAA,CAAA,CACV,SAAA,EAAW,CAAA,GAAI,CAAA;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react/streaming-markdown-renderer.tsx","../../src/react/use-streaming-markdown-parser.tsx"],"mappings":";;;;;;;AAiCA;;;UAAiB,8BAAA;EASL;;;EALV,QAAA;EAiCQ;;;EA5BR,OAAA,GAAU,OAAA,CAAQ,8BAAA;EAqCV;;;EAhCR,UAAA;EA4CgD;;;;;;;;EAlChD,KAAA,GAAQ,sBAAA;EAUR;;;EALA,SAAA;EAOE;;;EAFF,WAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,GAAA,UACA,IAAA,EAAM,wBAAA;EAOC;;;EADT,eAAA,IACE,KAAA,EAAO,UAAA,CAAW,iBAAA,GAClB,QAAA,EAAU,qCAAA,EACV,IAAA,EAAM,wBAAA;EADN;;;EAOF,aAAA,GAAgB,8BAAA;EAAA;;;;EAMhB,cAAA,GAAiB,+BAAA;AAAA;;;;;;UAQF,kCAAA,eACD,wBAAA,GAA2B,wBAAA;EAe5B;;;EAVb,IAAA,EAAM,KAAA;EALN;;;EAUA,QAAA,EAAU,SAAA;EALJ;;;EAUN,WAAA,EAAa,SAAA;EAAA;;;EAKb,QAAA,GAAW,qCAAA;AAAA;AAQb;;;;;AAAA,KAAY,6BAAA,eACI,wBAAA,GAA2B,wBAAA,KACtC,KAAA,EAAO,kCAAA,CAAmC,KAAA,MAAW,SAAA;;;;;;UAOzC,iCAAA;EAR0B;;;EAYzC,UAAA;EAXwD;;;EAgBxD,QAAA,EAAU,wBAAA;AAAA;;;;;;KAQA,8BAAA,IACV,KAAA,EAAO,iCAAA,KACJ,SAAA;;AAFL;;;;KASY,sBAAA,aAER,SAAA,GACA,8BAAA;AAAA,KAEC,2BAAA,mBAA8C,yBAAA,IACjD,OAAA,CAAQ,wBAAA;EAA4B,IAAA,EAAM,SAAA;AAAA;AAAA,KAEvC,kCAAA;EACH,IAAA,EAAM,wBAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,OAAA,EAAS,2BAAA;EACT,UAAA,EAAY,2BAAA;EACZ,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,QAAA,EAAU,2BAAA;EACV,SAAA,EAAW,2BAAA;EACX,aAAA,EAAe,2BAAA;EACf,IAAA,EAAM,2BAAA;EACN,EAAA,EAAI,2BAAA;EACJ,MAAA,EAAQ,2BAAA;EACR,aAAA,EAAe,2BAAA;EACf,UAAA,EAAY,2BAAA;EACZ,SAAA,EAAW,2BAAA;EACX,SAAA,EAAW,2BAAA;EACX,KAAA,EAAO,2BAAA;EACP,IAAA,EAAM,2BAAA;EACN,QAAA,EAAU,2BAAA;EACV,QAAA,EAAU,2BAAA;EACV,IAAA,EAAM,2BAAA;AAAA;;;;;;KAQI,8BAAA,GAAiC,OAAA,YAClC,kCAAA,GAAmC,6BAAA,CAC1C,kCAAA,CAAmC,IAAA;;;;;;iBASvB,oCAAA,WACJ,8BAAA,CAAA,CACV,SAAA,EAAW,CAAA,GAAI,CAAA;;;;;;iBAqJD,yBAAA,CAAA;EACd,QAAA;EACA,OAAA;EACA,UAAA;EACA,KAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,aAAA;EACA;AAAA,GACC,8BAAA,GAA8B,kBAAA,CAAA,GAAA,CAAA,OAAA;;;;;;;AAtVjC;;;;iBClBgB,0BAAA,CACd,IAAA,UACA,OAAA,GAAU,OAAA,CAAQ,8BAAA,GAClB,eAAA"}
|
package/dist/react/index.mjs
CHANGED
|
@@ -34,9 +34,11 @@ function useStreamingMarkdownParser(text, options, isCompleteInput = false) {
|
|
|
34
34
|
function createStreamingMarkdownNodeRenderers(renderers) {
|
|
35
35
|
return renderers;
|
|
36
36
|
}
|
|
37
|
+
const { class: CARET_CLASS_NAME, ...CARET_ATTRS } = STREAMING_MARKDOWN_CARET_ATTRS;
|
|
37
38
|
function renderDefaultCaret() {
|
|
38
39
|
return /* @__PURE__ */ jsx("span", {
|
|
39
|
-
...
|
|
40
|
+
...CARET_ATTRS,
|
|
41
|
+
className: CARET_CLASS_NAME,
|
|
40
42
|
style: STREAMING_MARKDOWN_CARET_STYLE
|
|
41
43
|
});
|
|
42
44
|
}
|
package/dist/react/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/react/use-streaming-markdown-parser.tsx","../../src/react/streaming-markdown-renderer.tsx"],"sourcesContent":["import { updateStreamingMarkdownParserSession } from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserSession,\n} from \"@copilotkit/markdown\";\nimport { useMemo, useRef } from \"react\";\n\n/**\n * Internal prop-driven hook for streaming markdown parsing in React.\n *\n * @param text - Full markdown text that typically grows over time.\n * @param options - Optional parser option overrides.\n * @param isCompleteInput - When true, finalizes the parse state for the current text.\n * @returns The current immutable streaming markdown parser state.\n */\nexport function useStreamingMarkdownParser(\n text: string,\n options?: Partial<StreamingMarkdownParserOptions>,\n isCompleteInput = false,\n) {\n const sessionRef = useRef<StreamingMarkdownParserSession | null>(null);\n // The session compares options by value, so a new options object with the\n // same values keeps the parsed state (and node identity).\n const session = useMemo(() => {\n const next = updateStreamingMarkdownParserSession(\n sessionRef.current,\n text,\n options,\n isCompleteInput,\n );\n sessionRef.current = next;\n return next;\n }, [text, options, isCompleteInput]);\n\n return session.parserState;\n}\n","import {\n STREAMING_MARKDOWN_CARET_ATTRS,\n STREAMING_MARKDOWN_CARET_STYLE,\n STREAMING_MARKDOWN_ROOT_CLASS,\n buildStreamingMarkdownRenderTree,\n findStreamingMarkdownOpenNode,\n getStreamingMarkdownStyles,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeClassNames,\n StreamingMarkdownNodeRendererKey,\n StreamingMarkdownNodeType,\n StreamingMarkdownParserOptions,\n StreamingMarkdownRenderChild,\n StreamingMarkdownRenderElement,\n StreamingMarkdownRenderNode,\n} from \"@copilotkit/markdown\";\nimport { Fragment, createElement, useMemo, useRef } from \"react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useStreamingMarkdownParser } from \"./use-streaming-markdown-parser\";\n\nexport type {\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeRendererKey,\n} from \"@copilotkit/markdown\";\n\n/**\n * Props for the `StreamingMarkdownRenderer` component.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /**\n * Full markdown source that grows over time.\n */\n children: string;\n\n /**\n * Optional parser option overrides.\n */\n options?: Partial<StreamingMarkdownParserOptions>;\n\n /**\n * When `true`, finalizes the parser state after applying the latest text.\n */\n isComplete?: boolean;\n\n /**\n * Caret rendering behavior for streaming output.\n *\n * - `false`/`undefined`: no caret\n * - `true`: render the default caret\n * - `ReactNode`: render a custom caret node\n * - function: render a custom caret node from parser state\n */\n caret?: StreamingMarkdownCaret;\n\n /**\n * Optional class applied to the root element.\n */\n className?: string;\n\n /**\n * Called when a link or autolink is clicked.\n */\n onLinkClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n url: string,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Called when a citation reference is clicked.\n */\n onCitationClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n citation: StreamingMarkdownCitationRenderData,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Optional custom renderers keyed by streaming markdown node type.\n */\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n\n /**\n * Extra classes for the default element of a node type, for example\n * `{ codeBlock: \"my-code-block\" }`.\n */\n nodeClassNames?: StreamingMarkdownNodeClassNames;\n}\n\n/**\n * Props supplied to a custom node renderer.\n *\n * @public\n */\nexport interface StreamingMarkdownNodeRendererProps<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> {\n /**\n * The immutable AST node being rendered.\n */\n node: TNode;\n\n /**\n * Already-rendered child content for container nodes.\n */\n children: ReactNode;\n\n /**\n * Default element tree produced by `StreamingMarkdownRenderer` for this node.\n */\n defaultNode: ReactNode;\n\n /**\n * Citation metadata when rendering a citation node.\n */\n citation?: StreamingMarkdownCitationRenderData;\n}\n\n/**\n * Custom renderer callback for a specific streaming markdown node.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderer<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> = (props: StreamingMarkdownNodeRendererProps<TNode>) => ReactNode;\n\n/**\n * Render context supplied to caret renderer callbacks.\n *\n * @public\n */\nexport interface StreamingMarkdownCaretRenderProps {\n /**\n * Whether the parser state is complete for the current source text.\n */\n isComplete: boolean;\n\n /**\n * The deepest currently open AST node, if any.\n */\n openNode: StreamingMarkdownAstNode | null;\n}\n\n/**\n * Callback type for custom caret renderers.\n *\n * @public\n */\nexport type StreamingMarkdownCaretRenderer = (\n props: StreamingMarkdownCaretRenderProps,\n) => ReactNode;\n\n/**\n * Supported caret prop values.\n *\n * @public\n */\nexport type StreamingMarkdownCaret =\n | boolean\n | ReactNode\n | StreamingMarkdownCaretRenderer;\n\ntype StreamingMarkdownNodeOfType<TNodeType extends StreamingMarkdownNodeType> =\n Extract<StreamingMarkdownAstNode, { type: TNodeType }>;\n\ntype StreamingMarkdownNodeByRendererKey = {\n node: StreamingMarkdownAstNode;\n document: StreamingMarkdownNodeOfType<\"document\">;\n paragraph: StreamingMarkdownNodeOfType<\"paragraph\">;\n heading: StreamingMarkdownNodeOfType<\"heading\">;\n blockquote: StreamingMarkdownNodeOfType<\"blockquote\">;\n list: StreamingMarkdownNodeOfType<\"list\">;\n listItem: StreamingMarkdownNodeOfType<\"list-item\">;\n codeBlock: StreamingMarkdownNodeOfType<\"code-block\">;\n table: StreamingMarkdownNodeOfType<\"table\">;\n tableRow: StreamingMarkdownNodeOfType<\"table-row\">;\n tableCell: StreamingMarkdownNodeOfType<\"table-cell\">;\n thematicBreak: StreamingMarkdownNodeOfType<\"thematic-break\">;\n text: StreamingMarkdownNodeOfType<\"text\">;\n em: StreamingMarkdownNodeOfType<\"em\">;\n strong: StreamingMarkdownNodeOfType<\"strong\">;\n strikethrough: StreamingMarkdownNodeOfType<\"strikethrough\">;\n inlineCode: StreamingMarkdownNodeOfType<\"inline-code\">;\n softBreak: StreamingMarkdownNodeOfType<\"soft-break\">;\n hardBreak: StreamingMarkdownNodeOfType<\"hard-break\">;\n image: StreamingMarkdownNodeOfType<\"image\">;\n link: StreamingMarkdownNodeOfType<\"link\">;\n autolink: StreamingMarkdownNodeOfType<\"autolink\">;\n citation: StreamingMarkdownNodeOfType<\"citation\">;\n math: StreamingMarkdownNodeOfType<\"math\">;\n};\n\n/**\n * Custom renderers keyed by camelCase node names.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderers = Partial<{\n [TKey in StreamingMarkdownNodeRendererKey]: StreamingMarkdownNodeRenderer<\n StreamingMarkdownNodeByRendererKey[TKey]\n >;\n}>;\n\n/**\n * Helper for creating typed streaming markdown node renderer maps.\n *\n * @public\n */\nexport function createStreamingMarkdownNodeRenderers<\n T extends StreamingMarkdownNodeRenderers,\n>(renderers: T): T {\n return renderers;\n}\n\ntype RenderContext = {\n onLinkClickRef: { current: StreamingMarkdownRendererProps[\"onLinkClick\"] };\n onCitationClickRef: {\n current: StreamingMarkdownRendererProps[\"onCitationClick\"];\n };\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n caretNode: ReactNode;\n};\n\nfunction renderDefaultCaret() {\n return (\n <span\n {...STREAMING_MARKDOWN_CARET_ATTRS}\n style={STREAMING_MARKDOWN_CARET_STYLE}\n />\n );\n}\n\nfunction resolveCaretNode(\n caret: StreamingMarkdownCaret | undefined,\n props: StreamingMarkdownCaretRenderProps,\n): ReactNode {\n if (caret === undefined || caret === false || caret === null) {\n return null;\n }\n\n if (caret === true) {\n return renderDefaultCaret();\n }\n\n if (typeof caret === \"function\") {\n return caret(props);\n }\n\n return caret;\n}\n\nfunction toReactProps(\n element: StreamingMarkdownRenderElement,\n context: RenderContext,\n) {\n const props: Record<string, unknown> = { key: element.key };\n const entries = Object.entries(element.attrs);\n for (const [name, value] of entries) {\n props[name === \"class\" ? \"className\" : name] = value;\n }\n // A task-list checkbox is display-only; readOnly tells React it is\n // intentionally uncontrolled by user input.\n if (element.tag === \"input\") props.readOnly = true;\n\n const action = element.action;\n if (action?.type === \"link\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onLinkClickRef.current?.(event, action.url, action.node);\n };\n }\n if (action?.type === \"citation\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onCitationClickRef.current?.(event, action.citation, action.node);\n };\n }\n return props;\n}\n\nconst VOID_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\"]);\n\nfunction renderList(\n children: StreamingMarkdownRenderChild[],\n context: RenderContext,\n slot: ReactNode,\n): ReactNode[] {\n return children.map((child) => renderChild(child, context, slot));\n}\n\n/** Unwraps single-entry lists so overrides see `\"text\"` rather than `[\"text\"]`. */\nfunction collapse(nodes: ReactNode[]): ReactNode {\n return nodes.length === 1 ? nodes[0] : nodes;\n}\n\nfunction renderChild(\n child: StreamingMarkdownRenderChild,\n context: RenderContext,\n slot: ReactNode,\n): ReactNode {\n switch (child.kind) {\n case \"node\":\n return renderRenderNode(child, context);\n case \"element\": {\n const props = toReactProps(child, context);\n return VOID_TAGS.has(child.tag)\n ? createElement(child.tag, props)\n : createElement(\n child.tag,\n props,\n ...renderList(child.children, context, slot),\n );\n }\n case \"text\":\n return child.text;\n case \"caret\":\n return <Fragment key={child.key}>{context.caretNode}</Fragment>;\n case \"slot\":\n return <Fragment key={child.key}>{slot}</Fragment>;\n default: {\n const unreachable: never = child;\n return unreachable;\n }\n }\n}\n\nfunction renderRenderNode(\n renderNode: StreamingMarkdownRenderNode,\n context: RenderContext,\n): ReactNode {\n const children =\n renderNode.children === null\n ? null\n : collapse(renderList(renderNode.children, context, null));\n const defaultNode = collapse(\n renderList(renderNode.output, context, children),\n );\n const renderer = (context.nodeRenderers?.[renderNode.rendererKey] ??\n context.nodeRenderers?.node) as StreamingMarkdownNodeRenderer | undefined;\n\n const rendered = renderer\n ? renderer({\n node: renderNode.node,\n children,\n defaultNode,\n citation: renderNode.citation,\n })\n : defaultNode;\n return <Fragment key={renderNode.key}>{rendered}</Fragment>;\n}\n\n/**\n * React renderer for streaming markdown parser output.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n children,\n options,\n isComplete = false,\n caret,\n className,\n onLinkClick,\n onCitationClick,\n nodeRenderers,\n nodeClassNames,\n}: StreamingMarkdownRendererProps) {\n const text = children ?? \"\";\n const parserState = useStreamingMarkdownParser(text, options, isComplete);\n const onLinkClickRef =\n useRef<StreamingMarkdownRendererProps[\"onLinkClick\"]>(onLinkClick);\n const onCitationClickRef =\n useRef<StreamingMarkdownRendererProps[\"onCitationClick\"]>(onCitationClick);\n onLinkClickRef.current = onLinkClick;\n onCitationClickRef.current = onCitationClick;\n\n const content = useMemo(() => {\n const caretNode = resolveCaretNode(caret, {\n isComplete: parserState.isComplete,\n openNode: findStreamingMarkdownOpenNode(parserState),\n });\n const tree = buildStreamingMarkdownRenderTree(parserState, {\n caret: caretNode != null,\n nodeClassNames,\n });\n if (!tree.root) return null;\n return renderRenderNode(tree.root, {\n onLinkClickRef,\n onCitationClickRef,\n nodeRenderers,\n caretNode,\n });\n }, [parserState, nodeRenderers, nodeClassNames, caret]);\n\n const rootClassName = className\n ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${className}`\n : STREAMING_MARKDOWN_ROOT_CLASS;\n\n return (\n <>\n <style>{getStreamingMarkdownStyles()}</style>\n <div className={rootClassName} data-streaming-markdown-root>\n {content}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/react/use-streaming-markdown-parser.tsx","../../src/react/streaming-markdown-renderer.tsx"],"sourcesContent":["import { updateStreamingMarkdownParserSession } from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserSession,\n} from \"@copilotkit/markdown\";\nimport { useMemo, useRef } from \"react\";\n\n/**\n * Internal prop-driven hook for streaming markdown parsing in React.\n *\n * @param text - Full markdown text that typically grows over time.\n * @param options - Optional parser option overrides.\n * @param isCompleteInput - When true, finalizes the parse state for the current text.\n * @returns The current immutable streaming markdown parser state.\n */\nexport function useStreamingMarkdownParser(\n text: string,\n options?: Partial<StreamingMarkdownParserOptions>,\n isCompleteInput = false,\n) {\n const sessionRef = useRef<StreamingMarkdownParserSession | null>(null);\n // The session compares options by value, so a new options object with the\n // same values keeps the parsed state (and node identity).\n const session = useMemo(() => {\n const next = updateStreamingMarkdownParserSession(\n sessionRef.current,\n text,\n options,\n isCompleteInput,\n );\n sessionRef.current = next;\n return next;\n }, [text, options, isCompleteInput]);\n\n return session.parserState;\n}\n","import {\n STREAMING_MARKDOWN_CARET_ATTRS,\n STREAMING_MARKDOWN_CARET_STYLE,\n STREAMING_MARKDOWN_ROOT_CLASS,\n buildStreamingMarkdownRenderTree,\n findStreamingMarkdownOpenNode,\n getStreamingMarkdownStyles,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeClassNames,\n StreamingMarkdownNodeRendererKey,\n StreamingMarkdownNodeType,\n StreamingMarkdownParserOptions,\n StreamingMarkdownRenderChild,\n StreamingMarkdownRenderElement,\n StreamingMarkdownRenderNode,\n} from \"@copilotkit/markdown\";\nimport { Fragment, createElement, useMemo, useRef } from \"react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useStreamingMarkdownParser } from \"./use-streaming-markdown-parser\";\n\nexport type {\n StreamingMarkdownCitationRenderData,\n StreamingMarkdownNodeRendererKey,\n} from \"@copilotkit/markdown\";\n\n/**\n * Props for the `StreamingMarkdownRenderer` component.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /**\n * Full markdown source that grows over time.\n */\n children: string;\n\n /**\n * Optional parser option overrides.\n */\n options?: Partial<StreamingMarkdownParserOptions>;\n\n /**\n * When `true`, finalizes the parser state after applying the latest text.\n */\n isComplete?: boolean;\n\n /**\n * Caret rendering behavior for streaming output.\n *\n * - `false`/`undefined`: no caret\n * - `true`: render the default caret\n * - `ReactNode`: render a custom caret node\n * - function: render a custom caret node from parser state\n */\n caret?: StreamingMarkdownCaret;\n\n /**\n * Optional class applied to the root element.\n */\n className?: string;\n\n /**\n * Called when a link or autolink is clicked.\n */\n onLinkClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n url: string,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Called when a citation reference is clicked.\n */\n onCitationClick?: (\n event: MouseEvent<HTMLAnchorElement>,\n citation: StreamingMarkdownCitationRenderData,\n node: StreamingMarkdownAstNode,\n ) => void;\n\n /**\n * Optional custom renderers keyed by streaming markdown node type.\n */\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n\n /**\n * Extra classes for the default element of a node type, for example\n * `{ codeBlock: \"my-code-block\" }`.\n */\n nodeClassNames?: StreamingMarkdownNodeClassNames;\n}\n\n/**\n * Props supplied to a custom node renderer.\n *\n * @public\n */\nexport interface StreamingMarkdownNodeRendererProps<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> {\n /**\n * The immutable AST node being rendered.\n */\n node: TNode;\n\n /**\n * Already-rendered child content for container nodes.\n */\n children: ReactNode;\n\n /**\n * Default element tree produced by `StreamingMarkdownRenderer` for this node.\n */\n defaultNode: ReactNode;\n\n /**\n * Citation metadata when rendering a citation node.\n */\n citation?: StreamingMarkdownCitationRenderData;\n}\n\n/**\n * Custom renderer callback for a specific streaming markdown node.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderer<\n TNode extends StreamingMarkdownAstNode = StreamingMarkdownAstNode,\n> = (props: StreamingMarkdownNodeRendererProps<TNode>) => ReactNode;\n\n/**\n * Render context supplied to caret renderer callbacks.\n *\n * @public\n */\nexport interface StreamingMarkdownCaretRenderProps {\n /**\n * Whether the parser state is complete for the current source text.\n */\n isComplete: boolean;\n\n /**\n * The deepest currently open AST node, if any.\n */\n openNode: StreamingMarkdownAstNode | null;\n}\n\n/**\n * Callback type for custom caret renderers.\n *\n * @public\n */\nexport type StreamingMarkdownCaretRenderer = (\n props: StreamingMarkdownCaretRenderProps,\n) => ReactNode;\n\n/**\n * Supported caret prop values.\n *\n * @public\n */\nexport type StreamingMarkdownCaret =\n | boolean\n | ReactNode\n | StreamingMarkdownCaretRenderer;\n\ntype StreamingMarkdownNodeOfType<TNodeType extends StreamingMarkdownNodeType> =\n Extract<StreamingMarkdownAstNode, { type: TNodeType }>;\n\ntype StreamingMarkdownNodeByRendererKey = {\n node: StreamingMarkdownAstNode;\n document: StreamingMarkdownNodeOfType<\"document\">;\n paragraph: StreamingMarkdownNodeOfType<\"paragraph\">;\n heading: StreamingMarkdownNodeOfType<\"heading\">;\n blockquote: StreamingMarkdownNodeOfType<\"blockquote\">;\n list: StreamingMarkdownNodeOfType<\"list\">;\n listItem: StreamingMarkdownNodeOfType<\"list-item\">;\n codeBlock: StreamingMarkdownNodeOfType<\"code-block\">;\n table: StreamingMarkdownNodeOfType<\"table\">;\n tableRow: StreamingMarkdownNodeOfType<\"table-row\">;\n tableCell: StreamingMarkdownNodeOfType<\"table-cell\">;\n thematicBreak: StreamingMarkdownNodeOfType<\"thematic-break\">;\n text: StreamingMarkdownNodeOfType<\"text\">;\n em: StreamingMarkdownNodeOfType<\"em\">;\n strong: StreamingMarkdownNodeOfType<\"strong\">;\n strikethrough: StreamingMarkdownNodeOfType<\"strikethrough\">;\n inlineCode: StreamingMarkdownNodeOfType<\"inline-code\">;\n softBreak: StreamingMarkdownNodeOfType<\"soft-break\">;\n hardBreak: StreamingMarkdownNodeOfType<\"hard-break\">;\n image: StreamingMarkdownNodeOfType<\"image\">;\n link: StreamingMarkdownNodeOfType<\"link\">;\n autolink: StreamingMarkdownNodeOfType<\"autolink\">;\n citation: StreamingMarkdownNodeOfType<\"citation\">;\n math: StreamingMarkdownNodeOfType<\"math\">;\n};\n\n/**\n * Custom renderers keyed by camelCase node names.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRenderers = Partial<{\n [TKey in StreamingMarkdownNodeRendererKey]: StreamingMarkdownNodeRenderer<\n StreamingMarkdownNodeByRendererKey[TKey]\n >;\n}>;\n\n/**\n * Helper for creating typed streaming markdown node renderer maps.\n *\n * @public\n */\nexport function createStreamingMarkdownNodeRenderers<\n T extends StreamingMarkdownNodeRenderers,\n>(renderers: T): T {\n return renderers;\n}\n\ntype RenderContext = {\n onLinkClickRef: { current: StreamingMarkdownRendererProps[\"onLinkClick\"] };\n onCitationClickRef: {\n current: StreamingMarkdownRendererProps[\"onCitationClick\"];\n };\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n caretNode: ReactNode;\n};\n\n// The shared caret attrs use the DOM name `class`; React needs `className`.\nconst { class: CARET_CLASS_NAME, ...CARET_ATTRS } =\n STREAMING_MARKDOWN_CARET_ATTRS;\n\nfunction renderDefaultCaret() {\n return (\n <span\n {...CARET_ATTRS}\n className={CARET_CLASS_NAME}\n style={STREAMING_MARKDOWN_CARET_STYLE}\n />\n );\n}\n\nfunction resolveCaretNode(\n caret: StreamingMarkdownCaret | undefined,\n props: StreamingMarkdownCaretRenderProps,\n): ReactNode {\n if (caret === undefined || caret === false || caret === null) {\n return null;\n }\n\n if (caret === true) {\n return renderDefaultCaret();\n }\n\n if (typeof caret === \"function\") {\n return caret(props);\n }\n\n return caret;\n}\n\nfunction toReactProps(\n element: StreamingMarkdownRenderElement,\n context: RenderContext,\n) {\n const props: Record<string, unknown> = { key: element.key };\n const entries = Object.entries(element.attrs);\n for (const [name, value] of entries) {\n props[name === \"class\" ? \"className\" : name] = value;\n }\n // A task-list checkbox is display-only; readOnly tells React it is\n // intentionally uncontrolled by user input.\n if (element.tag === \"input\") props.readOnly = true;\n\n const action = element.action;\n if (action?.type === \"link\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onLinkClickRef.current?.(event, action.url, action.node);\n };\n }\n if (action?.type === \"citation\") {\n props.onClick = (event: MouseEvent<HTMLAnchorElement>) => {\n context.onCitationClickRef.current?.(event, action.citation, action.node);\n };\n }\n return props;\n}\n\nconst VOID_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\"]);\n\nfunction renderList(\n children: StreamingMarkdownRenderChild[],\n context: RenderContext,\n slot: ReactNode,\n): ReactNode[] {\n return children.map((child) => renderChild(child, context, slot));\n}\n\n/** Unwraps single-entry lists so overrides see `\"text\"` rather than `[\"text\"]`. */\nfunction collapse(nodes: ReactNode[]): ReactNode {\n return nodes.length === 1 ? nodes[0] : nodes;\n}\n\nfunction renderChild(\n child: StreamingMarkdownRenderChild,\n context: RenderContext,\n slot: ReactNode,\n): ReactNode {\n switch (child.kind) {\n case \"node\":\n return renderRenderNode(child, context);\n case \"element\": {\n const props = toReactProps(child, context);\n return VOID_TAGS.has(child.tag)\n ? createElement(child.tag, props)\n : createElement(\n child.tag,\n props,\n ...renderList(child.children, context, slot),\n );\n }\n case \"text\":\n return child.text;\n case \"caret\":\n return <Fragment key={child.key}>{context.caretNode}</Fragment>;\n case \"slot\":\n return <Fragment key={child.key}>{slot}</Fragment>;\n default: {\n const unreachable: never = child;\n return unreachable;\n }\n }\n}\n\nfunction renderRenderNode(\n renderNode: StreamingMarkdownRenderNode,\n context: RenderContext,\n): ReactNode {\n const children =\n renderNode.children === null\n ? null\n : collapse(renderList(renderNode.children, context, null));\n const defaultNode = collapse(\n renderList(renderNode.output, context, children),\n );\n const renderer = (context.nodeRenderers?.[renderNode.rendererKey] ??\n context.nodeRenderers?.node) as StreamingMarkdownNodeRenderer | undefined;\n\n const rendered = renderer\n ? renderer({\n node: renderNode.node,\n children,\n defaultNode,\n citation: renderNode.citation,\n })\n : defaultNode;\n return <Fragment key={renderNode.key}>{rendered}</Fragment>;\n}\n\n/**\n * React renderer for streaming markdown parser output.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n children,\n options,\n isComplete = false,\n caret,\n className,\n onLinkClick,\n onCitationClick,\n nodeRenderers,\n nodeClassNames,\n}: StreamingMarkdownRendererProps) {\n const text = children ?? \"\";\n const parserState = useStreamingMarkdownParser(text, options, isComplete);\n const onLinkClickRef =\n useRef<StreamingMarkdownRendererProps[\"onLinkClick\"]>(onLinkClick);\n const onCitationClickRef =\n useRef<StreamingMarkdownRendererProps[\"onCitationClick\"]>(onCitationClick);\n onLinkClickRef.current = onLinkClick;\n onCitationClickRef.current = onCitationClick;\n\n const content = useMemo(() => {\n const caretNode = resolveCaretNode(caret, {\n isComplete: parserState.isComplete,\n openNode: findStreamingMarkdownOpenNode(parserState),\n });\n const tree = buildStreamingMarkdownRenderTree(parserState, {\n caret: caretNode != null,\n nodeClassNames,\n });\n if (!tree.root) return null;\n return renderRenderNode(tree.root, {\n onLinkClickRef,\n onCitationClickRef,\n nodeRenderers,\n caretNode,\n });\n }, [parserState, nodeRenderers, nodeClassNames, caret]);\n\n const rootClassName = className\n ? `${STREAMING_MARKDOWN_ROOT_CLASS} ${className}`\n : STREAMING_MARKDOWN_ROOT_CLASS;\n\n return (\n <>\n <style>{getStreamingMarkdownStyles()}</style>\n <div className={rootClassName} data-streaming-markdown-root>\n {content}\n </div>\n </>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED