Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const WorkflowEdgeComponent = ({
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
pointerEvents: 'all',
zIndex: 100,
zIndex: 1011,
}}
onClick={(e) => {
e.preventDefault()
Expand Down
74 changes: 64 additions & 10 deletions apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2771,10 +2771,37 @@ const WorkflowContent = React.memo(
(changes: NodeChange[]) => {
const hasSelectionChange = changes.some((c) => c.type === 'select')
setDisplayNodes((currentNodes) => {
const updated = applyNodeChanges(changes, currentNodes)
// Filter out cross-context selection changes before applying so that
// nodes at a different nesting level never appear selected, even for
// a single frame.
let changesToApply = changes
if (hasSelectionChange) {
const currentlySelected = currentNodes.filter((n) => n.selected)
// Only filter on additive multi-select (shift-click), not replacement
// clicks. A replacement click includes deselections of currently selected
// nodes; a shift-click only adds selections.
const isReplacementClick = changes.some(
(c) =>
c.type === 'select' &&
'selected' in c &&
!c.selected &&
currentlySelected.some((n) => n.id === c.id)
)
if (!isReplacementClick && currentlySelected.length > 0) {
const selectionContext = getNodeSelectionContextId(currentlySelected[0], blocks)
changesToApply = changes.filter((c) => {
if (c.type !== 'select' || !('selected' in c) || !c.selected) return true
const node = currentNodes.find((n) => n.id === c.id)
if (!node) return true
return getNodeSelectionContextId(node, blocks) === selectionContext
})
}
}

const updated = applyNodeChanges(changesToApply, currentNodes)
if (!hasSelectionChange) return updated

const preferredNodeId = [...changes]
const preferredNodeId = [...changesToApply]
.reverse()
.find(
(change): change is NodeChange & { id: string; selected: boolean } =>
Expand Down Expand Up @@ -3270,12 +3297,17 @@ const WorkflowContent = React.memo(
previousPositions: multiNodeDragStartRef.current,
})

// Process parent updates using shared helper
executeBatchParentUpdate(
selectedNodes,
potentialParentId,
'Batch moved nodes to new parent'
)
// Only reparent when an actual drag changed the target container.
// onNodeDragStart sets both potentialParentId and dragStartParentId to the
// clicked node's current parent; they only diverge when onNodeDrag detects
// the selection being dragged over a different container.
if (potentialParentId !== dragStartParentId) {
executeBatchParentUpdate(
selectedNodes,
potentialParentId,
'Batch moved nodes to new parent'
)
}

// Clear drag start state
setDragStartPosition(null)
Expand Down Expand Up @@ -3686,6 +3718,20 @@ const WorkflowContent = React.memo(
const handleNodeClick = useCallback(
(event: React.MouseEvent, node: Node) => {
const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey

// Ignore shift-clicks on nodes at a different nesting level
if (isMultiSelect) {
const clickedContext = getNodeSelectionContextId(node, blocks)
const currentlySelected = getNodes().filter((n) => n.selected)
if (currentlySelected.length > 0) {
const selectionContext = getNodeSelectionContextId(currentlySelected[0], blocks)
if (clickedContext !== selectionContext) {
usePanelEditorStore.getState().clearCurrentBlock()
return
}
}
}

setDisplayNodes((currentNodes) => {
const updated = currentNodes.map((currentNode) => ({
...currentNode,
Expand All @@ -3698,7 +3744,7 @@ const WorkflowContent = React.memo(
return resolveSelectionConflicts(updated, blocks, isMultiSelect ? node.id : undefined)
})
},
[blocks]
[blocks, getNodes]
)

/** Handles edge selection with container context tracking and Shift-click multi-selection. */
Expand Down Expand Up @@ -3807,9 +3853,17 @@ const WorkflowContent = React.memo(
(targetNode?.zIndex ?? 21) + 1
)

// Edges inside subflows need a z-index above the container's body area
// (which has pointer-events: auto) so they're directly clickable.
// Derive from the container's depth-based zIndex (+1) so the edge sits
// just above its parent container but below canvas blocks (z-21+) and
// child blocks (z-1000).
const containerNode = parentLoopId ? nodeMap.get(parentLoopId) : null
const baseZIndex = containerNode ? (containerNode.zIndex ?? 0) + 1 : 0

return {
...edge,
zIndex: connectedToElevated ? elevatedZIndex : 0,
zIndex: connectedToElevated ? elevatedZIndex : baseZIndex,
data: {
...edge.data,
isSelected: selectedEdges.has(edgeContextId),
Expand Down
Loading