From c9215546386925b765f65619ea2f393ffa2e3d11 Mon Sep 17 00:00:00 2001 From: jbingham17 Date: Thu, 5 Mar 2026 10:57:27 -0800 Subject: [PATCH 1/3] Improve memory display with full units and detailed stats Use full unit names (KB, MB, GB) with proper spacing and show used/total with percentages in the memory graph header and stats. Co-Authored-By: Claude Opus 4.6 --- src/components/MemoryBar.tsx | 4 ++-- src/components/MemoryGraph.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/MemoryBar.tsx b/src/components/MemoryBar.tsx index 870bfa4..390ccfb 100644 --- a/src/components/MemoryBar.tsx +++ b/src/components/MemoryBar.tsx @@ -8,9 +8,9 @@ interface MemoryBarProps { function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; - const sizes = ['B', 'K', 'M', 'G', 'T']; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + sizes[i]; + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } export function MemoryBar({ label, used, total, percent }: MemoryBarProps) { diff --git a/src/components/MemoryGraph.tsx b/src/components/MemoryGraph.tsx index 995fe06..0ec6343 100644 --- a/src/components/MemoryGraph.tsx +++ b/src/components/MemoryGraph.tsx @@ -63,7 +63,7 @@ export function MemoryGraph({ used, total, percent }: MemoryGraphProps) {
Memory - {percent}% + {formatBytes(used)} / {formatBytes(total)} ({percent}%)
@@ -113,7 +113,7 @@ export function MemoryGraph({ used, total, percent }: MemoryGraphProps) {
Used - {formatBytes(used)} + {formatBytes(used)} ({percent}%) Total @@ -121,7 +121,7 @@ export function MemoryGraph({ used, total, percent }: MemoryGraphProps) { Free - {formatBytes(total - used)} + {formatBytes(total - used)} ({total > 0 ? Math.round(((total - used) / total) * 100) : 0}%)
From 526cb45d7602dda43aadf8b3cf96f85422f28e78 Mon Sep 17 00:00:00 2001 From: jbingham17 Date: Thu, 5 Mar 2026 11:24:20 -0800 Subject: [PATCH 2/3] Add flashing LIVE status indicator to the status bar Adds a green dot that flashes on/off every second with a "LIVE" label, giving a visual heartbeat indicator in the bottom status bar. Co-Authored-By: Claude Opus 4.6 --- src/App.css | 28 ++++++++++++++++++++++++++++ src/components/StatusBar.tsx | 15 +++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/App.css b/src/App.css index 4faacb1..7926fde 100644 --- a/src/App.css +++ b/src/App.css @@ -450,6 +450,34 @@ body { .col-time { width: 60px; color: var(--color-text-dim); } .col-command { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +/* Status Indicator */ +.status-indicator { + display: flex; + align-items: center; + gap: 6px; +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-green); + opacity: 0.3; + transition: opacity 0.3s ease; +} + +.status-dot.active { + opacity: 1; + box-shadow: 0 0 8px var(--color-green), 0 0 16px rgba(52, 211, 153, 0.4); +} + +.status-text { + color: var(--color-green); + font-size: 10px; + font-weight: 700; + letter-spacing: 2px; +} + /* Status Bar */ .status-bar { display: flex; diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx index 0180316..19abb56 100644 --- a/src/components/StatusBar.tsx +++ b/src/components/StatusBar.tsx @@ -1,3 +1,5 @@ +import { useState, useEffect } from 'react'; + interface StatusBarProps { filter: string; onFilterChange: (filter: string) => void; @@ -6,6 +8,15 @@ interface StatusBarProps { } export function StatusBar({ filter, onFilterChange, refreshRate, onRefreshRateChange }: StatusBarProps) { + const [statusActive, setStatusActive] = useState(true); + + useEffect(() => { + const interval = setInterval(() => { + setStatusActive((prev) => !prev); + }, 1000); + return () => clearInterval(interval); + }, []); + const shortcuts = [ { key: 'F1', label: 'Help' }, { key: 'F2', label: 'Setup' }, @@ -19,6 +30,10 @@ export function StatusBar({ filter, onFilterChange, refreshRate, onRefreshRateCh return (
+
+ + LIVE +
Filter: Date: Thu, 5 Mar 2026 11:25:37 -0800 Subject: [PATCH 3/3] Add flashing alert bar for CPU and memory warnings New AlertBar component sits above the status bar and displays system health alerts. It flashes when CPU or memory usage exceeds thresholds (70% warn, 90% critical) and shows "All systems nominal" when healthy. Co-Authored-By: Claude Opus 4.6 --- src/App.css | 79 +++++++++++++++++++++++++++++++++++++ src/App.tsx | 3 ++ src/components/AlertBar.tsx | 66 +++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 src/components/AlertBar.tsx diff --git a/src/App.css b/src/App.css index 7926fde..cd07da7 100644 --- a/src/App.css +++ b/src/App.css @@ -450,6 +450,85 @@ body { .col-time { width: 60px; color: var(--color-text-dim); } .col-command { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +/* Alert Bar */ +.alert-bar { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 16px; + border-radius: 12px; + border: 1px solid var(--border-color); + flex-shrink: 0; + transition: opacity 0.2s ease; +} + +.alert-bar.alert-flash-off { + opacity: 0.4; +} + +.alert-bar.alert-info { + background: linear-gradient(145deg, rgba(52, 211, 153, 0.1) 0%, rgba(13, 18, 25, 0.8) 100%); + border-color: rgba(52, 211, 153, 0.3); +} + +.alert-bar.alert-warn { + background: linear-gradient(145deg, rgba(251, 191, 36, 0.1) 0%, rgba(13, 18, 25, 0.8) 100%); + border-color: rgba(251, 191, 36, 0.3); +} + +.alert-bar.alert-critical { + background: linear-gradient(145deg, rgba(248, 113, 113, 0.15) 0%, rgba(13, 18, 25, 0.8) 100%); + border-color: rgba(248, 113, 113, 0.4); +} + +.alert-icon { + font-weight: 900; + font-size: 12px; + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.alert-info .alert-icon { + color: var(--color-green); + background: rgba(52, 211, 153, 0.15); +} + +.alert-warn .alert-icon { + color: var(--color-yellow); + background: rgba(251, 191, 36, 0.15); +} + +.alert-critical .alert-icon { + color: var(--color-red); + background: rgba(248, 113, 113, 0.15); +} + +.alert-messages { + display: flex; + gap: 16px; + flex: 1; +} + +.alert-msg { + font-size: 11px; + font-weight: 600; + letter-spacing: 0.5px; +} + +.alert-msg-info { color: var(--color-green); } +.alert-msg-warn { color: var(--color-yellow); } +.alert-msg-critical { color: var(--color-red); } + +.alert-timestamp { + color: var(--color-text-dim); + font-size: 10px; + margin-left: auto; +} + /* Status Indicator */ .status-indicator { display: flex; diff --git a/src/App.tsx b/src/App.tsx index 60c8dc0..7421865 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { CpuGraph } from './components/CpuGraph'; import { MemoryGraph } from './components/MemoryGraph'; import { ProcessTable } from './components/ProcessTable'; import { StatusBar } from './components/StatusBar'; +import { AlertBar } from './components/AlertBar'; import { EnvironmentPanel } from './components/EnvironmentPanel'; import { useSystemMetrics } from './hooks/useSystemMetrics'; import './App.css'; @@ -71,6 +72,8 @@ function App() { + + a + b, 0) / cpuUsage.length; + + if (avgCpu > 90) { + alerts.push({ message: 'CPU usage critical', level: 'critical' }); + } else if (avgCpu > 70) { + alerts.push({ message: 'CPU usage high', level: 'warn' }); + } + + if (memPercent > 90) { + alerts.push({ message: 'Memory usage critical', level: 'critical' }); + } else if (memPercent > 70) { + alerts.push({ message: 'Memory usage high', level: 'warn' }); + } + + if (alerts.length === 0) { + alerts.push({ message: 'All systems nominal', level: 'info' }); + } + + return alerts; +} + +export function AlertBar({ cpuUsage, memPercent }: AlertBarProps) { + const [visible, setVisible] = useState(true); + const alerts = getAlerts(cpuUsage, memPercent); + const hasWarnings = alerts.some((a) => a.level !== 'info'); + + useEffect(() => { + if (!hasWarnings) return; + const interval = setInterval(() => { + setVisible((prev) => !prev); + }, 800); + return () => clearInterval(interval); + }, [hasWarnings]); + + const highestLevel = alerts.reduce<'info' | 'warn' | 'critical'>((max, a) => { + const order = { info: 0, warn: 1, critical: 2 }; + return order[a.level] > order[max] ? a.level : max; + }, 'info'); + + return ( +
+ + {highestLevel === 'critical' ? '!!' : highestLevel === 'warn' ? '!' : '~'} + +
+ {alerts.map((alert, i) => ( + + {alert.message} + + ))} +
+ + {new Date().toLocaleTimeString()} + +
+ ); +}