feat(docker): the Docker page leads with a verdict, lists services and containers as filterable tables, and each service has its own page with logs across its tasks (#5298–#5302)
CI / lint (push) Successful in 2s
CI / integration (push) Failing after 1m4s
CI / unit (push) Failing after 1m28s
CI / publish (push) Skipped

- #5298 "Docker overview: verdict line; only the problem tiles that aren't zero"
- #5299 "Docker overview: swarm services as a filterable, sortable table with a Stack column"
- #5300 "Docker overview: failures listed once, each with a link to the failed task's logs"
- #5301 "Docker overview: standalone containers in one table with a Host column"
- #5302 "Service page: replicas, image, task history, resource history and logs across all its tasks"

overview.py holds the rows, filters and sort as plain functions. The filter
bar sits outside the polled fragment and the URL keeps its state. A service's
page reads every task container Steward still holds anything for, the live
ones and removed ones known by their events, so a failed task's logs are
reachable after Docker has removed it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 20:08:38 -04:00
co-authored by Claude Opus 5.5
parent 7dccb2f303
commit 2093f399e6
13 changed files with 1339 additions and 268 deletions
+16
View File
@@ -197,6 +197,22 @@ strong, b, th { font-weight: 500; }
.chip { display: inline-block; margin: 0.1rem 0.3rem 0.1rem 0; padding: 0.05rem 0.45rem; font-size: 0.75rem;
color: var(--text-muted); border: 1px solid var(--border); border-radius: var(--fs-radius-sm); white-space: nowrap; }
.chip:hover { color: var(--text); border-color: var(--border-mid); }
/* A table's own heading inside its .card-flush: the name, a count and a link. */
.table-caption { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; margin: 0; padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-mid); font-family: var(--fs-font-body); font-size: 0.95rem; font-weight: 500; }
.table-caption .field-hint { margin-left: auto; }
/* A name cell cut short with "…", whole on hover (its title); a second line
under it; an image reference, long by nature, cut the same way. */
.cell-name { max-width: 18rem; }
.cell-name > a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-sub { font-size: 0.75rem; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.table td.cell-image { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8rem; color: var(--text-muted); }
/* The Docker page's one-line answer, above its tiles (#5298). */
.docker-verdict { margin: 0 0 0.75rem; font-size: 0.95rem; }
/* A service's resource history: CPU and memory side by side, each a figure
over a line that spans its half (docker/_service_history.html). */
.history-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1.25rem; }
.history-pair .spark { color: var(--blue); margin-top: 0.4rem; }
/* Forms */
.form-group { margin-bottom: 1rem; }