UNPKG

blax

Version:

Blax - HMS-Powered Multi-Agent Platform with Government Agency Analysis, Deep Research, and Enterprise-Ready Deployment. No local LLM keys required.

285 lines • 16.4 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useCallback } from 'react'; import { Box, Text, useInput, useApp, useStdout } from 'ink'; import TmuxConfigManager from '../config/tmuxConfig.js'; import { TmuxPanel as ThemedTmuxPanel, TmuxStatusBar, TmuxNotification, TmuxHelpModal } from './tmuxComponents.js'; import { getTheme, listThemes } from '../config/tmuxThemes.js'; export const TmuxPanel = ({ messageBus, watchdog, agentHub }) => { const { exit } = useApp(); const { stdout } = useStdout(); // Initialize configuration manager const [configManager] = useState(() => new TmuxConfigManager()); const [config, setConfig] = useState(() => configManager.getConfig()); const theme = getTheme(config.theme); const [state, setState] = useState({ mainContent: ['🦾 Welcome to HMS-Powered Terminal', 'šŸ”— Connecting to HMS systems...'], infoContent: { agents: [], deals: [], system: { health: 'healthy', uptime: 0, connections: 0 } }, input: '', mode: 'chat', selectedPane: 'main', showHelp: false, startTime: Date.now() }); // Terminal dimensions const [dimensions, setDimensions] = useState({ width: 80, height: 24 }); // Update terminal dimensions useEffect(() => { const updateDimensions = () => { setDimensions({ width: stdout.columns || 80, height: stdout.rows || 24 }); }; updateDimensions(); process.stdout.on('resize', updateDimensions); return () => { process.stdout.off('resize', updateDimensions); }; }, [stdout]); // Calculate panel sizes based on configuration const mainWidth = Math.floor(dimensions.width * config.layout.splitRatio); const infoWidth = dimensions.width - mainWidth - (config.layout.showBorders ? 1 : 0); // Show notification helper const showNotification = (message, type) => { setState(prev => ({ ...prev, notification: { message, type } })); setTimeout(() => { setState(prev => ({ ...prev, notification: undefined })); }, 3000); }; // Update info panel content const updateInfoContent = useCallback(async () => { try { // Get agents (mock data for now) const agents = []; // Get deals (simulated) const deals = [ { name: 'Healthcare Deal', status: 'active', participants: ['cardio', 'pharmacy'] }, { name: 'Finance Deal', status: 'pending', participants: ['audit', 'compliance'] } ]; // Get system status (mock data) const system = { health: 'healthy', uptime: Math.round(Date.now() / 1000), connections: 0 }; setState(prev => ({ ...prev, infoContent: { agents, deals, system } })); } catch (error) { console.error('Failed to update info content:', error); } }, [messageBus, agentHub]); // Handle input with enhanced keybindings useInput((input, key) => { // Help modal toggle if (key.f1 || (key.ctrl && input === '?')) { setState(prev => ({ ...prev, showHelp: !prev.showHelp })); return; } // Close help modal if (state.showHelp && (key.escape || key.return)) { setState(prev => ({ ...prev, showHelp: false })); return; } // Don't process other keys when help is open if (state.showHelp) return; // Exit application if (key.escape || (key.ctrl && input === 'c')) { exit(); return; } // Clear screen if (key.ctrl && input === 'l') { setState(prev => ({ ...prev, mainContent: [] })); return; } // Tab to switch panes if (key.tab) { setState(prev => ({ ...prev, selectedPane: prev.selectedPane === 'main' ? 'info' : 'main' })); return; } // Mode switching if (key.ctrl && input === 'm') { const modes = ['chat', 'command', 'status']; const currentIndex = modes.indexOf(state.mode); const nextMode = modes[(currentIndex + 1) % modes.length]; setState(prev => ({ ...prev, mode: nextMode })); showNotification(`Switched to ${nextMode.toUpperCase()} mode`, 'info'); return; } // Handle input based on mode if (key.return) { handleCommand(state.input); setState(prev => ({ ...prev, input: '' })); } else if (key.backspace) { setState(prev => ({ ...prev, input: prev.input.slice(0, -1) })); } else if (input && !key.ctrl && !key.meta) { setState(prev => ({ ...prev, input: prev.input + input })); } }); // Handle commands const handleCommand = async (command) => { if (!command.trim()) return; // Add command to main content setState(prev => ({ ...prev, mainContent: [...prev.mainContent, `> ${command}`] })); try { let response = ''; if (command.startsWith('/')) { // System commands response = await handleSystemCommand(command); } else { // Chat with HMS-powered agent const result = await agentHub.execute('chat', { sessionId: `tmux-panel-${Date.now()}`, userId: 'tmux-user', workspacePath: process.cwd(), environment: 'development', capabilities: ['chat', 'research', 'agency-analysis'], metadata: { interface: 'tmux-panel' } }, { prompt: command, includeResearch: command.includes('research') || command.includes('agency'), agentType: 'general' }); if (result.success) { const metadata = result.metadata; response = String(result.data); // Add HMS context info if available if (metadata?.hmsEnabled) { response += `\n\nšŸ¤– Agent: ${metadata.agentUsed || 'HMS Agent'} (${metadata.processingTime || 0}ms)`; if (metadata.researchContext) { response += `\nšŸ“š Enhanced with research data`; } } else if (metadata?.fallback) { response += `\n\nāš ļø Fallback mode (HMS unavailable)`; } } else { response = `Error: ${result.error?.message}`; } } // Add response to main content setState(prev => ({ ...prev, mainContent: [...prev.mainContent, response] })); } catch (error) { setState(prev => ({ ...prev, mainContent: [...prev.mainContent, `Error: ${error instanceof Error ? error.message : String(error)}`] })); } }; // Handle system commands with theme support const handleSystemCommand = async (command) => { const parts = command.substring(1).split(' '); const cmd = parts[0].toLowerCase(); const args = parts.slice(1); switch (cmd) { case 'agents': const agentCount = state.infoContent.agents.length; return `Active agents: ${agentCount > 0 ? agentCount : 'none'}`; case 'deals': const deals = state.infoContent.deals; return deals.length > 0 ? `Deals (${deals.length}): ${deals.map(d => `${d.name} (${d.status})`).join(', ')}` : 'No active deals'; case 'status': const uptime = Math.round((Date.now() - state.startTime) / 1000); const hmsStatus = await agentHub.healthCheck(); return `System: ${hmsStatus.status}, HMS-NET: ${hmsStatus.hmsNet ? 'āœ…' : 'āŒ'}, HMS-NFO: ${hmsStatus.hmsNfo ? 'āœ…' : 'āŒ'}, Uptime: ${uptime}s`; case 'clear': setState(prev => ({ ...prev, mainContent: [] })); return ''; case 'theme': if (args.length === 0) { return `Current theme: ${config.theme}\nAvailable themes: ${listThemes().join(', ')}`; } const themeName = args[0]; if (listThemes().includes(themeName)) { configManager.setTheme(themeName); setConfig(configManager.getConfig()); showNotification(`Theme changed to ${themeName}`, 'success'); return `Theme changed to: ${themeName}`; } return `Unknown theme: ${themeName}. Available: ${listThemes().join(', ')}`; case 'config': if (args.length === 0) { return `Split ratio: ${config.layout.splitRatio}, Theme: ${config.theme}, Refresh: ${config.features.refreshInterval}s`; } // Handle config changes const [setting, value] = args; if (setting === 'split' && value) { const ratio = parseFloat(value); if (ratio >= 0.1 && ratio <= 0.9) { configManager.setLayout(ratio); setConfig(configManager.getConfig()); return `Split ratio set to: ${ratio}`; } return 'Split ratio must be between 0.1 and 0.9'; } return 'Usage: /config [split <ratio>]'; case 'help': setState(prev => ({ ...prev, showHelp: true })); return ''; case 'preset': if (args.length === 0) { return `Available presets: ${Object.keys(TmuxConfigManager.getPresets()).join(', ')}`; } const presetName = args[0]; if (configManager.applyPreset(presetName)) { setConfig(configManager.getConfig()); showNotification(`Applied preset: ${presetName}`, 'success'); return `Applied preset: ${presetName}`; } return `Unknown preset: ${presetName}`; default: return `Unknown command: /${cmd}. Type /help for available commands.`; } }; // Update info panel periodically useEffect(() => { const interval = setInterval(updateInfoContent, 3000); updateInfoContent(); // Initial update return () => clearInterval(interval); }, [updateInfoContent]); // Render main panel with theming const renderMainPanel = () => { const contentHeight = dimensions.height - (config.layout.showTitles ? 4 : 3); // Title + input + borders const content = state.mainContent.slice(-Math.max(1, contentHeight)); return (_jsx(ThemedTmuxPanel, { title: `Main [${state.mode.toUpperCase()}]`, theme: theme, width: mainWidth, height: dimensions.height - 2, isActive: state.selectedPane === 'main', showBorder: config.layout.showBorders, showTitle: config.layout.showTitles, children: _jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Box, { flexGrow: 1, flexDirection: "column", children: content.map((line, index) => (_jsx(Text, { wrap: config.display.wordWrap ? 'wrap' : 'truncate', color: theme.colors.foreground, children: line }, index))) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.primary, children: "\u276F " }), _jsx(Text, { color: theme.colors.foreground, children: state.input }), _jsx(Text, { color: theme.colors.accent, children: "\u2588" })] })] }) })); }; // Render info panel with enhanced theming const renderInfoPanel = () => { const uptime = Math.round((Date.now() - state.startTime) / 1000); return (_jsx(ThemedTmuxPanel, { title: "Info", theme: theme, width: infoWidth, height: dimensions.height - 2, isActive: state.selectedPane === 'info', showBorder: config.layout.showBorders, showTitle: config.layout.showTitles, children: _jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.primary, children: "System" }), _jsxs(Text, { color: theme.colors.success, children: ["Health: ", state.infoContent.system.health] }), _jsxs(Text, { color: theme.colors.foreground, children: ["Uptime: ", uptime, "s"] }), _jsxs(Text, { color: theme.colors.foreground, children: ["Connections: ", state.infoContent.system.connections] })] }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "HMS Status" }), _jsxs(Text, { color: theme.colors.foreground, children: ["Theme: ", config.theme] }), _jsxs(Text, { color: theme.colors.foreground, children: ["Refresh: ", config.features.refreshInterval, "s"] }), _jsxs(Text, { color: theme.colors.foreground, children: ["Split: ", Math.round(config.layout.splitRatio * 100), "%"] })] }), _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { bold: true, color: theme.colors.accent, children: ["Agents (", state.infoContent.agents.length, ")"] }), state.infoContent.agents.map((agent, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.colors.foreground, children: agent.id }), _jsxs(Text, { color: theme.colors.muted, children: [agent.status, " - ", agent.task] })] }, index))), state.infoContent.agents.length === 0 && (_jsx(Text, { color: theme.colors.muted, children: "No active agents" }))] }), _jsxs(Box, { children: [_jsxs(Text, { bold: true, color: theme.colors.warning, children: ["Deals (", state.infoContent.deals.length, ")"] }), state.infoContent.deals.map((deal, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.colors.foreground, children: deal.name }), _jsx(Text, { color: theme.colors.muted, children: deal.status })] }, index))), state.infoContent.deals.length === 0 && (_jsx(Text, { color: theme.colors.muted, children: "No active deals" }))] })] }) })); }; return (_jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", children: [theme.statusBar.position === 'top' && (_jsx(TmuxStatusBar, { theme: theme, width: dimensions.width, leftContent: `HMS ${config.theme}`, centerContent: config.features.hmsIntegration ? 'HMS-Powered Terminal' : 'Local Mode', rightContent: `${state.mode.toUpperCase()}`, showTime: theme.statusBar.showTime })), _jsxs(Box, { flexDirection: config.layout.orientation === 'vertical' ? 'column' : 'row', flexGrow: 1, children: [renderMainPanel(), renderInfoPanel()] }), theme.statusBar.position === 'bottom' && (_jsx(TmuxStatusBar, { theme: theme, width: dimensions.width, leftContent: `🌐 HMS ${config.theme}`, centerContent: config.features.hmsIntegration ? 'šŸ¤– HMS-Powered Terminal' : 'šŸ’» Local Mode', rightContent: `${state.mode.toUpperCase()}`, showTime: theme.statusBar.showTime })), state.notification && (_jsx(Box, { children: _jsx(TmuxNotification, { theme: theme, message: state.notification.message, type: state.notification.type, width: 50 }) })), state.showHelp && (_jsx(TmuxHelpModal, { theme: theme, width: Math.min(60, dimensions.width - 4), height: Math.min(20, dimensions.height - 4), keybindings: { 'Switch Pane': config.keybindings.switchPane, 'Cycle Mode': config.keybindings.cycleMode, 'Clear Screen': config.keybindings.clear, 'Show Help': config.keybindings.help, 'Exit': config.keybindings.exit, }, onClose: () => setState(prev => ({ ...prev, showHelp: false })) }))] })); }; export default TmuxPanel; //# sourceMappingURL=tmuxPanel.js.map