Skip to content

Commit 109c0ff

Browse files
jbingham17claude
andcommitted
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 <noreply@anthropic.com>
1 parent 526cb45 commit 109c0ff

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

src/App.css

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,85 @@ body {
450450
.col-time { width: 60px; color: var(--color-text-dim); }
451451
.col-command { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
452452

453+
/* Alert Bar */
454+
.alert-bar {
455+
display: flex;
456+
align-items: center;
457+
gap: 12px;
458+
padding: 8px 16px;
459+
border-radius: 12px;
460+
border: 1px solid var(--border-color);
461+
flex-shrink: 0;
462+
transition: opacity 0.2s ease;
463+
}
464+
465+
.alert-bar.alert-flash-off {
466+
opacity: 0.4;
467+
}
468+
469+
.alert-bar.alert-info {
470+
background: linear-gradient(145deg, rgba(52, 211, 153, 0.1) 0%, rgba(13, 18, 25, 0.8) 100%);
471+
border-color: rgba(52, 211, 153, 0.3);
472+
}
473+
474+
.alert-bar.alert-warn {
475+
background: linear-gradient(145deg, rgba(251, 191, 36, 0.1) 0%, rgba(13, 18, 25, 0.8) 100%);
476+
border-color: rgba(251, 191, 36, 0.3);
477+
}
478+
479+
.alert-bar.alert-critical {
480+
background: linear-gradient(145deg, rgba(248, 113, 113, 0.15) 0%, rgba(13, 18, 25, 0.8) 100%);
481+
border-color: rgba(248, 113, 113, 0.4);
482+
}
483+
484+
.alert-icon {
485+
font-weight: 900;
486+
font-size: 12px;
487+
width: 22px;
488+
height: 22px;
489+
display: flex;
490+
align-items: center;
491+
justify-content: center;
492+
border-radius: 50%;
493+
}
494+
495+
.alert-info .alert-icon {
496+
color: var(--color-green);
497+
background: rgba(52, 211, 153, 0.15);
498+
}
499+
500+
.alert-warn .alert-icon {
501+
color: var(--color-yellow);
502+
background: rgba(251, 191, 36, 0.15);
503+
}
504+
505+
.alert-critical .alert-icon {
506+
color: var(--color-red);
507+
background: rgba(248, 113, 113, 0.15);
508+
}
509+
510+
.alert-messages {
511+
display: flex;
512+
gap: 16px;
513+
flex: 1;
514+
}
515+
516+
.alert-msg {
517+
font-size: 11px;
518+
font-weight: 600;
519+
letter-spacing: 0.5px;
520+
}
521+
522+
.alert-msg-info { color: var(--color-green); }
523+
.alert-msg-warn { color: var(--color-yellow); }
524+
.alert-msg-critical { color: var(--color-red); }
525+
526+
.alert-timestamp {
527+
color: var(--color-text-dim);
528+
font-size: 10px;
529+
margin-left: auto;
530+
}
531+
453532
/* Status Indicator */
454533
.status-indicator {
455534
display: flex;

src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CpuGraph } from './components/CpuGraph';
44
import { MemoryGraph } from './components/MemoryGraph';
55
import { ProcessTable } from './components/ProcessTable';
66
import { StatusBar } from './components/StatusBar';
7+
import { AlertBar } from './components/AlertBar';
78
import { EnvironmentPanel } from './components/EnvironmentPanel';
89
import { useSystemMetrics } from './hooks/useSystemMetrics';
910
import './App.css';
@@ -71,6 +72,8 @@ function App() {
7172

7273
<EnvironmentPanel filter={filter} />
7374

75+
<AlertBar cpuUsage={metrics.cpuUsage} memPercent={metrics.memPercent} />
76+
7477
<StatusBar
7578
filter={filter}
7679
onFilterChange={setFilter}

src/components/AlertBar.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { useState, useEffect } from 'react';
2+
3+
interface AlertBarProps {
4+
cpuUsage: number[];
5+
memPercent: number;
6+
}
7+
8+
function getAlerts(cpuUsage: number[], memPercent: number): { message: string; level: 'info' | 'warn' | 'critical' }[] {
9+
const alerts: { message: string; level: 'info' | 'warn' | 'critical' }[] = [];
10+
const avgCpu = cpuUsage.reduce((a, b) => a + b, 0) / cpuUsage.length;
11+
12+
if (avgCpu > 90) {
13+
alerts.push({ message: 'CPU usage critical', level: 'critical' });
14+
} else if (avgCpu > 70) {
15+
alerts.push({ message: 'CPU usage high', level: 'warn' });
16+
}
17+
18+
if (memPercent > 90) {
19+
alerts.push({ message: 'Memory usage critical', level: 'critical' });
20+
} else if (memPercent > 70) {
21+
alerts.push({ message: 'Memory usage high', level: 'warn' });
22+
}
23+
24+
if (alerts.length === 0) {
25+
alerts.push({ message: 'All systems nominal', level: 'info' });
26+
}
27+
28+
return alerts;
29+
}
30+
31+
export function AlertBar({ cpuUsage, memPercent }: AlertBarProps) {
32+
const [visible, setVisible] = useState(true);
33+
const alerts = getAlerts(cpuUsage, memPercent);
34+
const hasWarnings = alerts.some((a) => a.level !== 'info');
35+
36+
useEffect(() => {
37+
if (!hasWarnings) return;
38+
const interval = setInterval(() => {
39+
setVisible((prev) => !prev);
40+
}, 800);
41+
return () => clearInterval(interval);
42+
}, [hasWarnings]);
43+
44+
const highestLevel = alerts.reduce<'info' | 'warn' | 'critical'>((max, a) => {
45+
const order = { info: 0, warn: 1, critical: 2 };
46+
return order[a.level] > order[max] ? a.level : max;
47+
}, 'info');
48+
49+
return (
50+
<div className={`alert-bar alert-${highestLevel} ${hasWarnings && !visible ? 'alert-flash-off' : ''}`}>
51+
<span className="alert-icon">
52+
{highestLevel === 'critical' ? '!!' : highestLevel === 'warn' ? '!' : '~'}
53+
</span>
54+
<div className="alert-messages">
55+
{alerts.map((alert, i) => (
56+
<span key={i} className={`alert-msg alert-msg-${alert.level}`}>
57+
{alert.message}
58+
</span>
59+
))}
60+
</div>
61+
<span className="alert-timestamp">
62+
{new Date().toLocaleTimeString()}
63+
</span>
64+
</div>
65+
);
66+
}

0 commit comments

Comments
 (0)