import React, { useState, useEffect, useRef } from 'react'; import axios from 'axios'; interface ChatInterfaceProps { onCommand: (cmd: { task: string; type: '3d' | 'pcb' }) => void; } const ChatInterface: React.FC = ({ onCommand: _onCommand }) => { const [messages, setMessages] = useState([ { role: 'assistant', content: 'Welcome to QuLab Infinite. I am ECH0, your R&D Navigator. What are we building today?' } ]); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const endRef = useRef(null); const scrollToBottom = () => { endRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(() => { scrollToBottom(); }, [messages]); const handleSend = async () => { if (!input.trim() || isLoading) return; const newMsg = { role: 'user', content: input }; setMessages(prev => [...prev, newMsg]); setInput(''); setIsLoading(true); try { const res = await axios.post('http://localhost:8000/chat', { message: newMsg.content }); const data = res.data; const assistantMsg = { role: 'assistant', content: data.response }; setMessages(prev => [...prev, assistantMsg]); if (data.action_result) { // If ECH0 performed a simulation, we can log it or show it. // For now, we print it as a system message. const sysMsg = { role: 'system', content: `[SIMULATION OUTPUT]: ${JSON.stringify(data.action_result.products)} produced. ${data.action_result.observations[0]}` }; setMessages(prev => [...prev, sysMsg]); // If it was a PCB/3D task (legacy check), we could still trigger onCommand // But generally ECH0Service handles universal lab now. // We'll leave the prop just in case we want to force UI switches, // but for now relying on the chat response is cleaner. } } catch (error) { console.error("Chat Error", error); setMessages(prev => [...prev, { role: 'assistant', content: "Communication Application Error. Is the backend or Ollama online?" }]); } finally { setIsLoading(false); } }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') handleSend(); }; return (
AI

ECH0 COMMAND

● {isLoading ? 'THINKING...' : 'ONLINE'}
{messages.map((msg, i) => (
{msg.content}
))}
setInput(e.target.value)} onKeyDown={handleKeyDown} placeholder="Type a command (e.g., 'Combine Sodium and Water')..." disabled={isLoading} style={{ flexGrow: 1, background: 'transparent', border: 'none', color: 'white', padding: '0.5rem', outline: 'none', fontFamily: 'var(--font-main)', fontSize: '1rem', opacity: isLoading ? 0.5 : 1 }} />
); }; export default ChatInterface;