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