diff --git a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx new file mode 100644 index 000000000..9920408ea --- /dev/null +++ b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx @@ -0,0 +1,141 @@ +import type { ErrorInfo, ReactNode } from 'react'; +import { Component } from 'react'; +import ReactMarkdown, { type Components } from 'react-markdown'; +import remarkGfm from 'remark-gfm'; + +export type ChatMarkdownMessageProps = { + text: string; + role: 'assistant' | 'user'; + streaming?: boolean; +}; + +type MarkdownErrorBoundaryProps = { + fallbackText: string; + children: ReactNode; +}; + +type MarkdownErrorBoundaryState = { + hasError: boolean; +}; + +class MarkdownErrorBoundary extends Component< + MarkdownErrorBoundaryProps, + MarkdownErrorBoundaryState +> { + state: MarkdownErrorBoundaryState = { hasError: false }; + + static getDerivedStateFromError(): MarkdownErrorBoundaryState { + return { hasError: true }; + } + + componentDidCatch(_error: unknown, _errorInfo: ErrorInfo) { + // Keep the original message visible without logging its potentially sensitive content. + } + + render() { + if (this.state.hasError) { + return ( + + {this.props.fallbackText} + + ); + } + return this.props.children; + } +} + +const markdownComponents: Components = { + a: ({ children }) => {children}, + img: ({ alt }) => {alt?.trim() ? `图片:${alt}` : '图片已省略'}, + h1: ({ children }) => ( +

{children}

+ ), + h2: ({ children }) => ( +

{children}

+ ), + h3: ({ children }) => ( +

{children}

+ ), + p: ({ children }) =>

{children}

, + ul: ({ children }) => ( + + ), + ol: ({ children }) => ( +
    {children}
+ ), + li: ({ children }) =>
  • {children}
  • , + blockquote: ({ children }) => ( +
    + {children} +
    + ), + pre: ({ children }) => ( +
    +      {children}
    +    
    + ), + code: ({ className, children, ...props }) => { + const isBlock = Boolean(className?.includes('language-')); + return isBlock ? ( + + {children} + + ) : ( + + {children} + + ); + }, + table: ({ children }) => ( +
    + + {children} +
    +
    + ), + th: ({ children }) => ( + + {children} + + ), + td: ({ children }) => ( + + {children} + + ), + hr: () => ( +
    + ), +}; + +export function ChatMarkdownMessage({ + text, + role, + streaming = false, +}: ChatMarkdownMessageProps) { + if (role === 'user') { + return {text}; + } + + return ( + +
    + + {text} + +
    +
    + ); +}