// — root of the Preact rewrite. Wires `Header` + `StatusChips` // to the real `/api/state` snapshot via `useAgentState`. Still a slice, // not the whole page — see main.tsx's file comment for what's left // (the live SSE stream, login flow, inbox/todos, term input, overflow). import type { BadgeTone } from '@hive/shared/badge.js'; import { Header } from './components/Header.js'; import { StatusChips } from './components/StatusChips.js'; import { useAgentState } from './hooks/useAgentState.js'; import { fmtAge, fmtTokens } from './lib/format.js'; import { resolveDashboardBase } from './lib/dashboardBase.js'; import { submitPauseResume } from './lib/pauseAction.js'; import { postModel, postEffort } from './lib/modelEffort.js'; import type { TokenUsage } from './types.js'; const ALIVE_LABELS: Record = { online: { glyph: '●', text: 'alive', tone: 'positive' }, rate_limited: { glyph: '⊘', text: 'rate limited', tone: 'warning' }, needs_login_idle: { glyph: '◌', text: 'needs login', tone: 'warning' }, needs_login_in_progress: { glyph: '◌', text: 'logging in', tone: 'warning' }, }; const STATE_LABELS: Record = { idle: { glyph: '💤', text: 'idle', tone: 'neutral' }, thinking: { glyph: '🧠', text: 'thinking', tone: 'accent' }, compacting: { glyph: '📦', text: 'compacting', tone: 'warning' }, }; const STATE_TOOLTIPS: Record = { offline: 'harness unreachable or claude not logged in', idle: 'turn loop running, no claude invocation in flight', thinking: 'claude is executing the current turn', compacting: "operator-triggered /compact running on the persistent session", }; // `total` here matches app.js's `renderOneUsage` exactly: input + both // cache buckets, output_tokens deliberately excluded (it's shown in the // title breakdown, not folded into the headline number). function tokenTotal(u: TokenUsage | null): number | null { if (!u) return null; return (u.input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0); } export function Root() { const { state, refresh } = useAgentState(); if (!state) { return (
{}} effort="" availableEfforts={[]} onSelectEffort={() => {}} paused={false} onTogglePause={() => {}} />
); } const alive = ALIVE_LABELS[state.status] ?? { glyph: '○', text: 'offline', tone: 'negative' as BadgeTone }; // Mirrors app.js's refreshState: any non-'online' status forces the // turn-state badge to 'offline' regardless of the server's last-known // turn_state (login/rate-limit takes visual priority over a stale // idle/thinking reading from before the harness went offline). const effectiveTurnState = state.status === 'online' ? state.turn_state : 'offline'; const turnDef = STATE_LABELS[effectiveTurnState] ?? { glyph: '○', text: 'offline', tone: 'negative' as BadgeTone }; const stateAge = fmtAge(Date.now() - state.turn_state_since * 1000); const ctx = tokenTotal(state.ctx_usage); const cost = tokenTotal(state.cost_usage); return (
{ postModel(name).then(() => refresh()); }} effort={state.effort} availableEfforts={state.available_efforts} onSelectEffort={(level) => { postEffort(level).then(() => refresh()); }} ctxLabel={ctx !== null ? fmtTokens(ctx) : undefined} costLabel={cost !== null ? fmtTokens(cost) : undefined} paused={state.paused} onTogglePause={() => { submitPauseResume(resolveDashboardBase(state.dashboard_port), state.label, state.paused ? 'resume' : 'pause'); }} />
); }