From 272d8af6205a02daf5cead3305532019ad1293bf Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 31 May 2026 14:06:30 +0200 Subject: [PATCH] dashboard: split queued vs running rebuild visual (#769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Container row's pending highlight fired for both queued + running rebuild-queue entries, so a long queue painted half the SW4RM tab amber. Mara on #769: don't highlight while queued, show running as spinner on agent icon. - tabs.js: derive opRunning (transient OR op.state === 'running') separately from pending; add a pending-running row class. Operator-initiated transients still count as running so the rebuild-now-clicked → backend-picks-it-up window stays visually consistent. - dashboard.css: move the amber border + tint from .pending to .pending-running (queued rows now keep their normal look, with the badge text still saying "rebuild queued"). Add a 1s linear rotating amber arc as .pending-running > .container-icon::after, with overflow:visible so the ring sits just outside the icon and composes with the mauve selected ring. - docs/web-ui.md::Container row: describe the queued vs running split in prose. Drop the orphaned (#398) cookie while I'm at it — small #712 freebie. --- docs/web-ui.md | 8 ++++- frontend/packages/dashboard/src/dashboard.css | 31 +++++++++++++++++-- frontend/packages/dashboard/src/tabs.js | 9 ++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/docs/web-ui.md b/docs/web-ui.md index 51a6a6f9..11ff2084 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -433,8 +433,14 @@ body sits to the right with three stacked lines entry for this agent is `queued` or `running` but no operator-initiated transient is set, the card surfaces a `building…` / `meta-updating…` badge sourced from - `rebuildQueueState` (#398) — so the SW4RM tab shows the same + `rebuildQueueState` — so the SW4RM tab shows the same rebuild progress visible on the SYST3M tab's R3BU1LD QU3U3. + The row visual splits queued vs running: a **queued** entry shows + only the pending-state pill (no row tint, so a long queue doesn't + paint half the tab amber); a **running** entry keeps the amber + row tint AND draws a **rotating amber ring** around the agent + icon, so it's obvious at a glance which container is actually + moving. Container name + port, and a `ctx · Nk` chip showing the agent's last-turn context size (from `ContainerView.ctx_tokens`, read from the turn-stats sqlite on each `build_all` sweep; diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index 20260a0e..c0a92dba 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -341,11 +341,38 @@ a:hover { flex: 1; min-width: 0; } -.container-row.pending { +/* Pending state splits queued vs running (#769): queued ops show + only the pending-state badge — the row sits unhighlighted so a + long queue doesn't paint half the SW4RM tab amber. Running ops + keep the amber row tint AND get a rotating amber ring on the + agent icon so it's obvious which container is actually moving. */ +.container-row.pending .actions { opacity: 0.4; pointer-events: none; } +.container-row.pending-running { border-color: var(--amber); background: rgba(250, 179, 135, 0.05); } -.container-row.pending .actions { opacity: 0.4; pointer-events: none; } +/* The spinner is a transparent border ring with two adjacent sides + coloured amber, rotated by a CSS animation — looks like an + orbiting arc around the icon. Override the icon's overflow:hidden + so the ring can sit just outside the square; it composes naturally + with the mauve selected-ring (selected + running shows both). */ +@keyframes container-icon-spin { + to { transform: rotate(360deg); } +} +.container-row.pending-running > .container-icon { + overflow: visible; +} +.container-row.pending-running > .container-icon::after { + content: ''; + position: absolute; + inset: -4px; + border-radius: 10px; + border: 2px solid transparent; + border-top-color: var(--amber); + border-right-color: var(--amber); + animation: container-icon-spin 1s linear infinite; + pointer-events: none; +} .container-row .head { display: flex; align-items: center; diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index c5460031..605bcc1f 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -497,10 +497,19 @@ window.marked = marked; : (op.kind === 'meta_update' ? 'meta-update queued' : op.kind === 'destroy' ? 'destroy queued' : 'rebuild queued'))); + // Split the visual into queued vs running (#769): queued ops get + // no row highlight — the badge text alone says "wait, queued" — + // while running ops keep the amber row tint + add a rotating + // amber ring on the agent icon. Operator-initiated transients + // always count as running (the op is already in flight even if + // the queue worker hasn't picked it up yet). + const opRunning = transientKind != null + || (op != null && op.state === 'running'); const selected = selectionState.has(c.name); const li = el('li', { class: 'container-row' + (pending ? ' pending' : '') + + (opRunning ? ' pending-running' : '') + (selected ? ' selected' : ''), }); // Topology: depth contributes left-padding; the glyph string in