Machine menu shows each machine's address instead of its load

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-19 10:45:24 -07:00
co-authored by Claude Fable 5.1
parent d3f2a83249
commit a838adcb27
4 changed files with 16 additions and 9 deletions
+8 -3
View File
@@ -745,12 +745,17 @@ func (s *Server) me(w http.ResponseWriter, _ *http.Request) {
}
}
_, rebootErr := os.Stat("/run/reboot-required")
var demoURL string
s.cfg.View(func(c *config.Config) { demoURL = c.DemoURL })
var demoURL, addr string
s.cfg.View(func(c *config.Config) {
demoURL = c.DemoURL
if h, _, err := net.SplitHostPort(c.Listen); err == nil && h != "" && h != "0.0.0.0" && h != "::" {
addr = h // the address this console listens on, for the machine menu
}
})
jsonOK(w, 200, map[string]any{
"host": s.host, "user": s.user, "version": s.version,
"load": load, "uptime": uptime, "service_started": s.started,
"reboot_needed": rebootErr == nil, "fingerprint": s.fingerprint, "arch": runtime.GOARCH, "demo_url": demoURL,
"reboot_needed": rebootErr == nil, "fingerprint": s.fingerprint, "arch": runtime.GOARCH, "demo_url": demoURL, "addr": addr,
})
}
+6 -4
View File
@@ -2,10 +2,12 @@ import { useEffect, useRef, useState } from 'react'
import { MachineStatus } from './api'
import { useEscape } from './ui'
const hostOf = (url: string) => { try { return new URL(url).hostname } catch { return url } }
// The host picker at the top of the rail. The hub is always first; managed machines follow
// with their live status. Picking one re-points the whole console at it.
export default function MachineSwitcher({ hub, current, machines, onChange, onManage }: {
hub: string; current: string | null; machines: MachineStatus[]; onChange: (name: string | null) => void; onManage: () => void
export default function MachineSwitcher({ hub, hubAddr, current, machines, onChange, onManage }: {
hub: string; hubAddr?: string; current: string | null; machines: MachineStatus[]; onChange: (name: string | null) => void; onManage: () => void
}) {
const [open, setOpen] = useState(false)
const box = useRef<HTMLDivElement>(null)
@@ -28,11 +30,11 @@ export default function MachineSwitcher({ hub, current, machines, onChange, onMa
{open && (
<div className="mmenu">
<button className={'mitem' + (!current ? ' on' : '')} onClick={() => { setOpen(false); onChange(null) }}>
<span className="dot ok" /><span className="mname">{hub}</span><span className="muted small">this host</span>
<span className="dot ok" /><span className="mname">{hub}</span><span className="muted small mono">{hubAddr || 'this host'}</span>
</button>
{machines.map((m) => (
<button key={m.name} className={'mitem' + (current === m.name ? ' on' : '')} onClick={() => { setOpen(false); onChange(m.name) }} title={m.error || m.url}>
<span className={'dot ' + dotFor(m)} /><span className="mname">{m.name}</span><span className="muted small">{m.online ? m.load : 'offline'}</span>
<span className={'dot ' + dotFor(m)} /><span className="mname">{m.name}</span><span className="muted small mono">{hostOf(m.url)}{m.online ? '' : ' · offline'}</span>
</button>
))}
<button className="mitem manage" onClick={() => { setOpen(false); onManage() }}>+ manage machines…</button>
+1 -1
View File
@@ -210,7 +210,7 @@ export default function Shell({ me: hubMe, onLoggedOut }: { me: Me; onLoggedOut:
<div className="body">
<Rail groups={GROUPS} view={view}
header={<MachineSwitcher hub={hubMe.host} current={machine} machines={machines} onChange={switchMachine} onManage={() => { setView('machines'); setDrawer(false) }} />}
header={<MachineSwitcher hub={hubMe.host} hubAddr={hubMe.addr} current={machine} machines={machines} onChange={switchMachine} onManage={() => { setView('machines'); setDrawer(false) }} />}
onView={async (id) => { if (id !== view && !(await okToLeave())) return; setView(id as View); setDrawer(false) }} />
<div className="backdrop" onClick={() => setDrawer(false)} />
+1 -1
View File
@@ -54,7 +54,7 @@ async function req<T>(method: string, path: string, body?: unknown, local = fals
return r.json()
}
export interface Me { demo_url?: string; host: string; user: string; version: string; load: string; uptime: number; reboot_needed?: boolean; fingerprint?: string; arch?: string }
export interface Me { demo_url?: string; addr?: string; host: string; user: string; version: string; load: string; uptime: number; reboot_needed?: boolean; fingerprint?: string; arch?: string }
export interface PushResult { name: string; ok: boolean; version?: string; error?: string }
export interface MachineStatus { name: string; url: string; fingerprint?: string; added: string; online: boolean; host?: string; version?: string; load?: string; uptime?: number; reboot_needed?: boolean; last_seen?: string; error?: string }
export interface Term { id: string; name: string; running: boolean; exit_code: number; created: string; clients: number }