'use client'; import { useState, useRef, useEffect } from 'react'; import { X, Bot, Sparkles, Send, Shield, BookOpen, Heart, Brain, ChevronDown, Eye, EyeOff } from 'lucide-react'; import ThinkingIndicator from './chat/ThinkingIndicator'; import MarkdownMessage from './chat/MarkdownMessage'; interface Message { role: 'user' | 'assistant'; content: string; agent?: string; trace?: TraceStep[]; timestamp?: Date; } interface TraceStep { agent: string; action: string; result?: string; } const agentIcons: Record = { Gatekeeper: Shield, Theologian: BookOpen, Healer: Heart, Orchestrator: Brain, System: Sparkles, }; const agentColors: Record = { Gatekeeper: { bg: 'bg-purple-500/20', border: 'border-purple-500/30', text: 'text-purple-400' }, Theologian: { bg: 'bg-blue-500/20', border: 'border-blue-500/30', text: 'text-blue-400' }, Healer: { bg: 'bg-rose-500/20', border: 'border-rose-500/30', text: 'text-rose-400' }, Orchestrator: { bg: 'bg-amber-500/20', border: 'border-amber-500/30', text: 'text-amber-400' }, System: { bg: 'bg-neutral-500/20', border: 'border-neutral-500/30', text: 'text-neutral-400' }, }; export default function ChatWidget() { const [chatOpen, setChatOpen] = useState(false); const [messages, setMessages] = useState([ { role: 'assistant', content: "Hello, I'm **ORA**, your AI spiritual companion. I'm here to help you explore Scripture, reflect on your faith, and grow in wisdom. How may I serve you today?", agent: 'Gatekeeper', timestamp: new Date(), } ]); const [input, setInput] = useState(''); const [isTyping, setIsTyping] = useState(false); const [activeAgent, setActiveAgent] = useState('Gatekeeper'); const [showTrace, setShowTrace] = useState>({}); const messagesEndRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(() => { scrollToBottom(); }, [messages, isTyping]); const handleSend = async () => { if (!input.trim()) return; const userMsg: Message = { role: 'user', content: input, timestamp: new Date(), }; setMessages(prev => [...prev, userMsg]); setInput(''); setIsTyping(true); setActiveAgent('Gatekeeper'); // Start with Gatekeeper try { const response = await fetch('http://localhost:6000/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: input }) }); const data = await response.json(); if (data.success) { // Simulate agent handoff animation if (data.response.agent !== 'Gatekeeper') { setActiveAgent(data.response.agent); await new Promise(resolve => setTimeout(resolve, 500)); } const assistantMsg: Message = { role: 'assistant', content: data.response.content, agent: data.response.agent, trace: data.response.trace || [], timestamp: new Date(), }; setMessages(prev => [...prev, assistantMsg]); } } catch (error) { console.error('Chat failed:', error); setMessages(prev => [...prev, { role: 'assistant', content: "I apologize, but I'm having trouble connecting to my reasoning core. Please ensure the ORA backend is running on port 6000.", agent: 'System', timestamp: new Date(), }]); } finally { setIsTyping(false); } }; const toggleTrace = (index: number) => { setShowTrace(prev => ({ ...prev, [index]: !prev[index] })); }; const AgentIcon = ({ agent }: { agent: string }) => { const Icon = agentIcons[agent] || Sparkles; const colors = agentColors[agent] || agentColors.System; return (
); }; return (
{/* Chat Button */} {/* Chat Box */}
{/* Header */}
ORA Sovereign AI Companion
{/* Messages */}
{messages.map((m, i) => (
{/* Avatar */}
{m.role === 'user' ? (
) : ( )}
{/* Message Content */}
{/* Agent Badge */} {m.agent && m.role === 'assistant' && (
{m.agent} {m.trace && m.trace.length > 0 && ( )}
)} {/* Trace Steps */} {showTrace[i] && m.trace && m.trace.length > 0 && (
Reasoning Trace
{m.trace.map((step, j) => (
{j + 1}
{step.agent}: {step.action} {step.result && (
{step.result}
)}
))}
)} {/* Message Bubble */}
{/* Timestamp */} {m.timestamp && ( {m.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} )}
))} {/* Thinking Indicator */} {isTyping && ( )}
{/* Quick Actions */}
{['Pray with me', 'Explain a verse', 'Daily reflection'].map((action) => ( ))}
{/* Input */}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && !e.shiftKey && handleSend()} placeholder="Ask ORA anything..." className="bg-transparent border-none focus:outline-none text-sm text-white w-full placeholder-neutral-500" disabled={isTyping} />
); }