/** * Chat Message Component * Renders user / assistant / system / toolresult messages * with markdown, thinking sections, images, and tool cards. */ import { useState, useCallback, memo } from 'react'; import { User, Sparkles, Copy, Check, ChevronDown, ChevronRight, Wrench } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import type { RawMessage } from '@/stores/chat'; import { extractText, extractThinking, extractImages, extractToolUse, formatTimestamp } from './message-utils'; interface ChatMessageProps { message: RawMessage; showThinking: boolean; isStreaming?: boolean; } export const ChatMessage = memo(function ChatMessage({ message, showThinking, isStreaming = false, }: ChatMessageProps) { const isUser = message.role === 'user'; const isToolResult = message.role === 'toolresult'; const text = extractText(message); const thinking = extractThinking(message); const images = extractImages(message); const tools = extractToolUse(message); // Don't render empty tool results when thinking is hidden if (isToolResult && !showThinking) return null; // Don't render empty messages if (!text && !thinking && images.length === 0 && tools.length === 0) return null; return (
{text}
) : (
{children}
);
},
a({ href, children }) {
return (
{children}
);
},
}}
>
{text}
{typeof input === 'string' ? input : JSON.stringify(input, null, 2) as string}
)}