agent ui: consolidate status into state-row badges
drop the "● harness alive — turn loop running" paragraph; the new #alive-badge chip in the state row carries the same signal across all statuses (loading / online / needs-login / offline) with colour coding. token-usage chip renamed + restyled as #ctx-badge — primary number is total context-window tokens used, mirroring claude code's "N tokens" indicator. every state-row badge now has hover detail: state-badge gets per-state tooltips + age suffix, model-chip explains the /model command, last-turn shows the raw ms duration, ctx-badge breaks out input / cache_read / cache_write / output. new todo entry for the per-turn stats sink (start/end/model/ tokens/tool-call-count) the harness should be writing.
This commit is contained in:
parent
85c0df2e64
commit
b444dac6e8
4 changed files with 79 additions and 12 deletions
|
|
@ -91,10 +91,10 @@
|
|||
document.title = `${label} // hyperhive`;
|
||||
}
|
||||
|
||||
function renderOnline(_label, root) {
|
||||
root.append(
|
||||
el('p', { class: 'status-online' }, '● harness alive — turn loop running'),
|
||||
);
|
||||
function renderOnline(_label, _root) {
|
||||
// Online state is conveyed by the `#alive-badge` chip in the
|
||||
// state row — no longer a separate paragraph in the status
|
||||
// block (keeps the terminal the star, status row stays compact).
|
||||
}
|
||||
|
||||
function renderNeedsLoginIdle(root) {
|
||||
|
|
@ -348,6 +348,13 @@
|
|||
const h = Math.floor(m / 60);
|
||||
return h + 'h ' + (m % 60) + 'm';
|
||||
}
|
||||
const STATE_TOOLTIPS = {
|
||||
loading: 'harness not yet contacted',
|
||||
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',
|
||||
};
|
||||
function renderStateBadge() {
|
||||
const badge = $('state-badge');
|
||||
if (!badge) return;
|
||||
|
|
@ -355,6 +362,7 @@
|
|||
const age = fmtAge(Date.now() - stateSince);
|
||||
badge.textContent = def.glyph + ' ' + def.text + ' · ' + age;
|
||||
badge.className = 'state-badge state-' + stateName;
|
||||
badge.title = (STATE_TOOLTIPS[stateName] || '') + '\nin this state for ' + age;
|
||||
const cancelBtn = $('cancel-btn');
|
||||
if (cancelBtn) cancelBtn.hidden = stateName !== 'thinking';
|
||||
}
|
||||
|
|
@ -405,27 +413,53 @@
|
|||
list.append(li);
|
||||
}
|
||||
}
|
||||
// Harness reachability badge: derived from the same `s.status` the
|
||||
// status block reads. Each status maps to a glyph + label + colour
|
||||
// class. Lives in the state row so the operator sees boot/login/
|
||||
// online without losing terminal real-estate to a paragraph.
|
||||
const ALIVE_LABELS = {
|
||||
loading: { glyph: '…', text: 'connecting', cls: 'status-loading' },
|
||||
online: { glyph: '●', text: 'alive', cls: 'status-online' },
|
||||
needs_login_idle: { glyph: '◌', text: 'needs login', cls: 'status-needs-login' },
|
||||
needs_login_in_progress: { glyph: '◌', text: 'logging in', cls: 'status-needs-login' },
|
||||
offline: { glyph: '○', text: 'offline', cls: 'status-offline' },
|
||||
};
|
||||
function renderAliveBadge(status) {
|
||||
const el_ = $('alive-badge');
|
||||
if (!el_) return;
|
||||
const def = ALIVE_LABELS[status] || ALIVE_LABELS.loading;
|
||||
el_.textContent = def.glyph + ' ' + def.text;
|
||||
el_.className = 'status-badge ' + def.cls;
|
||||
}
|
||||
|
||||
function renderModelChip(model) {
|
||||
const el_ = $('model-chip');
|
||||
if (!el_) return;
|
||||
if (!model) { el_.hidden = true; return; }
|
||||
el_.hidden = false;
|
||||
el_.textContent = 'model · ' + model;
|
||||
el_.title = `claude --model ${model}\nset via the operator's /model command; persists across turns until changed`;
|
||||
}
|
||||
// Context badge — mirrors Claude Code's bottom-right "N tokens"
|
||||
// indicator. Primary number is total prompt tokens used in the
|
||||
// current context window (input + both cache axes); hover for the
|
||||
// breakdown including output. Kept as chrome on the state row so
|
||||
// the terminal stays the star.
|
||||
function renderTokenUsage(u) {
|
||||
const el_ = $('token-usage');
|
||||
const el_ = $('ctx-badge');
|
||||
if (!el_) return;
|
||||
if (!u) { el_.hidden = true; return; }
|
||||
const ctx = u.input_tokens + u.cache_read_input_tokens + u.cache_creation_input_tokens;
|
||||
const fmt = (n) => n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n);
|
||||
el_.hidden = false;
|
||||
el_.title = [
|
||||
'context window in use',
|
||||
'input: ' + u.input_tokens,
|
||||
'output: ' + u.output_tokens,
|
||||
'cache_read: ' + u.cache_read_input_tokens,
|
||||
'cache_write: ' + u.cache_creation_input_tokens,
|
||||
].join(' · ');
|
||||
el_.textContent = '· ctx ' + fmt(ctx) + ' in · ' + fmt(u.output_tokens) + ' out';
|
||||
'output (last turn): ' + u.output_tokens,
|
||||
].join('\n');
|
||||
el_.textContent = 'ctx · ' + fmt(ctx);
|
||||
}
|
||||
function renderLastTurn(ms) {
|
||||
const el_ = $('last-turn');
|
||||
|
|
@ -435,6 +469,7 @@
|
|||
else if (ms < 60_000) s = (ms / 1000).toFixed(1) + 's';
|
||||
else s = Math.floor(ms / 60_000) + 'm ' + Math.floor((ms / 1000) % 60) + 's';
|
||||
el_.textContent = '· last turn ' + s;
|
||||
el_.title = `wall-clock duration of the last completed claude turn (${ms} ms)`;
|
||||
el_.hidden = false;
|
||||
}
|
||||
function startStateTicker() {
|
||||
|
|
@ -499,6 +534,7 @@
|
|||
} else if (s.turn_state) {
|
||||
setStateAbs(s.turn_state, s.turn_state_since);
|
||||
}
|
||||
renderAliveBadge(s.status);
|
||||
renderModelChip(s.model);
|
||||
renderTokenUsage(s.token_usage);
|
||||
// Skip the re-render if nothing structurally changed. The most
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue