import { useState, useRef, useEffect } from 'react' import MessageBubble from './MessageBubble' import ChatInput from './ChatInput' import TypingIndicator from './TypingIndicator' export default function ChatInterface() { const [messages, setMessages] = useState([ { role: 'assistant', content: 'Hello! I can analyze images and answer questions about them. Upload an image and ask me anything!', }, ]) const [input, setInput] = useState('') const [uploadedImage, setUploadedImage] = useState(null) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const messagesEndRef = useRef(null) const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) } useEffect(() => { scrollToBottom() }, [messages, isLoading]) const handleImageUpload = (file) => { if (!file.type.startsWith('image/')) { setError('Please upload a valid image file') return } if (file.size > 10 * 1024 * 1024) { setError('Image size must be less than 10MB') return } const reader = new FileReader() reader.onload = (e) => { setUploadedImage({ file, preview: e.target.result, base64: e.target.result.split(',')[1], }) setError(null) } reader.readAsDataURL(file) } const handleSubmit = async () => { if (!input.trim() && !uploadedImage) { setError('Please enter a message or upload an image') return } setError(null) setIsLoading(true) const userMessage = { role: 'user', content: [], } if (input.trim()) { userMessage.content.push({ type: 'text', text: input, }) } if (uploadedImage) { userMessage.content.push({ type: 'image_url', image_url: { url: `data:image/jpeg;base64,${uploadedImage.base64}`, }, }) } setMessages(prev => [...prev, userMessage]) setInput('') setUploadedImage(null) try { const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ messages: [...messages, userMessage], }), }) if (!response.ok) { throw new Error('Failed to get response from the AI') } const reader = response.body.getReader() const decoder = new TextDecoder() let accumulatedContent = '' setMessages(prev => [...prev, { role: 'assistant', content: '' }]) while (true) { const { done, value } = await reader.read() if (done) break const chunk = decoder.decode(value, { stream: true }) accumulatedContent += chunk setMessages(prev => { const newMessages = [...prev] const lastMessage = newMessages[newMessages.length - 1] if (lastMessage.role === 'assistant') { lastMessage.content = accumulatedContent } return newMessages }) } } catch (err) { console.error('Chat error:', err) setError(err.message || 'An error occurred. Please try again.') } finally { setIsLoading(false) } } const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSubmit() } } return (
{messages.map((message, index) => ( ))} {isLoading && }
{error && (
{error}
)}
) }