@copilotkit/markdown 2.0.0-rc.20 → 2.0.0-rc.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -7
- package/dist/dom/index.cjs +255 -0
- package/dist/dom/index.cjs.map +1 -0
- package/dist/dom/index.d.cts +93 -0
- package/dist/dom/index.d.cts.map +1 -0
- package/dist/dom/index.d.mts +93 -0
- package/dist/dom/index.d.mts.map +1 -0
- package/dist/dom/index.mjs +254 -0
- package/dist/dom/index.mjs.map +1 -0
- package/dist/index.cjs +522 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +271 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +512 -12
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.cjs +66 -458
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +13 -22
- package/dist/react/index.d.cts.map +1 -1
- package/dist/react/index.d.mts +13 -22
- package/dist/react/index.d.mts.map +1 -1
- package/dist/react/index.mjs +67 -459
- package/dist/react/index.mjs.map +1 -1
- package/dist/react-native/index.cjs.map +1 -1
- package/dist/react-native/index.d.cts.map +1 -1
- package/dist/react-native/index.d.mts.map +1 -1
- package/dist/react-native/index.mjs.map +1 -1
- package/dist/vue/index.cjs +86 -199
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +46 -16
- package/dist/vue/index.d.cts.map +1 -1
- package/dist/vue/index.d.mts +46 -16
- package/dist/vue/index.d.mts.map +1 -1
- package/dist/vue/index.mjs +88 -201
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +6 -2
package/dist/react/index.mjs.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 {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownParserOptions,\n StreamingMarkdownParserState,\n} from \"@copilotkit/markdown\";\nimport { useMemo, useRef } from \"react\";\n\ninterface StreamingMarkdownParserSession {\n parserState: StreamingMarkdownParserState;\n text: string;\n optionsKey: string;\n isCompleteInput: boolean;\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 getSegmenterKey(\n segmenter: StreamingMarkdownParserOptions[\"segmenter\"],\n): string {\n if (segmenter === true || segmenter === false) {\n return String(segmenter);\n }\n\n const locale = segmenter.locale ?? \"\";\n const granularity = segmenter.granularity ?? \"word\";\n return `object:${locale}:${granularity}`;\n}\n\nfunction getOptionsKey(options: StreamingMarkdownParserOptions): string {\n return `${getSegmenterKey(options.segmenter)}|tables:${String(options.enableTables)}|autolinks:${String(options.enableAutolinks)}`;\n}\n\nfunction parseFullText(\n text: string,\n options: StreamingMarkdownParserOptions,\n isCompleteInput: boolean,\n): StreamingMarkdownParserState {\n const initialState = createStreamingMarkdownParserState(options);\n const parsedState =\n text.length > 0\n ? parseStreamingMarkdownChunk(initialState, text)\n : initialState;\n\n return isCompleteInput ? finalizeStreamingMarkdown(parsedState) : parsedState;\n}\n\nfunction createSession(\n text: string,\n options: StreamingMarkdownParserOptions,\n optionsKey: string,\n isCompleteInput: boolean,\n): StreamingMarkdownParserSession {\n return {\n parserState: parseFullText(text, options, isCompleteInput),\n text,\n optionsKey,\n isCompleteInput,\n };\n}\n\nfunction resolveNextSession(\n previous: StreamingMarkdownParserSession,\n text: string,\n options: StreamingMarkdownParserOptions,\n optionsKey: string,\n isCompleteInput: boolean,\n): StreamingMarkdownParserSession {\n const optionsChanged = previous.optionsKey !== optionsKey;\n const completionChanged = previous.isCompleteInput !== isCompleteInput;\n\n if (optionsChanged) {\n return createSession(text, options, optionsKey, isCompleteInput);\n }\n\n const textChanged = text !== previous.text;\n if (!textChanged && !completionChanged) {\n return previous;\n }\n\n if (!textChanged && completionChanged) {\n const parserState = isCompleteInput\n ? finalizeStreamingMarkdown(previous.parserState)\n : parseStreamingMarkdownChunk(previous.parserState, \"\");\n\n return {\n ...previous,\n parserState,\n isCompleteInput,\n };\n }\n\n let nextParserState: StreamingMarkdownParserState;\n\n if (text.startsWith(previous.text)) {\n const suffix = text.slice(previous.text.length);\n nextParserState =\n suffix.length > 0\n ? parseStreamingMarkdownChunk(previous.parserState, suffix)\n : previous.parserState;\n } else {\n nextParserState = parseFullText(text, options, false);\n }\n\n if (isCompleteInput) {\n nextParserState = finalizeStreamingMarkdown(nextParserState);\n }\n\n return {\n parserState: nextParserState,\n text,\n optionsKey,\n isCompleteInput,\n };\n}\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): StreamingMarkdownParserState {\n const sessionRef = useRef<StreamingMarkdownParserSession | null>(null);\n const segmenter = options?.segmenter;\n const segmenterKind =\n typeof segmenter === \"object\" && segmenter !== null\n ? \"object\"\n : String(segmenter ?? true);\n const segmenterLocale =\n typeof segmenter === \"object\" && segmenter !== null\n ? (segmenter.locale ?? \"\")\n : \"\";\n const segmenterGranularity =\n typeof segmenter === \"object\" && segmenter !== null\n ? (segmenter.granularity ?? \"word\")\n : \"\";\n const normalizedOptions = useMemo(\n () => normalizeOptions(options),\n [\n options?.enableAutolinks,\n options?.enableTables,\n segmenterKind,\n segmenterLocale,\n segmenterGranularity,\n ],\n );\n const optionsKey = getOptionsKey(normalizedOptions);\n\n const session = useMemo(() => {\n const previous =\n sessionRef.current ??\n createSession(text, normalizedOptions, optionsKey, isCompleteInput);\n\n const next = resolveNextSession(\n previous,\n text,\n normalizedOptions,\n optionsKey,\n isCompleteInput,\n );\n\n sessionRef.current = next;\n return next;\n }, [text, normalizedOptions, optionsKey, isCompleteInput]);\n\n return session.parserState;\n}\n","import type {\n CitationDefinition,\n CitationState,\n StreamingMarkdownAstNode,\n StreamingMarkdownNodeType,\n StreamingMarkdownParserOptions,\n TextSegment,\n} from \"@copilotkit/markdown\";\nimport { Fragment, useMemo, useRef } from \"react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useStreamingMarkdownParser } from \"./use-streaming-markdown-parser\";\n\n// URL allowlists for link/image rendering — mirrors the Vue renderer so both\n// frameworks honor the documented XSS guarantee (only http(s), mailto, tel,\n// relative, anchor, and non-SVG data:image/* URLs are allowed). Anything else\n// (javascript:, data:text/html, data:image/svg+xml, vbscript:, …) is dropped.\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 * Metadata available when a citation node is rendered.\n *\n * @public\n */\nexport interface StreamingMarkdownCitationRenderData {\n id: string;\n number: number | string;\n definition?: CitationDefinition;\n}\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/**\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\n/**\n * Supported key names for `nodeRenderers`.\n *\n * @public\n */\nexport type StreamingMarkdownNodeRendererKey =\n | \"node\"\n | \"document\"\n | \"paragraph\"\n | \"heading\"\n | \"blockquote\"\n | \"list\"\n | \"listItem\"\n | \"codeBlock\"\n | \"table\"\n | \"tableRow\"\n | \"tableCell\"\n | \"thematicBreak\"\n | \"text\"\n | \"em\"\n | \"strong\"\n | \"strikethrough\"\n | \"inlineCode\"\n | \"softBreak\"\n | \"hardBreak\"\n | \"image\"\n | \"link\"\n | \"autolink\"\n | \"citation\";\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};\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 nodeById: Map<number, StreamingMarkdownAstNode>;\n citations: CitationState;\n onLinkClickRef: { current: StreamingMarkdownRendererProps[\"onLinkClick\"] };\n onCitationClickRef: {\n current: StreamingMarkdownRendererProps[\"onCitationClick\"];\n };\n nodeRenderers?: StreamingMarkdownNodeRenderers;\n caretTargetNodeId: number | null;\n caretNode: ReactNode;\n};\n\ntype StreamingMarkdownTextNode = Extract<\n StreamingMarkdownAstNode,\n { type: \"text\" }\n>;\ntype StreamingMarkdownCitationNode = Extract<\n StreamingMarkdownAstNode,\n { type: \"citation\" }\n>;\n\nconst WORD_JOINER = \"\\u2060\";\nconst DEFAULT_ROOT_CLASS = \"cpk-streaming-markdown-root\";\nconst DEFAULT_CITATION_CLASS = \"cpk-streaming-markdown-citation\";\nconst DEFAULT_CITATION_LABEL_CLASS = \"cpk-streaming-markdown-citation-label\";\nconst DEFAULT_STYLES = `\n .${DEFAULT_ROOT_CLASS} .cpk-streaming-markdown-segment {\n opacity: 1;\n transition: opacity 400ms ease-out;\n @starting-style {\n opacity: 0;\n }\n }\n\n .${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_CLASS} {\n vertical-align: baseline;\n }\n\n .${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_LABEL_CLASS} {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: 1.4em;\n block-size: 1.4em;\n border-radius: 999px;\n border: 1px solid hsl(0 0% 50% / 0.35);\n background-color: hsl(0 0% 50% / 0.16);\n color: inherit;\n font-size: 0.7em;\n line-height: 1;\n font-variant-numeric: tabular-nums;\n text-decoration: none;\n transform: translateY(-0.15em);\n }\n`;\n\nconst DEFAULT_STYLES_FALLBACK = `\n .${DEFAULT_ROOT_CLASS} .cpk-streaming-markdown-segment {\n opacity: 1;\n transition: opacity 400ms ease-out;\n }\n\n .${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_CLASS} {\n vertical-align: baseline;\n }\n\n .${DEFAULT_ROOT_CLASS} .${DEFAULT_CITATION_LABEL_CLASS} {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: 1.4em;\n block-size: 1.4em;\n border-radius: 999px;\n border: 1px solid hsl(0 0% 50% / 0.35);\n background-color: hsl(0 0% 50% / 0.16);\n color: inherit;\n font-size: 0.7em;\n line-height: 1;\n font-variant-numeric: tabular-nums;\n text-decoration: none;\n transform: translateY(-0.15em);\n }\n`;\n\ntype ContainerNodeType = Extract<\n StreamingMarkdownNodeType,\n | \"document\"\n | \"paragraph\"\n | \"heading\"\n | \"blockquote\"\n | \"list\"\n | \"list-item\"\n | \"table\"\n | \"table-row\"\n | \"table-cell\"\n | \"em\"\n | \"strong\"\n | \"strikethrough\"\n | \"link\"\n>;\n\nconst NODE_TYPE_TO_RENDERER_KEY: Record<\n StreamingMarkdownNodeType,\n StreamingMarkdownNodeRendererKey\n> = {\n document: \"document\",\n paragraph: \"paragraph\",\n heading: \"heading\",\n blockquote: \"blockquote\",\n list: \"list\",\n \"list-item\": \"listItem\",\n \"code-block\": \"codeBlock\",\n table: \"table\",\n \"table-row\": \"tableRow\",\n \"table-cell\": \"tableCell\",\n \"thematic-break\": \"thematicBreak\",\n text: \"text\",\n em: \"em\",\n strong: \"strong\",\n strikethrough: \"strikethrough\",\n \"inline-code\": \"inlineCode\",\n \"soft-break\": \"softBreak\",\n \"hard-break\": \"hardBreak\",\n image: \"image\",\n link: \"link\",\n autolink: \"autolink\",\n citation: \"citation\",\n};\n\nfunction renderWithOverride<TNode extends StreamingMarkdownAstNode>(\n node: TNode,\n context: RenderContext,\n defaultNode: ReactNode,\n children: ReactNode = null,\n citation?: StreamingMarkdownCitationRenderData,\n): ReactNode {\n const rendererKey = NODE_TYPE_TO_RENDERER_KEY[node.type];\n const renderer = (context.nodeRenderers?.[rendererKey] ??\n context.nodeRenderers?.node) as\n | StreamingMarkdownNodeRenderer<TNode>\n | undefined;\n\n if (!renderer) {\n return defaultNode;\n }\n\n return renderer({\n node,\n children,\n defaultNode,\n citation,\n });\n}\n\nfunction renderChildren(\n node: { children: number[] },\n context: RenderContext,\n): ReactNode {\n return node.children.map((childId) => {\n const childNode = context.nodeById.get(childId);\n\n if (!childNode) {\n return null;\n }\n\n return <Fragment key={childId}>{renderNode(childNode, context)}</Fragment>;\n });\n}\n\nfunction renderCaret(nodeId: number, context: RenderContext): ReactNode {\n if (context.caretTargetNodeId !== nodeId) {\n return null;\n }\n\n return <Fragment key={`caret-${nodeId}`}>{context.caretNode}</Fragment>;\n}\n\nfunction renderDefaultCaret(): ReactNode {\n return (\n <span\n aria-hidden\n className=\"cpk-streaming-markdown-caret\"\n data-streaming-markdown-caret\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\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 renderTextSegments(node: StreamingMarkdownTextNode): ReactNode {\n if (node.text.length === 0) {\n return null;\n }\n\n if (node.segments.length === 0) {\n return (\n <span\n key={`segment-${node.id}-full`}\n className=\"cpk-streaming-markdown-segment\"\n data-streaming-markdown-segment-kind=\"full\"\n data-streaming-markdown-whitespace=\"false\"\n >\n {node.text}\n </span>\n );\n }\n\n return node.segments.map((segment: TextSegment) => (\n <span\n key={`segment-${node.id}-${segment.start}-${segment.kind}`}\n className=\"cpk-streaming-markdown-segment\"\n data-streaming-markdown-segment-kind={segment.kind}\n data-streaming-markdown-whitespace={String(segment.isWhitespace)}\n >\n {segment.noBreakBefore ? `${WORD_JOINER}${segment.text}` : segment.text}\n </span>\n ));\n}\n\nfunction handleLinkClick(\n context: RenderContext,\n node: StreamingMarkdownAstNode,\n url: string,\n) {\n return (event: MouseEvent<HTMLAnchorElement>) => {\n context.onLinkClickRef.current?.(event, url, node);\n };\n}\n\nfunction getCitationRenderData(\n node: StreamingMarkdownCitationNode,\n context: RenderContext,\n): StreamingMarkdownCitationRenderData {\n const number =\n node.number ?? context.citations.numbers[node.idRef] ?? node.idRef;\n const definition = context.citations.definitions[node.idRef];\n return {\n id: node.idRef,\n number,\n definition,\n } satisfies StreamingMarkdownCitationRenderData;\n}\n\nfunction renderDefaultCitation(\n node: StreamingMarkdownCitationNode,\n context: RenderContext,\n citation: StreamingMarkdownCitationRenderData,\n): ReactNode {\n const label = String(citation.number);\n const href = sanitizeHref(citation.definition?.url);\n\n if (!href) {\n return (\n <sup\n key={node.id}\n className={DEFAULT_CITATION_CLASS}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n <span role=\"doc-noteref\" className={DEFAULT_CITATION_LABEL_CLASS}>\n {label}\n </span>\n </sup>\n );\n }\n\n return (\n <sup\n key={node.id}\n className={DEFAULT_CITATION_CLASS}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n role=\"doc-noteref\"\n className={DEFAULT_CITATION_LABEL_CLASS}\n onClick={(event) => {\n context.onCitationClickRef.current?.(event, citation, node);\n }}\n >\n {label}\n </a>\n </sup>\n );\n}\n\nfunction renderContainerNode(\n node: Extract<StreamingMarkdownAstNode, { type: ContainerNodeType }>,\n context: RenderContext,\n): ReactNode {\n const children = (\n <>\n {renderChildren(node, context)}\n {renderCaret(node.id, context)}\n </>\n );\n\n if (node.type === \"document\") {\n return renderWithOverride(\n node,\n context,\n <Fragment key={node.id}>{children}</Fragment>,\n children,\n );\n }\n\n if (node.type === \"paragraph\") {\n const defaultNode = (\n <p\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </p>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"heading\") {\n const headingTag = `h${node.level}` as const;\n const defaultNode =\n headingTag === \"h1\" ? (\n <h1\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h1>\n ) : headingTag === \"h2\" ? (\n <h2\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h2>\n ) : headingTag === \"h3\" ? (\n <h3\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h3>\n ) : headingTag === \"h4\" ? (\n <h4\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h4>\n ) : headingTag === \"h5\" ? (\n <h5\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h5>\n ) : (\n <h6\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </h6>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"blockquote\") {\n const defaultNode = (\n <blockquote\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </blockquote>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"list\") {\n if (node.ordered) {\n const defaultNode = (\n <ol\n key={node.id}\n start={node.start ?? undefined}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n data-list-tight={String(node.tight)}\n >\n {children}\n </ol>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n const defaultNode = (\n <ul\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n data-list-tight={String(node.tight)}\n >\n {children}\n </ul>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"list-item\") {\n const defaultNode = (\n <li\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </li>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"table\") {\n const defaultNode = (\n <table\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n <tbody>{children}</tbody>\n </table>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"table-row\") {\n const defaultNode = (\n <tr\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </tr>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"table-cell\") {\n const parent =\n node.parentId == null ? undefined : context.nodeById.get(node.parentId);\n const isHeaderRow = parent?.type === \"table-row\" && parent.isHeader;\n if (isHeaderRow) {\n const defaultNode = (\n <th\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </th>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n const defaultNode = (\n <td\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </td>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"em\") {\n const defaultNode = (\n <em\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </em>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"strong\") {\n const defaultNode = (\n <strong\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </strong>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"strikethrough\") {\n const defaultNode = (\n <s\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </s>\n );\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n const href = sanitizeHref(node.url);\n const defaultNode = (\n <a\n key={node.id}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title={node.title}\n onClick={href ? handleLinkClick(context, node, href) : undefined}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {children}\n </a>\n );\n return renderWithOverride(node, context, defaultNode, children);\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n context: RenderContext,\n): ReactNode {\n if (\n node.type === \"document\" ||\n node.type === \"paragraph\" ||\n node.type === \"heading\" ||\n node.type === \"blockquote\" ||\n node.type === \"list\" ||\n node.type === \"list-item\" ||\n node.type === \"table\" ||\n node.type === \"table-row\" ||\n node.type === \"table-cell\" ||\n node.type === \"em\" ||\n node.type === \"strong\" ||\n node.type === \"strikethrough\" ||\n node.type === \"link\"\n ) {\n return renderContainerNode(node, context);\n }\n\n if (node.type === \"code-block\") {\n const defaultNode = (\n <pre\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n <code data-code-info={node.info ?? undefined}>\n {node.text}\n {renderCaret(node.id, context)}\n </code>\n </pre>\n );\n return renderWithOverride(node, context, defaultNode);\n }\n\n if (node.type === \"thematic-break\") {\n const defaultNode = (\n <hr\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n />\n );\n return renderWithOverride(node, context, defaultNode);\n }\n\n if (node.type === \"text\") {\n const children = renderTextSegments(node);\n const defaultNode = <Fragment key={node.id}>{children}</Fragment>;\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"inline-code\") {\n const defaultNode = (\n <code\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {node.text}\n </code>\n );\n return renderWithOverride(node, context, defaultNode, node.text);\n }\n\n if (node.type === \"soft-break\") {\n const children = \"\\n\";\n const defaultNode = <Fragment key={node.id}>{children}</Fragment>;\n return renderWithOverride(node, context, defaultNode, children);\n }\n\n if (node.type === \"hard-break\") {\n const defaultNode = (\n <br\n key={node.id}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n />\n );\n return renderWithOverride(node, context, defaultNode);\n }\n\n if (node.type === \"image\") {\n const defaultNode = (\n <img\n key={node.id}\n src={sanitizeImgSrc(node.url)}\n alt={node.alt}\n title={node.title}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n />\n );\n return renderWithOverride(node, context, defaultNode);\n }\n\n if (node.type === \"autolink\") {\n const href = sanitizeHref(node.url);\n const defaultNode = (\n <a\n key={node.id}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={href ? handleLinkClick(context, node, href) : undefined}\n data-streaming-markdown-node={node.type}\n data-node-open={String(!node.closed)}\n >\n {node.text}\n </a>\n );\n return renderWithOverride(node, context, defaultNode, node.text);\n }\n\n const citation = getCitationRenderData(node, context);\n const defaultNode = renderDefaultCitation(node, context, citation);\n return renderWithOverride(node, context, defaultNode, null, citation);\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}: 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 nodeById = useMemo(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n\n for (const node of parserState.nodes) {\n map.set(node.id, node);\n }\n\n return map;\n }, [parserState.nodes]);\n\n const context = useMemo(() => {\n const openNode = findDeepestOpenRenderableNode(parserState.stack, nodeById);\n const caretNode = resolveCaretNode(caret, {\n isComplete: parserState.isComplete,\n openNode,\n });\n const caretTargetNodeId =\n !parserState.isComplete && openNode != null && caretNode != null\n ? openNode.id\n : null;\n\n return {\n nodeById,\n citations: parserState.citations,\n onLinkClickRef,\n onCitationClickRef,\n nodeRenderers,\n caretTargetNodeId,\n caretNode,\n };\n }, [\n nodeById,\n parserState.citations,\n parserState.isComplete,\n parserState.stack,\n nodeRenderers,\n caret,\n ]);\n\n const rootNode =\n parserState.rootId == null ? undefined : nodeById.get(parserState.rootId);\n const rootClassName = className\n ? `${DEFAULT_ROOT_CLASS} ${className}`\n : DEFAULT_ROOT_CLASS;\n const styleText =\n typeof navigator !== \"undefined\" && /jsdom/i.test(navigator.userAgent)\n ? DEFAULT_STYLES_FALLBACK\n : DEFAULT_STYLES;\n\n if (!rootNode) {\n return (\n <>\n <style>{styleText}</style>\n <div className={rootClassName} data-streaming-markdown-root />\n </>\n );\n }\n\n return (\n <>\n <style>{styleText}</style>\n <div className={rootClassName} data-streaming-markdown-root>\n {renderNode(rootNode, context)}\n </div>\n </>\n );\n}\n\nfunction findDeepestOpenRenderableNode(\n stack: number[],\n nodeById: Map<number, StreamingMarkdownAstNode>,\n): StreamingMarkdownAstNode | null {\n for (let index = stack.length - 1; index >= 0; index -= 1) {\n const candidate = nodeById.get(stack[index]) ?? null;\n if (candidate && candidate.type !== \"document\") {\n return candidate;\n }\n }\n\n return null;\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\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["Fragment","Text","View"],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"sourcesContent":["import React, { Fragment, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Text, View } from \"react-native\";\nimport {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownParserState,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// Style types\n// ---------------------------------------------------------------------------\n\n/**\n * Style-object for the RN streaming markdown renderer.\n * Every key is optional — defaults cover all built-in node types.\n *\n * @public\n */\nexport type MarkdownStyle = {\n paragraph?: Record<string, unknown>;\n h1?: Record<string, unknown>;\n h2?: Record<string, unknown>;\n h3?: Record<string, unknown>;\n h4?: Record<string, unknown>;\n h5?: Record<string, unknown>;\n h6?: Record<string, unknown>;\n strong?: Record<string, unknown>;\n em?: Record<string, unknown>;\n strikethrough?: Record<string, unknown>;\n inlineCode?: Record<string, unknown>;\n codeBlock?: Record<string, unknown>;\n codeBlockText?: Record<string, unknown>;\n blockquote?: Record<string, unknown>;\n list?: Record<string, unknown>;\n listItem?: Record<string, unknown>;\n listBullet?: Record<string, unknown>;\n link?: Record<string, unknown>;\n image?: Record<string, unknown>;\n tableRow?: Record<string, unknown>;\n tableCell?: Record<string, unknown>;\n};\n\n/**\n * Default styles for all built-in node types.\n *\n * @public\n */\nexport const defaultMarkdownStyles: MarkdownStyle = {\n paragraph: {\n fontSize: 16,\n lineHeight: 24,\n color: \"#1a1a1a\",\n marginTop: 4,\n marginBottom: 4,\n },\n h1: {\n fontSize: 24,\n fontWeight: \"bold\",\n marginTop: 12,\n marginBottom: 8,\n color: \"#111111\",\n },\n h2: {\n fontSize: 20,\n fontWeight: \"bold\",\n marginTop: 10,\n marginBottom: 6,\n color: \"#111111\",\n },\n h3: {\n fontSize: 18,\n fontWeight: \"600\",\n marginTop: 8,\n marginBottom: 4,\n color: \"#222222\",\n },\n h4: {\n fontSize: 16,\n fontWeight: \"600\",\n marginTop: 6,\n marginBottom: 4,\n color: \"#222222\",\n },\n h5: {\n fontSize: 14,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#333333\",\n },\n h6: {\n fontSize: 13,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#444444\",\n },\n strong: { fontWeight: \"bold\" },\n em: { fontStyle: \"italic\" },\n strikethrough: { textDecorationLine: \"line-through\" },\n inlineCode: {\n backgroundColor: \"#f0f0f0\",\n fontFamily: \"monospace\",\n fontSize: 14,\n },\n codeBlock: {\n backgroundColor: \"#f0f0f0\",\n borderRadius: 8,\n padding: 12,\n marginVertical: 4,\n },\n codeBlockText: {\n fontFamily: \"monospace\",\n fontSize: 14,\n color: \"#1a1a1a\",\n },\n blockquote: {\n backgroundColor: \"#f5f5f5\",\n borderLeftWidth: 4,\n borderLeftColor: \"#cccccc\",\n paddingLeft: 12,\n marginVertical: 4,\n },\n list: { marginTop: 4, marginBottom: 4 },\n listItem: { flexDirection: \"row\", alignItems: \"flex-start\" } as any,\n listBullet: { marginRight: 6, fontSize: 16, lineHeight: 24 },\n link: { color: \"#0066cc\", textDecorationLine: \"underline\" },\n image: {},\n tableRow: {\n flexDirection: \"row\",\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n } as any,\n tableCell: { flex: 1, padding: 4 },\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the React Native `StreamingMarkdownRenderer`.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /** Full markdown source (may grow over time during streaming). */\n content: string;\n /** When true, finalizes the parser state after processing content. */\n isComplete?: boolean;\n /** Style overrides merged with `defaultMarkdownStyles`. */\n style?: MarkdownStyle;\n /**\n * When true, enables `Intl.Segmenter`-based per-token animation.\n * Defaults to `false` — safe on Hermes which may not ship Segmenter.\n */\n animate?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ntype RenderContext = {\n nodeById: Map<number, StreamingMarkdownAstNode>;\n s: MarkdownStyle;\n};\n\nfunction renderChildren(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderNode(child, ctx)}</Fragment>;\n });\n}\n\nfunction renderTextContent(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderInlineNode(child, ctx)}</Fragment>;\n });\n}\n\n/**\n * Render an inline node (text, strong, em, etc.) as a React Native `<Text>`.\n * Returns `null` for block-level nodes that can't be nested in `<Text>`.\n */\nfunction renderInlineNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"text\":\n return node.text;\n\n case \"strong\":\n return (\n <Text key={node.id} style={ctx.s.strong as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"em\":\n return (\n <Text key={node.id} style={ctx.s.em as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"strikethrough\":\n return (\n <Text key={node.id} style={ctx.s.strikethrough as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"inline-code\":\n return (\n <Text key={node.id} style={ctx.s.inlineCode as any}>\n {node.text}\n </Text>\n );\n\n case \"link\":\n case \"autolink\":\n // Non-navigable: render as styled Text only — zero URL/XSS surface.\n return (\n <Text key={node.id} style={ctx.s.link as any}>\n {\"children\" in node\n ? renderTextContent(node as any, ctx)\n : (node as any).text}\n </Text>\n );\n\n case \"image\":\n // Images can't be embedded in <Text>; render alt text.\n return (\n <Text key={node.id} style={ctx.s.image as any}>\n {(node as any).alt}\n </Text>\n );\n\n case \"soft-break\":\n return \"\\n\";\n\n case \"hard-break\":\n return \"\\n\";\n\n case \"citation\":\n // Citation marker (e.g. \"[1]\"). This is an inline-level node reached via\n // a paragraph/heading's renderTextContent; without this case it would\n // fall through to `default` and render nothing (no text/children).\n // Prefer the resolved number, fall back to the citation id.\n return (\n <Text\n key={node.id}\n style={{ fontSize: 12, verticalAlign: \"top\" } as any}\n >\n [{(node as any).number ?? (node as any).idRef}]\n </Text>\n );\n\n case \"document\":\n case \"paragraph\":\n case \"heading\":\n case \"blockquote\":\n case \"list\":\n case \"list-item\":\n case \"code-block\":\n case \"table\":\n case \"table-row\":\n case \"table-cell\":\n case \"thematic-break\":\n default:\n // Fallback for any inline nodes not explicitly handled\n if (\"text\" in node && typeof (node as any).text === \"string\") {\n return (node as any).text;\n }\n if (\"children\" in node) {\n return renderTextContent(node as any, ctx);\n }\n return null;\n }\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"document\":\n return <Fragment key={node.id}>{renderChildren(node, ctx)}</Fragment>;\n\n case \"paragraph\":\n return (\n <Text key={node.id} style={ctx.s.paragraph as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"heading\": {\n const level = node.level as 1 | 2 | 3 | 4 | 5 | 6;\n const headingStyle =\n ctx.s[`h${level}` as keyof MarkdownStyle] ?? ctx.s.h6;\n return (\n <Text key={node.id} style={headingStyle as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n }\n\n case \"blockquote\":\n return (\n <View key={node.id} style={ctx.s.blockquote as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"list\":\n return (\n <View key={node.id} style={ctx.s.list as any}>\n {node.children.map((itemId, index) => {\n const item = ctx.nodeById.get(itemId);\n if (!item) return null;\n const bullet = node.ordered\n ? `${(node.start ?? 1) + index}. `\n : \"• \";\n return (\n <View key={itemId} style={ctx.s.listItem as any}>\n <Text style={ctx.s.listBullet as any}>{bullet}</Text>\n <View style={{ flex: 1 }}>\n {renderChildren(\n item as Extract<\n StreamingMarkdownAstNode,\n { children: number[] }\n >,\n ctx,\n )}\n </View>\n </View>\n );\n })}\n </View>\n );\n\n case \"list-item\":\n // Handled inside \"list\" above; fallback in case a list-item appears standalone\n return (\n <View key={node.id} style={ctx.s.listItem as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"code-block\":\n return (\n <View key={node.id} style={ctx.s.codeBlock as any}>\n <Text style={ctx.s.codeBlockText as any}>{node.text}</Text>\n </View>\n );\n\n case \"table\":\n return <View key={node.id}>{renderChildren(node, ctx)}</View>;\n\n case \"table-row\":\n return (\n <View key={node.id} style={ctx.s.tableRow as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"table-cell\":\n return (\n <View key={node.id} style={ctx.s.tableCell as any}>\n <Text>{renderTextContent(node, ctx)}</Text>\n </View>\n );\n\n case \"thematic-break\":\n return (\n <View\n key={node.id}\n style={{\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n marginVertical: 8,\n }}\n />\n );\n\n case \"text\":\n return <Text key={node.id}>{node.text}</Text>;\n\n case \"strong\":\n case \"em\":\n case \"strikethrough\":\n case \"inline-code\":\n case \"link\":\n case \"autolink\":\n case \"image\":\n case \"soft-break\":\n case \"hard-break\":\n case \"citation\":\n // These are inline; wrap in Text for block-level context. Citation\n // rendering lives in renderInlineNode so block and inline paths agree.\n return <Text key={node.id}>{renderInlineNode(node, ctx)}</Text>;\n\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nfunction buildParserState(\n content: string,\n isComplete: boolean,\n animate: boolean,\n): StreamingMarkdownParserState {\n const state = createStreamingMarkdownParserState({\n segmenter: animate,\n enableTables: true,\n enableAutolinks: true,\n });\n const parsed = parseStreamingMarkdownChunk(state, content);\n return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;\n}\n\n/**\n * React Native streaming markdown renderer powered by the zero-dependency\n * `@copilotkit/markdown` parser.\n *\n * Defaults `segmenter: false` — safe on Hermes which may not ship `Intl.Segmenter`.\n * Pass `animate={true}` only when you have confirmed Segmenter availability.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n content,\n isComplete = false,\n style,\n animate = false,\n}: StreamingMarkdownRendererProps): React.ReactElement | null {\n const mergedStyles = useMemo<MarkdownStyle>(\n () =>\n style ? { ...defaultMarkdownStyles, ...style } : defaultMarkdownStyles,\n [style],\n );\n\n const parserState = useMemo(\n () => buildParserState(content ?? \"\", isComplete, animate),\n [content, isComplete, animate],\n );\n\n const nodeById = useMemo(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n for (const node of parserState.nodes) {\n map.set(node.id, node);\n }\n return map;\n }, [parserState.nodes]);\n\n const ctx: RenderContext = useMemo(\n () => ({ nodeById, s: mergedStyles }),\n [nodeById, mergedStyles],\n );\n\n if (parserState.rootId == null) {\n return null;\n }\n\n const rootNode = nodeById.get(parserState.rootId);\n if (!rootNode) {\n return null;\n }\n\n return <>{renderNode(rootNode, ctx)}</>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Fragment","Text","View"],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"sourcesContent":["import React, { Fragment, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Text, View } from \"react-native\";\nimport {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownParserState,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// Style types\n// ---------------------------------------------------------------------------\n\n/**\n * Style-object for the RN streaming markdown renderer.\n * Every key is optional — defaults cover all built-in node types.\n *\n * @public\n */\nexport type MarkdownStyle = {\n paragraph?: Record<string, unknown>;\n h1?: Record<string, unknown>;\n h2?: Record<string, unknown>;\n h3?: Record<string, unknown>;\n h4?: Record<string, unknown>;\n h5?: Record<string, unknown>;\n h6?: Record<string, unknown>;\n strong?: Record<string, unknown>;\n em?: Record<string, unknown>;\n strikethrough?: Record<string, unknown>;\n inlineCode?: Record<string, unknown>;\n codeBlock?: Record<string, unknown>;\n codeBlockText?: Record<string, unknown>;\n blockquote?: Record<string, unknown>;\n list?: Record<string, unknown>;\n listItem?: Record<string, unknown>;\n listBullet?: Record<string, unknown>;\n link?: Record<string, unknown>;\n image?: Record<string, unknown>;\n tableRow?: Record<string, unknown>;\n tableCell?: Record<string, unknown>;\n};\n\n/**\n * Default styles for all built-in node types.\n *\n * @public\n */\nexport const defaultMarkdownStyles: MarkdownStyle = {\n paragraph: {\n fontSize: 16,\n lineHeight: 24,\n color: \"#1a1a1a\",\n marginTop: 4,\n marginBottom: 4,\n },\n h1: {\n fontSize: 24,\n fontWeight: \"bold\",\n marginTop: 12,\n marginBottom: 8,\n color: \"#111111\",\n },\n h2: {\n fontSize: 20,\n fontWeight: \"bold\",\n marginTop: 10,\n marginBottom: 6,\n color: \"#111111\",\n },\n h3: {\n fontSize: 18,\n fontWeight: \"600\",\n marginTop: 8,\n marginBottom: 4,\n color: \"#222222\",\n },\n h4: {\n fontSize: 16,\n fontWeight: \"600\",\n marginTop: 6,\n marginBottom: 4,\n color: \"#222222\",\n },\n h5: {\n fontSize: 14,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#333333\",\n },\n h6: {\n fontSize: 13,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#444444\",\n },\n strong: { fontWeight: \"bold\" },\n em: { fontStyle: \"italic\" },\n strikethrough: { textDecorationLine: \"line-through\" },\n inlineCode: {\n backgroundColor: \"#f0f0f0\",\n fontFamily: \"monospace\",\n fontSize: 14,\n },\n codeBlock: {\n backgroundColor: \"#f0f0f0\",\n borderRadius: 8,\n padding: 12,\n marginVertical: 4,\n },\n codeBlockText: {\n fontFamily: \"monospace\",\n fontSize: 14,\n color: \"#1a1a1a\",\n },\n blockquote: {\n backgroundColor: \"#f5f5f5\",\n borderLeftWidth: 4,\n borderLeftColor: \"#cccccc\",\n paddingLeft: 12,\n marginVertical: 4,\n },\n list: { marginTop: 4, marginBottom: 4 },\n listItem: { flexDirection: \"row\", alignItems: \"flex-start\" } as any,\n listBullet: { marginRight: 6, fontSize: 16, lineHeight: 24 },\n link: { color: \"#0066cc\", textDecorationLine: \"underline\" },\n image: {},\n tableRow: {\n flexDirection: \"row\",\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n } as any,\n tableCell: { flex: 1, padding: 4 },\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the React Native `StreamingMarkdownRenderer`.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /** Full markdown source (may grow over time during streaming). */\n content: string;\n /** When true, finalizes the parser state after processing content. */\n isComplete?: boolean;\n /** Style overrides merged with `defaultMarkdownStyles`. */\n style?: MarkdownStyle;\n /**\n * When true, enables `Intl.Segmenter`-based per-token animation.\n * Defaults to `false` — safe on Hermes which may not ship Segmenter.\n */\n animate?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ntype RenderContext = {\n nodeById: Map<number, StreamingMarkdownAstNode>;\n s: MarkdownStyle;\n};\n\nfunction renderChildren(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderNode(child, ctx)}</Fragment>;\n });\n}\n\nfunction renderTextContent(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderInlineNode(child, ctx)}</Fragment>;\n });\n}\n\n/**\n * Render an inline node (text, strong, em, etc.) as a React Native `<Text>`.\n * Returns `null` for block-level nodes that can't be nested in `<Text>`.\n */\nfunction renderInlineNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"text\":\n return node.text;\n\n case \"strong\":\n return (\n <Text key={node.id} style={ctx.s.strong as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"em\":\n return (\n <Text key={node.id} style={ctx.s.em as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"strikethrough\":\n return (\n <Text key={node.id} style={ctx.s.strikethrough as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"inline-code\":\n return (\n <Text key={node.id} style={ctx.s.inlineCode as any}>\n {node.text}\n </Text>\n );\n\n case \"link\":\n case \"autolink\":\n // Non-navigable: render as styled Text only — zero URL/XSS surface.\n return (\n <Text key={node.id} style={ctx.s.link as any}>\n {\"children\" in node\n ? renderTextContent(node as any, ctx)\n : (node as any).text}\n </Text>\n );\n\n case \"image\":\n // Images can't be embedded in <Text>; render alt text.\n return (\n <Text key={node.id} style={ctx.s.image as any}>\n {(node as any).alt}\n </Text>\n );\n\n case \"soft-break\":\n return \"\\n\";\n\n case \"hard-break\":\n return \"\\n\";\n\n case \"citation\":\n // Citation marker (e.g. \"[1]\"). This is an inline-level node reached via\n // a paragraph/heading's renderTextContent; without this case it would\n // fall through to `default` and render nothing (no text/children).\n // Prefer the resolved number, fall back to the citation id.\n return (\n <Text\n key={node.id}\n style={{ fontSize: 12, verticalAlign: \"top\" } as any}\n >\n [{(node as any).number ?? (node as any).idRef}]\n </Text>\n );\n\n case \"document\":\n case \"paragraph\":\n case \"heading\":\n case \"blockquote\":\n case \"list\":\n case \"list-item\":\n case \"code-block\":\n case \"table\":\n case \"table-row\":\n case \"table-cell\":\n case \"thematic-break\":\n case \"math\":\n default:\n // Fallback for any inline nodes not explicitly handled. Math has no\n // React Native renderer, so it shows its source text.\n if (\"text\" in node && typeof (node as any).text === \"string\") {\n return (node as any).text;\n }\n if (\"children\" in node) {\n return renderTextContent(node as any, ctx);\n }\n return null;\n }\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"document\":\n return <Fragment key={node.id}>{renderChildren(node, ctx)}</Fragment>;\n\n case \"paragraph\":\n return (\n <Text key={node.id} style={ctx.s.paragraph as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"heading\": {\n const level = node.level as 1 | 2 | 3 | 4 | 5 | 6;\n const headingStyle =\n ctx.s[`h${level}` as keyof MarkdownStyle] ?? ctx.s.h6;\n return (\n <Text key={node.id} style={headingStyle as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n }\n\n case \"blockquote\":\n return (\n <View key={node.id} style={ctx.s.blockquote as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"list\":\n return (\n <View key={node.id} style={ctx.s.list as any}>\n {node.children.map((itemId, index) => {\n const item = ctx.nodeById.get(itemId);\n if (!item) return null;\n const bullet = node.ordered\n ? `${(node.start ?? 1) + index}. `\n : \"• \";\n return (\n <View key={itemId} style={ctx.s.listItem as any}>\n <Text style={ctx.s.listBullet as any}>{bullet}</Text>\n <View style={{ flex: 1 }}>\n {renderChildren(\n item as Extract<\n StreamingMarkdownAstNode,\n { children: number[] }\n >,\n ctx,\n )}\n </View>\n </View>\n );\n })}\n </View>\n );\n\n case \"list-item\":\n // Handled inside \"list\" above; fallback in case a list-item appears standalone\n return (\n <View key={node.id} style={ctx.s.listItem as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"code-block\":\n return (\n <View key={node.id} style={ctx.s.codeBlock as any}>\n <Text style={ctx.s.codeBlockText as any}>{node.text}</Text>\n </View>\n );\n\n case \"table\":\n return <View key={node.id}>{renderChildren(node, ctx)}</View>;\n\n case \"table-row\":\n return (\n <View key={node.id} style={ctx.s.tableRow as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"table-cell\":\n return (\n <View key={node.id} style={ctx.s.tableCell as any}>\n <Text>{renderTextContent(node, ctx)}</Text>\n </View>\n );\n\n case \"thematic-break\":\n return (\n <View\n key={node.id}\n style={{\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n marginVertical: 8,\n }}\n />\n );\n\n case \"text\":\n return <Text key={node.id}>{node.text}</Text>;\n\n case \"strong\":\n case \"em\":\n case \"strikethrough\":\n case \"inline-code\":\n case \"link\":\n case \"autolink\":\n case \"image\":\n case \"soft-break\":\n case \"hard-break\":\n case \"citation\":\n // These are inline; wrap in Text for block-level context. Citation\n // rendering lives in renderInlineNode so block and inline paths agree.\n return <Text key={node.id}>{renderInlineNode(node, ctx)}</Text>;\n\n case \"math\":\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nfunction buildParserState(\n content: string,\n isComplete: boolean,\n animate: boolean,\n): StreamingMarkdownParserState {\n const state = createStreamingMarkdownParserState({\n segmenter: animate,\n enableTables: true,\n enableAutolinks: true,\n });\n const parsed = parseStreamingMarkdownChunk(state, content);\n return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;\n}\n\n/**\n * React Native streaming markdown renderer powered by the zero-dependency\n * `@copilotkit/markdown` parser.\n *\n * Defaults `segmenter: false` — safe on Hermes which may not ship `Intl.Segmenter`.\n * Pass `animate={true}` only when you have confirmed Segmenter availability.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n content,\n isComplete = false,\n style,\n animate = false,\n}: StreamingMarkdownRendererProps): React.ReactElement | null {\n const mergedStyles = useMemo<MarkdownStyle>(\n () =>\n style ? { ...defaultMarkdownStyles, ...style } : defaultMarkdownStyles,\n [style],\n );\n\n const parserState = useMemo(\n () => buildParserState(content ?? \"\", isComplete, animate),\n [content, isComplete, animate],\n );\n\n const nodeById = useMemo(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n for (const node of parserState.nodes) {\n map.set(node.id, node);\n }\n return map;\n }, [parserState.nodes]);\n\n const ctx: RenderContext = useMemo(\n () => ({ nodeById, s: mergedStyles }),\n [nodeById, mergedStyles],\n );\n\n if (parserState.rootId == null) {\n return null;\n }\n\n const rootNode = nodeById.get(parserState.rootId);\n if (!rootNode) {\n return null;\n }\n\n return <>{renderNode(rootNode, ctx)}</>;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"mappings":";;;;;AAuBA;;;;KAAY,aAAA;EACV,SAAA,GAAY,MAAA;EACZ,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,MAAA,GAAS,MAAA;EACT,EAAA,GAAK,MAAA;EACL,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,SAAA,GAAY,MAAA;EACZ,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,QAAA,GAAW,MAAA;EACX,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,KAAA,GAAQ,MAAA;EACR,QAAA,GAAW,MAAA;EACX,SAAA,GAAY,MAAA;AAAA;;;;;;cAQD,qBAAA,EAAuB,aAAA;;;;;;UAkGnB,8BAAA;EAxHV;EA0HL,OAAA;EAzHS;EA2HT,UAAA;EA1HK;EA4HL,KAAA,GAAQ,aAAA;EA3HQ;;;;EAgIhB,OAAA;AAAA;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"mappings":";;;;;AAuBA;;;;KAAY,aAAA;EACV,SAAA,GAAY,MAAA;EACZ,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,MAAA,GAAS,MAAA;EACT,EAAA,GAAK,MAAA;EACL,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,SAAA,GAAY,MAAA;EACZ,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,QAAA,GAAW,MAAA;EACX,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,KAAA,GAAQ,MAAA;EACR,QAAA,GAAW,MAAA;EACX,SAAA,GAAY,MAAA;AAAA;;;;;;cAQD,qBAAA,EAAuB,aAAA;;;;;;UAkGnB,8BAAA;EAxHV;EA0HL,OAAA;EAzHS;EA2HT,UAAA;EA1HK;EA4HL,KAAA,GAAQ,aAAA;EA3HQ;;;;EAgIhB,OAAA;AAAA;;;;;;;;;;iBAoSc,yBAAA,CAAA;EACd,OAAA;EACA,UAAA;EACA,KAAA;EACA;AAAA,GACC,8BAAA,GAAiC,KAAA,CAAM,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"mappings":";;;;;AAuBA;;;;KAAY,aAAA;EACV,SAAA,GAAY,MAAA;EACZ,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,MAAA,GAAS,MAAA;EACT,EAAA,GAAK,MAAA;EACL,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,SAAA,GAAY,MAAA;EACZ,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,QAAA,GAAW,MAAA;EACX,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,KAAA,GAAQ,MAAA;EACR,QAAA,GAAW,MAAA;EACX,SAAA,GAAY,MAAA;AAAA;;;;;;cAQD,qBAAA,EAAuB,aAAA;;;;;;UAkGnB,8BAAA;EAxHV;EA0HL,OAAA;EAzHS;EA2HT,UAAA;EA1HK;EA4HL,KAAA,GAAQ,aAAA;EA3HQ;;;;EAgIhB,OAAA;AAAA;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"mappings":";;;;;AAuBA;;;;KAAY,aAAA;EACV,SAAA,GAAY,MAAA;EACZ,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,EAAA,GAAK,MAAA;EACL,MAAA,GAAS,MAAA;EACT,EAAA,GAAK,MAAA;EACL,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,SAAA,GAAY,MAAA;EACZ,aAAA,GAAgB,MAAA;EAChB,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,QAAA,GAAW,MAAA;EACX,UAAA,GAAa,MAAA;EACb,IAAA,GAAO,MAAA;EACP,KAAA,GAAQ,MAAA;EACR,QAAA,GAAW,MAAA;EACX,SAAA,GAAY,MAAA;AAAA;;;;;;cAQD,qBAAA,EAAuB,aAAA;;;;;;UAkGnB,8BAAA;EAxHV;EA0HL,OAAA;EAzHS;EA2HT,UAAA;EA1HK;EA4HL,KAAA,GAAQ,aAAA;EA3HQ;;;;EAgIhB,OAAA;AAAA;;;;;;;;;;iBAoSc,yBAAA,CAAA;EACd,OAAA;EACA,UAAA;EACA,KAAA;EACA;AAAA,GACC,8BAAA,GAAiC,KAAA,CAAM,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"sourcesContent":["import React, { Fragment, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Text, View } from \"react-native\";\nimport {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownParserState,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// Style types\n// ---------------------------------------------------------------------------\n\n/**\n * Style-object for the RN streaming markdown renderer.\n * Every key is optional — defaults cover all built-in node types.\n *\n * @public\n */\nexport type MarkdownStyle = {\n paragraph?: Record<string, unknown>;\n h1?: Record<string, unknown>;\n h2?: Record<string, unknown>;\n h3?: Record<string, unknown>;\n h4?: Record<string, unknown>;\n h5?: Record<string, unknown>;\n h6?: Record<string, unknown>;\n strong?: Record<string, unknown>;\n em?: Record<string, unknown>;\n strikethrough?: Record<string, unknown>;\n inlineCode?: Record<string, unknown>;\n codeBlock?: Record<string, unknown>;\n codeBlockText?: Record<string, unknown>;\n blockquote?: Record<string, unknown>;\n list?: Record<string, unknown>;\n listItem?: Record<string, unknown>;\n listBullet?: Record<string, unknown>;\n link?: Record<string, unknown>;\n image?: Record<string, unknown>;\n tableRow?: Record<string, unknown>;\n tableCell?: Record<string, unknown>;\n};\n\n/**\n * Default styles for all built-in node types.\n *\n * @public\n */\nexport const defaultMarkdownStyles: MarkdownStyle = {\n paragraph: {\n fontSize: 16,\n lineHeight: 24,\n color: \"#1a1a1a\",\n marginTop: 4,\n marginBottom: 4,\n },\n h1: {\n fontSize: 24,\n fontWeight: \"bold\",\n marginTop: 12,\n marginBottom: 8,\n color: \"#111111\",\n },\n h2: {\n fontSize: 20,\n fontWeight: \"bold\",\n marginTop: 10,\n marginBottom: 6,\n color: \"#111111\",\n },\n h3: {\n fontSize: 18,\n fontWeight: \"600\",\n marginTop: 8,\n marginBottom: 4,\n color: \"#222222\",\n },\n h4: {\n fontSize: 16,\n fontWeight: \"600\",\n marginTop: 6,\n marginBottom: 4,\n color: \"#222222\",\n },\n h5: {\n fontSize: 14,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#333333\",\n },\n h6: {\n fontSize: 13,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#444444\",\n },\n strong: { fontWeight: \"bold\" },\n em: { fontStyle: \"italic\" },\n strikethrough: { textDecorationLine: \"line-through\" },\n inlineCode: {\n backgroundColor: \"#f0f0f0\",\n fontFamily: \"monospace\",\n fontSize: 14,\n },\n codeBlock: {\n backgroundColor: \"#f0f0f0\",\n borderRadius: 8,\n padding: 12,\n marginVertical: 4,\n },\n codeBlockText: {\n fontFamily: \"monospace\",\n fontSize: 14,\n color: \"#1a1a1a\",\n },\n blockquote: {\n backgroundColor: \"#f5f5f5\",\n borderLeftWidth: 4,\n borderLeftColor: \"#cccccc\",\n paddingLeft: 12,\n marginVertical: 4,\n },\n list: { marginTop: 4, marginBottom: 4 },\n listItem: { flexDirection: \"row\", alignItems: \"flex-start\" } as any,\n listBullet: { marginRight: 6, fontSize: 16, lineHeight: 24 },\n link: { color: \"#0066cc\", textDecorationLine: \"underline\" },\n image: {},\n tableRow: {\n flexDirection: \"row\",\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n } as any,\n tableCell: { flex: 1, padding: 4 },\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the React Native `StreamingMarkdownRenderer`.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /** Full markdown source (may grow over time during streaming). */\n content: string;\n /** When true, finalizes the parser state after processing content. */\n isComplete?: boolean;\n /** Style overrides merged with `defaultMarkdownStyles`. */\n style?: MarkdownStyle;\n /**\n * When true, enables `Intl.Segmenter`-based per-token animation.\n * Defaults to `false` — safe on Hermes which may not ship Segmenter.\n */\n animate?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ntype RenderContext = {\n nodeById: Map<number, StreamingMarkdownAstNode>;\n s: MarkdownStyle;\n};\n\nfunction renderChildren(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderNode(child, ctx)}</Fragment>;\n });\n}\n\nfunction renderTextContent(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderInlineNode(child, ctx)}</Fragment>;\n });\n}\n\n/**\n * Render an inline node (text, strong, em, etc.) as a React Native `<Text>`.\n * Returns `null` for block-level nodes that can't be nested in `<Text>`.\n */\nfunction renderInlineNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"text\":\n return node.text;\n\n case \"strong\":\n return (\n <Text key={node.id} style={ctx.s.strong as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"em\":\n return (\n <Text key={node.id} style={ctx.s.em as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"strikethrough\":\n return (\n <Text key={node.id} style={ctx.s.strikethrough as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"inline-code\":\n return (\n <Text key={node.id} style={ctx.s.inlineCode as any}>\n {node.text}\n </Text>\n );\n\n case \"link\":\n case \"autolink\":\n // Non-navigable: render as styled Text only — zero URL/XSS surface.\n return (\n <Text key={node.id} style={ctx.s.link as any}>\n {\"children\" in node\n ? renderTextContent(node as any, ctx)\n : (node as any).text}\n </Text>\n );\n\n case \"image\":\n // Images can't be embedded in <Text>; render alt text.\n return (\n <Text key={node.id} style={ctx.s.image as any}>\n {(node as any).alt}\n </Text>\n );\n\n case \"soft-break\":\n return \"\\n\";\n\n case \"hard-break\":\n return \"\\n\";\n\n case \"citation\":\n // Citation marker (e.g. \"[1]\"). This is an inline-level node reached via\n // a paragraph/heading's renderTextContent; without this case it would\n // fall through to `default` and render nothing (no text/children).\n // Prefer the resolved number, fall back to the citation id.\n return (\n <Text\n key={node.id}\n style={{ fontSize: 12, verticalAlign: \"top\" } as any}\n >\n [{(node as any).number ?? (node as any).idRef}]\n </Text>\n );\n\n case \"document\":\n case \"paragraph\":\n case \"heading\":\n case \"blockquote\":\n case \"list\":\n case \"list-item\":\n case \"code-block\":\n case \"table\":\n case \"table-row\":\n case \"table-cell\":\n case \"thematic-break\":\n default:\n // Fallback for any inline nodes not explicitly handled\n if (\"text\" in node && typeof (node as any).text === \"string\") {\n return (node as any).text;\n }\n if (\"children\" in node) {\n return renderTextContent(node as any, ctx);\n }\n return null;\n }\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"document\":\n return <Fragment key={node.id}>{renderChildren(node, ctx)}</Fragment>;\n\n case \"paragraph\":\n return (\n <Text key={node.id} style={ctx.s.paragraph as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"heading\": {\n const level = node.level as 1 | 2 | 3 | 4 | 5 | 6;\n const headingStyle =\n ctx.s[`h${level}` as keyof MarkdownStyle] ?? ctx.s.h6;\n return (\n <Text key={node.id} style={headingStyle as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n }\n\n case \"blockquote\":\n return (\n <View key={node.id} style={ctx.s.blockquote as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"list\":\n return (\n <View key={node.id} style={ctx.s.list as any}>\n {node.children.map((itemId, index) => {\n const item = ctx.nodeById.get(itemId);\n if (!item) return null;\n const bullet = node.ordered\n ? `${(node.start ?? 1) + index}. `\n : \"• \";\n return (\n <View key={itemId} style={ctx.s.listItem as any}>\n <Text style={ctx.s.listBullet as any}>{bullet}</Text>\n <View style={{ flex: 1 }}>\n {renderChildren(\n item as Extract<\n StreamingMarkdownAstNode,\n { children: number[] }\n >,\n ctx,\n )}\n </View>\n </View>\n );\n })}\n </View>\n );\n\n case \"list-item\":\n // Handled inside \"list\" above; fallback in case a list-item appears standalone\n return (\n <View key={node.id} style={ctx.s.listItem as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"code-block\":\n return (\n <View key={node.id} style={ctx.s.codeBlock as any}>\n <Text style={ctx.s.codeBlockText as any}>{node.text}</Text>\n </View>\n );\n\n case \"table\":\n return <View key={node.id}>{renderChildren(node, ctx)}</View>;\n\n case \"table-row\":\n return (\n <View key={node.id} style={ctx.s.tableRow as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"table-cell\":\n return (\n <View key={node.id} style={ctx.s.tableCell as any}>\n <Text>{renderTextContent(node, ctx)}</Text>\n </View>\n );\n\n case \"thematic-break\":\n return (\n <View\n key={node.id}\n style={{\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n marginVertical: 8,\n }}\n />\n );\n\n case \"text\":\n return <Text key={node.id}>{node.text}</Text>;\n\n case \"strong\":\n case \"em\":\n case \"strikethrough\":\n case \"inline-code\":\n case \"link\":\n case \"autolink\":\n case \"image\":\n case \"soft-break\":\n case \"hard-break\":\n case \"citation\":\n // These are inline; wrap in Text for block-level context. Citation\n // rendering lives in renderInlineNode so block and inline paths agree.\n return <Text key={node.id}>{renderInlineNode(node, ctx)}</Text>;\n\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nfunction buildParserState(\n content: string,\n isComplete: boolean,\n animate: boolean,\n): StreamingMarkdownParserState {\n const state = createStreamingMarkdownParserState({\n segmenter: animate,\n enableTables: true,\n enableAutolinks: true,\n });\n const parsed = parseStreamingMarkdownChunk(state, content);\n return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;\n}\n\n/**\n * React Native streaming markdown renderer powered by the zero-dependency\n * `@copilotkit/markdown` parser.\n *\n * Defaults `segmenter: false` — safe on Hermes which may not ship `Intl.Segmenter`.\n * Pass `animate={true}` only when you have confirmed Segmenter availability.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n content,\n isComplete = false,\n style,\n animate = false,\n}: StreamingMarkdownRendererProps): React.ReactElement | null {\n const mergedStyles = useMemo<MarkdownStyle>(\n () =>\n style ? { ...defaultMarkdownStyles, ...style } : defaultMarkdownStyles,\n [style],\n );\n\n const parserState = useMemo(\n () => buildParserState(content ?? \"\", isComplete, animate),\n [content, isComplete, animate],\n );\n\n const nodeById = useMemo(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n for (const node of parserState.nodes) {\n map.set(node.id, node);\n }\n return map;\n }, [parserState.nodes]);\n\n const ctx: RenderContext = useMemo(\n () => ({ nodeById, s: mergedStyles }),\n [nodeById, mergedStyles],\n );\n\n if (parserState.rootId == null) {\n return null;\n }\n\n const rootNode = nodeById.get(parserState.rootId);\n if (!rootNode) {\n return null;\n }\n\n return <>{renderNode(rootNode, ctx)}</>;\n}\n"],"mappings":";;;;;;;;;;;AAoDA,MAAa,wBAAuC;CAClD,WAAW;EACT,UAAU;EACV,YAAY;EACZ,OAAO;EACP,WAAW;EACX,cAAc;EACf;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,IAAI;EACF,UAAU;EACV,YAAY;EACZ,WAAW;EACX,cAAc;EACd,OAAO;EACR;CACD,QAAQ,EAAE,YAAY,QAAQ;CAC9B,IAAI,EAAE,WAAW,UAAU;CAC3B,eAAe,EAAE,oBAAoB,gBAAgB;CACrD,YAAY;EACV,iBAAiB;EACjB,YAAY;EACZ,UAAU;EACX;CACD,WAAW;EACT,iBAAiB;EACjB,cAAc;EACd,SAAS;EACT,gBAAgB;EACjB;CACD,eAAe;EACb,YAAY;EACZ,UAAU;EACV,OAAO;EACR;CACD,YAAY;EACV,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB;EACjB,aAAa;EACb,gBAAgB;EACjB;CACD,MAAM;EAAE,WAAW;EAAG,cAAc;EAAG;CACvC,UAAU;EAAE,eAAe;EAAO,YAAY;EAAc;CAC5D,YAAY;EAAE,aAAa;EAAG,UAAU;EAAI,YAAY;EAAI;CAC5D,MAAM;EAAE,OAAO;EAAW,oBAAoB;EAAa;CAC3D,OAAO,EAAE;CACT,UAAU;EACR,eAAe;EACf,mBAAmB;EACnB,mBAAmB;EACpB;CACD,WAAW;EAAE,MAAM;EAAG,SAAS;EAAG;CACnC;AAkCD,SAAS,eACP,MACA,KACW;AACX,QAAO,KAAK,SAAS,KAAK,OAAO;EAC/B,MAAM,QAAQ,IAAI,SAAS,IAAI,GAAG;AAClC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,oBAAC,sBAAmB,WAAW,OAAO,IAAI,IAA3B,GAAuC;GAC7D;;AAGJ,SAAS,kBACP,MACA,KACW;AACX,QAAO,KAAK,SAAS,KAAK,OAAO;EAC/B,MAAM,QAAQ,IAAI,SAAS,IAAI,GAAG;AAClC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,oBAAC,sBAAmB,iBAAiB,OAAO,IAAI,IAAjC,GAA6C;GACnE;;;;;;AAOJ,SAAS,iBACP,MACA,KACW;AACX,SAAQ,KAAK,MAAb;EACE,KAAK,OACH,QAAO,KAAK;EAEd,KAAK,SACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,kBAAkB,MAAM,IAAI;KADpB,KAAK,GAET;EAGX,KAAK,KACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,kBAAkB,MAAM,IAAI;KADpB,KAAK,GAET;EAGX,KAAK,gBACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,kBAAkB,MAAM,IAAI;KADpB,KAAK,GAET;EAGX,KAAK,cACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,KAAK;KADG,KAAK,GAET;EAGX,KAAK;EACL,KAAK,WAEH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,cAAc,OACX,kBAAkB,MAAa,IAAI,GAClC,KAAa;KAHT,KAAK,GAIT;EAGX,KAAK,QAEH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC7B,KAAa;KADN,KAAK,GAET;EAGX,KAAK,aACH,QAAO;EAET,KAAK,aACH,QAAO;EAET,KAAK,WAKH,QACE,qBAAC;GAEC,OAAO;IAAE,UAAU;IAAI,eAAe;IAAO;;IAC9C;IACI,KAAa,UAAW,KAAa;IAAM;;KAHzC,KAAK,GAIL;EAcX;AAEE,OAAI,UAAU,QAAQ,OAAQ,KAAa,SAAS,SAClD,QAAQ,KAAa;AAEvB,OAAI,cAAc,KAChB,QAAO,kBAAkB,MAAa,IAAI;AAE5C,UAAO;;;AAIb,SAAS,WACP,MACA,KACW;AACX,SAAQ,KAAK,MAAb;EACE,KAAK,WACH,QAAO,oBAAC,sBAAwB,eAAe,MAAM,IAAI,IAAnC,KAAK,GAA0C;EAEvE,KAAK,YACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,kBAAkB,MAAM,IAAI;KADpB,KAAK,GAET;EAGX,KAAK,WAAW;GACd,MAAM,QAAQ,KAAK;AAGnB,UACE,oBAAC;IAAmB,OAFpB,IAAI,EAAE,IAAI,YAAmC,IAAI,EAAE;cAGhD,kBAAkB,MAAM,IAAI;MADpB,KAAK,GAET;;EAIX,KAAK,aACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,eAAe,MAAM,IAAI;KADjB,KAAK,GAET;EAGX,KAAK,OACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,KAAK,SAAS,KAAK,QAAQ,UAAU;IACpC,MAAM,OAAO,IAAI,SAAS,IAAI,OAAO;AACrC,QAAI,CAAC,KAAM,QAAO;IAClB,MAAM,SAAS,KAAK,UAChB,IAAI,KAAK,SAAS,KAAK,MAAM,MAC7B;AACJ,WACE,qBAAC;KAAkB,OAAO,IAAI,EAAE;gBAC9B,oBAAC;MAAK,OAAO,IAAI,EAAE;gBAAoB;OAAc,EACrD,oBAAC;MAAK,OAAO,EAAE,MAAM,GAAG;gBACrB,eACC,MAIA,IACD;OACI;OAVE,OAWJ;KAET;KArBO,KAAK,GAsBT;EAGX,KAAK,YAEH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,eAAe,MAAM,IAAI;KADjB,KAAK,GAET;EAGX,KAAK,aACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC/B,oBAAC;IAAK,OAAO,IAAI,EAAE;cAAuB,KAAK;KAAY;KADlD,KAAK,GAET;EAGX,KAAK,QACH,QAAO,oBAAC,kBAAoB,eAAe,MAAM,IAAI,IAAnC,KAAK,GAAsC;EAE/D,KAAK,YACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC9B,eAAe,MAAM,IAAI;KADjB,KAAK,GAET;EAGX,KAAK,aACH,QACE,oBAAC;GAAmB,OAAO,IAAI,EAAE;aAC/B,oBAAC,kBAAM,kBAAkB,MAAM,IAAI,GAAQ;KADlC,KAAK,GAET;EAGX,KAAK,iBACH,QACE,oBAAC,QAEC,OAAO;GACL,mBAAmB;GACnB,mBAAmB;GACnB,gBAAgB;GACjB,IALI,KAAK,GAMV;EAGN,KAAK,OACH,QAAO,oBAAC,kBAAoB,KAAK,QAAf,KAAK,GAAsB;EAE/C,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,WAGH,QAAO,oBAAC,kBAAoB,iBAAiB,MAAM,IAAI,IAArC,KAAK,GAAwC;EAEjE,QACE,QAAO;;;AAQb,SAAS,iBACP,SACA,YACA,SAC8B;CAM9B,MAAM,SAAS,4BALD,mCAAmC;EAC/C,WAAW;EACX,cAAc;EACd,iBAAiB;EAClB,CAAC,EACgD,QAAQ;AAC1D,QAAO,aAAa,0BAA0B,OAAO,GAAG;;;;;;;;;;;AAY1D,SAAgB,0BAA0B,EACxC,SACA,aAAa,OACb,OACA,UAAU,SACkD;CAC5D,MAAM,eAAe,cAEjB,QAAQ;EAAE,GAAG;EAAuB,GAAG;EAAO,GAAG,uBACnD,CAAC,MAAM,CACR;CAED,MAAM,cAAc,cACZ,iBAAiB,WAAW,IAAI,YAAY,QAAQ,EAC1D;EAAC;EAAS;EAAY;EAAQ,CAC/B;CAED,MAAM,WAAW,cAAc;EAC7B,MAAM,sBAAM,IAAI,KAAuC;AACvD,OAAK,MAAM,QAAQ,YAAY,MAC7B,KAAI,IAAI,KAAK,IAAI,KAAK;AAExB,SAAO;IACN,CAAC,YAAY,MAAM,CAAC;CAEvB,MAAM,MAAqB,eAClB;EAAE;EAAU,GAAG;EAAc,GACpC,CAAC,UAAU,aAAa,CACzB;AAED,KAAI,YAAY,UAAU,KACxB,QAAO;CAGT,MAAM,WAAW,SAAS,IAAI,YAAY,OAAO;AACjD,KAAI,CAAC,SACH,QAAO;AAGT,QAAO,4CAAG,WAAW,UAAU,IAAI,GAAI"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/react-native/streaming-markdown-renderer.tsx"],"sourcesContent":["import React, { Fragment, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Text, View } from \"react-native\";\nimport {\n createStreamingMarkdownParserState,\n finalizeStreamingMarkdown,\n parseStreamingMarkdownChunk,\n} from \"@copilotkit/markdown\";\nimport type {\n StreamingMarkdownAstNode,\n StreamingMarkdownParserState,\n} from \"@copilotkit/markdown\";\n\n// ---------------------------------------------------------------------------\n// Style types\n// ---------------------------------------------------------------------------\n\n/**\n * Style-object for the RN streaming markdown renderer.\n * Every key is optional — defaults cover all built-in node types.\n *\n * @public\n */\nexport type MarkdownStyle = {\n paragraph?: Record<string, unknown>;\n h1?: Record<string, unknown>;\n h2?: Record<string, unknown>;\n h3?: Record<string, unknown>;\n h4?: Record<string, unknown>;\n h5?: Record<string, unknown>;\n h6?: Record<string, unknown>;\n strong?: Record<string, unknown>;\n em?: Record<string, unknown>;\n strikethrough?: Record<string, unknown>;\n inlineCode?: Record<string, unknown>;\n codeBlock?: Record<string, unknown>;\n codeBlockText?: Record<string, unknown>;\n blockquote?: Record<string, unknown>;\n list?: Record<string, unknown>;\n listItem?: Record<string, unknown>;\n listBullet?: Record<string, unknown>;\n link?: Record<string, unknown>;\n image?: Record<string, unknown>;\n tableRow?: Record<string, unknown>;\n tableCell?: Record<string, unknown>;\n};\n\n/**\n * Default styles for all built-in node types.\n *\n * @public\n */\nexport const defaultMarkdownStyles: MarkdownStyle = {\n paragraph: {\n fontSize: 16,\n lineHeight: 24,\n color: \"#1a1a1a\",\n marginTop: 4,\n marginBottom: 4,\n },\n h1: {\n fontSize: 24,\n fontWeight: \"bold\",\n marginTop: 12,\n marginBottom: 8,\n color: \"#111111\",\n },\n h2: {\n fontSize: 20,\n fontWeight: \"bold\",\n marginTop: 10,\n marginBottom: 6,\n color: \"#111111\",\n },\n h3: {\n fontSize: 18,\n fontWeight: \"600\",\n marginTop: 8,\n marginBottom: 4,\n color: \"#222222\",\n },\n h4: {\n fontSize: 16,\n fontWeight: \"600\",\n marginTop: 6,\n marginBottom: 4,\n color: \"#222222\",\n },\n h5: {\n fontSize: 14,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#333333\",\n },\n h6: {\n fontSize: 13,\n fontWeight: \"600\",\n marginTop: 4,\n marginBottom: 2,\n color: \"#444444\",\n },\n strong: { fontWeight: \"bold\" },\n em: { fontStyle: \"italic\" },\n strikethrough: { textDecorationLine: \"line-through\" },\n inlineCode: {\n backgroundColor: \"#f0f0f0\",\n fontFamily: \"monospace\",\n fontSize: 14,\n },\n codeBlock: {\n backgroundColor: \"#f0f0f0\",\n borderRadius: 8,\n padding: 12,\n marginVertical: 4,\n },\n codeBlockText: {\n fontFamily: \"monospace\",\n fontSize: 14,\n color: \"#1a1a1a\",\n },\n blockquote: {\n backgroundColor: \"#f5f5f5\",\n borderLeftWidth: 4,\n borderLeftColor: \"#cccccc\",\n paddingLeft: 12,\n marginVertical: 4,\n },\n list: { marginTop: 4, marginBottom: 4 },\n listItem: { flexDirection: \"row\", alignItems: \"flex-start\" } as any,\n listBullet: { marginRight: 6, fontSize: 16, lineHeight: 24 },\n link: { color: \"#0066cc\", textDecorationLine: \"underline\" },\n image: {},\n tableRow: {\n flexDirection: \"row\",\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n } as any,\n tableCell: { flex: 1, padding: 4 },\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\n/**\n * Props for the React Native `StreamingMarkdownRenderer`.\n *\n * @public\n */\nexport interface StreamingMarkdownRendererProps {\n /** Full markdown source (may grow over time during streaming). */\n content: string;\n /** When true, finalizes the parser state after processing content. */\n isComplete?: boolean;\n /** Style overrides merged with `defaultMarkdownStyles`. */\n style?: MarkdownStyle;\n /**\n * When true, enables `Intl.Segmenter`-based per-token animation.\n * Defaults to `false` — safe on Hermes which may not ship Segmenter.\n */\n animate?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ntype RenderContext = {\n nodeById: Map<number, StreamingMarkdownAstNode>;\n s: MarkdownStyle;\n};\n\nfunction renderChildren(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderNode(child, ctx)}</Fragment>;\n });\n}\n\nfunction renderTextContent(\n node: Extract<StreamingMarkdownAstNode, { children: number[] }>,\n ctx: RenderContext,\n): ReactNode {\n return node.children.map((id) => {\n const child = ctx.nodeById.get(id);\n if (!child) return null;\n return <Fragment key={id}>{renderInlineNode(child, ctx)}</Fragment>;\n });\n}\n\n/**\n * Render an inline node (text, strong, em, etc.) as a React Native `<Text>`.\n * Returns `null` for block-level nodes that can't be nested in `<Text>`.\n */\nfunction renderInlineNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"text\":\n return node.text;\n\n case \"strong\":\n return (\n <Text key={node.id} style={ctx.s.strong as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"em\":\n return (\n <Text key={node.id} style={ctx.s.em as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"strikethrough\":\n return (\n <Text key={node.id} style={ctx.s.strikethrough as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"inline-code\":\n return (\n <Text key={node.id} style={ctx.s.inlineCode as any}>\n {node.text}\n </Text>\n );\n\n case \"link\":\n case \"autolink\":\n // Non-navigable: render as styled Text only — zero URL/XSS surface.\n return (\n <Text key={node.id} style={ctx.s.link as any}>\n {\"children\" in node\n ? renderTextContent(node as any, ctx)\n : (node as any).text}\n </Text>\n );\n\n case \"image\":\n // Images can't be embedded in <Text>; render alt text.\n return (\n <Text key={node.id} style={ctx.s.image as any}>\n {(node as any).alt}\n </Text>\n );\n\n case \"soft-break\":\n return \"\\n\";\n\n case \"hard-break\":\n return \"\\n\";\n\n case \"citation\":\n // Citation marker (e.g. \"[1]\"). This is an inline-level node reached via\n // a paragraph/heading's renderTextContent; without this case it would\n // fall through to `default` and render nothing (no text/children).\n // Prefer the resolved number, fall back to the citation id.\n return (\n <Text\n key={node.id}\n style={{ fontSize: 12, verticalAlign: \"top\" } as any}\n >\n [{(node as any).number ?? (node as any).idRef}]\n </Text>\n );\n\n case \"document\":\n case \"paragraph\":\n case \"heading\":\n case \"blockquote\":\n case \"list\":\n case \"list-item\":\n case \"code-block\":\n case \"table\":\n case \"table-row\":\n case \"table-cell\":\n case \"thematic-break\":\n case \"math\":\n default:\n // Fallback for any inline nodes not explicitly handled. Math has no\n // React Native renderer, so it shows its source text.\n if (\"text\" in node && typeof (node as any).text === \"string\") {\n return (node as any).text;\n }\n if (\"children\" in node) {\n return renderTextContent(node as any, ctx);\n }\n return null;\n }\n}\n\nfunction renderNode(\n node: StreamingMarkdownAstNode,\n ctx: RenderContext,\n): ReactNode {\n switch (node.type) {\n case \"document\":\n return <Fragment key={node.id}>{renderChildren(node, ctx)}</Fragment>;\n\n case \"paragraph\":\n return (\n <Text key={node.id} style={ctx.s.paragraph as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n\n case \"heading\": {\n const level = node.level as 1 | 2 | 3 | 4 | 5 | 6;\n const headingStyle =\n ctx.s[`h${level}` as keyof MarkdownStyle] ?? ctx.s.h6;\n return (\n <Text key={node.id} style={headingStyle as any}>\n {renderTextContent(node, ctx)}\n </Text>\n );\n }\n\n case \"blockquote\":\n return (\n <View key={node.id} style={ctx.s.blockquote as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"list\":\n return (\n <View key={node.id} style={ctx.s.list as any}>\n {node.children.map((itemId, index) => {\n const item = ctx.nodeById.get(itemId);\n if (!item) return null;\n const bullet = node.ordered\n ? `${(node.start ?? 1) + index}. `\n : \"• \";\n return (\n <View key={itemId} style={ctx.s.listItem as any}>\n <Text style={ctx.s.listBullet as any}>{bullet}</Text>\n <View style={{ flex: 1 }}>\n {renderChildren(\n item as Extract<\n StreamingMarkdownAstNode,\n { children: number[] }\n >,\n ctx,\n )}\n </View>\n </View>\n );\n })}\n </View>\n );\n\n case \"list-item\":\n // Handled inside \"list\" above; fallback in case a list-item appears standalone\n return (\n <View key={node.id} style={ctx.s.listItem as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"code-block\":\n return (\n <View key={node.id} style={ctx.s.codeBlock as any}>\n <Text style={ctx.s.codeBlockText as any}>{node.text}</Text>\n </View>\n );\n\n case \"table\":\n return <View key={node.id}>{renderChildren(node, ctx)}</View>;\n\n case \"table-row\":\n return (\n <View key={node.id} style={ctx.s.tableRow as any}>\n {renderChildren(node, ctx)}\n </View>\n );\n\n case \"table-cell\":\n return (\n <View key={node.id} style={ctx.s.tableCell as any}>\n <Text>{renderTextContent(node, ctx)}</Text>\n </View>\n );\n\n case \"thematic-break\":\n return (\n <View\n key={node.id}\n style={{\n borderBottomWidth: 1,\n borderBottomColor: \"#e0e0e0\",\n marginVertical: 8,\n }}\n />\n );\n\n case \"text\":\n return <Text key={node.id}>{node.text}</Text>;\n\n case \"strong\":\n case \"em\":\n case \"strikethrough\":\n case \"inline-code\":\n case \"link\":\n case \"autolink\":\n case \"image\":\n case \"soft-break\":\n case \"hard-break\":\n case \"citation\":\n // These are inline; wrap in Text for block-level context. Citation\n // rendering lives in renderInlineNode so block and inline paths agree.\n return <Text key={node.id}>{renderInlineNode(node, ctx)}</Text>;\n\n case \"math\":\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nfunction buildParserState(\n content: string,\n isComplete: boolean,\n animate: boolean,\n): StreamingMarkdownParserState {\n const state = createStreamingMarkdownParserState({\n segmenter: animate,\n enableTables: true,\n enableAutolinks: true,\n });\n const parsed = parseStreamingMarkdownChunk(state, content);\n return isComplete ? finalizeStreamingMarkdown(parsed) : parsed;\n}\n\n/**\n * React Native streaming markdown renderer powered by the zero-dependency\n * `@copilotkit/markdown` parser.\n *\n * Defaults `segmenter: false` — safe on Hermes which may not ship `Intl.Segmenter`.\n * Pass `animate={true}` only when you have confirmed Segmenter availability.\n *\n * @public\n */\nexport function StreamingMarkdownRenderer({\n content,\n isComplete = false,\n style,\n animate = false,\n}: StreamingMarkdownRendererProps): React.ReactElement | null {\n const mergedStyles = useMemo<MarkdownStyle>(\n () =>\n style ? { ...defaultMarkdownStyles, ...style } : defaultMarkdownStyles,\n [style],\n );\n\n const parserState = useMemo(\n () => buildParserState(content ?? \"\", isComplete, animate),\n [content, isComplete, animate],\n );\n\n const nodeById = useMemo(() => {\n const map = new Map<number, StreamingMarkdownAstNode>();\n for (const node of parserState.nodes) {\n map.set(node.id, node);\n }\n return map;\n }, [parserState.nodes]);\n\n const ctx: RenderContext = useMemo(\n () => ({ nodeById, s: mergedStyles }),\n [nodeById, mergedStyles],\n );\n\n if (parserState.rootId == null) {\n return null;\n }\n\n const rootNode = nodeById.get(parserState.rootId);\n if (!rootNode) {\n return null;\n }\n\n return <>{renderNode(rootNode, ctx)}</>;\n}\n"],"mappings":"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"}
|