import React, { useState, useRef, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { PaperAirplaneIcon, StopIcon } from '@heroicons/react/24/solid'; import MessageBubble from './MessageBubble'; import TypingIndicator from './TypingIndicator'; import FileUploader from './FileUploader'; import { sendMessage, sendMessageStream } from '../services/api'; import toast from 'react-hot-toast'; const ChatInterface = ({ conversationId, conversations, setConversations, darkMode }) => { const [message, setMessage] = useState(''); const [isLoading, setIsLoading] = useState(false); const [showFileUploader, setShowFileUploader] = useState(false); const messagesEndRef = useRef(null); const textareaRef = useRef(null); const currentConversation = conversations.find(conv => conv.id === conversationId); const messages = currentConversation?.messages || []; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); const handleSubmit = async (e) => { e.preventDefault(); if (!message.trim() || isLoading) return; const userMessage = { id: Date.now().toString(), role: 'user', content: message.trim(), timestamp: new Date(), }; // Add user message immediately setConversations(prev => prev.map(conv => conv.id === conversationId ? { ...conv, messages: [...conv.messages, userMessage], title: conv.messages.length === 0 ? message.slice(0, 50) + '...' : conv.title } : conv )); setMessage(''); setIsLoading(true); const assistantMessageId = (Date.now() + 1).toString(); try { let fullResponse = ''; // Add a placeholder for the assistant's message setConversations(prev => prev.map(conv => conv.id === conversationId ? { ...conv, messages: [...conv.messages, { id: assistantMessageId, role: 'assistant', content: '', timestamp: new Date() }] } : conv )); await sendMessageStream(message.trim(), (chunk) => { fullResponse += chunk; setConversations(prev => prev.map(conv => conv.id === conversationId ? { ...conv, messages: conv.messages.map(msg => msg.id === assistantMessageId ? { ...msg, content: fullResponse } : msg ), } : conv )); }); } catch (error) { toast.error('Failed to send message. Please try again.'); console.error('Error sending message:', error); // Optional: remove placeholder on error setConversations(prev => prev.map(conv => conv.id === conversationId ? { ...conv, messages: conv.messages.filter(msg => msg.id !== assistantMessageId) } : conv )); } finally { setIsLoading(false); } }; const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(e); } }; const adjustTextareaHeight = () => { const textarea = textareaRef.current; if (textarea) { textarea.style.height = 'auto'; textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px'; } }; useEffect(() => { adjustTextareaHeight(); }, [message]); return (