import React, { useState } from 'react'; import { copyToClipboard } from '../utils/clipboard'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Message, Role } from '../types'; import { Bot, User, AlertCircle, Copy, Check, Search, ChevronDown, ChevronRight, Sparkles } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { useLanguage } from '../contexts/LanguageContext'; import { ChatSource } from '../types'; interface ChatMessageProps { message: Message; onPreviewSource?: (source: ChatSource) => void; onOpenFile?: (source: ChatSource) => void; } const ChatMessage: React.FC = ({ message, onPreviewSource, onOpenFile }) => { const { t } = useLanguage(); const isUser = message.role === Role.USER; const [copied, setCopied] = useState(false); const [sourcesExpanded, setSourcesExpanded] = useState(false); const handleCopy = async () => { const success = await copyToClipboard(message.text); if (success) { setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const renderContent = (content: string) => { return (
                      {String(children).replace(/\n$/, '')}
                    
M
Mermaid diagram rendering
); } if (!inline && match) { return (
{language} Code Block
                      
                        {children}
                      
                    
); } return ( {children} ); }, h1: ({ children }) => (

{children}

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

{children}

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

{children}

), ul: ({ children }) => ( ), ol: ({ children }) => (
    {children}
), li: ({ children }) => (
  • {children}
  • ), p: ({ children }) => (

    {children}

    ), table: ({ children }) => (
    {children}
    ), th: ({ children }) => ( {children} ), td: ({ children }) => ( {children} ), blockquote: ({ children }) => (
    {children}
    ), }} > {content} ); }; return (
    {/* Avatar */}
    {isUser ? : }
    {/* Message Bubble + Sources */}
    {message.isError && (
    {t('errorLabel')}
    )}
    {renderContent(message.text)}
    {/* Copy Button (Always visible, icon only) */}
    {/* Timestamp */} {new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {!isUser && message.sources && message.sources.length > 0 && (
    {sourcesExpanded && ( {message.sources.map((source, index) => (
    onPreviewSource?.(source)} >
    {source.fileId ? ( ) : (
    {source.fileName}
    )}
    {(source.score * 100).toFixed(1)}%
    “{source.content}”
    {t('chunkNumber')} #{source.chunkIndex + 1} {t('sourcePreview')} →
    ))} )}
    )}
    ); }; export default ChatMessage;