feat: implement Stories 2.4-2.7 — E-R, Org Chart, Architecture, Sequence diagram type renderers

Adds four diagram type renderers completing the core diagram type suite:
- Story 2.4: E-R entity nodes with column tables, relationship edges with cardinality labels
- Story 2.5: Org chart person nodes with role/department tags, hierarchy edges
- Story 2.6: Architecture nodes (service, database, queue, load balancer, external), connection edges
- Story 2.7: Sequence participant nodes with lifelines + activation bars, fragment nodes,
  3 custom edge types (sync/async/return), custom time-ordered layout (not ELK)

Story 2.7 includes code review fixes: computeLayout returns LayoutResult so enriched
sequence edges flow through useAutoLayout, activation bar computation in layout,
immutable layout function, self-message U-shaped loop rendering, sequence node size
tests in buildElkGraph.

476 tests passing across 29 test files, zero regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-27 01:24:50 +00:00
parent 0a7838aa60
commit 1ff8ff8f06
38 changed files with 5620 additions and 22 deletions

View File

@@ -11,7 +11,7 @@ import {
SOFT_CAP_NODE_COUNT,
} from "../lib/elk-layout";
import type { ElkLayoutOptions } from "../lib/elk-layout";
import type { ElkLayoutOptions, LayoutResult } from "../lib/elk-layout";
const DEBOUNCE_MS = 300;
const LAYOUT_ANIMATION_MS = 200;
@@ -24,6 +24,7 @@ export function useAutoLayout() {
const nodeCount = useGraphStore((s) => s.nodeCount);
const setNodes = useGraphStore((s) => s.setNodes);
const setEdges = useGraphStore((s) => s.setEdges);
const layoutDirection = useGraphStore((s) => s.layoutDirection);
const edgeRouting = useGraphStore((s) => s.edgeRouting);
const isLayouting = useGraphStore((s) => s.isLayouting);
@@ -53,7 +54,7 @@ export function useAutoLayout() {
flowNodes.forEach((el) => el.classList.add("layouting"));
try {
const layoutedNodes = await computeLayout(
const result: LayoutResult = await computeLayout(
currentNodes,
currentEdges,
{
@@ -67,7 +68,10 @@ export function useAutoLayout() {
},
);
setNodes(layoutedNodes);
setNodes(result.nodes);
if (result.edges) {
setEdges(result.edges);
}
// Fit view after layout, with a small delay to let transition run
setTimeout(() => {
@@ -96,7 +100,7 @@ export function useAutoLayout() {
}, LAYOUT_ANIMATION_MS);
}
},
[setNodes, setIsLayouting, fitView],
[setNodes, setEdges, setIsLayouting, fitView],
);
const triggerLayout = useCallback(