Bug bash fixes: hash routing + back button, hub-only pages reset on machine switch, exited terminals reap after 10 min, self-update refuses while busy unless forced, touch-sized controls, power confirm by machine name, down interfaces collapsed and empty modules hidden, load label, offline backoff, cron log newest first, procs empty message, timer timestamp parsing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-18 17:46:16 -07:00
co-authored by Claude Fable 5.1
parent 27f9665606
commit f093b322ee
14 changed files with 126 additions and 36 deletions
+8 -5
View File
@@ -176,17 +176,18 @@ function body(id: string, { s, h, edit }: Ctx): ReactNode {
<div className="sub-head">
<span className={'dot ' + (x.up ? 'ok' : '')} />
<span>{x.iface}</span>
{!x.up && <span className="muted small">down</span>}
<span className="spacer" />
<span className="num">↓ {fmtRate(x.rx_bps)} ↑ {fmtRate(x.tx_bps)}</span>
{x.up && <span className="num">↓ {fmtRate(x.rx_bps)} ↑ {fmtRate(x.tx_bps)}</span>}
<HideBtn k={`net:${x.iface}`} edit={edit} />
</div>
{h?.net[x.iface] && (
{x.up && h?.net[x.iface] && (
<Sparkline series={[{ name: 'rx', values: h.net[x.iface].rx, color: S1 }, { name: 'tx', values: h.net[x.iface].tx, color: S2 }]} format={fmtRate} />
)}
<div className="legend">
{x.up && <div className="legend">
<span><span className="sw" style={{ background: S1 }} />rx · {fmtBytes(x.rx_total)} total</span>
<span><span className="sw" style={{ background: S2 }} />tx · {fmtBytes(x.tx_total)} total</span>
</div>
</div>}
</div>
))}
<HiddenChips prefix="net" edit={edit} label={(k) => k} />
@@ -345,7 +346,9 @@ export default function Dashboard() {
unhideItem: (k) => update((l) => { const s = new Set(l.hiddenItems); s.delete(k); return { ...l, hiddenItems: s } }),
}
const ctx: Ctx = { s: latest, h: hist.current, edit }
const visible = (kind: Kind) => layout.order.filter((id) => byId(id)?.kind === kind && !layout.hidden.has(id))
// Modules with nothing to show on this host (no fans, no UPS…) stay out of the way unless customizing.
const empty = (id: string) => (id === 'fans' && latest.fans.length === 0) || (id === 'ups' && !latest.ups) || (id === 'temps' && latest.temps.length === 0) || (id === 'disks' && latest.disks.length === 0)
const visible = (kind: Kind) => layout.order.filter((id) => byId(id)?.kind === kind && !layout.hidden.has(id) && (editing || !empty(id)))
const hiddenMods = layout.order.filter((id) => layout.hidden.has(id))
// Move a module past its nearest same-kind neighbour (tiles stay with tiles, cards with cards).