fix: diagram rendering improvements
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 57s
CI / docker (push) Successful in 52s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 37s

- Recursive compound rendering: CompoundNode checks if children are
  themselves compound types (WHEN inside CHOICE) and renders them
  recursively. Added EIP_WHEN, EIP_OTHERWISE, DO_CATCH, DO_FINALLY
  to frontend COMPOUND_TYPES.
- Edge z-ordering: edges are distributed to their containing compound
  and rendered after the background rect, so they're not hidden behind
  compound containers.
- Error section sizing: normalize error handler node coordinates to
  start at (0,0), compute red tint background height from actual
  content with symmetric padding for vertical centering.
- Toolbar as HTML overlay: moved from SVG foreignObject to absolute-
  positioned HTML div so it stays fixed size at any zoom level. Uses
  design system tokens for consistent styling.
- Zoom: replaced viewBox approach with CSS transform on content group.
  Default zoom is 100% anchored top-left. Fit-to-view still available
  via button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-27 16:33:24 +01:00
parent 20d1182259
commit 9b7626f6ff
8 changed files with 326 additions and 176 deletions

View File

@@ -56,27 +56,43 @@ export function useDiagramData(
];
let currentY = mainBounds.maxY + SECTION_GAP;
let maxWidth = mainBounds.maxX;
for (const es of errorSections) {
const errorBounds = computeBounds(es.nodes);
const offX = errorBounds.minX;
const offY = errorBounds.minY;
// Normalize node coordinates relative to the section's own origin
const shiftedNodes = shiftNodes(es.nodes, offX, offY);
const errorNodeIds = new Set<string>();
collectNodeIds(es.nodes, errorNodeIds);
const errorEdges = allEdges.filter(
e => errorNodeIds.has(e.sourceId) && errorNodeIds.has(e.targetId),
);
// Shift edge points too
const errorEdges = allEdges
.filter(e => errorNodeIds.has(e.sourceId) && errorNodeIds.has(e.targetId))
.map(e => ({
...e,
points: e.points.map(p => [p[0] - offX, p[1] - offY]),
}));
const sectionHeight = errorBounds.maxY - errorBounds.minY;
const sectionWidth = errorBounds.maxX - errorBounds.minX;
sections.push({
label: es.label,
nodes: es.nodes,
nodes: shiftedNodes,
edges: errorEdges,
offsetY: currentY,
variant: 'error',
});
const errorBounds = computeBounds(es.nodes);
currentY += (errorBounds.maxY - errorBounds.minY) + SECTION_GAP;
currentY += sectionHeight + SECTION_GAP;
if (sectionWidth > maxWidth) maxWidth = sectionWidth;
}
const totalWidth = layout.width ?? mainBounds.maxX;
const totalWidth = Math.max(layout.width ?? 0, mainBounds.maxX, maxWidth);
const totalHeight = currentY;
return { sections, totalWidth, totalHeight };
@@ -85,6 +101,16 @@ export function useDiagramData(
return { ...result, isLoading, error };
}
/** Shift all node coordinates by subtracting an offset, recursively. */
function shiftNodes(nodes: DiagramNode[], offX: number, offY: number): DiagramNode[] {
return nodes.map(n => ({
...n,
x: (n.x ?? 0) - offX,
y: (n.y ?? 0) - offY,
children: n.children ? shiftNodes(n.children, offX, offY) : undefined,
}));
}
function collectNodeIds(nodes: DiagramNode[], set: Set<string>) {
for (const n of nodes) {
if (n.id) set.add(n.id);