From c9215546386925b765f65619ea2f393ffa2e3d11 Mon Sep 17 00:00:00 2001 From: jbingham17 Date: Thu, 5 Mar 2026 10:57:27 -0800 Subject: [PATCH 1/4] 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/4] 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/4] 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()} + +
+ ); +} From 01c76e1862f770814f2af7ae7f095005064310c1 Mon Sep 17 00:00:00 2001 From: jbingham17 Date: Thu, 5 Mar 2026 13:49:51 -0800 Subject: [PATCH 4/4] Add mouse-reactive pulsating effects to CPU and memory graph dashboards Graphs now respond to mouse hover with a radial glow that follows the cursor and pulsating animations on the chart area, values, core indicators, and memory bar. Co-Authored-By: Claude Opus 4.6 --- src/App.css | 117 +++++++++++++++++++++++++++++++++ src/components/CpuGraph.tsx | 20 +++++- src/components/MemoryGraph.tsx | 20 +++++- 3 files changed, 153 insertions(+), 4 deletions(-) diff --git a/src/App.css b/src/App.css index cd07da7..f7816f3 100644 --- a/src/App.css +++ b/src/App.css @@ -219,12 +219,46 @@ body { /* Graph Cards */ .cpu-graph, .memory-graph { + --mouse-x: 50%; + --mouse-y: 50%; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 12px; padding: 16px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); transition: border-color 0.3s, box-shadow 0.3s; + position: relative; + overflow: hidden; +} + +.cpu-graph::before, +.memory-graph::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient( + circle 200px at var(--mouse-x) var(--mouse-y), + rgba(96, 165, 250, 0.12) 0%, + transparent 70% + ); + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; + z-index: 0; +} + +.cpu-graph > *, +.memory-graph > * { + position: relative; + z-index: 1; +} + +.cpu-graph:hover::before, +.memory-graph:hover::before { + opacity: 1; } .cpu-graph:hover, @@ -233,6 +267,89 @@ body { box-shadow: 0 4px 30px rgba(96, 165, 250, 0.15); } +/* Pulsating graph effect on hover */ +.cpu-graph.graph-hovered .graph-container, +.memory-graph.graph-hovered .graph-container { + animation: graphPulsate 2s ease-in-out infinite; +} + +.cpu-graph.graph-hovered .graph-value, +.memory-graph.graph-hovered .graph-value { + animation: valuePulsate 1.5s ease-in-out infinite; +} + +.cpu-graph.graph-hovered .core-indicator { + animation: corePulsate 2s ease-in-out infinite; +} + +.cpu-graph.graph-hovered .core-indicator:nth-child(odd) { + animation-delay: 0.15s; +} + +.cpu-graph.graph-hovered .core-indicator:nth-child(3n) { + animation-delay: 0.3s; +} + +.memory-graph.graph-hovered .memory-bar-fill { + animation: barPulsate 1.8s ease-in-out infinite; +} + +.memory-graph.graph-hovered .memory-stat .stat-value { + animation: valuePulsate 2s ease-in-out infinite; +} + +.memory-graph.graph-hovered .memory-stat:nth-child(2) .stat-value { + animation-delay: 0.2s; +} + +.memory-graph.graph-hovered .memory-stat:nth-child(3) .stat-value { + animation-delay: 0.4s; +} + +@keyframes graphPulsate { + 0%, 100% { + filter: brightness(1) saturate(1); + transform: scale(1); + } + 50% { + filter: brightness(1.3) saturate(1.4); + transform: scale(1.01); + } +} + +@keyframes valuePulsate { + 0%, 100% { + opacity: 1; + text-shadow: 0 0 20px currentColor; + } + 50% { + opacity: 0.8; + text-shadow: 0 0 40px currentColor, 0 0 60px currentColor; + } +} + +@keyframes corePulsate { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.85; + transform: scale(1.02); + } +} + +@keyframes barPulsate { + 0%, 100% { + filter: brightness(1); + box-shadow: 0 0 20px currentColor; + } + 50% { + filter: brightness(1.4); + box-shadow: 0 0 35px currentColor, 0 0 50px currentColor; + } +} + .graph-header { display: flex; justify-content: space-between; diff --git a/src/components/CpuGraph.tsx b/src/components/CpuGraph.tsx index a5a510a..fc03870 100644 --- a/src/components/CpuGraph.tsx +++ b/src/components/CpuGraph.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef, useCallback } from 'react'; import { AreaChart, Area, @@ -36,6 +36,16 @@ const COLORS = [ export function CpuGraph({ cpuUsage }: CpuGraphProps) { const [history, setHistory] = useState([]); + const [isHovered, setIsHovered] = useState(false); + const containerRef = useRef(null); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const x = ((e.clientX - rect.left) / rect.width) * 100; + const y = ((e.clientY - rect.top) / rect.height) * 100; + e.currentTarget.style.setProperty('--mouse-x', `${x}%`); + e.currentTarget.style.setProperty('--mouse-y', `${y}%`); + }, []); useEffect(() => { const newPoint: HistoryPoint = { time: Date.now() }; @@ -57,7 +67,13 @@ export function CpuGraph({ cpuUsage }: CpuGraphProps) { : 0; return ( -
+
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onMouseMove={handleMouseMove} + >
CPU diff --git a/src/components/MemoryGraph.tsx b/src/components/MemoryGraph.tsx index 0ec6343..890a364 100644 --- a/src/components/MemoryGraph.tsx +++ b/src/components/MemoryGraph.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef, useCallback } from 'react'; import { AreaChart, Area, @@ -32,6 +32,16 @@ function formatBytes(bytes: number): string { export function MemoryGraph({ used, total, percent }: MemoryGraphProps) { const [history, setHistory] = useState([]); + const [isHovered, setIsHovered] = useState(false); + const containerRef = useRef(null); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const x = ((e.clientX - rect.left) / rect.width) * 100; + const y = ((e.clientY - rect.top) / rect.height) * 100; + e.currentTarget.style.setProperty('--mouse-x', `${x}%`); + e.currentTarget.style.setProperty('--mouse-y', `${y}%`); + }, []); useEffect(() => { const newPoint: HistoryPoint = { @@ -59,7 +69,13 @@ export function MemoryGraph({ used, total, percent }: MemoryGraphProps) { const color = getMemoryColor(percent); return ( -
+
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onMouseMove={handleMouseMove} + >
Memory