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
JavaScript
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