dashboard: render agent topology as a tree in the container list
Closes #363 (frontend half of milestone #361). Consumes the `ContainerView.parent: Option<String>` field landing in damocles' backend slice — when present, the container list renders depth-first with sibling-position tree glyphs (├─ / └─ joints + │ continuation columns). When absent (pre-#361 state — every container's `parent` is None) the tree collapses to a flat list with no glyphs and no indent — bit-identical to the legacy render. ## Tree shape - Roots: containers whose `parent` is None OR whose parent name isn't in the current container set (orphan tolerance). - Sibling ordering: alphabetical by name within each level (matches damocles' wire spec at #363#issuecomment-3356). - Cycle safety: any container not reached via the root-walk gets emitted as a root at the end — no agent ever silently disappears from the list when the topology is malformed. - Tree glyphs: ancestor at depth d contributes a │ continuation column when that ancestor has more siblings below; otherwise a 3-space gap. The joint is ├─ for non-last siblings, └─ for the last child. - Depth-0 ancestor column is suppressed: roots already separate visually as top-level rows, no need for a column 0 vertical line. ## DOM / CSS - New `buildAgentTree(containers)` + `treePrefix(node)` helpers in app.js. The render loop walks the tree-ordered list instead of the legacy alphabetical containers array. - Each container row gets `data-depth=N` (only when N > 0) and a `<span class="tree-prefix">…</span>` prepended (absolute-positioned into the row's left margin so the existing flex icon/body layout isn't disrupted). - CSS: per-depth `margin-left` step rules for depths 1-6 (hardcoded rather than typed-attr() because CSS Values 5 is Chromium-only as of 2026). 6 depths cover any reasonable hive topology with headroom; deeper agents render at depth 6 indent without further step — visually clamps gracefully. - `.tree-prefix` rendered with `var(--purple-dim)` so the structural lines read as supporting chrome, not as content. ## Validation - `npm run build` clean. Bundle deltas: dashboard app.js +1.8kb (tree builder + treePrefix + render-loop tweak), dashboard.css +0.4kb (tree-prefix + per-depth indent rules). - The render is a no-op until `ContainerView.parent` is populated — validation in production deferred to once damocles' meta-topology field lands. The pre-#361 path (every parent=None) is exercised by every existing dashboard load. - Forward-compatible with damocles' design pivot at #364 (topology source moved from agent.nix to meta/topology.json). The wire shape on ContainerView is unchanged from the frontend's perspective — the field is just sourced from a different backend store.
This commit is contained in:
parent
3fa12bf363
commit
8c7bc850f3
2 changed files with 110 additions and 4 deletions
|
|
@ -82,6 +82,36 @@ a:hover {
|
|||
background: rgba(24, 24, 37, 0.55);
|
||||
transition: opacity 200ms ease, border-color 200ms ease;
|
||||
}
|
||||
/* Topology indent (#363). Each depth level shifts the row right by one
|
||||
step; the .tree-prefix span (drawn by app.js::treePrefix) carries
|
||||
the ├─ / └─ glyph and any continuation lines that thread through
|
||||
ancestor columns. When every container has parent=null (pre-#361
|
||||
state) `[data-depth]` is absent on every row and these rules are
|
||||
no-ops — the layout reads exactly like the legacy flat list.
|
||||
Per-depth indent: hardcoded steps for 6 levels (sufficient for any
|
||||
plausible hive topology) — the typed `attr()` function from CSS
|
||||
Values 5 would collapse this to one rule, but browser support is
|
||||
still partial (Chromium-only as of 2026). */
|
||||
.container-row[data-depth] { position: relative; }
|
||||
.container-row[data-depth="1"] { margin-left: 1.8em; }
|
||||
.container-row[data-depth="2"] { margin-left: 3.6em; }
|
||||
.container-row[data-depth="3"] { margin-left: 5.4em; }
|
||||
.container-row[data-depth="4"] { margin-left: 7.2em; }
|
||||
.container-row[data-depth="5"] { margin-left: 9em; }
|
||||
.container-row[data-depth="6"] { margin-left: 10.8em; }
|
||||
.container-row .tree-prefix {
|
||||
/* Tree-line glyphs sit in the left margin so the rest of the
|
||||
container card layout (icon + body flex split) stays unchanged.
|
||||
Mono font for column alignment with the ├─ / └─ / │ bricks. */
|
||||
position: absolute;
|
||||
left: -1.6em;
|
||||
top: 0.6em;
|
||||
color: var(--purple-dim);
|
||||
font-family: inherit;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
/* Live cards get the icon-left / body-right split; tombstone rows keep
|
||||
the plain stacked block layout. The icon is a background-image div
|
||||
with no intrinsic size, so its load state can never reflow the row
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue