import { useState, useEffect, useRef } from 'react'; const MODEL_COLORS: Record = { claude: '#ff8c32', codex: '#10b981', gemini: '#3b82f6' }; const MODEL_LABELS: Record = { claude: 'Claude', codex: 'Codex', gemini: 'Gemini' }; interface ChatMessage { role: 'user' | 'assistant'; model: string; content: string; timestamp: string; } interface ToolCall { tool: string; target: string; model: string; timestamp: string; status: string; } interface AppState { activeModel: string; models: Record; chatMessages: Record; toolTimeline: ToolCall[]; } export default function App() { const [state, setState] = useState(null); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const [debugInfo, setDebugInfo] = useState | null>(null); const messagesEndRef = useRef(null); const refreshState = async () => { setState(await window.api.getState() as AppState); }; useEffect(() => { refreshState(); }, []); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [state?.chatMessages]); const handleSend = async () => { if (!input.trim() || isLoading) return; setInput(''); setIsLoading(true); await window.api.sendMessage(input.trim()); await refreshState(); setIsLoading(false); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }; const handleModelChange = async (model: string) => { await window.api.setModel(model); await refreshState(); }; const handleDebug = async () => { setDebugInfo(await window.api.debugInfo() as Record); }; if (!state) return
Loading...
; const messages = state.chatMessages[state.activeModel] || []; const color = MODEL_COLORS[state.activeModel] || '#888'; return (
{/* Left: Chat */}
{state.models[state.activeModel]?.connected ? '● connected' : '○ disconnected'}
{messages.length === 0 &&
Start a conversation...
} {messages.map((msg, i) => (
{msg.role === 'user' ? 'You' : MODEL_LABELS[msg.model]} {new Date(msg.timestamp).toLocaleTimeString()}
{msg.content}
))}