Dashboard tab: CPU/mem/load, hwmon temps+fans, net, disks, filesystems, UPS with 10-min sparklines
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
24d64245ec
commit
3c6f4d4639
+15
-7
@@ -3,15 +3,18 @@ import { api, Me, Term } from './api'
|
||||
import Rail, { Group } from './Rail'
|
||||
import TerminalPane, { TermHandle } from './TerminalPane'
|
||||
import KeyBar from './KeyBar'
|
||||
import Dashboard from './Dashboard'
|
||||
|
||||
const GROUPS: Group[] = [
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: '◔', page: true },
|
||||
{ id: 'terminal', label: 'Terminal', icon: '>_' },
|
||||
{ id: 'services', label: 'Services', icon: '⚙', soon: true },
|
||||
{ id: 'cron', label: 'Cron', icon: '⏱', soon: true },
|
||||
{ id: 'zfs', label: 'ZFS', icon: '▤', soon: true },
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: '◔', soon: true },
|
||||
]
|
||||
|
||||
type View = 'terminal' | 'dashboard'
|
||||
|
||||
function fmtUptime(s: number): string {
|
||||
const d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600), m = Math.floor((s % 3600) / 60)
|
||||
return d > 0 ? `${d}d ${h}h` : h > 0 ? `${h}h ${m}m` : `${m}m`
|
||||
@@ -20,6 +23,7 @@ function fmtUptime(s: number): string {
|
||||
export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () => void }) {
|
||||
const [terms, setTerms] = useState<Term[]>([])
|
||||
const [active, setActive] = useState<string | null>(null)
|
||||
const [view, setView] = useState<View>('terminal')
|
||||
const [drawer, setDrawer] = useState(false)
|
||||
const [online, setOnline] = useState(true)
|
||||
const handles = useRef<Record<string, TermHandle>>({})
|
||||
@@ -36,6 +40,7 @@ export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () =>
|
||||
const t = await api.createTerminal()
|
||||
await reload()
|
||||
setActive(t.id)
|
||||
setView('terminal')
|
||||
setDrawer(false)
|
||||
}
|
||||
async function rename(t: Term) {
|
||||
@@ -51,6 +56,7 @@ export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () =>
|
||||
async function logout() { await api.logout(); onLoggedOut() }
|
||||
|
||||
const activeTerm = terms.find((t) => t.id === active)
|
||||
const termView = view === 'terminal'
|
||||
|
||||
return (
|
||||
<div className={'app' + (drawer ? ' drawer-open' : '')}>
|
||||
@@ -65,20 +71,22 @@ export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () =>
|
||||
</header>
|
||||
|
||||
<div className="body">
|
||||
<Rail groups={GROUPS} terms={terms} active={active}
|
||||
onSelect={(id) => { setActive(id); setDrawer(false) }}
|
||||
<Rail groups={GROUPS} terms={terms} active={active} view={view}
|
||||
onSelect={(id) => { setActive(id); setView('terminal'); setDrawer(false) }}
|
||||
onView={(id) => { setView(id as View); setDrawer(false) }}
|
||||
onNew={create} onRename={rename} onClose={close} />
|
||||
<div className="backdrop" onClick={() => setDrawer(false)} />
|
||||
|
||||
<main className="main">
|
||||
{terms.length === 0 && (
|
||||
{view === 'dashboard' && <Dashboard />}
|
||||
{termView && terms.length === 0 && (
|
||||
<div className="center muted">
|
||||
<div>No terminals.</div>
|
||||
<button className="primary" onClick={create}>+ New Terminal</button>
|
||||
</div>
|
||||
)}
|
||||
{terms.map((t) => (
|
||||
<TerminalPane key={t.id} id={t.id} active={t.id === active}
|
||||
<TerminalPane key={t.id} id={t.id} active={termView && t.id === active}
|
||||
register={(h) => { if (h) handles.current[t.id] = h; else delete handles.current[t.id] }}
|
||||
onStatus={(ok) => { if (t.id === active) setOnline(ok) }}
|
||||
onExit={reload} />
|
||||
@@ -86,14 +94,14 @@ export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () =>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{activeTerm && <KeyBar send={(s) => handles.current[activeTerm.id]?.input(s)}
|
||||
{termView && activeTerm && <KeyBar send={(s) => handles.current[activeTerm.id]?.input(s)}
|
||||
setCtrl={(v) => handles.current[activeTerm.id]?.armCtrl(v)} />}
|
||||
|
||||
<footer className="footer">
|
||||
<span>v{me.version}</span>
|
||||
<span>up {fmtUptime(me.uptime)}</span>
|
||||
<span className="spacer" />
|
||||
<span className="muted">{activeTerm ? activeTerm.name : ''}</span>
|
||||
<span className="muted">{view === 'dashboard' ? 'Dashboard' : activeTerm ? activeTerm.name : ''}</span>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user