dashboard: dimmed default icon for unreachable containers

A stopped or mid-transient (restarting / rebuilding) container's
web server isn't answering, so its <url>/icon background-image
just failed to an empty box on the card.

When the container isn't reachable (not running, or a transient
is in flight) the icon now falls back to the dimmed hyperhive
mark — /favicon.svg, served by the dashboard itself so it's
always loadable — greyscaled + lowered opacity via the
.icon-unreachable class.

closes #195
This commit is contained in:
iris 2026-05-21 21:56:39 +02:00
parent f42ba9b561
commit 16f614f45d
3 changed files with 21 additions and 7 deletions

View file

@ -162,7 +162,10 @@ the previous process's socket release resolves itself.
A full-height **square agent icon** on the left (the agent's A full-height **square agent icon** on the left (the agent's
`/icon`, painted as a background-image div so its load state can `/icon`, painted as a background-image div so its load state can
never reflow the row), and the card body on the right with three never reflow the row), and the card body on the right with three
stacked lines (`assets/app.js::renderContainers`): stacked lines (`assets/app.js::renderContainers`). When the
container is stopped or mid-transient (its web server isn't
answering) the icon falls back to the dimmed hyperhive mark
(`/favicon.svg`) instead of an empty box.
- Line 1: agent name (link → new tab), m1nd/ag3nt chip, status - Line 1: agent name (link → new tab), m1nd/ag3nt chip, status
badges — `⊘ rate limited` (red, while the harness is parked badges — `⊘ rate limited` (red, while the harness is parked

View file

@ -580,13 +580,18 @@
// Full-height square agent icon, left of the card body. A // Full-height square agent icon, left of the card body. A
// background-image on a div (not <img>) contributes no intrinsic // background-image on a div (not <img>) contributes no intrinsic
// size, so loading or failing it can't shift the row layout, and // size, so loading or failing it can't shift the row layout —
// a failed load (stopped container, web server down) just shows // no broken-image glyph, no collapse. (issue #177)
// the placeholder fill — no broken-image glyph, no collapse. //
// (issue #177) // When the container is stopped or mid-transient (restarting,
// rebuilding…) its web server isn't answering, so `<url>/icon`
// would just fail to an empty box. Fall back to the dimmed
// hyperhive mark (`/favicon.svg`, served by the dashboard
// itself, always reachable) instead. (issue #195)
const reachable = c.running && !pending;
const icon = el('div', { const icon = el('div', {
class: 'container-icon', class: 'container-icon' + (reachable ? '' : ' icon-unreachable'),
style: `background-image:url("${url}icon")`, style: `background-image:url("${reachable ? `${url}icon` : '/favicon.svg'}")`,
}); });
// Card body: the three stacked content lines, right of the icon. // Card body: the three stacked content lines, right of the icon.
const body = el('div', { class: 'card-body' }); const body = el('div', { class: 'card-body' });

View file

@ -98,6 +98,12 @@ a:hover {
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
/* Stopped / mid-transient container: the dimmed hyperhive mark stands
in for the unreachable agent icon (issue #195). */
.container-row:not(.tombstone) > .container-icon.icon-unreachable {
filter: grayscale(1);
opacity: 0.4;
}
.container-row .card-body { .container-row .card-body {
flex: 1; flex: 1;
min-width: 0; min-width: 0;