/** * ChatWindow Component * * Main chat interface component that handles: * - Message display * - User input * - Streaming responses from A2UI protocol */ import React, { useEffect, useRef, useState } from "react"; import { A2UIMessage, MessageType, Tool } from "../types/a2ui"; import { streamChat, checkHealth, getTools, clearHistory } from "../utils/api"; import MessageBubble from "./MessageBubble"; import "./ChatWindow.css"; interface ChatMessageDisplay extends A2UIMessage { id: string; timestamp: string; } const ChatWindow: React.FC = () => { const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(""); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [isConnected, setIsConnected] = useState(false); const [tools, setTools] = useState([]); const messagesEndRef = useRef(null); // Check backend health on mount useEffect(() => { const checkConnection = async () => { const healthy = await checkHealth(); setIsConnected(healthy); if (healthy) { const availableTools = await getTools(); setTools(availableTools); } }; checkConnection(); // Recheck every 30 seconds const interval = setInterval(checkConnection, 30000); return () => clearInterval(interval); }, []); // Scroll to bottom when new messages arrive useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const handleSendMessage = async (e: React.FormEvent) => { e.preventDefault(); if (!inputValue.trim()) return; if (!isConnected) { setError("Not connected to backend. Please check your connection."); return; } const userMessage = inputValue.trim(); setInputValue(""); setIsLoading(true); setError(null); try { // Add user message to display setMessages((prev) => [ ...prev, { type: MessageType.USER_MESSAGE, content: userMessage, id: `msg-${Date.now()}`, timestamp: new Date().toISOString(), }, ]); // Stream response from backend await streamChat( userMessage, (message: A2UIMessage) => { setMessages((prev) => [ ...prev, { ...message, id: `msg-${Date.now()}-${Math.random()}`, timestamp: message.timestamp || new Date().toISOString(), }, ]); }, (error: Error) => { setError(`Error: ${error.message}`); } ); } catch (err) { setError(`Error: ${err instanceof Error ? err.message : "Unknown error"}`); } finally { setIsLoading(false); } }; const handleClearHistory = async () => { if (window.confirm("Are you sure you want to clear chat history?")) { const success = await clearHistory(); if (success) { setMessages([]); } else { setError("Failed to clear history"); } } }; return (
{/* Header */}

AI Agent Chat

{isConnected ? "●" : "●"} {isConnected ? "Connected" : "Disconnected"} {tools.length > 0 && ( {tools.length} tools available )}
{/* Messages area */}
{messages.length === 0 && (

Welcome to AI Agent Chat

{isConnected ? "Start a conversation by typing a message below." : "Waiting for backend connection..."}

{tools.length > 0 && (

Available Tools:

    {tools.map((tool) => (
  • {tool.name} - {tool.description}
  • ))}
)}
)} {messages.map((message) => ( ))}
{isLoading && (
Agent is processing...
)}
{/* Error display */} {error && (
⚠️ {error}
)} {/* Input area */}
setInputValue(e.target.value)} placeholder={isConnected ? "Type your message..." : "Waiting for connection..."} disabled={!isConnected || isLoading} className="message-input" />
{/* Action buttons */}
); }; export default ChatWindow;