Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions agents/dashboard/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const orbitron = Orbitron({

export const metadata: Metadata = {
title: "HyperStation | Mission Control",
description: "Advanced Multi-Agent Orchestration Dashboard",
description: "Advanced Multi-Agent Orchestration Dashboard for neurodivergent-first development",
};

export default function RootLayout({
Expand All @@ -27,13 +27,34 @@ export default function RootLayout({
<body
className={`${mono.variable} ${orbitron.variable} antialiased bg-[#050505] text-cyan-500 overflow-hidden`}
>
<div className="fixed inset-0 pointer-events-none z-0">
{/* Skip navigation โ€” WCAG 2.4.1: keyboard users jump straight to content */}
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[9999] focus:px-4 focus:py-2 focus:bg-cyan-500 focus:text-black focus:rounded focus:font-bold focus:outline-none"
>
Skip to main content
</a>

{/* Decorative background โ€” hidden from assistive technology */}
<div
className="fixed inset-0 pointer-events-none z-0"
role="presentation"
aria-hidden="true"
>
<div className="absolute inset-0 bg-[linear-gradient(rgba(18,18,18,0)_1px,transparent_1px),linear-gradient(90deg,rgba(18,18,18,0)_1px,transparent_1px)] bg-[size:40px_40px] [mask-image:radial-gradient(ellipse_60%_60%_at_50%_50%,#000_70%,transparent_100%)] opacity-20"></div>
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-cyan-900/10 via-[#050505] to-[#050505]"></div>
</div>
<div className="relative z-10 h-screen w-screen flex flex-col">
{children}
</div>

{/* Main content landmark โ€” all page content must live inside this */}
<main
id="main-content"
role="main"
aria-label="HyperStation Mission Control Dashboard"
className="relative z-10 h-screen w-screen flex flex-col"
tabIndex={-1}
>
{children}
</main>
</body>
</html>
);
Expand Down
96 changes: 60 additions & 36 deletions agents/dashboard/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const AgentCard = ({ agent }: { agent: Agent }) => (
layout
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
role="listitem"
aria-label={`${agent.name} (${agent.role}) status ${agent.status}`}
className={clsx(
"relative p-4 rounded-sm border-l-4 bg-zinc-900/50 backdrop-blur-sm transition-all cursor-default",
agent.status === "online" || agent.status === "working" ? "border-emerald-500" :
Expand All @@ -65,7 +67,7 @@ const AgentCard = ({ agent }: { agent: Agent }) => (
<h3 className="font-bold text-sm tracking-wider uppercase text-zinc-300 group-hover:text-white">{agent.name}</h3>
<p className="text-xs text-zinc-500 uppercase">{agent.role}</p>
</div>
<div className={clsx(
<div aria-hidden="true" className={clsx(
"h-2 w-2 rounded-full animate-pulse",
agent.status === "online" || agent.status === "working" ? "bg-emerald-500" :
agent.status === "thinking" ? "bg-cyan-500 shadow-[0_0_10px_#00f0ff]" :
Expand All @@ -79,7 +81,14 @@ const AgentCard = ({ agent }: { agent: Agent }) => (
<span>CPU</span>
<span>{agent.cpu}%</span>
</div>
<div className="h-1 w-full bg-zinc-800 rounded-full overflow-hidden">
<div
className="h-1 w-full bg-zinc-800 rounded-full overflow-hidden"
role="progressbar"
aria-label={`${agent.name} CPU usage`}
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={agent.cpu}
>
<div
className="h-full bg-cyan-500/50 transition-all duration-500"
style={{ width: `${agent.cpu}%` }}
Expand All @@ -90,7 +99,14 @@ const AgentCard = ({ agent }: { agent: Agent }) => (
<span>RAM</span>
<span>{agent.ram}%</span>
</div>
<div className="h-1 w-full bg-zinc-800 rounded-full overflow-hidden">
<div
className="h-1 w-full bg-zinc-800 rounded-full overflow-hidden"
role="progressbar"
aria-label={`${agent.name} RAM usage`}
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={agent.ram}
>
<div
className="h-full bg-purple-500/50 transition-all duration-500"
style={{ width: `${agent.ram}%` }}
Expand Down Expand Up @@ -221,24 +237,24 @@ export default function Dashboard() {
{/* Header */}
<header className="h-14 border-b border-zinc-800 bg-black/50 flex items-center justify-between px-6 shrink-0">
<div className="flex items-center gap-4">
<Activity className="text-cyan-500 animate-pulse" size={20} />
<Activity className="text-cyan-500 animate-pulse" size={20} aria-hidden="true" />
<h1 className="text-lg font-bold tracking-[0.2em] text-cyan-500 glow-text">
HYPERSTATION <span className="text-zinc-600 text-xs tracking-normal">BETA</span>
</h1>
</div>
<div className="flex items-center gap-6 text-xs font-mono text-zinc-500">
<div className="flex items-center gap-2" title={`API: ${API_BASE_URL}`}>
{connected ? <Wifi size={14} className="text-emerald-500" /> : <WifiOff size={14} className="text-red-500" />}
<div className="flex items-center gap-2" title={`API: ${API_BASE_URL}`} role="status" aria-live="polite" aria-label={`API status: ${connected ? "connected" : "offline"}`}>
{connected ? <Wifi size={14} className="text-emerald-500" aria-hidden="true" /> : <WifiOff size={14} className="text-red-500" aria-hidden="true" />}
<span className={connected ? "text-emerald-500" : "text-red-500"}>
{connected ? "CONNECTED" : "OFFLINE"}
</span>
</div>
<div className="flex items-center gap-2">
<Server size={14} />
<div className="flex items-center gap-2" role="status" aria-live="polite">
<Server size={14} aria-hidden="true" />
<span className="text-emerald-500">SYSTEM OPTIMAL</span>
</div>
<div className="flex items-center gap-2">
<ClockIcon size={14} />
<ClockIcon size={14} aria-hidden="true" />
<Clock />
</div>
</div>
Expand All @@ -248,13 +264,13 @@ export default function Dashboard() {
<main className="flex-1 flex overflow-hidden">

{/* Left Panel: The Crew */}
<aside className="w-64 border-r border-zinc-800 bg-black/20 flex flex-col p-4 gap-4 overflow-y-auto shrink-0">
<aside className="w-64 border-r border-zinc-800 bg-black/20 flex flex-col p-4 gap-4 overflow-y-auto shrink-0" aria-label="Active agents">
<h2 className="text-xs font-bold text-zinc-600 uppercase tracking-widest mb-2 flex items-center gap-2">
<Shield size={12} /> Active Agents
<Shield size={12} aria-hidden="true" /> Active Agents
</h2>
<div className="space-y-3">
<div className="space-y-3" role="list" aria-label="Agent list">
{agents.length === 0 && !connected && (
<div className="text-xs text-zinc-500 text-center py-4">Connecting to Neural Net...</div>
<div className="text-xs text-zinc-500 text-center py-4" role="status" aria-live="polite">Connecting to Neural Net...</div>
)}
{agents.map(agent => (
<AgentCard key={agent.id} agent={agent} />
Expand All @@ -266,11 +282,15 @@ export default function Dashboard() {
<section className="flex-1 flex flex-col min-w-0 bg-black/40 relative">

{/* Tabs */}
<div className="flex border-b border-zinc-800">
<div className="flex border-b border-zinc-800" role="tablist" aria-label="Dashboard views">
{['uplink', 'hyperflow', 'live', 'neural', 'tasks'].map(tab => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
role="tab"
aria-selected={activeTab === tab}
aria-controls={`panel-${tab}`}
id={`tab-${tab}`}
className={clsx(
"px-6 py-3 text-xs font-bold uppercase tracking-wider transition-colors border-r border-zinc-800",
activeTab === tab
Expand All @@ -289,7 +309,7 @@ export default function Dashboard() {
{/* Viewport Content */}
<div className="flex-1 overflow-y-auto p-0 font-mono relative bg-black">
{activeTab === 'uplink' && (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" role="tabpanel" id="panel-uplink" aria-labelledby="tab-uplink" tabIndex={0}>
<div className="p-6 pb-0">
<div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-start">
<div className="xl:col-span-2">
Expand All @@ -305,41 +325,41 @@ export default function Dashboard() {
)}

{activeTab === 'hyperflow' && (
<div className="h-full w-full rounded-lg overflow-hidden border border-zinc-800 relative">
<div className="absolute top-2 left-2 z-10 text-[10px] text-cyan-500 bg-black/50 px-2 py-1 rounded border border-cyan-900/50">
<div className="h-full w-full rounded-lg overflow-hidden border border-zinc-800 relative" role="tabpanel" id="panel-hyperflow" aria-labelledby="tab-hyperflow" tabIndex={0} aria-label="HyperFlow editor">
<div className="absolute top-2 left-2 z-10 text-[10px] text-cyan-500 bg-black/50 px-2 py-1 rounded border border-cyan-900/50" aria-hidden="true">
HYPERFLOW EDITOR v1.0
</div>
<HyperCanvas />
</div>
)}

{activeTab === 'live' && (
<div className="space-y-1">
<div className="space-y-1" role="tabpanel" id="panel-live" aria-labelledby="tab-live" tabIndex={0} aria-label="Live operations logs">
<div className="sticky top-0 bg-black/80 backdrop-blur-md border-b border-zinc-800 p-2 mb-4 text-xs text-cyan-600 font-bold flex justify-between">
<span>{/* SYSTEM_LOGS_STREAM_V2.0 */}</span>
<span className="animate-pulse">โ— LIVE</span>
<span className="animate-pulse" aria-hidden="true">โ— LIVE</span>
</div>
<div className="flex flex-col-reverse gap-1 min-h-0">
<div className="flex flex-col-reverse gap-1 min-h-0" aria-live="off" aria-label="Log stream">
{logs.map((log, i) => <LogEntry key={log.id || i} log={log} />)}
</div>
</div>
)}

{activeTab === 'neural' && (
<div className="h-full flex items-center justify-center text-zinc-700 flex-col gap-4">
<div className="h-full flex items-center justify-center text-zinc-700 flex-col gap-4" role="tabpanel" id="panel-neural" aria-labelledby="tab-neural" tabIndex={0} aria-label="Neural network view">
<NeuralViz />
</div>
)}

{activeTab === 'tasks' && (
<div className="space-y-4">
<div className="space-y-4" role="tabpanel" id="panel-tasks" aria-labelledby="tab-tasks" tabIndex={0} aria-label="Mission log">
{tasks.map(task => (
<div key={task.id} className="border border-zinc-800 bg-zinc-900/20 p-4 rounded-sm">
<div className="flex justify-between mb-2">
<h3 className="font-bold text-cyan-400">{task.description || task.title}</h3>
<span className="text-xs px-2 py-0.5 rounded bg-zinc-800 text-zinc-400 uppercase">{task.status}</span>
</div>
<div className="h-1 bg-zinc-800 w-full rounded-full overflow-hidden">
<div className="h-1 bg-zinc-800 w-full rounded-full overflow-hidden" role="progressbar" aria-label={`Task progress: ${task.description || task.title || task.id}`} aria-valuemin={0} aria-valuemax={100} aria-valuenow={task.progress || 0}>
<div className="h-full bg-emerald-500 transition-all" style={{ width: `${task.progress || 0}%` }} />
</div>
<div className="flex gap-2 mt-3">
Expand All @@ -359,10 +379,12 @@ export default function Dashboard() {
{/* Command Input */}
<div className="h-16 border-t border-zinc-800 bg-black/60 backdrop-blur p-3">
<form onSubmit={handleCommand} className="flex gap-2 h-full">
<div className="flex items-center justify-center w-10 bg-zinc-900 border border-zinc-700 text-cyan-500">
<Terminal size={18} />
<div className="flex items-center justify-center w-10 bg-zinc-900 border border-zinc-700 text-cyan-500" aria-hidden="true">
<Terminal size={18} aria-hidden="true" />
</div>
<label className="sr-only" htmlFor="command-input">Command</label>
<input
id="command-input"
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
Expand All @@ -373,7 +395,7 @@ export default function Dashboard() {
type="submit"
className="px-6 bg-cyan-900/20 border border-cyan-800 text-cyan-400 hover:bg-cyan-500 hover:text-black transition-all font-bold uppercase text-xs tracking-wider flex items-center gap-2"
>
Execute <Send size={14} />
Execute <Send size={14} aria-hidden="true" />
</button>
</form>
</div>
Expand All @@ -386,20 +408,20 @@ export default function Dashboard() {

<div>
<h2 className="text-xs font-bold text-zinc-600 uppercase tracking-widest mb-4 flex items-center gap-2">
<Zap size={12} /> Resource Usage
<Zap size={12} aria-hidden="true" /> Resource Usage
</h2>
<div className="space-y-4">
{/* Mock Charts */}
<div className="h-32 border border-zinc-800 bg-zinc-900/30 relative overflow-hidden flex items-end gap-1 p-2">
{[40, 60, 30, 80, 50, 90, 70, 40, 60, 50].map((h, i) => (
<div key={i} className="flex-1 bg-cyan-500/20 hover:bg-cyan-500/50 transition-colors" style={{ height: `${h}%` }} />
<div key={i} className="flex-1 bg-cyan-500/20 hover:bg-cyan-500/50 transition-colors" style={{ height: `${h}%` }} aria-hidden="true" />
))}
<div className="absolute top-2 left-2 text-[10px] text-zinc-500">CPU LOAD</div>
</div>

<div className="h-32 border border-zinc-800 bg-zinc-900/30 relative overflow-hidden flex items-end gap-1 p-2">
{[20, 30, 25, 40, 35, 30, 45, 50, 60, 55].map((h, i) => (
<div key={i} className="flex-1 bg-purple-500/20 hover:bg-purple-500/50 transition-colors" style={{ height: `${h}%` }} />
<div key={i} className="flex-1 bg-purple-500/20 hover:bg-purple-500/50 transition-colors" style={{ height: `${h}%` }} aria-hidden="true" />
))}
<div className="absolute top-2 left-2 text-[10px] text-zinc-500">MEMORY ALLOCATION</div>
</div>
Expand All @@ -408,7 +430,7 @@ export default function Dashboard() {

<div>
<h2 className="text-xs font-bold text-zinc-600 uppercase tracking-widest mb-4 flex items-center gap-2">
<Database size={12} /> Database Health
<Database size={12} aria-hidden="true" /> Database Health
</h2>
<div className="grid grid-cols-2 gap-2">
<div className="bg-zinc-900/50 p-3 border border-zinc-800 rounded flex flex-col items-center">
Expand All @@ -424,11 +446,11 @@ export default function Dashboard() {

<div className="mt-auto border-t border-zinc-800 pt-4">
<div className="flex items-center gap-3 text-xs text-zinc-500">
<AlertTriangle size={14} className="text-yellow-600" />
<AlertTriangle size={14} className="text-yellow-600" aria-hidden="true" />
<span>2 Warnings in last hour</span>
</div>
<div className="flex items-center gap-3 text-xs text-zinc-500 mt-2">
<CheckCircle size={14} className="text-emerald-600" />
<CheckCircle size={14} className="text-emerald-600" aria-hidden="true" />
<span>System Integrity Verified</span>
</div>
</div>
Expand All @@ -437,18 +459,20 @@ export default function Dashboard() {
</main>

{!token && (
<div className="fixed inset-0 z-40 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
<div className="fixed inset-0 z-40 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4" role="dialog" aria-modal="true" aria-labelledby="auth-title">
<div className="w-full max-w-md bg-zinc-900 border border-zinc-700 rounded-lg p-6">
<h2 className="text-cyan-400 font-bold tracking-widest uppercase text-sm mb-4">Authenticate</h2>
<label className="block text-xs text-zinc-500 uppercase mb-1">Email</label>
<h2 id="auth-title" className="text-cyan-400 font-bold tracking-widest uppercase text-sm mb-4">Authenticate</h2>
<label className="block text-xs text-zinc-500 uppercase mb-1" htmlFor="login-email">Email</label>
<input
id="login-email"
className="w-full bg-black/40 border border-zinc-700 rounded p-2 mb-4 text-sm"
value={loginUsername}
onChange={(e) => setLoginUsername(e.target.value)}
placeholder="admin@hypercode.ai"
/>
<label className="block text-xs text-zinc-500 uppercase mb-1">Password</label>
<label className="block text-xs text-zinc-500 uppercase mb-1" htmlFor="login-password">Password</label>
<input
id="login-password"
type="password"
className="w-full bg-black/40 border border-zinc-700 rounded p-2 mb-4 text-sm"
value={loginPassword}
Expand Down
Loading