feat(theme): dark/light mode with a Radix Colors-based palette

Adds a working Dark/Light toggle (next to the language switcher, same row
as EN/VI) and re-themes the whole dashboard, not just the handful of
components that already used semantic tokens.

- Tailwind darkMode:"class" + CSS-variable color tokens (client/src/index.css,
  tailwind.config.js): surface.0-5, border/border-light, accent/accent-hover,
  fg.primary/secondary/muted, status.success/danger/warning. One class flip
  on <html> re-themes everything — no per-element dark: variant pairs.
- useTheme() hook: localStorage-persisted, defaults to dark, no
  prefers-color-scheme fallback (client/src/hooks/useTheme.ts).
- Mechanical, table-driven migration (scripts/migrate-color-tokens.mjs,
  scripts/tokenize-status-colors.mjs, scripts/darken-status-colors.mjs) of
  every raw neutral/gray/slate + emerald/red/amber Tailwind utility across
  client/src onto the new tokens, so every badge/button/component pulls the
  same shade per status/role instead of each picking its own.
- Palette values are the literal Radix Colors (radix-ui.com/colors) scale
  constants — slate/blue/green/red/amber steps 1-12 — adopted after three
  rounds of hand-picked values that kept overshooting (flat, then too dark,
  then glaring); see docs/superpowers/specs/2026-07-31-color-redesign-
  dark-light-mode-design.md for the full history and role mapping.
- PipelineMap: done/current/failed/passed-no-evidence/detected share one
  visual language (border + text + translucent wash of the same status
  color); `current` alone stays a solid accent fill, the one state that
  gets to look bolder ("you are here").
- LaneCard: removed the stage/kind/auto-stage chips that duplicated the
  Workspace lane-detail header already showing them.

Categorical/decorative hues (violet, indigo, cyan, teal, sky, rose, pink,
orange, yellow, and blue where it plays a role-coloring part e.g. message
bubbles) are deliberately out of scope — collapsing those onto shared
tokens would erase the distinction between different kinds of thing, not
a status.
This commit is contained in:
2026-07-31 10:54:31 +07:00
parent 4905d63b97
commit b673363351
82 changed files with 3776 additions and 2578 deletions
+112 -108
View File
@@ -278,28 +278,28 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Server className="w-4 h-4 text-emerald-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Runtime</span>
<Server className="w-4 h-4 text-status-success" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Runtime</span>
</div>
<span className="text-[10px] font-mono text-gray-500">
<span className="text-[10px] font-mono text-fg-muted">
{info.server.cpus} cores · {info.server.arch}
</span>
</div>
<div className="space-y-2.5">
<div className="flex items-center gap-3">
<span className="text-xs text-gray-400 w-28 flex-shrink-0">Uptime</span>
<span className="text-xs text-gray-200 font-mono ml-auto">
<span className="text-xs text-fg-secondary w-28 flex-shrink-0">Uptime</span>
<span className="text-xs text-fg-secondary font-mono ml-auto">
{formatUptime(info.server.uptime)}
</span>
</div>
<div className="flex items-center gap-3">
<span className="text-xs text-gray-400 w-28 flex-shrink-0">CPU (1/5/15m)</span>
<span className="text-xs text-fg-secondary w-28 flex-shrink-0">CPU (1/5/15m)</span>
<div className="flex gap-1 ml-auto">
{(info.server.cpu_load || []).slice(0, 3).map((load, i) => (
<span
key={i}
className={`text-[10px] font-mono px-1.5 py-0.5 rounded ${i === 0 && load > info.server.cpus ? "bg-red-500/20 text-red-400" : "bg-surface-3 text-gray-300"}`}
className={`text-[10px] font-mono px-1.5 py-0.5 rounded ${i === 0 && load > info.server.cpus ? "bg-status-danger/20 text-status-danger" : "bg-surface-3 text-fg-secondary"}`}
>
{load.toFixed(2)}
</span>
@@ -307,8 +307,8 @@ function SystemHealthTab() {
</div>
</div>
<div className="flex items-center gap-3">
<span className="text-xs text-gray-400 w-28 flex-shrink-0">Node RSS</span>
<span className="text-xs text-gray-200 font-mono ml-auto">
<span className="text-xs text-fg-secondary w-28 flex-shrink-0">Node RSS</span>
<span className="text-xs text-fg-secondary font-mono ml-auto">
{formatBytes(info.server.memory.rss)}
</span>
</div>
@@ -321,12 +321,12 @@ function SystemHealthTab() {
>
<div className="space-y-1">
<div className="flex justify-between text-[10px]">
<span className="text-gray-500">Host Memory</span>
<span className="text-gray-400 font-mono">{memUsedPct.toFixed(0)}%</span>
<span className="text-fg-muted">Host Memory</span>
<span className="text-fg-secondary font-mono">{memUsedPct.toFixed(0)}%</span>
</div>
<div className="w-full bg-surface-3 rounded-full h-2">
<div
className={`h-2 rounded-full transition-all duration-700 ${memUsedPct > 90 ? "bg-red-500" : memUsedPct > 70 ? "bg-amber-500" : "bg-emerald-500"}`}
className={`h-2 rounded-full transition-all duration-700 ${memUsedPct > 90 ? "bg-status-danger" : memUsedPct > 70 ? "bg-status-warning" : "bg-status-success"}`}
style={{ width: `${memUsedPct}%` }}
/>
</div>
@@ -338,12 +338,12 @@ function SystemHealthTab() {
>
<div className="space-y-1">
<div className="flex justify-between text-[10px]">
<span className="text-gray-500">V8 Heap</span>
<span className="text-gray-400 font-mono">{heapUsedPct.toFixed(0)}%</span>
<span className="text-fg-muted">V8 Heap</span>
<span className="text-fg-secondary font-mono">{heapUsedPct.toFixed(0)}%</span>
</div>
<div className="w-full bg-surface-3 rounded-full h-2">
<div
className={`h-2 rounded-full transition-all duration-700 ${heapUsedPct > 85 ? "bg-red-500" : heapUsedPct > 60 ? "bg-amber-500" : "bg-blue-500"}`}
className={`h-2 rounded-full transition-all duration-700 ${heapUsedPct > 85 ? "bg-status-danger" : heapUsedPct > 60 ? "bg-status-warning" : "bg-blue-600"}`}
style={{ width: `${heapUsedPct}%` }}
/>
</div>
@@ -356,13 +356,13 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Database className="w-4 h-4 text-blue-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Storage</span>
<Database className="w-4 h-4 text-blue-500" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Storage</span>
</div>
<Tip
raw={`Write velocity (events):\n5 min: ${info.db.load_stats?.m5 ?? 0}\n15 min: ${info.db.load_stats?.m15 ?? 0}\n1 hr: ${info.db.load_stats?.h1 ?? 0}`}
>
<span className="text-[10px] font-mono text-emerald-400 bg-emerald-500/5 px-2 py-0.5 rounded border border-emerald-500/10 cursor-default">
<span className="text-[10px] font-mono text-status-success bg-status-success/5 px-2 py-0.5 rounded border border-status-success/10 cursor-default">
⚡ {info.db.load_stats?.m5 ?? 0}/{info.db.load_stats?.m15 ?? 0}/
{info.db.load_stats?.h1 ?? 0}
</span>
@@ -370,8 +370,8 @@ function SystemHealthTab() {
</div>
<div className="flex items-center gap-3">
<span className="text-xs text-gray-400 w-28 flex-shrink-0">Database</span>
<span className="text-xs text-gray-200 font-mono ml-auto">
<span className="text-xs text-fg-secondary w-28 flex-shrink-0">Database</span>
<span className="text-xs text-fg-secondary font-mono ml-auto">
{formatBytes(info.db.size)} · {info.db.pragmas?.journal_mode?.toUpperCase() || "WAL"}
</span>
</div>
@@ -425,7 +425,7 @@ function SystemHealthTab() {
y="46"
textAnchor="middle"
dominantBaseline="middle"
className="fill-gray-300"
className="fill-fg-secondary"
fontSize="12"
fontWeight="700"
fontFamily="monospace"
@@ -437,7 +437,7 @@ function SystemHealthTab() {
y="60"
textAnchor="middle"
dominantBaseline="middle"
className="fill-gray-600"
className="fill-fg-muted"
fontSize="8"
>
total
@@ -475,8 +475,8 @@ function SystemHealthTab() {
className="w-2.5 h-2.5 rounded-sm flex-shrink-0"
style={{ backgroundColor: item.color }}
/>
<span className="text-gray-400">{item.label}</span>
<span className="text-gray-500 ml-auto pl-3 font-mono">
<span className="text-fg-secondary">{item.label}</span>
<span className="text-fg-muted ml-auto pl-3 font-mono">
{Math.round(item.pct)}%
</span>
</div>
@@ -490,13 +490,13 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<ShieldCheck className="w-4 h-4 text-emerald-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Health Score</span>
<ShieldCheck className="w-4 h-4 text-status-success" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Health Score</span>
</div>
<Tip
raw={`Composite Index formula:\n0.4 × Success Rate (${successRate.toFixed(1)}%)\n+ 0.25 × Cache Hit (${cacheHitRate.toFixed(1)}%)\n+ 0.25 × (100 − Error Rate) (${(100 - errorRate).toFixed(1)}%)\n+ 0.10 × (100 − Heap%) (${(100 - heapUsedPct).toFixed(1)}%)\n= ${healthScore.toFixed(1)}`}
>
<span className="text-[10px] text-gray-500 cursor-help border-b border-dashed border-gray-700">
<span className="text-[10px] text-fg-muted cursor-help border-b border-dashed border-border-light">
ⓘ Formula
</span>
</Tip>
@@ -526,7 +526,7 @@ function SystemHealthTab() {
y="57"
textAnchor="middle"
dominantBaseline="middle"
className="fill-gray-100"
className="fill-fg-primary"
fontSize="24"
fontWeight="800"
fontFamily="monospace"
@@ -538,7 +538,7 @@ function SystemHealthTab() {
y="76"
textAnchor="middle"
dominantBaseline="middle"
className="fill-gray-600"
className="fill-fg-muted"
fontSize="9"
fontWeight="500"
>
@@ -555,8 +555,8 @@ function SystemHealthTab() {
raw={`Cache Hit Rate: ${cacheHitRate.toFixed(1)}%\nHits: ${info.transcript_cache?.hits ?? 0}\nMisses: ${info.transcript_cache?.misses ?? 0}`}
>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Cache</p>
<p className="text-xs font-mono font-bold text-blue-400">
<p className="text-[9px] text-fg-muted uppercase">Cache</p>
<p className="text-xs font-mono font-bold text-blue-500">
{cacheHitRate.toFixed(0)}%
</p>
</div>
@@ -566,9 +566,9 @@ function SystemHealthTab() {
raw={`Error Rate: ${errorRate.toFixed(2)}%\n<5% = healthy, 5-15% = warning, >15% = critical`}
>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Errors</p>
<p className="text-[9px] text-fg-muted uppercase">Errors</p>
<p
className={`text-xs font-mono font-bold ${errorRate < 5 ? "text-emerald-400" : errorRate < 15 ? "text-amber-400" : "text-red-400"}`}
className={`text-xs font-mono font-bold ${errorRate < 5 ? "text-status-success" : errorRate < 15 ? "text-status-warning" : "text-status-danger"}`}
>
{errorRate.toFixed(1)}%
</p>
@@ -579,7 +579,7 @@ function SystemHealthTab() {
raw={`Transcript compactions: ${workflow.compaction?.totalCompactions ?? 0}\nReduces context window by summarizing turns.`}
>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Compact</p>
<p className="text-[9px] text-fg-muted uppercase">Compact</p>
<p className="text-xs font-mono font-bold text-violet-400">
{workflow.compaction?.totalCompactions ?? 0}
</p>
@@ -590,8 +590,8 @@ function SystemHealthTab() {
raw={`Tokens recovered: ${(workflow.compaction?.tokensRecovered ?? 0).toLocaleString()}\nFreed by compaction.`}
>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Saved</p>
<p className="text-xs font-mono font-bold text-emerald-400">
<p className="text-[9px] text-fg-muted uppercase">Saved</p>
<p className="text-xs font-mono font-bold text-status-success">
{((workflow.compaction?.tokensRecovered ?? 0) / 1000).toFixed(1)}K
</p>
</div>
@@ -606,10 +606,10 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Bot className="w-4 h-4 text-blue-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Token Usage</span>
<Bot className="w-4 h-4 text-blue-500" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Token Usage</span>
</div>
<span className="text-[10px] font-mono text-gray-500">
<span className="text-[10px] font-mono text-fg-muted">
{(totalTokens / 1000).toFixed(1)}K total
</span>
</div>
@@ -619,10 +619,10 @@ function SystemHealthTab() {
const pct =
totalTokens > 0 ? ((m.input_tokens + m.output_tokens) / totalTokens) * 100 : 0;
const colors = [
"bg-blue-400",
"bg-blue-500",
"bg-violet-400",
"bg-emerald-400",
"bg-amber-400",
"bg-status-success",
"bg-status-warning",
"bg-pink-400",
"bg-cyan-400",
];
@@ -634,7 +634,7 @@ function SystemHealthTab() {
>
<div className="flex items-center gap-3 cursor-default">
<span
className="text-xs text-gray-400 w-28 truncate flex-shrink-0"
className="text-xs text-fg-secondary w-28 truncate flex-shrink-0"
title={formatModelName(m.model) ?? m.model}
>
{formatModelName(m.model) ?? m.model}
@@ -645,7 +645,7 @@ function SystemHealthTab() {
style={{ width: `${pct}%` }}
/>
</div>
<span className="text-xs text-gray-500 w-12 text-right flex-shrink-0 font-mono">
<span className="text-xs text-fg-muted w-12 text-right flex-shrink-0 font-mono">
{pct.toFixed(1)}%
</span>
</div>
@@ -653,7 +653,7 @@ function SystemHealthTab() {
);
})}
{modelStats.length === 0 && (
<p className="text-xs text-gray-600 text-center py-4">No model data</p>
<p className="text-xs text-fg-muted text-center py-4">No model data</p>
)}
</div>
</div>
@@ -663,9 +663,9 @@ function SystemHealthTab() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<BarChart3 className="w-4 h-4 text-violet-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Concurrency</span>
<span className="text-xs text-fg-muted uppercase tracking-wider">Concurrency</span>
</div>
<span className="text-[10px] font-mono text-gray-500">{lanes.length} intervals</span>
<span className="text-[10px] font-mono text-fg-muted">{lanes.length} intervals</span>
</div>
{/* Sparkline-style bar chart matching Analytics sparkline */}
@@ -698,7 +698,7 @@ function SystemHealthTab() {
);
})}
{lanes.length === 0 && (
<p className="text-xs text-gray-600 text-center w-full self-center">
<p className="text-xs text-fg-muted text-center w-full self-center">
No concurrency data
</p>
)}
@@ -708,8 +708,8 @@ function SystemHealthTab() {
<div className="grid grid-cols-3 gap-3 border-t border-border/40 pt-3">
<Tip block raw={`Peak: ${maxLaneCount} sessions running simultaneously.`}>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Peak</p>
<p className="text-sm font-mono font-bold text-gray-200">{maxLaneCount}</p>
<p className="text-[9px] text-fg-muted uppercase">Peak</p>
<p className="text-sm font-mono font-bold text-fg-secondary">{maxLaneCount}</p>
</div>
</Tip>
<Tip
@@ -717,16 +717,16 @@ function SystemHealthTab() {
raw={`${lanes.filter((l) => l.count > 0).length} of ${lanes.length} intervals have active sessions.`}
>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Active</p>
<p className="text-sm font-mono font-bold text-emerald-400">
<p className="text-[9px] text-fg-muted uppercase">Active</p>
<p className="text-sm font-mono font-bold text-status-success">
{lanes.filter((l) => l.count > 0).length}
</p>
</div>
</Tip>
<Tip block raw={`Average concurrency across all intervals.`}>
<div className="text-center cursor-default">
<p className="text-[9px] text-gray-600 uppercase">Avg</p>
<p className="text-sm font-mono font-bold text-blue-400">
<p className="text-[9px] text-fg-muted uppercase">Avg</p>
<p className="text-sm font-mono font-bold text-blue-500">
{lanes.length > 0
? (lanes.reduce((s, l) => s + l.count, 0) / lanes.length).toFixed(1)
: "0"}
@@ -743,23 +743,23 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Zap className="w-4 h-4 text-amber-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Tool Usage</span>
<Zap className="w-4 h-4 text-status-warning" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Tool Usage</span>
</div>
<span className="text-[10px] font-mono text-gray-500">top {topTools.length}</span>
<span className="text-[10px] font-mono text-fg-muted">top {topTools.length}</span>
</div>
<div className="space-y-2">
{topTools.map((tool, i) => {
const pct = maxToolCount > 0 ? Math.round((tool.count / maxToolCount) * 100) : 0;
const colors = [
"bg-amber-400",
"bg-blue-400",
"bg-emerald-400",
"bg-status-warning",
"bg-blue-500",
"bg-status-success",
"bg-violet-400",
"bg-pink-400",
"bg-cyan-400",
"bg-red-400",
"bg-status-danger",
"bg-indigo-400",
];
return (
@@ -770,7 +770,7 @@ function SystemHealthTab() {
>
<div className="flex items-center gap-3 cursor-default">
<span
className="text-xs text-gray-400 w-28 truncate flex-shrink-0"
className="text-xs text-fg-secondary w-28 truncate flex-shrink-0"
title={tool.tool_name}
>
{tool.tool_name}
@@ -781,7 +781,7 @@ function SystemHealthTab() {
style={{ width: `${pct}%` }}
/>
</div>
<span className="text-xs text-gray-500 w-10 text-right flex-shrink-0 font-mono">
<span className="text-xs text-fg-muted w-10 text-right flex-shrink-0 font-mono">
{tool.count > 999 ? `${(tool.count / 1000).toFixed(1)}K` : tool.count}
</span>
</div>
@@ -789,7 +789,7 @@ function SystemHealthTab() {
);
})}
{topTools.length === 0 && (
<p className="text-xs text-gray-600 text-center py-6">No tool data yet</p>
<p className="text-xs text-fg-muted text-center py-6">No tool data yet</p>
)}
</div>
</div>
@@ -799,7 +799,7 @@ function SystemHealthTab() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<GitBranch className="w-4 h-4 text-violet-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">
<span className="text-xs text-fg-muted uppercase tracking-wider">
Subagent Effectiveness
</span>
</div>
@@ -809,10 +809,10 @@ function SystemHealthTab() {
{effectiveness.map((item, i) => {
const color =
item.successRate >= 90
? "bg-emerald-400"
? "bg-status-success"
: item.successRate >= 70
? "bg-amber-400"
: "bg-red-400";
? "bg-status-warning"
: "bg-status-danger";
return (
<Tip
block
@@ -820,7 +820,7 @@ function SystemHealthTab() {
raw={`${item.subagent_type || "default"}\nSuccess: ${item.successRate.toFixed(1)}%\nTotal: ${item.total} · OK: ${item.completed} · Errors: ${item.errors}`}
>
<div className="flex items-center gap-3 cursor-default">
<span className="text-xs text-gray-400 w-28 truncate flex-shrink-0">
<span className="text-xs text-fg-secondary w-28 truncate flex-shrink-0">
{item.subagent_type || "default"}
</span>
<div className="flex-1 bg-surface-3 rounded-full h-2">
@@ -830,7 +830,7 @@ function SystemHealthTab() {
/>
</div>
<span
className={`text-xs w-12 text-right flex-shrink-0 font-mono ${item.successRate >= 90 ? "text-emerald-400" : item.successRate >= 70 ? "text-amber-400" : "text-red-400"}`}
className={`text-xs w-12 text-right flex-shrink-0 font-mono ${item.successRate >= 90 ? "text-status-success" : item.successRate >= 70 ? "text-status-warning" : "text-status-danger"}`}
>
{item.successRate.toFixed(0)}%
</span>
@@ -839,7 +839,7 @@ function SystemHealthTab() {
);
})}
{effectiveness.length === 0 && (
<p className="text-xs text-gray-600 text-center py-6">No subagent data yet</p>
<p className="text-xs text-fg-muted text-center py-6">No subagent data yet</p>
)}
</div>
</div>
@@ -851,11 +851,11 @@ function SystemHealthTab() {
<div className="card p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Plug className="w-4 h-4 text-amber-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Integration</span>
<Plug className="w-4 h-4 text-status-warning" />
<span className="text-xs text-fg-muted uppercase tracking-wider">Integration</span>
</div>
<span
className={`text-[10px] font-mono px-2 py-0.5 rounded ${info.hooks.installed ? "bg-emerald-500/10 text-emerald-400 border border-emerald-500/20" : "bg-surface-3 text-gray-500 border border-border"}`}
className={`text-[10px] font-mono px-2 py-0.5 rounded ${info.hooks.installed ? "bg-status-success/10 text-status-success border border-status-success/20" : "bg-surface-3 text-fg-muted border border-border"}`}
>
{info.hooks.installed ? "Active" : "Offline"}
</span>
@@ -871,9 +871,9 @@ function SystemHealthTab() {
>
<div className="flex items-center gap-3 bg-surface-2/50 px-3 py-2 rounded-lg border border-border/30 cursor-default">
<div
className={`w-2 h-2 rounded-full flex-shrink-0 ${active ? "bg-emerald-400" : "bg-gray-600"}`}
className={`w-2 h-2 rounded-full flex-shrink-0 ${active ? "bg-status-success" : "bg-surface-4"}`}
/>
<span className="text-xs text-gray-300 truncate font-mono">
<span className="text-xs text-fg-secondary truncate font-mono">
{cwd.split("/").pop() || cwd}
</span>
</div>
@@ -882,8 +882,8 @@ function SystemHealthTab() {
</div>
) : (
<div className="flex flex-col items-center justify-center py-6 border border-dashed border-border/40 rounded-lg">
<Search className="w-4 h-4 text-gray-600 mb-2" />
<p className="text-xs text-gray-500">No project hooks registered</p>
<Search className="w-4 h-4 text-fg-muted mb-2" />
<p className="text-xs text-fg-muted">No project hooks registered</p>
</div>
)}
@@ -891,11 +891,11 @@ function SystemHealthTab() {
block
raw={`WebSocket connections: ${info.server.ws_connections}\nProtocol: RFC 6455`}
>
<div className="flex items-center gap-3 bg-emerald-500/5 px-3 py-2.5 rounded-lg border border-emerald-500/10 cursor-default">
<Activity className="w-3.5 h-3.5 text-emerald-400 animate-pulse" />
<div className="flex items-center gap-3 bg-status-success/5 px-3 py-2.5 rounded-lg border border-status-success/10 cursor-default">
<Activity className="w-3.5 h-3.5 text-status-success animate-pulse" />
<div>
<p className="text-[10px] text-emerald-400 font-medium">WebSocket Active</p>
<p className="text-[10px] text-gray-500">
<p className="text-[10px] text-status-success font-medium">WebSocket Active</p>
<p className="text-[10px] text-fg-muted">
{info.server.ws_connections} connection
{info.server.ws_connections !== 1 ? "s" : ""}
</p>
@@ -909,9 +909,9 @@ function SystemHealthTab() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Cpu className="w-4 h-4 text-cyan-400" />
<span className="text-xs text-gray-500 uppercase tracking-wider">Platform</span>
<span className="text-xs text-fg-muted uppercase tracking-wider">Platform</span>
</div>
<span className="text-[10px] font-mono text-gray-500">{info.server.node_version}</span>
<span className="text-[10px] font-mono text-fg-muted">{info.server.node_version}</span>
</div>
<div className="space-y-2">
@@ -935,16 +935,18 @@ function SystemHealthTab() {
{ label: "Platform", value: `${info.server.platform} / ${info.server.arch}` },
].map((row) => (
<div key={row.label} className="flex items-center gap-3">
<span className="text-xs text-gray-400 w-28 flex-shrink-0">{row.label}</span>
<span className="text-xs text-gray-200 font-mono ml-auto">{row.value}</span>
<span className="text-xs text-fg-secondary w-28 flex-shrink-0">{row.label}</span>
<span className="text-xs text-fg-secondary font-mono ml-auto">{row.value}</span>
</div>
))}
</div>
<Tip block raw={info.db.path}>
<div className="flex items-center gap-2 bg-surface-2/50 px-3 py-2 rounded-lg border border-border/30 cursor-default">
<HardDrive className="w-3 h-3 text-gray-500 flex-shrink-0" />
<span className="text-[10px] text-gray-400 font-mono truncate">{info.db.path}</span>
<HardDrive className="w-3 h-3 text-fg-muted flex-shrink-0" />
<span className="text-[10px] text-fg-secondary font-mono truncate">
{info.db.path}
</span>
</div>
</Tip>
</div>
@@ -1145,8 +1147,8 @@ export function Dashboard() {
if (error) {
return (
<div className="text-center py-20">
<p className="text-red-400 mb-2">{t("failedConnect")}</p>
<p className="text-sm text-gray-500">{error}</p>
<p className="text-status-danger mb-2">{t("failedConnect")}</p>
<p className="text-sm text-fg-muted">{error}</p>
<button onClick={load} className="btn-primary mt-4">
{t("common:retry")}
</button>
@@ -1163,20 +1165,20 @@ export function Dashboard() {
</div>
<div>
<div className="flex items-center gap-2">
<h1 className="text-lg font-semibold text-gray-100">{t("title")}</h1>
<h1 className="text-lg font-semibold text-fg-primary">{t("title")}</h1>
{wsConnected ? (
<span className="flex items-center gap-1.5 text-[11px] text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 px-2 py-0.5 rounded-full">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse-dot" />
<span className="flex items-center gap-1.5 text-[11px] text-status-success bg-status-success/10 border border-status-success/20 px-2 py-0.5 rounded-full">
<span className="w-1.5 h-1.5 rounded-full bg-status-success animate-pulse-dot" />
{t("common:live")}
</span>
) : (
<span className="flex items-center gap-1.5 text-[11px] text-gray-400 bg-gray-500/10 border border-gray-500/20 px-2 py-0.5 rounded-full">
<span className="w-1.5 h-1.5 rounded-full bg-gray-400" />
<span className="flex items-center gap-1.5 text-[11px] text-fg-secondary bg-surface-4/10 border border-border-light/20 px-2 py-0.5 rounded-full">
<span className="w-1.5 h-1.5 rounded-full bg-surface-4" />
{t("common:offline")}
</span>
)}
</div>
<p className="text-xs text-gray-500">{t("subtitle")}</p>
<p className="text-xs text-fg-muted">{t("subtitle")}</p>
</div>
</div>
<div className="flex items-center gap-3">
@@ -1187,7 +1189,7 @@ export function Dashboard() {
className={`px-2.5 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-2 ${
activeTab === "monitor"
? "bg-accent/15 text-accent shadow-sm"
: "text-gray-500 hover:text-gray-300"
: "text-fg-muted hover:text-fg-secondary"
}`}
>
<Activity className="w-3.5 h-3.5" /> Monitor
@@ -1197,7 +1199,7 @@ export function Dashboard() {
className={`px-2.5 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-2 ${
activeTab === "health"
? "bg-accent/15 text-accent shadow-sm"
: "text-gray-500 hover:text-gray-300"
: "text-fg-muted hover:text-fg-secondary"
}`}
>
<Server className="w-3.5 h-3.5" /> Health
@@ -1225,7 +1227,7 @@ export function Dashboard() {
label={t("activeAgents")}
value={stats?.active_agents ?? ""}
icon={Bot}
accentColor="text-emerald-400"
accentColor="text-status-success"
loading={!stats}
/>
<StatCard
@@ -1261,7 +1263,7 @@ export function Dashboard() {
: undefined
}
icon={DollarSign}
accentColor="text-emerald-400"
accentColor="text-status-success"
loading={totalCost === null}
/>
</div>
@@ -1270,7 +1272,9 @@ export function Dashboard() {
{/* Active agents */}
<div ref={agentsContainerRef} className="min-w-0 overflow-y-auto pr-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-gray-300">{t("activeAgentsSection")}</h3>
<h3 className="text-sm font-medium text-fg-secondary">
{t("activeAgentsSection")}
</h3>
<button onClick={() => navigate("/kanban")} className="btn-ghost text-xs">
{t("viewBoard")} <ArrowRight className="w-3 h-3" />
</button>
@@ -1312,7 +1316,7 @@ export function Dashboard() {
{hasChildren && (
<button
onClick={toggleExpanded}
className="p-1 text-gray-500 hover:text-gray-300 transition-colors flex-shrink-0"
className="p-1 text-fg-muted hover:text-fg-secondary transition-colors flex-shrink-0"
aria-label={isExpanded ? "Collapse subagents" : "Expand subagents"}
aria-expanded={isExpanded}
>
@@ -1375,7 +1379,7 @@ export function Dashboard() {
>
{t("common:subagent_label", { count: totalDesc })}
{activeDesc > 0 && (
<span className="text-emerald-400 ml-1">
<span className="text-status-success ml-1">
({activeDesc} {t("common:active")})
</span>
)}
@@ -1436,7 +1440,7 @@ export function Dashboard() {
{/* Recent activity */}
<div ref={activityContainerRef} className="min-w-0 overflow-y-auto pl-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-gray-300">{t("recentActivity")}</h3>
<h3 className="text-sm font-medium text-fg-secondary">{t("recentActivity")}</h3>
<button onClick={() => navigate("/activity")} className="btn-ghost text-xs">
{t("viewAll")} <ArrowRight className="w-3 h-3" />
</button>
@@ -1469,7 +1473,7 @@ export function Dashboard() {
: "waiting"
}
/>
<span className="text-sm text-gray-300 truncate flex-1">
<span className="text-sm text-fg-secondary truncate flex-1">
{event.summary || event.event_type}
</span>
{(() => {
@@ -1479,7 +1483,7 @@ export function Dashboard() {
const isAuto = /^Session [0-9a-f]{8}$/i.test(sname);
return (
<span
className="text-[11px] text-gray-500 truncate max-w-[9rem] flex-shrink-0"
className="text-[11px] text-fg-muted truncate max-w-[9rem] flex-shrink-0"
title={event.session_id}
>
{sname && !isAuto ? (
@@ -1491,11 +1495,11 @@ export function Dashboard() {
);
})()}
{event.tool_name && (
<span className="text-[11px] text-gray-500 font-mono">
<span className="text-[11px] text-fg-muted font-mono">
{event.tool_name}
</span>
)}
<span className="text-[11px] text-gray-600 flex-shrink-0">
<span className="text-[11px] text-fg-muted flex-shrink-0">
{timeAgo(event.created_at)}
</span>
</div>