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:
+18
-5
@@ -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, []); }} />
|
||||
|
||||
Reference in New Issue
Block a user