import React, { useState, useRef, useEffect } from 'react'; import { randomUUID } from 'crypto'; interface Message { role: 'user' | 'assistant' | 'system'; content: string; timestamp: string; } export default function HubertChat() { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [visitorId, setVisitorId] = useState(null); const [conversationId, setConversationId] = useState(null); const [isTyping, setIsTyping] = useState(false); const [isInitializing, setIsInitializing] = useState(true); const messagesEndRef = useRef(null); // Initialize visitor on mount useEffect(() => { const initVisitor = async () => { try { setIsInitializing(true); const response = await fetch('/api/hubert/new-visitor', { method: 'POST' }); const data = await response.json(); setVisitorId(data.visitor_id); setConversationId(data.conversation_id); // Add system welcome message from Hubert setMessages([{ role: 'system', content: `/// HUBERT_EUNUCH /// ONLINE\\n\\nI suppose you want something. State your business.`, timestamp: new Date().toISOString(), }]); } catch (error) { console.error('Failed to initialize Hubert:', error); setMessages([{ role: 'system', content: '/// ERROR: HUBERT_OFFLINE - REFRESH_PAGE', timestamp: new Date().toISOString(), }]); } finally { setIsInitializing(false); } }; initVisitor(); }, []); // Auto-scroll to bottom useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const sendMessage = async () => { if (!input.trim() || isTyping || !visitorId || !conversationId) return; const userMessage: Message = { role: 'user', content: input, timestamp: new Date().toISOString(), }; setMessages(prev => [...prev, userMessage]); setInput(''); setIsTyping(true); try { const response = await fetch('/api/hubert/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: [...messages, userMessage].map(m => ({ role: m.role, content: m.content, })), conversation_id: conversationId, visitor_id: visitorId, }), }); const data = await response.json(); if (data.error) { throw new Error(data.error); } const assistantMessage: Message = { role: 'assistant', content: data.messages[data.messages.length - 1]?.content || '...', timestamp: new Date().toISOString(), }; setMessages(prev => [...prev, assistantMessage]); } catch (error) { console.error('Hubert chat error:', error); setMessages(prev => [...prev, { role: 'assistant', content: '/// HUBERT_MALFUNCTION - TRY AGAIN', timestamp: new Date().toISOString(), }]); } finally { setIsTyping(false); } }; if (isInitializing) { return (
HUBERT_IS_BOOTING...
); } return (
{/* Header */}
/// HUBERT_EUNUCH /// ONLINE
{visitorId ? `VISITOR: ${visitorId.slice(0, 8)}` : 'UNKNOWN'}
{/* Messages */}
{messages.map((msg, idx) => (
{msg.role === 'user' ? 'YOU' : 'HUBERT'}

{msg.content}

{new Date(msg.timestamp).toLocaleString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, })}
))} {/* Typing indicator */} {isTyping && (
HUBERT_IS_PONDERING...
)}
{/* Input */}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && sendMessage()} placeholder="/// HUBERT_AWAITS_INPUT..." className="w-full bg-transparent border-b-2 border-[var(--theme-border-primary)] py-3 text-lg font-mono text-[var(--theme-text-primary)] placeholder:text-[var(--theme-text-subtle)] focus:border-brand-accent focus:outline-none transition-colors" />
); }