Skip to content

feat(dashboard): navigation redesign — feature containers, drill-in, layer index view, tier pinning#414

Open
maximbelyakov wants to merge 9 commits into
Egonex-AI:mainfrom
maximbelyakov:feat/navigation-redesign
Open

feat(dashboard): navigation redesign — feature containers, drill-in, layer index view, tier pinning#414
maximbelyakov wants to merge 9 commits into
Egonex-AI:mainfrom
maximbelyakov:feat/navigation-redesign

Conversation

@maximbelyakov

Copy link
Copy Markdown

Navigation redesign for large real-world graphs

Built while mapping a production Java+TS monorepo (1,000+ nodes). The existing layer-detail view stops working once a layer holds dozens of homogeneous files (e.g. 46 DynamoDB repositories in one folder): folder containers become a single giant box, Louvain fallback produces anonymous "Cluster A/B/C", zoom auto-expand floods the viewport with full cards.

Data model (core, backward-compatible)

  • layer.containers?: {name, nodeIds, group?}[] — manual feature grouping of a layer's nodes; wins over folder/community derivation. Optional group arranges containers into titled sections.
  • layer.tier?: number — pins the layer's vertical position in the project overview.

Navigation: project → layer → feature

  • Containers render as compact chips; click drills in to a feature mini-canvas (full cards + typed edges + portals), Esc/breadcrumb walks back. Zoom auto-expand removed.
  • Layers with many data-defined containers render as a scrollable sectioned index (LayerIndexView) instead of a canvas — a graph canvas conveys nothing when a layer is 38 features in a star around one hub.
  • Small layers (≤10 files) skip auto-grouping: plain full cards with real edges.
  • Single-folder layers keep ONE container named after the folder; community clusters are labeled by member files.

Overview fixes

  • aggregateLayerEdges merged pairs alphabetically, destroying dependency direction → vertical order was arbitrary. Now counts directions separately and keeps the NET direction.
  • layer.tier + ELK partitioning pins storage layers to the bottom row. Found and worked around: postCompaction=LEFT silently violates ELK partitions (reproduced in isolation).
  • Against-tier edges are flipped visually (overview has no arrowheads) so they exit the upper card's bottom instead of looping into its top.
  • Portal ELK size accounts for the member-name list (overlap fix).

All 43 dashboard tests pass; tests updated/added where behavior changed. Builds on #411/#412 (branch includes their commits).

ContainerNode had no React Flow Handle components, so every edge whose
endpoint was a container atom (container->portal dashed edges and
aggregated container->container edges) was silently skipped by React
Flow. Add invisible target/top + source/bottom handles, and call
updateNodeInternals when the container resizes on expand/collapse so
edge anchors follow the new bounds instead of pointing at the stale
pre-expansion geometry.
…hbor names on portal cards

1. Portal edges were sourced off container atoms with re-routing on
   expand explicitly deferred (TODO in Stage 1). Implement it: when a
   container is expanded, its container->portal edge is replaced with
   per-file edges from the actual cross-layer files inside it
   (portalCrossFiles map computed in Stage 1).
2. Portal cards now list the names of the external files behind the
   aggregated connection count (up to 6, then +N more), via a new
   findExternalNeighborFiles() helper - the user can see WHICH files in
   the neighboring layer this layer talks to without leaving the view.
1. Community clusters are now labeled by their member files
   ("sagas · service · actions +2") instead of "Cluster A/B/C".
2. A single folder covering the whole filtered set (e.g. a Redux slice
   folder like src/store/meetingTypes) is kept as ONE container named
   after the folder instead of being split into anonymous Louvain
   communities. Tests updated + new coverage for both behaviors.
- Node cards: 310px max width (was 220), titles wrap to 2 lines and
  truncate at 60 chars (was 24), summaries clamp at 3 lines (was 2).
  NODE_WIDTH/HEIGHT layout constants bumped to match.
- Portal edges: stroke opacity 0.2 -> 0.55, width 1.6 - they were
  nearly invisible on the dark background, making layers look
  disconnected even when edges existed.
…layer index view, tier pinning

- layer.containers (core schema): manual feature grouping with optional
  per-container 'group' for index-view sections; singleton feature
  containers allowed; single-folder layers keep ONE named container;
  small layers (<=10 files) skip auto-grouping entirely
- containers are navigation chips: click drills into a feature
  mini-canvas (project -> layer -> feature), Esc walks back; breadcrumb
  shows the trail; zoom auto-expand removed
- LayerIndexView: layers with many data-defined containers render as a
  scrollable sectioned feature index instead of a canvas
- dense grid fallback for >12-children containers expanded via focus/tour
- aggregateLayerEdges: net-direction merge of antiparallel pairs
  (alphabetical canonicalization destroyed dependency direction)
- layer.tier + ELK partitioning pins storage layers to the bottom row
- portal ELK size accounts for the member-name list (overlap fix)
elk.layered.compaction.postCompaction.strategy=LEFT moves nodes across
layer boundaries after layering, so a partition-2 layer (Copilot) ended
up rendered in the top row next to partition-0. Disable post-compaction
whenever partitioning is active. Reproduced with elkjs in isolation:
minimal options place the node correctly; adding the compaction option
breaks it.
With pinned tiers, an aggregated edge whose net direction points from a
lower tier to a higher one (e.g. services -> copilot) left the lower
card's bottom handle, looped around the canvas and entered the upper
card's top handle. The overview draws no arrowheads, so flip such edges
visually: exit the upper card's bottom, enter the lower card's top.
activeContainerId was read inside the layer-detail structural memo but
missing from its dependency array, so clicking a feature tile switched
the view to the canvas while the topology stayed the full-layer chip
maze — the drilled feature's children never rendered.
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