From d79e7d0168c9800a5452eea73162d662a6b7a140 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:45:30 +0100 Subject: [PATCH] fix: color edges into compound nodes green when descendants were executed Edges into/out of compound nodes (DO_TRY, EIP_CHOICE, etc.) now show as traversed (green) when any descendant node was executed, instead of grey. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ProcessDiagram/ProcessDiagram.tsx | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx index 8c6a2178..a19ea37e 100644 --- a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx +++ b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx @@ -81,18 +81,44 @@ export function ProcessDiagram({ application, currentRouteId, direction, effectiveLayout, ); - // Collect ENDPOINT node IDs — these are always "traversed" when overlay is active - // because the endpoint is the route entry point (not in the processor execution tree). - const endpointNodeIds = useMemo(() => { + // Build a set of node IDs that should count as "traversed" for edge coloring. + // This includes ENDPOINT nodes (always traversed) and compound nodes where + // any descendant was executed (so edges into/out of DO_TRY etc. are green). + const traversedNodeIds = useMemo(() => { const ids = new Set(); - if (!overlayActive || !sections.length) return ids; + if (!overlayActive || !effectiveOverlay || !sections.length) return ids; + + // ENDPOINTs are always traversed for (const section of sections) { for (const node of section.nodes) { if (node.type === 'ENDPOINT' && node.id) ids.add(node.id); } } + + // Mark compound nodes as traversed if any descendant was executed + function hasExecutedDescendant(node: DiagramNodeType): boolean { + if (effectiveOverlay!.has(node.id)) return true; + if (node.children) { + for (const child of node.children) { + if (hasExecutedDescendant(child)) return true; + } + } + return false; + } + function markCompounds(nodes: DiagramNodeType[]) { + for (const node of nodes) { + if (isCompoundType(node.type) && node.children?.length && hasExecutedDescendant(node)) { + ids.add(node.id); + } + if (node.children) markCompounds(node.children); + } + } + for (const section of sections) { + markCompounds(section.nodes); + } + return ids; - }, [overlayActive, sections]); + }, [overlayActive, effectiveOverlay, sections]); const zoom = useZoomPan(); const toolbar = useToolbarHover(); @@ -299,8 +325,8 @@ export function ProcessDiagram({ {/* Main section top-level edges (not inside compounds) */} {mainSection.edges.filter(e => topLevelEdge(e, mainSection.nodes)).map((edge, i) => { - const sourceHasState = effectiveOverlay?.has(edge.sourceId) || endpointNodeIds.has(edge.sourceId); - const targetHasState = effectiveOverlay?.has(edge.targetId) || endpointNodeIds.has(edge.targetId); + const sourceHasState = effectiveOverlay?.has(edge.sourceId) || traversedNodeIds.has(edge.sourceId); + const targetHasState = effectiveOverlay?.has(edge.targetId) || traversedNodeIds.has(edge.targetId); const isTraversed = effectiveOverlay ? (!!sourceHasState && !!targetHasState) : undefined;