import { useCallback, useEffect } from 'react'; import type { ProcessDiagramProps } from './types'; import { useDiagramData } from './useDiagramData'; import { useZoomPan } from './useZoomPan'; import { useToolbarHover, NodeToolbar } from './NodeToolbar'; import { DiagramNode } from './DiagramNode'; import { DiagramEdge } from './DiagramEdge'; import { CompoundNode } from './CompoundNode'; import { ErrorSection } from './ErrorSection'; import { ZoomControls } from './ZoomControls'; import { isCompoundType } from './node-colors'; import styles from './ProcessDiagram.module.css'; const PADDING = 40; export function ProcessDiagram({ application, routeId, direction = 'LR', selectedNodeId, onNodeSelect, onNodeAction, nodeConfigs, className, }: ProcessDiagramProps) { const { sections, totalWidth, totalHeight, isLoading, error } = useDiagramData( application, routeId, direction, ); const zoom = useZoomPan(); const toolbar = useToolbarHover(); const contentWidth = totalWidth + PADDING * 2; const contentHeight = totalHeight + PADDING * 2; // Fit to view on first data load useEffect(() => { if (totalWidth > 0 && totalHeight > 0) { zoom.fitToView(contentWidth, contentHeight); } }, [totalWidth, totalHeight]); // eslint-disable-line react-hooks/exhaustive-deps const handleNodeClick = useCallback( (nodeId: string) => { onNodeSelect?.(nodeId); }, [onNodeSelect], ); const handleNodeAction = useCallback( (nodeId: string, action: import('./types').NodeAction) => { if (action === 'inspect') onNodeSelect?.(nodeId); onNodeAction?.(nodeId, action); }, [onNodeSelect, onNodeAction], ); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Escape') { onNodeSelect?.(''); return; } zoom.onKeyDown(e, contentWidth, contentHeight); }, [onNodeSelect, zoom, contentWidth, contentHeight], ); if (isLoading) { return (