import type { Message as MessageType } from '../../types'; import { ContentRenderer } from './ContentRenderer'; import { formatDate, copyToClipboard } from '../../utils'; import { Button } from '../ui'; import { Copy, Check, Trash2, User, Bot, MoreHorizontal, Share, Edit } from 'lucide-react'; import { useState, useRef, useEffect } from 'react'; import { useChatStore } from '../../store/chatStore'; import toast from 'react-hot-toast'; interface MessageProps { message: MessageType; showActions?: boolean; isLast?: boolean; } export function Message({ message, showActions = true, isLast = false }: MessageProps) { const [copied, setCopied] = useState(false); const [showMenu, setShowMenu] = useState(false); const [isEditing, setIsEditing] = useState(false); const [editContent, setEditContent] = useState(message.content); const menuRef = useRef(null); const { deleteMessage, updateMessage } = useChatStore(); const handleCopy = async () => { try { await copyToClipboard(message.content); setCopied(true); setTimeout(() => setCopied(false), 2000); toast.success('Message copied to clipboard'); setShowMenu(false); } catch (error) { toast.error('Failed to copy message'); } }; const handleDelete = () => { if (window.confirm('Are you sure you want to delete this message?')) { deleteMessage(message.conversationId, message.id); toast.success('Message deleted'); } setShowMenu(false); }; const handleEdit = () => { setIsEditing(true); setEditContent(message.content); setShowMenu(false); }; const handleSaveEdit = () => { if (editContent.trim() !== message.content) { updateMessage(message.conversationId, message.id, { content: editContent.trim() }); toast.success('Message updated'); } setIsEditing(false); }; const handleCancelEdit = () => { setIsEditing(false); setEditContent(message.content); }; const handleShare = async () => { if (navigator.share) { try { await navigator.share({ text: message.content, title: 'Shared from ChatSaaS', }); } catch (error) { // User cancelled sharing } } else { // Fallback to copy await handleCopy(); } setShowMenu(false); }; // Close menu when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { setShowMenu(false); } }; if (showMenu) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [showMenu]); const isUser = message.role === 'user'; const isAssistant = message.role === 'assistant'; const isSystem = message.role === 'system'; return (
{/* Avatar */}
{isUser ? ( ) : isAssistant ? ( ) : ( S )}
{/* Message Content */}
{/* Message Bubble */}
{isEditing && isUser ? (