- Add /api/system endpoints for CPU, RAM, and disk metrics (SSE stream) - Add /api/system/logs for unified log stream from all apps (SSE stream) - Create Vitals component with three gauges: arc (CPU), bar (RAM), circular (Disk) - Create UnifiedLogs component with real-time scrolling logs and status highlighting - Update DashboardLanding with stats, vitals, and activity sections Design follows Dieter Rams / Teenage Engineering aesthetic with neutral palette. https://claude.ai/code/session_013L9HKHxMEoub76B1zuKive
92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
import { useEffect, useState } from 'hono/jsx'
|
|
import { apps } from '../state'
|
|
import {
|
|
DashboardContainer,
|
|
DashboardHeader,
|
|
DashboardSubtitle,
|
|
DashboardTitle,
|
|
StatCard,
|
|
StatLabel,
|
|
StatValue,
|
|
StatsGrid,
|
|
} from '../styles'
|
|
import { UnifiedLogs, type UnifiedLogLine } from './UnifiedLogs'
|
|
import { Vitals } from './Vitals'
|
|
|
|
interface SystemMetrics {
|
|
cpu: number
|
|
ram: { used: number, total: number, percent: number }
|
|
disk: { used: number, total: number, percent: number }
|
|
}
|
|
|
|
export function DashboardLanding() {
|
|
const [metrics, setMetrics] = useState<SystemMetrics>({
|
|
cpu: 0,
|
|
ram: { used: 0, total: 0, percent: 0 },
|
|
disk: { used: 0, total: 0, percent: 0 },
|
|
})
|
|
const [logs, setLogs] = useState<UnifiedLogLine[]>([])
|
|
|
|
const regularApps = apps.filter(app => !app.tool)
|
|
const toolApps = apps.filter(app => app.tool)
|
|
const runningApps = apps.filter(app => app.state === 'running')
|
|
|
|
// Subscribe to system metrics SSE
|
|
useEffect(() => {
|
|
const metricsSource = new EventSource('/api/system/metrics/stream')
|
|
metricsSource.onmessage = e => {
|
|
try {
|
|
setMetrics(JSON.parse(e.data))
|
|
} catch {}
|
|
}
|
|
return () => metricsSource.close()
|
|
}, [])
|
|
|
|
// Subscribe to unified logs SSE
|
|
useEffect(() => {
|
|
const logsSource = new EventSource('/api/system/logs/stream')
|
|
logsSource.onmessage = e => {
|
|
try {
|
|
const line = JSON.parse(e.data) as UnifiedLogLine
|
|
setLogs((prev: UnifiedLogLine[]) => [...prev.slice(-199), line])
|
|
} catch {}
|
|
}
|
|
return () => logsSource.close()
|
|
}, [])
|
|
|
|
const handleClearLogs = async () => {
|
|
try {
|
|
await fetch('/api/system/logs/clear', { method: 'POST' })
|
|
setLogs([])
|
|
} catch {}
|
|
}
|
|
|
|
return (
|
|
<DashboardContainer>
|
|
<DashboardHeader>
|
|
<DashboardTitle>Toes</DashboardTitle>
|
|
<DashboardSubtitle>Your personal web appliance</DashboardSubtitle>
|
|
</DashboardHeader>
|
|
|
|
<StatsGrid>
|
|
<StatCard>
|
|
<StatValue>{regularApps.length}</StatValue>
|
|
<StatLabel>Apps</StatLabel>
|
|
</StatCard>
|
|
<StatCard>
|
|
<StatValue>{toolApps.length}</StatValue>
|
|
<StatLabel>Tools</StatLabel>
|
|
</StatCard>
|
|
<StatCard>
|
|
<StatValue>{runningApps.length}</StatValue>
|
|
<StatLabel>Running</StatLabel>
|
|
</StatCard>
|
|
</StatsGrid>
|
|
|
|
<Vitals cpu={metrics.cpu} ram={metrics.ram} disk={metrics.disk} />
|
|
|
|
<UnifiedLogs logs={logs} onClear={handleClearLogs} />
|
|
</DashboardContainer>
|
|
)
|
|
}
|