From 7e978bba36e5d748a1ae12919162a13db9b8d1be Mon Sep 17 00:00:00 2001 From: Celal Zamanoglu <95054566+celalzamanoglu@users.noreply.github.com> Date: Thu, 5 Feb 2026 20:53:35 +0300 Subject: [PATCH] Fix debugger loop block visual overlap when status row appears (#SKY-7310) (#4641) --- .../routes/workflows/editor/FlowRenderer.tsx | 16 ++++++++++++--- .../src/routes/workflows/editor/Workspace.tsx | 8 ++++---- .../workflows/editor/workflowEditorUtils.ts | 20 +++++++++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx index eaf17853..8a3024c6 100644 --- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx @@ -36,7 +36,7 @@ import { import "@xyflow/react/dist/style.css"; import { nanoid } from "nanoid"; import { useCallback, useEffect, useRef, useState } from "react"; -import { useBlocker } from "react-router-dom"; +import { useBlocker, useParams } from "react-router-dom"; import { AWSSecretParameter, debuggableWorkflowBlockTypes, @@ -308,6 +308,7 @@ function FlowRenderer({ onContainerResize, onRequestDeleteNode, }: Props) { + const { blockLabel: targettedBlockLabel } = useParams(); const reactFlowInstance = useReactFlow(); const debugStore = useDebugStore(); const { title, initializeTitle } = useWorkflowTitleStore(); @@ -356,11 +357,11 @@ function FlowRenderer({ const doLayout = useCallback( (nodes: Array, edges: Array) => { - const layoutedElements = layout(nodes, edges); + const layoutedElements = layout(nodes, edges, targettedBlockLabel); setNodes(layoutedElements.nodes); setEdges(layoutedElements.edges); }, - [setNodes, setEdges], + [setNodes, setEdges, targettedBlockLabel], ); useEffect(() => { @@ -370,6 +371,15 @@ function FlowRenderer({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [nodesInitialized]); + // Re-layout when the targetted block changes to account for the status row + // that appears when a block is being debugged + useEffect(() => { + if (nodesInitialized && targettedBlockLabel) { + doLayout(nodes, edges); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [targettedBlockLabel]); + useEffect(() => { const topLevelBlocks = getWorkflowBlocks(nodes, edges); const debuggable = topLevelBlocks.filter((block) => diff --git a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx index a86e9a64..f098b9e9 100644 --- a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx @@ -708,11 +708,11 @@ function Workspace({ const doLayout = useCallback( (nodes: Array, edges: Array) => { - const layoutedElements = layout(nodes, edges); + const layoutedElements = layout(nodes, edges, blockLabel); setNodes(layoutedElements.nodes); setEdges(layoutedElements.edges); }, - [setNodes, setEdges], + [setNodes, setEdges, blockLabel], ); // Listen for conditional branch changes to trigger re-layout @@ -724,7 +724,7 @@ function Workspace({ const currentNodes = getNodes() as Array; const currentEdges = getEdges(); - const layoutedElements = layout(currentNodes, currentEdges); + const layoutedElements = layout(currentNodes, currentEdges, blockLabel); setNodes(layoutedElements.nodes); setEdges(layoutedElements.edges); }, 10); // Small delay to ensure visibility updates complete @@ -737,7 +737,7 @@ function Workspace({ handleBranchChange, ); }; - }, [getNodes, getEdges, setNodes, setEdges]); + }, [getNodes, getEdges, setNodes, setEdges, blockLabel]); function addNode({ nodeType, diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 894ca6a6..7c415ccb 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -262,9 +262,13 @@ function getNestingLevel(node: AppNode, nodes: Array): number { return level; } +// Extra margin to add when a block is being debugged and shows the status row +const TARGETTED_BLOCK_EXTRA_MARGIN = 48; + function layout( nodes: Array, edges: Array, + targettedBlockLabel?: string, ): { nodes: Array; edges: Array } { const loopNodes = nodes.filter( (node) => node.type === "loop" && !node.hidden, @@ -288,12 +292,18 @@ function layout( ...n, position: { x: 0, y: 0 }, })); + // Check if this loop node is the targetted block (being debugged) + // If so, add extra margin to account for the status row that appears + const nodeLabel = isWorkflowBlockNode(node) ? node.data.label : undefined; + const isTargetted = + targettedBlockLabel && nodeLabel === targettedBlockLabel; + const marginy = isTargetted ? 225 + TARGETTED_BLOCK_EXTRA_MARGIN : 225; const layouted = layoutUtil( childNodesWithResetPositions, childEdges, { marginx: (loopNodeWidth - maxChildWidth) / 2, - marginy: 225, + marginy, }, nodes, ); @@ -337,12 +347,18 @@ function layout( position: { x: 0, y: 0 }, })); + // Check if this conditional node is the targetted block (being debugged) + // If so, add extra margin to account for the status row that appears + const nodeLabel = isWorkflowBlockNode(node) ? node.data.label : undefined; + const isTargetted = + targettedBlockLabel && nodeLabel === targettedBlockLabel; + const marginy = isTargetted ? 225 + TARGETTED_BLOCK_EXTRA_MARGIN : 225; const layouted = layoutUtil( childNodesWithResetPositions, childEdges, { marginx: (conditionalNodeWidth - maxChildWidth) / 2, - marginy: 225, + marginy, }, nodes, );