import { Component, Suspense, lazy, memo, useEffect, type ReactNode, } from "react"; import { cn } from "@/lib/utils"; interface MarkdownTextProps { children: string; className?: string; streaming?: boolean; preserveStreamingLayout?: boolean; onOpenFilePreview?: (path: string) => void; } const loadMarkdownRenderer = () => import("@/components/MarkdownTextRenderer"); const LazyMarkdownRenderer = lazy(loadMarkdownRenderer); const MemoizedMarkdownRenderer = memo(function MemoizedMarkdownRenderer({ source, className, highlightCode, streaming, onOpenFilePreview, }: { source: string; className?: string; highlightCode: boolean; streaming: boolean; onOpenFilePreview?: (path: string) => void; }) { return ( {source} ); }); class MarkdownRendererBoundary extends Component< { children: ReactNode; fallback: ReactNode; resetKey: string }, { failed: boolean } > { state = { failed: false }; static getDerivedStateFromError() { return { failed: true }; } componentDidUpdate(previous: Readonly<{ resetKey: string }>) { if (this.state.failed && previous.resetKey !== this.props.resetKey) { this.setState({ failed: false }); } } render() { return this.state.failed ? this.props.fallback : this.props.children; } } export function preloadMarkdownText(): Promise { return loadMarkdownRenderer().then(() => undefined); } /** Lazy boundary for the heavier GFM, math, and code renderer. */ export function MarkdownText({ children, className, streaming = false, preserveStreamingLayout = false, onOpenFilePreview, }: MarkdownTextProps) { const renderedSource = children; const renderPhase = streaming ? "streaming" : "complete"; const highlightCode = !streaming; const renderWithStreamingLayout = streaming || preserveStreamingLayout; useEffect(() => { if (streaming) void preloadMarkdownText(); }, [streaming]); const plainFallback = (
{renderedSource}
); return ( ); }