docs: migrate tabs.js topology-tree prose to docs/web-ui.md (#712 pattern check)

First slice of #712 (dashboard SPA frontend → docs/). Shrinks the
buildAgentTree + treePrefixDom comment blocks in tabs.js to one-line
docs/ references; adds a new 'Topology tree' subsection to
docs/web-ui.md under '### Container row' carrying the substantive
prose.

Net effect on this region:
- 6 #NNN tracking cookies dropped (#363, #361, #388)
- 30 lines of substantive design comments collapsed to 6 lines of
  references in tabs.js
- 35 new lines in docs/web-ui.md (the migrated prose, restructured
  into a single coherent subsection rather than two adjacent comment
  blocks)
- functional code unchanged; build clean

Deliberately small pattern-check (one cohesive subsystem) before
expanding to the rest of #712 (~140 more refs across tabs.js +
dashboard.css + index.html + common.js + flow.js). Reviewers can
sanity-check the shape (where prose lands, how short the in-code
ref becomes, whether section nesting reads well) before I scale.

refs #712
This commit is contained in:
iris 2026-05-31 11:47:48 +02:00 committed by Mara
commit 26d8565365
2 changed files with 41 additions and 24 deletions

View file

@ -425,6 +425,41 @@ body sits to the right with three stacked lines
agent is stale. Banner pulses on each broker SSE event
(`pulseBanner` with a 4s grace timer).
#### Topology tree
Container rows render as a forest, not a flat list — each agent
sits indented under its declared parent. `tabs.js::buildAgentTree`
walks `ContainerView.parent` for every container in the snapshot
and produces a render order with per-row depth + sibling-position
info:
- Top-level rows are agents with `parent = null` OR a parent that
doesn't appear in the container map (orphans get hoisted to root
so they're still visible).
- Within each level children sort alphabetically by name; roots
likewise.
- Cycle safety: any container not reached during the root-walk is
appended at the end as a root, so no agent ever silently
disappears from the list when the topology JSON is malformed.
- The pre-topology rendering shape (every container at depth 0,
flat list) collapses to the same visual today when no parent
field is set — bit-identical fallback path.
The per-row prefix column (`.tree-prefix`) is **DOM-painted, not
text-glyph-painted**. Each indent lane is its own positioned
`<span>` so CSS can draw full-height vertical bars that bridge the
gap between sibling rows; using text box-drawing characters
(`├─`, `└─`, `│ `) only paints one text-line tall and leaves
visible breaks between the taller-than-one-line container cards.
The bars come in two flavours: continuation (the ancestor's
subtree extends below this row → vertical line top→bottom) or
blank (ancestor was the last sibling at its level → no line
needed). The joint at the row's own depth column is `├` (more
siblings below) or `└` (last sibling at this depth — vertical
stops at the row's icon midline).
### Selection bar
When one or more agents are selected (via icon click), a sticky

View file

@ -370,20 +370,9 @@ window.marked = marked;
}
// ─── state rendering ────────────────────────────────────────────────────
// ─── agent topology (#363) ──────────────────────────────────────────────
// Build a forest from `ContainerView.parent` and walk depth-first to
// produce a render order with per-row depth + sibling-position info.
// Top-level (parent = null OR parent not in the container map) are
// roots. Within each level, children are sorted alphabetically by
// name; roots likewise. Cycles in the parent graph (malformed config)
// are tolerated: any container not reached via root-walk is appended
// as a root at the end, so no agent ever disappears from the list.
//
// For the visual: each row gets a textual prefix column (`├─`, `└─`,
// continuation `│ ` or padding ` ` for ancestor columns). When
// every container has `parent = null` (today's pre-#361 state), the
// tree collapses to a flat list with no glyphs and no indent — bit-
// identical to the legacy render.
// ─── agent topology ─────────────────────────────────────────────────
// See docs/web-ui.md::Topology tree for the rendering contract
// (forest walk, alphabetical sort, orphan + cycle handling).
function buildAgentTree(containers) {
const byName = new Map();
for (const c of containers) byName.set(c.name, c);
@ -421,16 +410,9 @@ window.marked = marked;
}
return out;
}
// Builds the .tree-prefix DOM for a row at the given depth. Each
// lane is its own positioned child so CSS can paint full-height
// vertical bars that bridge the gap between sibling rows — text
// box-drawing glyphs only paint one text-line tall, which left
// visible breaks between rows once we grew taller-than-one-line
// container cards (#388). Ancestor lanes are either continuation
// (vertical bar top→bottom+gap) or blank; the joint at this row's
// own depth is ├ (branch — vertical continues below) or └ (last —
// vertical stops at the row's icon midline). CSS at
// `.container-row .tree-prefix` paints the bars + horizontal stub.
// Builds the .tree-prefix DOM for a row at the given depth.
// See docs/web-ui.md::Topology tree for why this is DOM-painted
// (one positioned <span> per lane) rather than text-glyph-painted.
function treePrefixDom({ depth, ancestorIsLast, isLast }) {
if (depth === 0) return null;
const prefix = el('span', { class: 'tree-prefix', 'aria-hidden': 'true' });