fix(ui): take scoped hierarchy hops from the discovered levels

scopedHierarchy derived each node's hop column from its z coordinate, which
only the arranged hop rings encode. While a scope loads, its preview is built
from the overall layout and keeps the server's global coordinates, so the
columns were wrong until the complete answer arrived. The hop now comes from
the levels the traversal records (roots at 0); z is only a fallback for
callers without levels.

Signed-off-by: Bernhard Jackiewicz <bj@techport.io>
This commit is contained in:
Bernhard Jackiewicz
2026-10-02 18:48:34 +02:00
parent 69b2ac6b59
commit 447e2d9183
2 changed files with 13 additions and 2 deletions
+8
View File
@@ -215,6 +215,14 @@ it('wraps large hierarchy levels into readable blocks without losing or overlapp
}
expect(result.data.nodes.every(node => node.color === '#abcabc')).toBe(true);
});
it('takes hop columns from discovered levels, not from the global layout depth of a preview', () => {
// A preview from the overall layout keeps server coordinates; z says nothing about hops.
const nodes = [{ ...node(1), z: -250 }, { ...node(2), z: 40 }, { ...node(3), z: -90 }];
const levels = new Map([[1, 0], [2, 1], [3, 2]]);
const result = scopedHierarchy({ data: { nodes, edges: [], total_nodes: 3 }, roots: new Set([1]), depth: 2, exhausted: false, levels }, 'preview');
expect(Object.fromEntries(result.placements.map(placement => [placement.name, placement.hop]))).toEqual({ n1: 0, n2: 1, n3: 2 });
expect(result.depth).toBe(3);
});
it('retains simple columns for small hierarchy levels', () => {
const nodes = [{ ...node(1), z: 0 }, { ...node(2), z: -18 }, { ...node(3), z: -18 }];
const result = scopedHierarchy({ data: { nodes, edges: [], total_nodes: 3 }, roots: new Set([1]), depth: 1, exhausted: false }, 'small');
+5 -2
View File
@@ -208,11 +208,14 @@ export function limitGraphRender(data: GraphData, nodeLimit: number, edgeLimit:
/** The same evidence in successive hop blocks. Small hops keep one column;
* large hops wrap into compact grids so fitting thousands of neighbors does
* not turn the entire scene into a single vertical line. */
* not turn the entire scene into a single vertical line.
*
* Hops come from the discovered levels. Only arranged rings encode them in z;
* a preview built from the overall layout keeps its global coordinates. */
export function scopedHierarchy(scope: ScopedGraph, name: string): import('./hierarchy-layout').HierarchyProjection {
const columns = new Map<number, GraphNode[]>();
for (const node of scope.data.nodes) {
const hop = Math.max(0, Math.round(-node.z / 18));
const hop = scope.roots.has(node.id) ? 0 : scope.levels?.get(node.id) ?? Math.max(0, Math.round(-node.z / 18));
const entries = columns.get(hop) ?? []; entries.push(node); columns.set(hop, entries);
}
const nodes: GraphNode[] = [], remap = new Map<number, number>();