import React, { useState, useEffect } from 'react'; import { Box, Text, Newline, useInput, useApp } from 'ink'; import Spinner from 'ink-spinner'; import BigText from 'ink-big-text'; import Gradient from 'ink-gradient'; import { SystemMonitor } from '../commands/start/system-monitor.ts'; import { ProcessManager } from '../commands/start/process-manager.ts'; import { getMemoryManager } from '../core/global-initialization.ts'; import AgentMonitor from './components/agent-monitor.tsx'; import TaskManager from './components/task-manager.tsx'; import MemoryBrowser from './components/memory-browser.tsx'; import { getWebSocketClient } from './services/websocket-client.ts'; import { getKeyboardHandler } from './services/keyboard-handler.ts'; import HelpOverlay from './components/help-overlay.tsx'; interface DashboardProps { processManager: ProcessManager; systemMonitor: SystemMonitor; } type ViewMode = 'overview' | 'agents' | 'tasks' | 'memory' | 'logs' | 'config'; export const InkDashboard: React.FC = ({ processManager, systemMonitor }) => { const [currentView, setCurrentView] = useState('overview'); const [systemHealth, setSystemHealth] = useState(null); const [processes, setProcesses] = useState([]); const [memoryStats, setMemoryStats] = useState(null); const [isLoading, setIsLoading] = useState(true); const [wsConnected, setWsConnected] = useState(false); const [liveUpdates, setLiveUpdates] = useState(true); const [helpVisible, setHelpVisible] = useState(false); const { exit } = useApp(); // Initialize WebSocket connection for real-time updates useEffect(() => { const wsClient = getWebSocketClient(); const connectWebSocket = async () => { try { await wsClient.connect(); setWsConnected(true); // Subscribe to real-time updates wsClient.subscribe('system-metrics'); wsClient.subscribe('agent-updates'); wsClient.subscribe('task-updates'); wsClient.subscribe('memory-updates'); // Handle real-time data updates wsClient.on('system-metrics', (data) => { if (liveUpdates) { setSystemHealth(data); } }); wsClient.on('agent-update', (data) => { if (liveUpdates) { setProcesses(prev => { const updated = [...prev]; const index = updated.findIndex(p => p.id === data.id); if (index >= 0) { updated[index] = { ...updated[index], ...data }; } else { updated.push(data); } return updated; }); } }); wsClient.on('memory-update', (data) => { if (liveUpdates) { setMemoryStats(data); } }); wsClient.on('disconnected', () => { setWsConnected(false); }); wsClient.on('reconnecting', () => { setWsConnected(false); }); wsClient.on('connected', () => { setWsConnected(true); }); } catch (error) { // WebSocket connection failed - work in offline mode console.log('WebSocket connection failed, working in offline mode'); setWsConnected(false); // Continue without WebSocket connection } }; // Try to connect but don't fail if it doesn't work connectWebSocket().catch(() => { // Silently handle connection failures setWsConnected(false); }); return () => { // Only disconnect if we have a connection try { wsClient.disconnect(); } catch (error) { // Ignore disconnection errors } }; }, [liveUpdates]); // Initialize keyboard handler useEffect(() => { const keyboardHandler = getKeyboardHandler(); keyboardHandler.pushContext('dashboard'); const handleKeyboardAction = (action: any) => { switch (action.type) { case 'quit': exit(); break; case 'navigate': if (action.payload?.view) { setCurrentView(action.payload.view); } break; case 'back': if (currentView !== 'overview') { setCurrentView('overview'); } else { exit(); } break; case 'refresh': setLiveUpdates(!liveUpdates); break; case 'toggle-help': case 'show-help': setHelpVisible(!helpVisible); break; } }; const handleHelpToggle = (data: any) => { setHelpVisible(data.visible); }; keyboardHandler.on('action', handleKeyboardAction); keyboardHandler.on('help-toggled', handleHelpToggle); return () => { keyboardHandler.off('action', handleKeyboardAction); keyboardHandler.off('help-toggled', handleHelpToggle); keyboardHandler.popContext(); }; }, [exit, currentView, liveUpdates, helpVisible]); // Update system data every 2 seconds (fallback when WebSocket is not available) useEffect(() => { if (!wsConnected || !liveUpdates) { const updateData = async () => { try { const health = systemMonitor.getSystemHealth(); const processData = processManager.getAllProcesses(); const memoryManager = await getMemoryManager(); const memStats = await memoryManager.getHealthStatus(); setSystemHealth(health); setProcesses(processData); setMemoryStats(memStats); setIsLoading(false); } catch (error) { console.error('Error updating dashboard data:', error); } }; updateData(); const interval = setInterval(updateData, 2000); return () => clearInterval(interval); } return undefined; // Return undefined when not setting up interval }, [processManager, systemMonitor, wsConnected, liveUpdates]); // Handle keyboard input using the keyboard handler useInput((input, key) => { // Don't handle input if help is visible (let help overlay handle it) if (helpVisible && (input === 'h' || key.escape)) { setHelpVisible(false); return; } // Only handle input if we're in the main dashboard view const isMainView = ['overview', 'logs', 'config'].includes(currentView); if (isMainView) { const keyboardHandler = getKeyboardHandler(); const handled = keyboardHandler.handleKeyPress(input, key); // If not handled by keyboard handler, use fallback logic if (!handled) { if (key.escape || input === 'q') { exit(); } else if (input === 'h') { setHelpVisible(true); } } } }); if (isLoading) { return ( Loading Claude Flow Dashboard... ); } // Render sub-components with back navigation switch (currentView) { case 'agents': return setCurrentView('overview')} />; case 'tasks': return setCurrentView('overview')} />; case 'memory': return setCurrentView('overview')} />; default: break; } return ( {/* Header */} {/* Navigation */} [1] Overview [2] Agents [3] Tasks [4] Memory [5] Logs [6] Config [q] Quit {/* Main Content */} {/* Left Panel - Current View */} {currentView === 'overview' && } {currentView === 'logs' && } {currentView === 'config' && } {/* Right Panel - System Status */} System Status {wsConnected ? '🟢 Live Updates' : '🔴 Polling Mode'} {systemHealth && ( <> Health: {systemHealth.overall === 'healthy' ? ✓ Healthy : systemHealth.overall === 'warning' ? ⚠ Warning : ✗ Critical } Processes: {systemHealth.processes} running Memory: {systemHealth.memory.percentage.toFixed(1)}% Uptime: {Math.floor(systemHealth.uptime / 1000)}s {systemHealth.alerts.length > 0 && ( <> Alerts: {systemHealth.alerts.map((alert: string, i: number) => ( • {alert} ))} )} )} { /* Footer */} Use number keys to navigate • Press 'q' to quit • Press 'r' to toggle real-time updates {wsConnected ? ' • 🟢 Live' : ' • 🔴 Polling (2s)'} ); }; // Overview Panel Component const OverviewPanel: React.FC<{ systemHealth: any; processes: any[] }> = ({ systemHealth, processes }) => { const runningProcesses = processes.filter(p => p.status === 'running'); const errorProcesses = processes.filter(p => p.status === 'error'); return ( System Overview Process Status Total: {processes.length} Running: {runningProcesses.length} Errors: {errorProcesses.length} Resource Usage Memory: {systemHealth?.memory.percentage.toFixed(1)}% CPU: {systemHealth?.cpu?.usage || 0}% Uptime: {Math.floor((systemHealth?.uptime || 0) / 1000)}s Running Processes: {runningProcesses.map((process, i) => ( • {process.name} (PID: {process.pid || 'N/A'}) ))} Quick Actions: • Press [2] to manage agents • Press [3] to view tasks • Press [4] to browse memory bank ); }; // Tasks Panel Component const TasksPanel: React.FC = () => { return ( Task Management Task management interface coming soon... This will show: • Active tasks and their progress • Task queue and priorities • Task assignment to agents • Task completion statistics ); }; // Memory Panel Component const MemoryPanel: React.FC<{ memoryStats: any }> = ({ memoryStats }) => { return ( Memory Bank {memoryStats ? ( <> Total Entries: {memoryStats.overview.totalEntries} Total Size: {(memoryStats.overview.totalSize / 1024 / 1024).toFixed(2)} MB Cache Hit Rate: {(memoryStats.performance.cacheHitRatio * 100).toFixed(1)}% Health: {memoryStats.health.recommendedCleanup ? ⚠ Needs Cleanup : ✓ Healthy } Performance Metrics: Avg Query Time: {memoryStats.performance.averageQueryTime.toFixed(2)}ms Avg Write Time: {memoryStats.performance.averageWriteTime.toFixed(2)}ms Compressed Entries: {memoryStats.overview.compressedEntries} ) : ( Loading memory statistics... )} ); }; // Logs Panel Component const LogsPanel: React.FC = () => { return ( System Logs Log viewer interface coming soon... This will show: • Real-time log streaming • Log filtering and search • Error log analysis • Performance metrics ); }; // Config Panel Component const ConfigPanel: React.FC = () => { return ( Configuration Configuration interface coming soon... This will allow: • System settings management • Agent configuration • Performance tuning • Profile management ); }; export default InkDashboard;