diff --git a/ui/src/components/ProcessDiagram/Minimap.tsx b/ui/src/components/ProcessDiagram/Minimap.tsx index 3e644a6a..3a3796d1 100644 --- a/ui/src/components/ProcessDiagram/Minimap.tsx +++ b/ui/src/components/ProcessDiagram/Minimap.tsx @@ -1,6 +1,7 @@ import { useCallback, useRef } from 'react'; import type { DiagramSection } from './types'; import type { DiagramNode as DiagramNodeType } from '../../api/queries/diagrams'; +import type { NodeExecutionState } from '../ExecutionDiagram/types'; import { colorForType } from './node-colors'; import styles from './ProcessDiagram.module.css'; @@ -21,13 +22,15 @@ interface MinimapProps { containerHeight: number; /** Called when user clicks/drags on minimap to pan */ onPan: (translateX: number, translateY: number) => void; + /** Execution overlay for coloring nodes */ + executionOverlay?: Map; } export function Minimap({ sections, totalWidth, totalHeight, scale, translateX, translateY, containerWidth, containerHeight, - onPan, + onPan, executionOverlay, }: MinimapProps) { const svgRef = useRef(null); const dragging = useRef(false); @@ -116,7 +119,7 @@ export function Minimap({ {/* Render simplified nodes for each section */} {sections.map((section, si) => ( - {renderMinimapNodes(section.nodes)} + {renderMinimapNodes(section.nodes, executionOverlay)} ))} @@ -138,27 +141,43 @@ export function Minimap({ ); } -function renderMinimapNodes(nodes: DiagramNodeType[]): React.ReactNode[] { +function nodeColor( + node: DiagramNodeType, + overlay?: Map, +): { fill: string; opacity: number } { + if (!overlay) return { fill: colorForType(node.type), opacity: 0.7 }; + const state = node.id ? overlay.get(node.id) : undefined; + if (state?.status === 'COMPLETED') return { fill: '#3D7C47', opacity: 0.85 }; + if (state?.status === 'FAILED') return { fill: '#C0392B', opacity: 0.85 }; + // ENDPOINT is always traversed when overlay is active (route entry point) + if (node.type === 'ENDPOINT' && overlay.size > 0) return { fill: '#3D7C47', opacity: 0.85 }; + // Skipped (overlay active but node not executed) + return { fill: '#9CA3AF', opacity: 0.35 }; +} + +function renderMinimapNodes( + nodes: DiagramNodeType[], + overlay?: Map, +): React.ReactNode[] { const elements: React.ReactNode[] = []; for (const node of nodes) { const x = node.x ?? 0; const y = node.y ?? 0; const w = node.width ?? 160; const h = node.height ?? 40; - const color = colorForType(node.type); if (node.children && node.children.length > 0) { - // Compound: border only + const { fill, opacity } = nodeColor(node, overlay); elements.push( , + fill="none" stroke={fill} strokeWidth={2} rx={2} opacity={opacity} />, ); - elements.push(...renderMinimapNodes(node.children)); + elements.push(...renderMinimapNodes(node.children, overlay)); } else { - // Leaf: filled rectangle + const { fill, opacity } = nodeColor(node, overlay); elements.push( , + fill={fill} rx={2} opacity={opacity} />, ); } } diff --git a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx index 4e621ab7..8ff191c2 100644 --- a/ui/src/components/ProcessDiagram/ProcessDiagram.tsx +++ b/ui/src/components/ProcessDiagram/ProcessDiagram.tsx @@ -409,6 +409,7 @@ export function ProcessDiagram({ containerWidth={zoom.containerRef.current?.clientWidth ?? 0} containerHeight={zoom.containerRef.current?.clientHeight ?? 0} onPan={zoom.panTo} + executionOverlay={effectiveOverlay} />