/** * ChatMessage Component * Renders an individual chat message with markdown support */ import React, { useState } from 'react'; import { Copy, Check, Bot, User } from 'lucide-react'; import type { ChatMessage as ChatMessageType } from '../../types/chat.types'; interface ChatMessageProps { message: ChatMessageType; } export const ChatMessage: React.FC = ({ message }) => { const [copied, setCopied] = useState(false); const isUser = message.role === 'user'; const isAssistant = message.role === 'assistant'; const handleCopy = async () => { try { await navigator.clipboard.writeText(message.content); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error('Failed to copy:', err); } }; const formatTimestamp = (timestamp: string) => { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); if (minutes < 1) return 'Just now'; if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; return date.toLocaleDateString(); }; // Simple markdown parser for basic formatting const renderContent = (content: string) => { // Split by code blocks first const parts = content.split(/(```[\s\S]*?```|`[^`]+`)/g); return parts.map((part, index) => { // Code block if (part.startsWith('```') && part.endsWith('```')) { const code = part.slice(3, -3).trim(); const lines = code.split('\n'); const language = lines[0].match(/^\w+$/) ? lines.shift() : ''; return (
            {language && (
              
{language}
)} {lines.join('\n')}
); } // Inline code if (part.startsWith('`') && part.endsWith('`')) { return ( {part.slice(1, -1)} ); } // Regular text with basic formatting let formatted = part; // Bold: **text** or __text__ formatted = formatted.replace( /(\*\*|__)(.*?)\1/g, '$2' ); // Italic: *text* or _text_ formatted = formatted.replace( /([*_])(.*?)\1/g, '$2' ); // Bullet lists formatted = formatted.replace( /^[•\-*]\s+(.+)$/gm, '
  • $1
  • ' ); // Numbered lists formatted = formatted.replace( /^\d+\.\s+(.+)$/gm, '
  • $1
  • ' ); return ( ); }); }; return (
    {/* Avatar */} {!isUser && (
    )} {/* Message Content */}
    {/* Message text */}
    {renderContent(message.content)}
    {/* Tools used badge */} {isAssistant && message.toolsUsed && message.toolsUsed.length > 0 && (
    Tools: {message.toolsUsed.join(', ')}
    )}
    {/* Timestamp and actions */}
    {formatTimestamp(message.timestamp)} {/* Copy button */}
    {/* User Avatar */} {isUser && (
    )}
    ); }; export default ChatMessage;