File size: 6,197 Bytes
8892a6c 55bbf78 8892a6c 55bbf78 8892a6c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import { useSimulationDispatch, useSimulationState } from '../../store/simulationStore';
import { motion } from 'framer-motion';
import { getApiBase } from '../../hooks/useSimulation';
const AGENT_DISPLAY = {
COMMANDER: 'Commander',
DETECTIVE: 'Detective',
CODER: 'Coder',
MANAGER: 'Manager',
EVALUATOR: 'Validator',
DBA_AGENT: 'Data Agent',
SRE_AGENT: 'Reliability',
SECURITY_AGENT: 'Security',
COMPLIANCE_AGENT: 'Compliance',
};
export default function Header({ onClearReset }) {
const dispatch = useSimulationDispatch();
const { slaRemaining, spent, budget, activeAgents, telemetry, validatorRuntime } = useSimulationState();
const slaMin = Math.floor(slaRemaining / 60);
const slaSec = Math.floor(slaRemaining % 60);
const slaColor = slaRemaining > 300 ? 'text-emerald-400' : slaRemaining > 60 ? 'text-amber-400' : 'text-red-500';
const slaUrgent = slaRemaining <= 60;
const spentPct = budget > 0 ? (spent / budget) * 100 : 0;
const budgetColor = spentPct < 50 ? 'bg-emerald-500' : spentPct < 80 ? 'bg-amber-500' : 'bg-red-500';
const runtime = validatorRuntime || telemetry.validator_runtime || {};
const runtimeLabel = runtime.label || 'Validator Unavailable';
const runtimeBadgeClass = runtime.ready
? runtime.gpu_metrics_applicable
? 'bg-blue-950/40 text-blue-300 border-blue-500/30'
: 'bg-emerald-950/40 text-emerald-300 border-emerald-500/30'
: 'bg-red-950/40 text-red-300 border-red-500/30';
const handleClear = async () => {
const confirmed = window.confirm('Clear the current live dashboard and reset the session?');
if (!confirmed) return;
dispatch({ type: 'CLEAR_SIMULATION' });
if (onClearReset) onClearReset();
try {
await fetch(`${getApiBase()}/api/frontend/clear`, { method: 'POST' });
} catch (error) {
console.warn('Failed to clear backend replay state:', error);
}
};
return (
<header className="flex items-center justify-between px-4 py-2 bg-zinc-900 border-b border-zinc-800 h-14 shrink-0">
{/* ββ Left: Branding ββ */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white text-sm font-bold">
F
</div>
<div>
<h1 className="text-sm font-semibold text-zinc-100 leading-none">FrontierLabs</h1>
<p className="text-[10px] text-zinc-500 leading-none mt-0.5">Swarm-OS v1.0</p>
</div>
</div>
{/* Active Agents β color-coded by role */}
<div className="flex items-center gap-1 ml-4 px-2 py-1 rounded-md bg-zinc-800/50">
{activeAgents.map((a) => {
const colorMap = {
COMMANDER: 'bg-blue-900/40 text-blue-400 border-blue-500/30',
DETECTIVE: 'bg-amber-900/40 text-amber-400 border-amber-500/30',
CODER: 'bg-emerald-900/40 text-emerald-400 border-emerald-500/30',
MANAGER: 'bg-purple-900/40 text-purple-400 border-purple-500/30',
EVALUATOR: 'bg-pink-900/40 text-pink-400 border-pink-500/30',
DBA_AGENT: 'bg-emerald-900/40 text-emerald-300 border-emerald-500/30',
SRE_AGENT: 'bg-cyan-900/40 text-cyan-300 border-cyan-500/30',
SECURITY_AGENT: 'bg-orange-900/40 text-orange-300 border-orange-500/30',
COMPLIANCE_AGENT: 'bg-yellow-900/40 text-yellow-300 border-yellow-500/30',
};
const cls = colorMap[a] || 'bg-zinc-700 text-zinc-300 border-zinc-600';
return (
<span key={a} className={`text-[9px] px-1.5 py-0.5 rounded border font-mono font-semibold ${cls}`}>
{AGENT_DISPLAY[a] || a}
</span>
);
})}
</div>
</div>
{/* ββ Center: SLA Timer + Budget ββ */}
<div className="flex items-center gap-6">
{/* SLA Timer */}
<div className="flex items-center gap-2">
<span className="text-[10px] text-zinc-500 uppercase tracking-wider">SLA</span>
<motion.span
className={`font-mono text-lg font-bold ${slaColor}`}
animate={slaUrgent ? { scale: [1, 1.05, 1] } : {}}
transition={{ repeat: Infinity, duration: 1 }}
>
{String(slaMin).padStart(2, '0')}:{String(slaSec).padStart(2, '0')}
</motion.span>
</div>
{/* Budget */}
<div className="flex flex-col items-center gap-0.5 min-w-[120px]">
<div className="flex items-center justify-between w-full">
<span className="text-[10px] text-zinc-500">BUDGET</span>
<span className="text-[10px] font-mono text-zinc-300">${spent.toFixed(3)} / ${budget.toFixed(3)}</span>
</div>
<div className="w-full h-1 bg-zinc-800 rounded-full overflow-hidden">
<motion.div
className={`h-full ${budgetColor} rounded-full`}
initial={{ width: 0 }}
animate={{ width: `${Math.min(spentPct, 100)}%` }}
transition={{ duration: 0.5 }}
/>
</div>
</div>
</div>
{/* ββ Right: Model Badge ββ */}
<div className="flex items-center gap-3">
<button
onClick={handleClear}
className="text-[10px] font-mono px-2 py-1 rounded border border-zinc-700 bg-zinc-900 hover:bg-zinc-800 text-zinc-300 transition-colors"
>
Clear
</button>
<span className={`text-[10px] font-mono px-2 py-1 rounded border ${runtimeBadgeClass}`}>
{runtimeLabel}
</span>
<div className="flex flex-col items-end">
<span className="text-[10px] text-zinc-500">PRIMARY</span>
<span className="text-[10px] font-mono text-blue-400">
SwarmOS-Llama-3.1-8B-GRPO
</span>
<span className="text-[9px] text-zinc-600">4-bit QLoRA Β· GGUF Β· Local</span>
</div>
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" title="Model Active" />
</div>
</header>
);
}
|