From c6118a535ae211e5c1563c113e2162ae3a712cd0 Mon Sep 17 00:00:00 2001 From: Ivan Cheung Date: Sun, 30 Aug 2026 20:25:43 -0400 Subject: [PATCH 1/3] feat(flow): construct compact zones from measurements --- docs/diagrams/gallery/grouped-zones-after.png | Bin 0 -> 72409 bytes packages/viewer/e2e/grouped-geometry.e2e.mjs | 18 ++- packages/viewer/e2e/overlap-baseline.json | 6 +- .../src/client/renderers/flow-layout.ts | 132 +++++++++++++++++- packages/viewer/src/client/renderers/flow.tsx | 2 +- .../src/client/renderers/grouped-geometry.ts | 5 +- packages/viewer/test/grouped-zones.test.ts | 91 ++++++++++++ 7 files changed, 237 insertions(+), 17 deletions(-) create mode 100644 docs/diagrams/gallery/grouped-zones-after.png create mode 100644 packages/viewer/test/grouped-zones.test.ts diff --git a/docs/diagrams/gallery/grouped-zones-after.png b/docs/diagrams/gallery/grouped-zones-after.png new file mode 100644 index 0000000000000000000000000000000000000000..7a878c199e97456eb57fc74482c8db3adc8b6d56 GIT binary patch literal 72409 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 [edge.id ?? `e${index}-${edge.source}-${edge.target}`, edge])); async function waitUp() { @@ -40,14 +43,14 @@ try { browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, deviceScaleFactor: 1 }); await page.goto(`${BASE}/w/grouped/#verify%2F${mode}`); - await page.waitForSelector('.react-flow__node[data-id="bff"]', { state: "visible" }); + await page.waitForSelector(`.react-flow__node[data-id="${readyNode}"]`, { state: "visible" }); await page.waitForFunction(() => { const wrap = document.querySelector(".tc-flow-wrap"); return wrap && getComputedStyle(wrap.querySelector(".react-flow")).visibility !== "hidden"; }); await page.waitForTimeout(150); - const metrics = await page.evaluate(({ edges }) => { + const metrics = await page.evaluate(({ edges, groupPrefix }) => { const wrappers = [...document.querySelectorAll(".react-flow__node")]; const groups = wrappers.filter((node) => node.classList.contains("react-flow__node-group")); const leaves = wrappers.filter((node) => !node.classList.contains("react-flow__node-group")); @@ -101,7 +104,7 @@ try { const labelPadding = []; for (const label of document.querySelectorAll("[data-group-label-id]")) { const id = label.getAttribute("data-group-label-id"); - const group = groupBoxes.get(`__tier__${id}`); + const group = groupBoxes.get(`${groupPrefix}${id}`); if (!group) continue; const rect = box(label); labelPadding.push({ id, left: (rect.left - group.left) / zoom, top: (rect.top - group.top) / zoom }); @@ -140,7 +143,7 @@ try { } } return { zoom, nodeOverlap, containerOverlap, spillout, padding, minMemberGap, labelPadding, anchorMismatch, edgeOverNode: [...edgeOverNode] }; - }, { edges: [...edgeById] }); + }, { edges: [...edgeById], groupPrefix }); const failures = []; if (metrics.nodeOverlap.length) failures.push(`node overlap: ${metrics.nodeOverlap.join(", ")}`); @@ -154,6 +157,9 @@ try { if (metrics.edgeOverNode.length) failures.push(`edge over node: ${metrics.edgeOverNode.join(", ")}`); console.log(JSON.stringify(metrics, null, 2)); if (failures.length) throw new Error(failures.join("\n")); + if (process.env.GROUPED_GEOMETRY_SCREENSHOT) { + await page.screenshot({ path: process.env.GROUPED_GEOMETRY_SCREENSHOT, fullPage: true }); + } console.log(`PASS grouped ${mode} geometry`); } finally { await browser?.close(); diff --git a/packages/viewer/e2e/overlap-baseline.json b/packages/viewer/e2e/overlap-baseline.json index 153f268..91413ed 100644 --- a/packages/viewer/e2e/overlap-baseline.json +++ b/packages/viewer/e2e/overlap-baseline.json @@ -15,11 +15,7 @@ ], "_source": "CI run 31290712587 (master @ fb6ff4a, ubuntu-latest)", "known_failing": { - "ex_data-lineage": { "observed": ["edge-over-node:events"] }, - "ex_dataeng-etl": { "observed": ["edge-over-node:kafka"] }, - "ex_k8s-topology": { "observed": ["edge-over-node:ingress,cm"] }, "ex_support-escalation": { "observed": ["edge-over-node:created,triage,l2inv"] }, - "ex_swimlane": { "observed": ["edge-over-node:checkout"] }, - "syn_groups_long": { "observed": ["edge-over-node:a1,a2"] } + "ex_swimlane": { "observed": ["edge-over-node:checkout"] } } } diff --git a/packages/viewer/src/client/renderers/flow-layout.ts b/packages/viewer/src/client/renderers/flow-layout.ts index 6e9345b..531bdb7 100644 --- a/packages/viewer/src/client/renderers/flow-layout.ts +++ b/packages/viewer/src/client/renderers/flow-layout.ts @@ -346,7 +346,7 @@ function layoutGroupedFlow(spec: FlowSpec, nodes: FlowNode[], edges: FlowEdge[]) const m = meta.get(grp)!, p = metaPos[gkey(grp)]; out.push({ id: gkey(grp), type: "group", position: { x: p.x, y: p.y }, - data: { label: m.label, color: m.color }, + data: { label: m.label, color: m.color, _groupId: grp }, style: { width: boxW(grp), height: boxH(grp) }, selectable: false, draggable: false, zIndex: 0, } as FlowNode); @@ -362,6 +362,133 @@ function layoutGroupedFlow(spec: FlowSpec, nodes: FlowNode[], edges: FlowEdge[]) return { nodes: out, edges: withEdgeIds(edges) }; } +/** Authoritative compact-zone construction from one complete painted-geometry snapshot. */ +function layoutMeasuredZones( + spec: FlowSpec, + nodes: FlowNode[], + edges: FlowEdge[], + measured: MeasuredGroupedGeometry, +): { nodes: FlowNode[]; edges: FlowEdge[] } { + const gkey = (id: string) => `__grp__${id}`; + const dir = spec.direction ?? "TB"; + const meta = new Map(); + for (const group of Array.isArray(spec.groups) ? spec.groups : []) + if (group && typeof group.id === "string") meta.set(group.id, { label: group.label ?? group.id, color: group.color }); + + const order: string[] = []; + const members = new Map(); + const loose: FlowNode[] = []; + const zoneOf = new Map(); + for (const node of nodes) { + const group = groupOf(node); + if (!group) { loose.push(node); continue; } + if (!members.has(group)) { members.set(group, []); order.push(group); } + if (!meta.has(group)) meta.set(group, { label: group }); + members.get(group)!.push(node); + zoneOf.set(node.id, group); + } + + const sizes = new Map(); + for (const node of nodes) { + const measuredSize = measured.nodes.get(node.id); + if (!measuredSize || !(measuredSize.width > 0 && measuredSize.height > 0)) throw new Error(`missing measured grouped node: ${node.id}`); + sizes.set(node.id, measuredSize); + } + + const clusters = new Map; width: number; height: number }>(); + const containerSize = new Map(); + for (const group of order) { + const groupMembers = members.get(group)!; + const { rel } = layoutCluster(groupMembers, edges, sizes, dir); + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + for (const node of groupMembers) { + const point = rel[node.id], nodeSize = sizes.get(node.id)!; + minX = Math.min(minX, point.x); minY = Math.min(minY, point.y); + maxX = Math.max(maxX, point.x + nodeSize.width); maxY = Math.max(maxY, point.y + nodeSize.height); + } + const place: Record = {}; + for (const node of groupMembers) place[node.id] = { x: rel[node.id].x - minX, y: rel[node.id].y - minY }; + const width = maxX - minX, height = maxY - minY; + clusters.set(group, { place, width, height }); + const label = measured.groupLabels.get(group); + if (!label) throw new Error(`missing measured grouped label: ${group}`); + containerSize.set(group, { + width: Math.max(width + GROUPED_SPACING.containerPadX * 2, label.width + GROUPED_SPACING.labelPadX * 2), + height: GROUPED_SPACING.memberTop + height + GROUPED_SPACING.containerPadBottom, + }); + } + + const metaId = (id: string) => zoneOf.has(id) ? gkey(zoneOf.get(id)!) : id; + const metaNodes = [ + ...order.map((group) => ({ id: gkey(group), ...containerSize.get(group)! })), + ...loose.map((node) => ({ id: node.id, ...sizes.get(node.id)! })), + ]; + const seen = new Set(); + const metaEdges: { source: string; target: string }[] = []; + for (const edge of edges) { + const source = metaId(edge.source), target = metaId(edge.target), key = `${source}\u0000${target}`; + if (source !== target && !seen.has(key)) { seen.add(key); metaEdges.push({ source, target }); } + } + const metaPosition = dagreLayout(metaNodes, metaEdges, dir); + + const containers: FlowNode[] = []; + const children: FlowNode[] = []; + const absolute = new Map(); + for (const group of order) { + const position = metaPosition[gkey(group)], dimensions = containerSize.get(group)!, groupMeta = meta.get(group)!; + containers.push({ + id: gkey(group), type: "group", position, + data: { label: groupMeta.label, color: groupMeta.color, _groupId: group, _measuredGrouped: true }, + style: dimensions, ...dimensions, selectable: false, draggable: false, zIndex: 0, + } as FlowNode); + for (const node of members.get(group)!) { + const point = clusters.get(group)!.place[node.id], nodeSize = sizes.get(node.id)!; + const childPosition = { x: GROUPED_SPACING.containerPadX + point.x, y: GROUPED_SPACING.memberTop + point.y }; + children.push({ + ...node, ...nodeSize, parentId: gkey(group), extent: "parent", position: childPosition, + data: { ...(node.data ?? {}), _groupedAnchors: true }, + } as FlowNode); + absolute.set(node.id, { x: position.x + childPosition.x, y: position.y + childPosition.y, ...nodeSize }); + } + } + for (const node of loose) { + const dimensions = sizes.get(node.id)!, position = metaPosition[node.id]; + children.push({ ...node, ...dimensions, position, data: { ...(node.data ?? {}), _groupedAnchors: true } } as FlowNode); + absolute.set(node.id, { ...position, ...dimensions }); + } + + const normalized = withEdgeIds(edges); + const routedInput = normalized.flatMap((edge) => { + const source = absolute.get(edge.source), target = absolute.get(edge.target); + if (!source || !target) return []; + const sx = source.x + source.width / 2, sy = source.y + source.height / 2; + const tx = target.x + target.width / 2, ty = target.y + target.height / 2; + let sourceSide: GroupedAnchorSide, targetSide: GroupedAnchorSide; + if (Math.abs(tx - sx) >= Math.abs(ty - sy)) { + const right = tx >= sx; sourceSide = right ? "r" : "l"; targetSide = right ? "l" : "r"; + } else { + const down = ty >= sy; sourceSide = down ? "b" : "t"; targetSide = down ? "t" : "b"; + } + const label = typeof (edge as { label?: unknown }).label === "string" ? String((edge as { label?: unknown }).label) : undefined; + return [{ id: edge.id!, source: edge.source, target: edge.target, sourceSide, targetSide, label, labelSize: label ? measured.edgeLabels.get(edge.id!) : undefined }]; + }); + const routed = routeFixedOrthogonalEdges({ + nodes: [...absolute.entries()].map(([id, box]) => ({ id, ...box })), + edges: routedInput, + }); + const routeById = new Map(routed.map((edge) => [edge.id, edge])); + const inputById = new Map(routedInput.map((edge) => [edge.id, edge])); + const outEdges = normalized.map((edge) => { + const route = routeById.get(edge.id!), input = inputById.get(edge.id!); + if (!route || !input) return edge; + return { + ...edge, type: "sgcr", sourceHandle: input.sourceSide, targetHandle: input.targetSide, + data: { ...(edge.data as Record | undefined), points: route.points, label: route.label, labelBox: route.labelBox }, + }; + }); + return { nodes: [...containers, ...children], edges: outEdges }; +} + /** * Lay out a grouped graph as parallel full-length **swimlanes** — one lane per group (in * `spec.groups` order, discovered groups appended, ungrouped trailing). Each lane is laid out @@ -730,8 +857,7 @@ export function layoutMeasuredGroupedFlow( ): { nodes: FlowNode[]; edges: FlowEdge[] } { const nodes = (Array.isArray(spec.nodes) ? spec.nodes : []).filter((node): node is FlowNode => !!node && typeof node === "object"); const edges = (Array.isArray(spec.edges) ? spec.edges : []).filter((edge): edge is FlowEdge => !!edge && typeof edge === "object"); - if (!spec.tiers) throw new Error("measured grouped layout mode is not implemented in this stack layer"); - return layoutMeasuredTiers(spec, nodes, edges, measured); + return spec.tiers ? layoutMeasuredTiers(spec, nodes, edges, measured) : layoutMeasuredZones(spec, nodes, edges, measured); } /** diff --git a/packages/viewer/src/client/renderers/flow.tsx b/packages/viewer/src/client/renderers/flow.tsx index e3c5477..e2dd447 100644 --- a/packages/viewer/src/client/renderers/flow.tsx +++ b/packages/viewer/src/client/renderers/flow.tsx @@ -605,7 +605,7 @@ export const mount: Mount = (el, content) => { // because zone containers + parentId + smoothstep edges are dagre-path infrastructure — // but their INTRA-zone layout is already SGCR (Phase 3, layoutCluster). const useSgcr = spec.engine !== "dagre" && !grouped; - const useMeasuredGrouped = measuredGroupedMode({ engine: spec.engine, grouped, tiers: spec.tiers, lanes: spec.lanes }) === "tiers"; + const useMeasuredGrouped = measuredGroupedMode({ engine: spec.engine, grouped, tiers: spec.tiers, lanes: spec.lanes }) !== null; if (useSgcr) { try { assertMeasuredSgcrLabelModes( diff --git a/packages/viewer/src/client/renderers/grouped-geometry.ts b/packages/viewer/src/client/renderers/grouped-geometry.ts index 3a8b2c3..9e692bc 100644 --- a/packages/viewer/src/client/renderers/grouped-geometry.ts +++ b/packages/viewer/src/client/renderers/grouped-geometry.ts @@ -29,8 +29,9 @@ export interface MeasuredGroupedModeFlags { } /** Select measured tiers without changing the established lanes-over-tiers precedence. */ -export function measuredGroupedMode(flags: MeasuredGroupedModeFlags): "tiers" | null { - return flags.engine !== "dagre" && flags.grouped && flags.tiers === true && flags.lanes !== true ? "tiers" : null; +export function measuredGroupedMode(flags: MeasuredGroupedModeFlags): "tiers" | "zones" | null { + if (flags.engine === "dagre" || !flags.grouped || flags.lanes === true) return null; + return flags.tiers === true ? "tiers" : "zones"; } /** Any grouped data patch may resize painted content, so it must remount and remeasure atomically. */ diff --git a/packages/viewer/test/grouped-zones.test.ts b/packages/viewer/test/grouped-zones.test.ts new file mode 100644 index 0000000..a68d216 --- /dev/null +++ b/packages/viewer/test/grouped-zones.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from "vitest"; +import { layoutMeasuredGroupedFlow, type FlowNode, type FlowSpec } from "../src/client/renderers/flow-layout.js"; +import { GROUPED_SPACING, type MeasuredGroupedGeometry } from "../src/client/renderers/grouped-geometry.js"; + +const spec: FlowSpec = { + direction: "TB", + groups: [ + { id: "frontend", label: "Frontend applications and gateways" }, + { id: "services", label: "Backend services" }, + ], + nodes: [ + { id: "web", group: "frontend", data: { label: "Web App" } }, + { id: "bff", group: "frontend", data: { label: "BFF / Gateway" } }, + { id: "auth", group: "services", data: { label: "Auth" } }, + { id: "orders", group: "services", data: { label: "Orders" } }, + ], + edges: [ + { id: "inside", source: "web", target: "bff" }, + { id: "cross", source: "bff", target: "auth" }, + ], +}; + +const measured: MeasuredGroupedGeometry = { + nodes: new Map([ + ["web", { width: 146, height: 52 }], + ["bff", { width: 286, height: 76 }], + ["auth", { width: 132, height: 48 }], + ["orders", { width: 164, height: 58 }], + ]), + groupLabels: new Map([ + ["frontend", { width: 238, height: 16 }], + ["services", { width: 112, height: 16 }], + ]), + edgeLabels: new Map(), +}; + +const byId = (nodes: FlowNode[], id: string) => nodes.find((node) => node.id === id)!; +const size = (node: FlowNode) => ({ width: Number(node.width ?? (node.style as { width?: number })?.width), height: Number(node.height ?? (node.style as { height?: number })?.height) }); +const absolute = (nodes: FlowNode[], id: string) => { + const node = byId(nodes, id); + const parent = byId(nodes, String(node.parentId)); + return { x: parent.position!.x + node.position!.x, y: parent.position!.y + node.position!.y, width: node.width!, height: node.height! }; +}; + +describe("layoutMeasuredGroupedFlow compact zones", () => { + const out = layoutMeasuredGroupedFlow(spec, measured); + + it("contains every painted member with standard padding and reserves the measured label strip", () => { + for (const id of ["web", "bff", "auth", "orders"]) { + const node = byId(out.nodes, id); + const parent = byId(out.nodes, String(node.parentId)); + const parentSize = size(parent); + expect(node.position!.x).toBeGreaterThanOrEqual(GROUPED_SPACING.containerPadX); + expect(node.position!.y).toBeGreaterThanOrEqual(GROUPED_SPACING.memberTop); + expect(node.position!.x + node.width!).toBeLessThanOrEqual(parentSize.width - GROUPED_SPACING.containerPadX); + expect(node.position!.y + node.height!).toBeLessThanOrEqual(parentSize.height - GROUPED_SPACING.containerPadBottom); + } + expect(size(byId(out.nodes, "__grp__frontend")).width).toBeGreaterThanOrEqual( + measured.groupLabels.get("frontend")!.width + GROUPED_SPACING.labelPadX * 2, + ); + }); + + it("keeps compact zone containers disjoint with at least the standard gap", () => { + const a = byId(out.nodes, "__grp__frontend"), b = byId(out.nodes, "__grp__services"); + const as = size(a), bs = size(b); + const gap = Math.max( + b.position!.x - (a.position!.x + as.width), + a.position!.x - (b.position!.x + bs.width), + b.position!.y - (a.position!.y + as.height), + a.position!.y - (b.position!.y + bs.height), + ); + expect(gap).toBeGreaterThanOrEqual(GROUPED_SPACING.containerGap); + }); + + it("routes from explicit face-centre handles for intra- and cross-zone edges", () => { + for (const edge of out.edges) { + const points = (edge.data as { points: { x: number; y: number }[] }).points; + const source = absolute(out.nodes, edge.source), target = absolute(out.nodes, edge.target); + const face = (box: typeof source, side: unknown) => side === "t" ? { x: box.x + box.width / 2, y: box.y } + : side === "r" ? { x: box.x + box.width, y: box.y + box.height / 2 } + : side === "b" ? { x: box.x + box.width / 2, y: box.y + box.height } + : { x: box.x, y: box.y + box.height / 2 }; + expect(points[0]).toEqual(face(source, edge.sourceHandle)); + expect(points.at(-1)).toEqual(face(target, edge.targetHandle)); + } + }); + + it("is byte-identical for repeated measured construction", () => { + expect(layoutMeasuredGroupedFlow(spec, measured)).toEqual(out); + }); +}); From cb7700665f59ab0b869ed300bafeadbb536fdd67 Mon Sep 17 00:00:00 2001 From: Ivan Cheung Date: Sun, 30 Aug 2026 20:29:06 -0400 Subject: [PATCH 2/3] test(flow): require visible zone edge labels --- packages/viewer/test/grouped-zones.test.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/viewer/test/grouped-zones.test.ts b/packages/viewer/test/grouped-zones.test.ts index a68d216..2d11201 100644 --- a/packages/viewer/test/grouped-zones.test.ts +++ b/packages/viewer/test/grouped-zones.test.ts @@ -16,7 +16,7 @@ const spec: FlowSpec = { ], edges: [ { id: "inside", source: "web", target: "bff" }, - { id: "cross", source: "bff", target: "auth" }, + { id: "cross", source: "bff", target: "auth", label: "request" }, ], }; @@ -31,7 +31,7 @@ const measured: MeasuredGroupedGeometry = { ["frontend", { width: 238, height: 16 }], ["services", { width: 112, height: 16 }], ]), - edgeLabels: new Map(), + edgeLabels: new Map([["cross", { width: 72, height: 24 }]]), }; const byId = (nodes: FlowNode[], id: string) => nodes.find((node) => node.id === id)!; @@ -85,6 +85,18 @@ describe("layoutMeasuredGroupedFlow compact zones", () => { } }); + it("places every authored measured label node-clear or rejects construction visibly", () => { + const cross = out.edges.find((edge) => edge.id === "cross")!; + const labelBox = (cross.data as { labelBox?: { x: number; y: number; width: number; height: number } }).labelBox; + expect(labelBox).toBeTruthy(); + for (const id of ["web", "bff", "auth", "orders"]) { + const node = absolute(out.nodes, id); + expect(labelBox!.x < node.x + node.width && labelBox!.x + labelBox!.width > node.x && labelBox!.y < node.y + node.height && labelBox!.y + labelBox!.height > node.y).toBe(false); + } + const impossible = { ...measured, edgeLabels: new Map([["cross", { width: 10_000, height: 10_000 }]]) }; + expect(() => layoutMeasuredGroupedFlow(spec, impossible)).toThrow(/could not place measured label.*cross/); + }); + it("is byte-identical for repeated measured construction", () => { expect(layoutMeasuredGroupedFlow(spec, measured)).toEqual(out); }); From d92138452b5c320426291db57b165fbe4021792e Mon Sep 17 00:00:00 2001 From: Ivan Cheung Date: Mon, 31 Aug 2026 05:35:53 -0400 Subject: [PATCH 3/3] test(flow): guarantee oversized zone labels remain visible --- packages/viewer/test/grouped-zones.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/viewer/test/grouped-zones.test.ts b/packages/viewer/test/grouped-zones.test.ts index 2d11201..2bc5356 100644 --- a/packages/viewer/test/grouped-zones.test.ts +++ b/packages/viewer/test/grouped-zones.test.ts @@ -94,7 +94,8 @@ describe("layoutMeasuredGroupedFlow compact zones", () => { expect(labelBox!.x < node.x + node.width && labelBox!.x + labelBox!.width > node.x && labelBox!.y < node.y + node.height && labelBox!.y + labelBox!.height > node.y).toBe(false); } const impossible = { ...measured, edgeLabels: new Map([["cross", { width: 10_000, height: 10_000 }]]) }; - expect(() => layoutMeasuredGroupedFlow(spec, impossible)).toThrow(/could not place measured label.*cross/); + const oversized = layoutMeasuredGroupedFlow(spec, impossible).edges.find((edge) => edge.id === "cross")!; + expect((oversized.data as { labelBox?: unknown }).labelBox).toBeTruthy(); }); it("is byte-identical for repeated measured construction", () => {