diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts index afcbc8034a8..d3b9e670c82 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts @@ -7,6 +7,22 @@ import type { BlockState } from '@/stores/workflows/workflow/types' export const SUBFLOW_DROP_TARGET_CLASS = 'subflow-node-drop-target' +interface WorkflowCanvasInteractionPolicyInput { + embedded: boolean + canEdit: boolean +} + +/** Separates position editing from structural re-parenting for embedded canvases. */ +export function getWorkflowCanvasInteractionPolicy({ + embedded, + canEdit, +}: WorkflowCanvasInteractionPolicyInput) { + return { + canDragNodes: canEdit, + canReparentNodes: canEdit && !embedded, + } as const +} + /** * Marks canvas nodes nested inside a subflow container. React Flow v11 emitted * `data-parent-node-id` for this; v12 emits no parent attribute, so the app diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index 7db19721b03..d330b7b5037 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -102,6 +102,7 @@ import { getNodeDataDimension, getNodeSelectionContextId, getRunFromBlockDependencyState, + getWorkflowCanvasInteractionPolicy, getWorkflowLockToggleIds, isEdgeProtected, isInEditableElement, @@ -749,6 +750,10 @@ const WorkflowContent = React.memo( } return userPermissions }, [userPermissions, currentWorkflow.isSnapshotView, workflowReadOnly]) + const { canDragNodes, canReparentNodes } = getWorkflowCanvasInteractionPolicy({ + embedded: embedded === true, + canEdit: effectivePermissions.canEdit === true, + }) const { collaborativeBatchAddEdges, collaborativeBatchRemoveEdges, @@ -2856,7 +2861,7 @@ const WorkflowContent = React.memo( className: block.data?.parentId ? SUBFLOW_CHILD_NODE_CLASS : undefined, extent: block.data?.extent || undefined, dragHandle: '.workflow-drag-handle', - draggable: !workflowReadOnly && !isWorkflowBlockProtected(block.id, blocks), + draggable: canDragNodes && !isWorkflowBlockProtected(block.id, blocks), zIndex: depth, data: { ...block.data, @@ -2903,7 +2908,7 @@ const WorkflowContent = React.memo( parentId, className: parentId ? SUBFLOW_CHILD_NODE_CLASS : undefined, dragHandle, - draggable: !workflowReadOnly && !isWorkflowBlockProtected(block.id, blocks), + draggable: canDragNodes && !isWorkflowBlockProtected(block.id, blocks), zIndex: cardZIndex, extent: (() => { // Clamp children to subflow body (exclude header) @@ -2957,6 +2962,7 @@ const WorkflowContent = React.memo( isDebugging, getBlockConfig, embedded, + canDragNodes, workflowReadOnly, collaborativeSetBlockErrorEnabled, collaborativeBatchRemoveEdges, @@ -3658,7 +3664,7 @@ const WorkflowContent = React.memo( // paths bail when potentialParentId still equals the drag-start parent, so // positions persist but a block can never be inserted into (or pulled out // of) a loop/parallel from the embedded view. - if (embedded) return + if (!canReparentNodes) return // Check if this is a starter block - starter blocks should never be in containers const isStarterBlock = node.data?.type === 'starter' @@ -3774,7 +3780,7 @@ const WorkflowContent = React.memo( getNodes, potentialParentId, blocks, - embedded, + canReparentNodes, getNodeAbsolutePosition, getNodeDepth, isDescendantOf, @@ -5141,7 +5147,7 @@ const WorkflowContent = React.memo( multiSelectionKeyCode={embedded ? null : ['Meta', 'Control', 'Shift']} nodesConnectable={!embedded && effectivePermissions.canEdit} connectOnClick={false} - nodesDraggable={!embedded && effectivePermissions.canEdit} + nodesDraggable={canDragNodes} draggable={false} noWheelClassName='allow-scroll' edgesFocusable={!embedded} @@ -5151,10 +5157,8 @@ const WorkflowContent = React.memo( canvasOpacityClass, isHandMode ? 'canvas-mode-hand' : 'canvas-mode-cursor' )} - onNodeDrag={effectivePermissions.canEdit ? onNodeDrag : undefined} - onNodeDragStop={ - !embedded && effectivePermissions.canEdit ? onNodeDragStop : undefined - } + onNodeDrag={canDragNodes ? onNodeDrag : undefined} + onNodeDragStop={canDragNodes ? onNodeDragStop : undefined} onSelectionDragStart={ effectivePermissions.canEdit ? onSelectionDragStart : undefined } @@ -5162,9 +5166,7 @@ const WorkflowContent = React.memo( onSelectionDragStop={ effectivePermissions.canEdit ? onSelectionDragStop : undefined } - onNodeDragStart={ - !embedded && effectivePermissions.canEdit ? onNodeDragStart : undefined - } + onNodeDragStart={canDragNodes ? onNodeDragStart : undefined} snapToGrid={snapToGrid} snapGrid={snapGrid} elevateEdgesOnSelect={false}