import { renderHtml } from '@tanstack/markdown/html' import { streamingMarkdownExtension } from '@tanstack/markdown/extensions/streaming' import type { CodeHighlighter, MarkdownExtension } from '@tanstack/markdown' const DEFAULT_EXTENSIONS: Array = [ streamingMarkdownExtension(), ] export interface TextPartProps { /** The text content to render */ content: string /** The role of the message (user, assistant, or system) - optional for standalone use */ role?: 'user' | 'assistant' | 'system' /** Base class applied to all text parts */ class?: string /** Additional class for user messages */ userClass?: string /** Additional class for assistant messages (also used for system messages) */ assistantClass?: string /** * Additional TanStack Markdown extensions, appended after the built-in * streaming extension. */ extensions?: Array /** * Synchronous code highlighter. Its output is inserted as trusted HTML, * so use only a highlighter that escapes source text (for example * `createTanStackMarkdownHighlighter` from `@tanstack/highlight/markdown`). */ highlighter?: CodeHighlighter } /** * TextPart component - renders markdown text with TanStack Markdown. * * Raw HTML is escaped and executable URLs are removed, so AI output can be * rendered without a separate sanitizer. * * @example Syntax highlighting * ```tsx * import { highlightMarkdownCode } from './markdown-highlighter' * * * ``` */ export function TextPart(props: TextPartProps) { const roleClass = () => props.role === 'user' ? (props.userClass ?? '') : props.role === 'assistant' ? (props.assistantClass ?? '') : '' const combinedClass = () => [props.class ?? '', roleClass()].filter(Boolean).join(' ') // TanStack Markdown escapes this HTML string. const html = () => renderHtml(props.content, { extensions: props.extensions ? [...DEFAULT_EXTENSIONS, ...props.extensions] : DEFAULT_EXTENSIONS, frontmatter: false, headingIds: false, highlighter: props.highlighter, }) return
}