// The sidebar footer's daemon-status dot, sat next to the spawn button. // // Colour is the link state; hovering (or focusing — it is a button, so the // keyboard reaches it) reveals the release the daemon is running: commit hash // and commit date. import { formatRelease, type DaemonLink, type LinkState, type LinkStatus } from "./daemonLink.js"; /** One human-readable phrase per link state, used for both the tooltip's first * line and the button's accessible name. */ const LABELS: Record = { connecting: "Connecting…", online: "Connected", reconnecting: "Reconnecting…", down: "Daemon unreachable", updating: "Updated — reloading", }; export interface StatusIndicatorHandle { el: HTMLElement; dispose(): void; } export function createStatusIndicator(link: DaemonLink): StatusIndicatorHandle { const el = document.createElement("button"); el.type = "button"; el.className = "btn btn-ghost btn-icon status-chip"; el.setAttribute("aria-describedby", "status-tip"); const dot = document.createElement("span"); dot.className = "status-dot"; dot.setAttribute("aria-hidden", "true"); el.appendChild(dot); const tip = document.createElement("span"); tip.className = "status-tip"; tip.id = "status-tip"; tip.setAttribute("role", "tooltip"); el.appendChild(tip); const unsubscribe = link.subscribe((status) => render(status)); function render(status: LinkStatus): void { const label = LABELS[status.state]; const release = formatRelease(status.build); el.dataset.state = status.state; el.setAttribute("aria-label", `${label} — ${release}`); tip.textContent = ""; tip.append(line("status-tip-state", status.hostname ? `${label} · ${status.hostname}` : label)); tip.append(line("status-tip-release", release)); } function line(cls: string, text: string): HTMLElement { const span = document.createElement("span"); span.className = cls; span.textContent = text; return span; } return { el, dispose(): void { unsubscribe(); el.remove(); }, }; }