feat(app): real daemon health footer (live, uptime, version)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:09:35 +07:00
parent f7763a84fc
commit defceb1169
5 changed files with 53 additions and 12 deletions
+18 -5
View File
@@ -7,8 +7,8 @@ import { Wizard } from "./Wizard";
import { EventCenter } from "./EventCenter";
import { maybeNotify } from "./notify";
import { COLORS } from "./theme";
import { getStatusFull, applyPreset, onDaemonEvent, onDaemonRawEvent, setWorkspaceMeta, focusSurface, getEventLog, markEventsRead } from "./socketBridge";
import type { EventRecord } from "./socketBridge";
import { getStatusFull, applyPreset, onDaemonEvent, onDaemonRawEvent, setWorkspaceMeta, focusSurface, getEventLog, markEventsRead, getHealth } from "./socketBridge";
import type { EventRecord, DaemonHealth } from "./socketBridge";
import { leafIds } from "./layoutTypes";
import type { Group, WorkspaceView, SurfaceState } from "./layoutTypes";
@@ -21,6 +21,8 @@ export function App() {
const [events, setEvents] = useState<EventRecord[]>([]);
const [wizard, setWizard] = useState(false);
const [eventsOpen, setEventsOpen] = useState(true);
const [health, setHealth] = useState<DaemonHealth | null>(null);
const [connected, setConnected] = useState(false);
const [focusedId, setFocusedId] = useState<string | null>(null);
const activeRef = useRef<string | null>(null);
const wsRef = useRef<WorkspaceView[]>([]);
@@ -49,12 +51,18 @@ export function App() {
if (!activeRef.current && st.workspaces.length) setActiveId(st.workspaces[0].id);
}, []);
const loadHealth = useCallback(async () => {
try { setHealth(await getHealth()); setConnected(true); }
catch { setConnected(false); }
}, []);
const wsOf = (surfaceId: string): WorkspaceView | undefined =>
wsRef.current.find((w) => surfaceId in w.surfaces);
useEffect(() => {
void refresh();
void seedEvents();
void loadHealth();
const unlisten = onDaemonEvent((evt) => {
if (evt.evt === "event") {
const rec = evt.data.record;
@@ -74,9 +82,14 @@ export function App() {
void refresh();
}
});
const reconnect = onDaemonRawEvent("spacesh:disconnected", () => { void refresh(); void seedEvents(); });
const reconnect = onDaemonRawEvent("spacesh:disconnected", () => {
setConnected(false);
void refresh();
void seedEvents();
void loadHealth();
});
return () => { void unlisten.then((f) => f()); void reconnect.then((f) => f()); };
}, [refresh, seedEvents]);
}, [refresh, seedEvents, loadHealth]);
const unread = useMemo(() => events.filter((e) => !e.read).length, [events]);
const active = workspaces.find((w) => w.id === activeId) ?? null;
@@ -93,7 +106,7 @@ export function App() {
<div style={{ display: "flex", flexDirection: "column", height: "100vh", background: COLORS.bgApp }}>
<TopBar active={active} eventsOpen={eventsOpen} onToggleEvents={() => setEventsOpen((v) => !v)} unread={unread} />
<div style={{ flex: 1, display: "flex", minHeight: 0 }}>
<Sidebar groups={groups} workspaces={workspaces} activeId={activeId} onSelect={selectWorkspace} onNew={() => setWizard(true)} />
<Sidebar groups={groups} workspaces={workspaces} activeId={activeId} onSelect={selectWorkspace} onNew={() => setWizard(true)} health={health} connected={connected} />
<div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
{active && (
<CenterToolbar selected="" onSelect={(p) => { if (active) void applyPreset(active.id, p, []); }} />