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 } from 'lucide-react'; 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$/, '')}
                    
💡 Mermaid diagram (rendering requires mermaid.js)
); } // Code block with language if (!inline && match) { return (
{language}
                      
                        {children}
                      
                    
); } // Inline code return ( {children} ); }, // Style headings h2: ({ children }) => (

{children}

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

{children}

), // Style lists ul: ({ children }) => ( ), ol: ({ children }) => (
    {children}
), // Style paragraphs p: ({ children }) => (

{children}

), // Style tables table: ({ children }) => (
{children}
), th: ({ children }) => ( {children} ), td: ({ 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' })} {/* Sources (Collapsible) */} {!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;