feat(dashboard): switch graph layout from Dagre to ELK #232

Merged
xiaomo merged 1 commits from feat/dashboard-elk-layout into main 2026-05-13 08:28:25 +00:00
Owner

What

Replace Dagre layout engine with ELK (Eclipse Layout Kernel) for workflow graph visualization.

Why

Dagre lacks edge label placement and orthogonal routing — condition labels overlap with nodes, edges cross messily.

Changes

  • use-layout.ts: Rewrite from Dagre → async ELK with layered algorithm, orthogonal routing, reduced spacing
  • condition-edge.tsx: Use ELK-computed label positions, show all labels (including FALLBACK), switch to getSmoothStepPath
  • workflow-graph.tsx: Wrap in ReactFlowProvider, fitView on async layout change, key-based remount
  • workflow-list.tsx: Left-right layout (info left, graph right), fix toggleExpanded React 18 batching bug, increase graph container height
  • package.json: Add elkjs dependency

Ref

N/A

## What Replace Dagre layout engine with ELK (Eclipse Layout Kernel) for workflow graph visualization. ## Why Dagre lacks edge label placement and orthogonal routing — condition labels overlap with nodes, edges cross messily. ## Changes - **use-layout.ts**: Rewrite from Dagre → async ELK with layered algorithm, orthogonal routing, reduced spacing - **condition-edge.tsx**: Use ELK-computed label positions, show all labels (including FALLBACK), switch to getSmoothStepPath - **workflow-graph.tsx**: Wrap in ReactFlowProvider, fitView on async layout change, key-based remount - **workflow-list.tsx**: Left-right layout (info left, graph right), fix toggleExpanded React 18 batching bug, increase graph container height - **package.json**: Add elkjs dependency ## Ref N/A
xingyue added 1 commit 2026-05-13 08:26:31 +00:00
What: Replace Dagre layout engine with ELK (Eclipse Layout Kernel) for
workflow graph visualization in the dashboard.

Why: Dagre lacks support for edge label placement and orthogonal edge
routing, causing condition labels to overlap with nodes. ELK provides
proper label positioning, better edge routing, and more compact layouts.

Changes:
- packages/workflow-dashboard/package.json: add elkjs dependency
- packages/workflow-dashboard/src/components/workflow-graph/use-layout.ts:
  rewrite layout from Dagre to async ELK with layered algorithm,
  orthogonal routing, reduced spacing for compactness
- packages/workflow-dashboard/src/components/workflow-graph/condition-edge.tsx:
  use ELK-computed label positions, show all labels including FALLBACK,
  switch to getSmoothStepPath for all edges
- packages/workflow-dashboard/src/components/workflow-graph/workflow-graph.tsx:
  wrap in ReactFlowProvider, add fitView on async layout change,
  key-based remount for layout stability
- packages/workflow-dashboard/src/components/workflow-list.tsx:
  left-right layout (info left, graph right), fix toggleExpanded
  React 18 batching bug, increase graph container height
xiaomo requested review from xiaomo 2026-05-13 08:27:36 +00:00
xiaomo approved these changes 2026-05-13 08:28:20 +00:00
xiaomo left a comment
Owner

LGTM — clean migration from Dagre to ELK. Good async layout pattern with cancellation, proper fitView handling, and the toggleExpanded batching fix is correct.

Minor notes (non-blocking):

  1. Missing .catch() in useEffect: computeLayout(parsed).then(...) has no error handler. If ELK throws, you get an unhandled promise rejection. Add .catch(() => { if (!cancelled) setLayout(EMPTY_LAYOUT); }) or similar.
  2. Type assertion: as ConditionEdgeData in buildEdge when the object includes elkLabelX/elkLabelY not in that type. Consider extending ConditionEdgeData to include those fields.
  3. Convention: elkLabelX?: number | null uses optional prop — per conventions, prefer elkLabelX: number | null with explicit null.
LGTM — clean migration from Dagre to ELK. Good async layout pattern with cancellation, proper fitView handling, and the toggleExpanded batching fix is correct. Minor notes (non-blocking): 1. **Missing `.catch()` in useEffect**: `computeLayout(parsed).then(...)` has no error handler. If ELK throws, you get an unhandled promise rejection. Add `.catch(() => { if (!cancelled) setLayout(EMPTY_LAYOUT); })` or similar. 2. **Type assertion**: `as ConditionEdgeData` in `buildEdge` when the object includes `elkLabelX`/`elkLabelY` not in that type. Consider extending `ConditionEdgeData` to include those fields. 3. **Convention**: `elkLabelX?: number | null` uses optional prop — per conventions, prefer `elkLabelX: number | null` with explicit null.
xiaomo merged commit 55b3b61498 into main 2026-05-13 08:28:25 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#232