import React, { useState, useRef, useEffect } from 'react'; import { config } from '../config'; import TopNavBar from './TopNavBar'; import SideNavBar from './SideNavBar'; /* ─── Terminal Panel ─── */ const COMMANDS = { help: () => ['Commands: help | status | clear | echo '], status: () => ['Agent A (INV-01): STANDBY', 'Agent B (VAL-01): STANDBY', `WebSocket: ${config.WS_URL} — CONNECTED`, 'Episode: None active'], }; const TerminalDrawer = ({ onClose }) => { const [input, setInput] = useState(''); const [lines, setLines] = useState([{ type: 'system', text: '// NEXUS Terminal v2.0 — type "help" for commands' }]); const [history, setHistory] = useState([]); const [histIdx, setHistIdx] = useState(-1); const endRef = useRef(null); const inputRef = useRef(null); useEffect(() => { endRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [lines]); useEffect(() => { inputRef.current?.focus(); }, []); const run = (e) => { e.preventDefault(); const cmd = input.trim(); if (!cmd) return; setHistory(h => [cmd, ...h].slice(0, 50)); setHistIdx(-1); if (cmd.toLowerCase() === 'clear') { setLines([]); setInput(''); return; } const parts = cmd.toLowerCase().split(' '); let output, type; if (parts[0] === 'echo') { output = [cmd.slice(5) || '']; type = 'output'; } else if (COMMANDS[parts[0]]) { output = COMMANDS[parts[0]](); type = 'output'; } else { output = [`Command not found: ${parts[0]}. Type "help".`]; type = 'error'; } setLines(l => [...l, { type: 'input', text: `nexus@terminal:~$ ${cmd}` }, ...output.map(t => ({ type, text: t }))]); setInput(''); }; const handleKey = (e) => { if (e.key === 'ArrowUp') { const i = Math.min(histIdx + 1, history.length - 1); setHistIdx(i); setInput(history[i] ?? ''); e.preventDefault(); } if (e.key === 'ArrowDown') { const i = Math.max(histIdx - 1, -1); setHistIdx(i); setInput(i === -1 ? '' : history[i]); e.preventDefault(); } }; const colorMap = { system: 'text-slate-600 italic', input: 'text-primary', output: 'text-on-surface/80', error: 'text-error' }; return (
inputRef.current?.focus()}>
{lines.map((l, i) =>
{l.text}
)}
nexus@terminal:~$ setInput(e.target.value)} onKeyDown={handleKey} className="flex-1 bg-transparent font-mono text-xs text-on-surface focus:outline-none placeholder:text-slate-700" placeholder="type a command and press Enter..." />
); }; /* ─── Communication Panel ─── */ const CommunicationDrawer = () => (
{[ { agent: 'AGENT_A', msg: 'Awaiting objective. Standing by for episode_start event.', time: '—', color: 'text-primary' }, { agent: 'AGENT_B', msg: 'Validation module idle. Ready to receive investigator output.', time: '—', color: 'text-secondary' }, { agent: 'SYSTEM', msg: 'No active episode. Use START to begin.', time: '—', color: 'text-outline-variant' }, ].map((m, i) => (
[{m.agent}] {m.msg} {m.time}
))}
); /* ─── Reward Analytics Panel ─── */ const AnalyticsDrawer = () => { const stats = [ { label: 'Avg Reward', value: '—', color: 'text-primary' }, { label: 'Best Step', value: '—', color: 'text-tertiary' }, { label: 'Root Cause', value: '—', color: 'text-tertiary' }, { label: 'Steps Run', value: '—', color: 'text-on-surface' }, { label: 'Episodes', value: '—', color: 'text-on-surface' }, { label: 'Success Rate', value: '—', color: 'text-secondary' }, ]; return (
{/* Reward chart placeholder */}

Cumulative Reward Over Steps

{[12, 24, 18, 36, 30, 48, 42, 60].map((h, i) => (
))}

No live data — connect to episode to populate

{/* Stat grid */}
{stats.map(s => (
{s.label} {s.value}
))}
); }; /* ─── Layout ─── */ const TABS = [ { id: 'communication', label: 'Communication', icon: 'forum' }, { id: 'terminal', label: 'Terminal', icon: 'code' }, ]; const Layout = ({ children }) => { const [activeTab, setActiveTab] = useState(null); // null = closed const toggle = (id) => setActiveTab(prev => prev === id ? null : id); /* drawer height when open */ const drawerH = 'h-64'; return (
{/* Main scrollable area — leave room for fixed footer + optional drawer */}
{children}
{/* Sliding drawer */} {activeTab && (
{/* Drawer title bar */}
{TABS.find(t => t.id === activeTab)?.icon} {TABS.find(t => t.id === activeTab)?.label}
{/* Drawer content */}
{activeTab === 'terminal' && setActiveTab(null)} />} {activeTab === 'communication' && } {activeTab === 'analytics' && }
)} {/* Footer tab bar */}
); }; export default Layout;