import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Monitor, RefreshCw, ShieldAlert, Skull, X } from "lucide-react"; import { fetchGlobalSettings, fetchSystemStats, killVitestProcesses, updateGlobalSettings, type KillVitestResponse, type SystemStatsResponse, } from "../api"; import "./SystemStatsModal.css"; interface SystemStatsModalProps { isOpen: boolean; onClose: () => void; projectId?: string; } function formatBytes(bytes: number): string { if (!Number.isFinite(bytes) || bytes < 0) return "—"; const mb = bytes / (1024 * 1024); if (mb < 1024) return `${mb.toFixed(0)} MB`; return `${(mb / 1024).toFixed(2)} GB`; } function toPercent(used: number, total: number): string { if (!Number.isFinite(used) || !Number.isFinite(total) || total <= 0) return "—"; return `${((used / total) * 100).toFixed(1)}%`; } type Severity = "normal" | "warning" | "critical"; function heapSeverity(used: number, limit: number): Severity { if (limit <= 0) return "normal"; const pct = used / limit; if (pct >= 0.85) return "critical"; if (pct >= 0.65) return "warning"; return "normal"; } function rssSeverity(rss: number, totalSystemMem: number): Severity { if (totalSystemMem <= 0) return "normal"; const pct = rss / totalSystemMem; if (pct >= 0.5) return "critical"; if (pct >= 0.25) return "warning"; return "normal"; } function systemMemSeverity(used: number, total: number): Severity { if (total <= 0) return "normal"; const pct = used / total; if (pct >= 0.9) return "critical"; if (pct >= 0.75) return "warning"; return "normal"; } function cpuSeverity(percent: number | null, cores: number): Severity { if (percent === null || !Number.isFinite(percent) || percent < 0) return "normal"; const normalized = cores > 0 ? percent / cores : percent; if (normalized >= 80) return "critical"; if (normalized >= 50) return "warning"; return "normal"; } function severityClassName(severity: Severity): string { if (severity === "critical") return "system-stats-modal__value--critical"; if (severity === "warning") return "system-stats-modal__value--warning"; return ""; } function formatTimestamp(value: string | null | undefined, notYetLabel: string): string { if (!value) return notYetLabel; const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) return notYetLabel; return parsed.toLocaleString(); } /** * SystemStatsModal groups dashboard runtime telemetry into five sections: * process memory metrics, CPU/load information, host memory usage, task counts * by column, and agent state counts. */ export function SystemStatsModal({ isOpen, onClose, projectId }: SystemStatsModalProps) { const { t } = useTranslation("app"); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [autoKillEnabled, setAutoKillEnabled] = useState(true); const [killThreshold, setKillThreshold] = useState(90); const [isKilling, setIsKilling] = useState(false); const [confirmKill, setConfirmKill] = useState(false); const [killResult, setKillResult] = useState(null); const [settingsError, setSettingsError] = useState(null); const [lastRefreshedAt, setLastRefreshedAt] = useState(null); const loadStats = useCallback(async (options?: { preserveKillResult?: boolean }) => { setLoading(true); try { const response = await fetchSystemStats(projectId); setStats(response); setError(null); setLastRefreshedAt(Date.now()); if (!options?.preserveKillResult) { setKillResult(null); } } catch (err) { setError(err instanceof Error ? err.message : t("systemStats.errorLoadStats", "Failed to load system stats")); } finally { setLoading(false); } }, [projectId]); useEffect(() => { if (!isOpen) return; void loadStats(); const timer = window.setInterval(() => { void loadStats(); }, 5_000); return () => { window.clearInterval(timer); }; }, [isOpen, loadStats]); useEffect(() => { if (!isOpen) return; const loadSettings = async () => { try { const settings = await fetchGlobalSettings(); setAutoKillEnabled(settings.vitestAutoKillEnabled ?? true); setKillThreshold(settings.vitestKillThresholdPct ?? 90); setSettingsError(null); } catch (err) { setSettingsError(err instanceof Error ? err.message : t("systemStats.errorLoadVitestSettings", "Failed to load vitest settings")); } }; void loadSettings(); }, [isOpen]); useEffect(() => { if (!isOpen) return; const onKeydown = (event: KeyboardEvent) => { if (event.key === "Escape") onClose(); }; document.addEventListener("keydown", onKeydown); return () => document.removeEventListener("keydown", onKeydown); }, [isOpen, onClose]); const processRows = useMemo(() => { if (!stats) return []; const system = stats.systemStats; const heapClassName = severityClassName(heapSeverity(system.heapUsed, system.heapLimit)); const rssClassName = severityClassName(rssSeverity(system.rss, system.systemTotalMem)); return [ { label: t("systemStats.rowRss", "RSS"), value: formatBytes(system.rss), detail: toPercent(system.rss, system.systemTotalMem), className: rssClassName }, { label: t("systemStats.rowHeapUsed", "Heap Used"), value: formatBytes(system.heapUsed), detail: t("systemStats.rowHeapUsedDetail", "of {{total}}", { total: formatBytes(system.heapTotal) }), className: heapClassName }, { label: t("systemStats.rowHeapLimit", "Heap Limit"), value: formatBytes(system.heapLimit), detail: t("systemStats.rowHeapLimitDetail", "V8 limit") }, { label: t("systemStats.rowExternal", "External"), value: formatBytes(system.external) }, { label: t("systemStats.rowArrayBuffers", "Array Buffers"), value: formatBytes(system.arrayBuffers) }, ]; }, [stats]); const persistAutoKill = useCallback(async (enabled: boolean) => { setAutoKillEnabled(enabled); try { await updateGlobalSettings({ vitestAutoKillEnabled: enabled }); setSettingsError(null); } catch (err) { setSettingsError(err instanceof Error ? err.message : t("systemStats.errorSaveVitestSettings", "Failed to save vitest settings")); } }, []); const persistKillThreshold = useCallback(async (nextThreshold: number) => { const clamped = Math.min(99, Math.max(50, Number.isFinite(nextThreshold) ? Math.round(nextThreshold) : 90)); setKillThreshold(clamped); try { await updateGlobalSettings({ vitestKillThresholdPct: clamped }); setSettingsError(null); } catch (err) { setSettingsError(err instanceof Error ? err.message : t("systemStats.errorSaveVitestSettings", "Failed to save vitest settings")); } }, []); const handleKillVitest = useCallback(async () => { if (isKilling) return; if (!confirmKill) { setConfirmKill(true); return; } setIsKilling(true); try { const result = await killVitestProcesses(projectId); setKillResult(result); setConfirmKill(false); await loadStats({ preserveKillResult: true }); } catch (err) { setError(err instanceof Error ? err.message : t("systemStats.errorKillVitest", "Failed to kill vitest processes")); } finally { setIsKilling(false); } }, [confirmKill, isKilling, loadStats, projectId]); if (!isOpen) return null; const system = stats?.systemStats; const isBackgroundRefreshing = loading && Boolean(stats); const refreshLabel = lastRefreshedAt ? t("systemStats.updatedAt", "Updated {{time}}", { time: new Date(lastRefreshedAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", }), }) : t("systemStats.waitingFirstUpdate", "Waiting for first update"); const taskStats = stats?.taskStats; const usedSystemMem = system ? system.systemTotalMem - system.systemFreeMem : 0; const usedSystemMemSeverity = system ? systemMemSeverity(usedSystemMem, system.systemTotalMem) : "normal"; const usedSystemClassName = system ? severityClassName(usedSystemMemSeverity) : ""; const usedSystemMemPercent = system && Number.isFinite(system.systemTotalMem) && system.systemTotalMem > 0 ? Math.max(0, Math.min(100, (usedSystemMem / system.systemTotalMem) * 100)) : 0; const usedSystemMemProgressLabel = system ? t("systemStats.systemMemProgressLabel", "System memory used: {{percent}}% ({{used}} of {{total}})", { percent: usedSystemMemPercent.toFixed(1), used: formatBytes(usedSystemMem), total: formatBytes(system.systemTotalMem), }) : t("systemStats.systemMemUnavailable", "System memory usage unavailable"); const vitestProcessCount = stats?.vitestProcessCount; const cpuLoadSeverity = cpuSeverity(system?.cpuPercent ?? null, system?.cpuCount ?? 0); const cpuClassName = severityClassName(cpuLoadSeverity); const cpuPercentValue = system?.cpuPercent ?? null; const cpuBarPercent = cpuPercentValue === null ? 0 : Math.max(0, Math.min(100, cpuPercentValue)); const cpuPercentLabel = cpuPercentValue === null ? t("systemStats.cpuSampling", "Sampling…") : `${cpuPercentValue.toFixed(1)}%`; const cpuProgressLabel = cpuPercentValue === null ? t("systemStats.cpuProgressUnavailable", "App CPU usage unavailable: waiting for another sample") : t("systemStats.cpuProgressLabel", "App CPU usage: {{percent}}%", { percent: cpuPercentValue.toFixed(1) }); const killResultClassName = killResult ? killResult.killed > 0 ? "system-stats-modal__kill-result system-stats-modal__kill-result--success" : "system-stats-modal__kill-result system-stats-modal__kill-result--error" : ""; const lastAutoKillLabel = formatTimestamp(stats?.vitestLastAutoKillAt, t("systemStats.notYet", "Not yet")); return (
{ if (event.target === event.currentTarget) onClose(); }} data-testid="system-stats-modal-overlay" >

{t("systemStats.title", "System Stats")}

{t("systemStats.autoRefresh", "Auto-refresh · 5s")} {refreshLabel}
{loading && !stats &&
{t("systemStats.loading", "Loading system stats…")}
} {error && !stats && (
{error}
)} {stats && (

{t("systemStats.sectionProcess", "Process")}

{processRows.map((row) => (
{row.label}
{row.value} {row.detail ? {row.detail} : null}
))}

{t("systemStats.sectionCpu", "CPU & Load")}

{t("systemStats.rowAppCpu", "App CPU")}
{cpuPercentLabel} {cpuPercentValue === null ? t("systemStats.cpuFirstSamplePending", "First sample pending") : t("systemStats.cpuProcessUsage", "process usage")}
{t("systemStats.rowLoadAvg", "Load Avg")}
{system?.loadAvg.map((value) => value.toFixed(2)).join(" ") ?? "—"}
{t("systemStats.rowCores", "Cores")}
{system?.cpuCount ?? "—"}
{t("systemStats.rowPlatform", "Platform")}
{system?.platform ?? "—"}
{t("systemStats.rowNode", "Node")}
{system?.nodeVersion ?? "—"}
{t("systemStats.rowPid", "PID")}
{system?.pid ?? "—"}

{t("systemStats.sectionSystem", "System")}

{t("systemStats.rowMemoryUsed", "Memory Used")}
{system ? formatBytes(usedSystemMem) : "—"} {system ? `${toPercent(usedSystemMem, system.systemTotalMem)} of ${formatBytes(system.systemTotalMem)}` : ""}
{t("systemStats.rowMemoryFree", "Memory Free")}
{system ? formatBytes(system.systemFreeMem) : "—"}

{t("systemStats.sectionTasks", "Tasks")}

{t("systemStats.rowTotal", "Total")}
{taskStats?.total ?? 0}
{Object.entries(taskStats?.byColumn ?? {}).map(([column, count]) => (
{column}
{count}
))}

{t("systemStats.sectionAgents", "Agents")}

{t("systemStats.agentIdle", "idle")}
{taskStats?.agents.idle ?? 0}
{t("systemStats.agentActive", "active")}
{taskStats?.agents.active ?? 0}
{t("systemStats.agentRunning", "running")}
{taskStats?.agents.running ?? 0}
{t("systemStats.agentError", "error")}
{taskStats?.agents.error ?? 0}

{t("systemStats.sectionVitest", "Vitest Controls")}

{t("systemStats.vitestProcesses", "Vitest Processes")}
{vitestProcessCount ?? "—"}
{ const nextValue = Number.parseInt(event.target.value, 10); void persistKillThreshold(Number.isNaN(nextValue) ? 90 : nextValue); }} /> { const nextValue = Number.parseInt(event.target.value, 10); void persistKillThreshold(Number.isNaN(nextValue) ? 90 : nextValue); }} onBlur={() => { void persistKillThreshold(killThreshold); }} />
{killResult &&

{t("systemStats.killedProcesses", "Killed {{count}} processes", { count: killResult.killed })}

}

{t("systemStats.lastAutoKill", "Last auto-kill: {{time}}", { time: lastAutoKillLabel })}

{settingsError &&

{settingsError}

}
)} {error && stats &&
{t("systemStats.footerRefreshFailed", "Latest refresh failed: {{error}}", { error })}
}
); }