From 6307e0e7294e10ce742ae7fcbab7a3359f071a87 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 21 Mar 2026 09:46:52 -0700 Subject: [PATCH 1/5] fix(canvas): correct z-index layering for selected blocks and connected edges --- .../[workspaceId]/w/[workflowId]/workflow.tsx | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index 59e56a16b6c..fb0d5098fcb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -201,8 +201,6 @@ const edgeTypes: EdgeTypes = { const defaultEdgeOptions = { type: 'custom' } const reactFlowStyles = [ - '[&_.react-flow__edges]:!z-0', - '[&_.react-flow__node]:z-[21]', '[&_.react-flow__handle]:!z-[30]', '[&_.react-flow__edge-labels]:!z-[1001]', '[&_.react-flow__pane]:select-none', @@ -2478,6 +2476,7 @@ const WorkflowContent = React.memo( // Local state for nodes - allows smooth drag without store updates on every frame const [displayNodes, setDisplayNodes] = useState([]) + const [lastInteractedNodeId, setLastInteractedNodeId] = useState(null) const selectedNodeIds = useMemo( () => displayNodes.filter((node) => node.selected).map((node) => node.id), @@ -2489,6 +2488,14 @@ const WorkflowContent = React.memo( syncPanelWithSelection(selectedNodeIds) }, [selectedNodeIdsKey]) + // Keep the most recently selected block on top even after deselection, so a + // dragged block doesn't suddenly drop behind other overlapping blocks. + useEffect(() => { + if (selectedNodeIds.length > 0) { + setLastInteractedNodeId(selectedNodeIds[selectedNodeIds.length - 1]) + } + }, [selectedNodeIdsKey]) + useEffect(() => { // Check for pending selection (from paste/duplicate), otherwise preserve existing selection if (pendingSelection && pendingSelection.length > 0) { @@ -3723,18 +3730,51 @@ const WorkflowContent = React.memo( [removeEdge, edges, blocks, addNotification, activeWorkflowId] ) + // Elevate nodes using React Flow's native zIndex so selected/recent blocks + // always sit above edges and other blocks. + // + // Z-index layers (regular blocks): + // 21 — default + // 22 — last interacted (dragged/selected, now deselected) so it stays on + // top of siblings until another block is touched + // 31 — currently selected (above connected edges at z-22 and handles at z-30) + // + // Subflow container nodes are skipped — they use depth-based zIndex for + // correct parent/child layering and must not be bumped. + // Child blocks inside containers already carry zIndex 1000 and are bumped by + // +10 when selected so they stay above their sibling child blocks. + const nodesForRender = useMemo(() => { + return displayNodes.map((node) => { + if (node.type === 'subflowNode') return node + const base = node.zIndex ?? 21 + const target = node.selected + ? base + 10 + : node.id === lastInteractedNodeId + ? Math.max(base, 22) + : base + if (target === node.zIndex) return node + return { ...node, zIndex: target } + }) + }, [displayNodes, lastInteractedNodeId]) + /** Transforms edges to include selection state and delete handlers. Memoized to prevent re-renders. */ const edgesWithSelection = useMemo(() => { const nodeMap = new Map(displayNodes.map((n) => [n.id, n])) + const elevatedNodeIdSet = new Set( + lastInteractedNodeId ? [...selectedNodeIds, lastInteractedNodeId] : selectedNodeIds + ) return edgesForDisplay.map((edge) => { const sourceNode = nodeMap.get(edge.source) const targetNode = nodeMap.get(edge.target) const parentLoopId = sourceNode?.parentId || targetNode?.parentId const edgeContextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}` + const connectedToElevated = + elevatedNodeIdSet.has(edge.source) || elevatedNodeIdSet.has(edge.target) return { ...edge, + zIndex: connectedToElevated ? 22 : 0, data: { ...edge.data, isSelected: selectedEdges.has(edgeContextId), @@ -3745,7 +3785,14 @@ const WorkflowContent = React.memo( }, } }) - }, [edgesForDisplay, displayNodes, selectedEdges, handleEdgeDelete]) + }, [ + edgesForDisplay, + displayNodes, + selectedNodeIds, + selectedEdges, + handleEdgeDelete, + lastInteractedNodeId, + ]) /** Handles Delete/Backspace to remove selected edges or blocks. */ useEffect(() => { @@ -3885,7 +3932,7 @@ const WorkflowContent = React.memo( {showTrainingModal && } Date: Sat, 21 Mar 2026 09:52:40 -0700 Subject: [PATCH 2/5] fix(canvas): derive subflow edge z-index from connected node z-index --- .../app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index fb0d5098fcb..a0b24304f61 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -3771,10 +3771,13 @@ const WorkflowContent = React.memo( const edgeContextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}` const connectedToElevated = elevatedNodeIdSet.has(edge.source) || elevatedNodeIdSet.has(edge.target) + // Derive elevated z-index from connected nodes so edges inside subflows + // (child nodes at z-1000) stay above their sibling child blocks. + const elevatedZIndex = Math.max(22, sourceNode?.zIndex ?? 21, targetNode?.zIndex ?? 21) return { ...edge, - zIndex: connectedToElevated ? 22 : 0, + zIndex: connectedToElevated ? elevatedZIndex : 0, data: { ...edge.data, isSelected: selectedEdges.has(edgeContextId), From 818f245582965c1d3074cc620e84a8d37321cc51 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 21 Mar 2026 10:02:14 -0700 Subject: [PATCH 3/5] fix(canvas): fix nodesForRender early-return guard for regular blocks --- .../sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index a0b24304f61..d08b88e8d18 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -3752,7 +3752,7 @@ const WorkflowContent = React.memo( : node.id === lastInteractedNodeId ? Math.max(base, 22) : base - if (target === node.zIndex) return node + if (target === (node.zIndex ?? 21)) return node return { ...node, zIndex: target } }) }, [displayNodes, lastInteractedNodeId]) From a7fe890a9835d4f6d2c6d0ae98a964fb80b51d52 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 21 Mar 2026 10:04:20 -0700 Subject: [PATCH 4/5] lint --- apps/sim/app/workspace/[workspaceId]/files/loading.tsx | 8 ++++---- .../[workspaceId]/knowledge/[id]/[documentId]/loading.tsx | 8 ++++---- .../workspace/[workspaceId]/knowledge/[id]/loading.tsx | 8 ++++---- .../sim/app/workspace/[workspaceId]/knowledge/loading.tsx | 8 ++++---- apps/sim/app/workspace/[workspaceId]/logs/loading.tsx | 8 ++++---- .../workspace/[workspaceId]/scheduled-tasks/loading.tsx | 8 ++++---- .../workspace/[workspaceId]/tables/[tableId]/loading.tsx | 6 +++--- apps/sim/app/workspace/[workspaceId]/tables/loading.tsx | 8 ++++---- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/loading.tsx b/apps/sim/app/workspace/[workspaceId]/files/loading.tsx index 806f4f73b30..9f2250d79e2 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 6 export default function FilesLoading() { return (
-
+
@@ -18,7 +18,7 @@ export default function FilesLoading() {
-
+
@@ -27,7 +27,7 @@ export default function FilesLoading() {
- + @@ -40,7 +40,7 @@ export default function FilesLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/loading.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/loading.tsx index 577be0ff2b0..4b98c9a8543 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 4 export default function DocumentLoading() { return (
-
+
@@ -18,7 +18,7 @@ export default function DocumentLoading() {
-
+
@@ -32,7 +32,7 @@ export default function DocumentLoading() {
- + @@ -45,7 +45,7 @@ export default function DocumentLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/loading.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/loading.tsx index ad27d19a9a7..b926f0d4f5c 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 7 export default function KnowledgeBaseLoading() { return (
-
+
@@ -19,7 +19,7 @@ export default function KnowledgeBaseLoading() {
-
+
@@ -34,7 +34,7 @@ export default function KnowledgeBaseLoading() {
- + @@ -47,7 +47,7 @@ export default function KnowledgeBaseLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx index acb223d33a7..9011fcccca6 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 6 export default function KnowledgeLoading() { return (
-
+
@@ -18,7 +18,7 @@ export default function KnowledgeLoading() {
-
+
@@ -28,7 +28,7 @@ export default function KnowledgeLoading() {
- + @@ -41,7 +41,7 @@ export default function KnowledgeLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/logs/loading.tsx b/apps/sim/app/workspace/[workspaceId]/logs/loading.tsx index 6c20d8d3774..d3565504d4c 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 6 export default function LogsLoading() { return (
-
+
@@ -21,7 +21,7 @@ export default function LogsLoading() {
-
+
@@ -36,7 +36,7 @@ export default function LogsLoading() {
- + @@ -49,7 +49,7 @@ export default function LogsLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx index 4bdba810493..4330f14d360 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 4 export default function ScheduledTasksLoading() { return (
-
+
@@ -17,7 +17,7 @@ export default function ScheduledTasksLoading() {
-
+
@@ -26,7 +26,7 @@ export default function ScheduledTasksLoading() {
- + @@ -39,7 +39,7 @@ export default function ScheduledTasksLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/loading.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/loading.tsx index 3eb4cd58d01..02a5db5882c 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/loading.tsx @@ -6,7 +6,7 @@ const COLUMN_COUNT = 5 export default function TableDetailLoading() { return (
-
+
@@ -22,7 +22,7 @@ export default function TableDetailLoading() {
- + {Array.from({ length: COLUMN_COUNT }).map((_, i) => ( {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + {Array.from({ length: COLUMN_COUNT }).map((_, colIndex) => (
@@ -32,7 +32,7 @@ export default function TableDetailLoading() {
-
+
@@ -17,7 +17,7 @@ export default function TablesLoading() {
-
+
@@ -26,7 +26,7 @@ export default function TablesLoading() {
- + @@ -39,7 +39,7 @@ export default function TablesLoading() { {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => ( - + From 80b269f5cf11845c122f3b6d59db3376a771e6f4 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 21 Mar 2026 10:05:35 -0700 Subject: [PATCH 5/5] fix(canvas): ensure elevated edges and last-interacted nodes sit above siblings at same base z-index --- .../workspace/[workspaceId]/w/[workflowId]/workflow.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index d08b88e8d18..374340af825 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -3750,7 +3750,7 @@ const WorkflowContent = React.memo( const target = node.selected ? base + 10 : node.id === lastInteractedNodeId - ? Math.max(base, 22) + ? Math.max(base + 1, 22) : base if (target === (node.zIndex ?? 21)) return node return { ...node, zIndex: target } @@ -3773,7 +3773,11 @@ const WorkflowContent = React.memo( elevatedNodeIdSet.has(edge.source) || elevatedNodeIdSet.has(edge.target) // Derive elevated z-index from connected nodes so edges inside subflows // (child nodes at z-1000) stay above their sibling child blocks. - const elevatedZIndex = Math.max(22, sourceNode?.zIndex ?? 21, targetNode?.zIndex ?? 21) + const elevatedZIndex = Math.max( + 22, + (sourceNode?.zIndex ?? 21) + 1, + (targetNode?.zIndex ?? 21) + 1 + ) return { ...edge,