/** * DevLoopPanel - Control panel for closed-loop development * * Features: * - Start/stop development cycles * - Real-time phase monitoring * - Iteration history view * - Test result visualization */ import { useState, useEffect, useCallback } from 'react'; import { PlayCircle, StopCircle, RefreshCw, CheckCircle, XCircle, AlertCircle, Clock, ListChecks, Zap, ChevronDown, ChevronRight, } from 'lucide-react'; import useWebSocket from 'react-use-websocket'; interface LoopState { phase: string; iteration: number; current_task: string | null; last_update: string; } interface CycleResult { objective: string; status: string; iterations: number; duration_seconds: number; } interface TestResult { scenario_id: string; scenario_name: string; passed: boolean; duration_ms: number; error?: string; } const PHASE_COLORS: Record = { idle: 'bg-gray-500', planning: 'bg-blue-500', implementing: 'bg-purple-500', testing: 'bg-yellow-500', analyzing: 'bg-orange-500', fixing: 'bg-red-500', verifying: 'bg-green-500', }; const PHASE_ICONS: Record = { idle: , planning: , implementing: , testing: , analyzing: , fixing: , verifying: , }; export function DevLoopPanel() { const [state, setState] = useState({ phase: 'idle', iteration: 0, current_task: null, last_update: new Date().toISOString(), }); const [objective, setObjective] = useState(''); const [history, setHistory] = useState([]); const [testResults, setTestResults] = useState([]); const [expanded, setExpanded] = useState(true); const [isStarting, setIsStarting] = useState(false); // WebSocket connection for real-time updates const { lastJsonMessage, readyState } = useWebSocket( 'ws://localhost:8000/api/devloop/ws', { shouldReconnect: () => true, reconnectInterval: 3000, } ); // Handle WebSocket messages useEffect(() => { if (!lastJsonMessage) return; const msg = lastJsonMessage as any; switch (msg.type) { case 'connection_ack': case 'state_update': case 'state': if (msg.state) { setState(msg.state); } break; case 'cycle_complete': setHistory(prev => [msg.result, ...prev].slice(0, 10)); setIsStarting(false); break; case 'cycle_error': console.error('DevLoop error:', msg.error); setIsStarting(false); break; case 'test_progress': if (msg.result) { setTestResults(prev => [...prev, msg.result]); } break; } }, [lastJsonMessage]); // Start a development cycle const startCycle = useCallback(async () => { if (!objective.trim()) return; setIsStarting(true); setTestResults([]); try { const response = await fetch('http://localhost:8000/api/devloop/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ objective: objective.trim(), max_iterations: 10, }), }); if (!response.ok) { const error = await response.json(); console.error('Failed to start cycle:', error); setIsStarting(false); } } catch (error) { console.error('Failed to start cycle:', error); setIsStarting(false); } }, [objective]); // Stop the current cycle const stopCycle = useCallback(async () => { try { await fetch('http://localhost:8000/api/devloop/stop', { method: 'POST', }); } catch (error) { console.error('Failed to stop cycle:', error); } }, []); // Quick start: Create support_arm study const quickStartSupportArm = useCallback(() => { setObjective('Create support_arm optimization study with 5 design variables (center_space, arm_thk, arm_angle, end_thk, base_thk), objectives (minimize displacement, minimize mass), and stress constraint (< 30% yield)'); // Auto-start after a brief delay setTimeout(() => { startCycle(); }, 500); }, [startCycle]); const isActive = state.phase !== 'idle'; const wsConnected = readyState === WebSocket.OPEN; return (
{/* Header */}
setExpanded(!expanded)} >
{expanded ? ( ) : ( )}

DevLoop Control

{/* Status indicator */}
{state.phase.toUpperCase()}
{expanded && (
{/* Objective Input */}