Skip to content

feat(canvas): real containment hierarchy for the map view - #26

Merged
rishibaghel25 merged 1 commit into
mainfrom
feat/hierarchy-map
Oct 17, 2026
Merged

rishibaghel25 merged 1 commit into
mainfrom
feat/hierarchy-map

Conversation

@rishibaghel25

Copy link
Copy Markdown
Member

Mirrors SaaS PR #20.

What the map is now

Kubernetes                    ← big node, helm logo, resource count
├── Cluster      ├── CronJobs    ├── DaemonSets
├── Deployments  ├── Ingresses   ├── Namespaces
│                                 ├── cert-manager
│                                 │   ├── cert-manager
│                                 │   ├── cert-manager-webhook
│                                 │   └── cert-manager-cainjector
│                                 └── cs
│                                     ├── manager-*
│                                     ├── az-*, clickhouse-*

Why the previous attempts were wrong

Each got one half of it:

  • Grouping by type alone destroyed parentage — one flat Pods ×9 owned
    by nothing.
  • A free radial layout destroyed grouping — a circle of identical discs
    where a Pod, a Service and a PVC were indistinguishable.
  • Labelled blocks kept grouping but had no hierarchy inside them, so a
    namespace's resources were a flat shelf belonging to nothing.

What this does

  • buildHierarchy() synthesises platform roots (Kubernetes / Docker /
    Host), resource-type nodes beneath them, and the containment edges
    between parent and child
  • applyHierarchyLayout() centres each node over its children, grid-wrapping
    wide fan-outs so a 50-pod namespace stays a readable rectangle
  • Namespaced resources are drawn under their namespace, so the parentage
    is real. The type node still links to them (dashed) so Pods reaches its
    pods
  • Three kinds of line, drawn differently so structure is legible:
    containment solid, type→member dashed because it spans levels, original
    snapshot edges faint in the background

Two layout bugs caught by local tests

cy was computed once and never advanced per row, so every row of a
wrapped grid stacked on the same line — 91 overlapping pairs.

Column count came from child count alone, which put four ~1200px
namespace blocks side by side and made the tree 6000px wide. It is now
derived from how wide the children actually are, targeting a 1500px level:
aspect 4.21 → 1.17.

Verified

On the SaaS side against the real kind cluster: 187 resources + 14 hierarchy
nodes, 368 edges, 0 overlapping pairs, deterministic, no console errors.
Locally at 117 nodes: all placed, no lost nodes, no duplicate ids.

Mirrors SaaS PR #20.

    Kubernetes                    (big node, helm logo)
    |- Cluster  |- CronJobs  |- Deployments  |- Namespaces
    |                                         |- cert-manager
    |                                         |   |- cert-manager
    |                                         |   |- cert-manager-webhook
    |                                         |- cs
    |                                             |- manager-*, az-*

Three earlier attempts each got half of it wrong: grouping by type alone
destroyed parentage, a free radial layout destroyed grouping (identical
discs, undifferentiable), labelled blocks kept grouping but had no
hierarchy inside them.

- `hierarchyMap.ts` — synthesises platform roots, resource-type nodes and
  containment edges; layout centres each node over its children and
  grid-wraps wide fan-outs
- `MapHierarchyNodes.tsx` — root node with the Kubernetes helm / Docker
  marks, and type nodes (`Pods 44`)

Namespaced resources are drawn under their namespace so the parentage is
real; the type node still links to them so "Pods" reaches its pods.

Two layout bugs fixed: `cy` was computed once and never advanced per row
so wrapped grids stacked on one line (91 overlapping pairs), and column
count came from child count alone which made the tree 6000px wide
(aspect 4.21 -> 1.17).
const MAX_COLS = 8
/** Roughly how wide a level of the tree should be. */
const TARGET_BLOCK_W = 1500
const CELL_W = Math.max(ENT_W, TYPE_W) + GAP_X
/** Roughly how wide a level of the tree should be. */
const TARGET_BLOCK_W = 1500
const CELL_W = Math.max(ENT_W, TYPE_W) + GAP_X
const CELL_H = ENT_H + GAP_Y
@rishibaghel25
rishibaghel25 merged commit cc185bc into main Oct 17, 2026
5 checks passed
@rishibaghel25
rishibaghel25 deleted the feat/hierarchy-map branch October 17, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant