@@ -2771,10 +2771,37 @@ const WorkflowContent = React.memo(
27712771 ( changes : NodeChange [ ] ) => {
27722772 const hasSelectionChange = changes . some ( ( c ) => c . type === 'select' )
27732773 setDisplayNodes ( ( currentNodes ) => {
2774- const updated = applyNodeChanges ( changes , currentNodes )
2774+ // Filter out cross-context selection changes before applying so that
2775+ // nodes at a different nesting level never appear selected, even for
2776+ // a single frame.
2777+ let changesToApply = changes
2778+ if ( hasSelectionChange ) {
2779+ const currentlySelected = currentNodes . filter ( ( n ) => n . selected )
2780+ // Only filter on additive multi-select (shift-click), not replacement
2781+ // clicks. A replacement click includes deselections of currently selected
2782+ // nodes; a shift-click only adds selections.
2783+ const isReplacementClick = changes . some (
2784+ ( c ) =>
2785+ c . type === 'select' &&
2786+ 'selected' in c &&
2787+ ! c . selected &&
2788+ currentlySelected . some ( ( n ) => n . id === c . id )
2789+ )
2790+ if ( ! isReplacementClick && currentlySelected . length > 0 ) {
2791+ const selectionContext = getNodeSelectionContextId ( currentlySelected [ 0 ] , blocks )
2792+ changesToApply = changes . filter ( ( c ) => {
2793+ if ( c . type !== 'select' || ! ( 'selected' in c ) || ! c . selected ) return true
2794+ const node = currentNodes . find ( ( n ) => n . id === c . id )
2795+ if ( ! node ) return true
2796+ return getNodeSelectionContextId ( node , blocks ) === selectionContext
2797+ } )
2798+ }
2799+ }
2800+
2801+ const updated = applyNodeChanges ( changesToApply , currentNodes )
27752802 if ( ! hasSelectionChange ) return updated
27762803
2777- const preferredNodeId = [ ...changes ]
2804+ const preferredNodeId = [ ...changesToApply ]
27782805 . reverse ( )
27792806 . find (
27802807 ( change ) : change is NodeChange & { id : string ; selected : boolean } =>
@@ -3270,12 +3297,17 @@ const WorkflowContent = React.memo(
32703297 previousPositions : multiNodeDragStartRef . current ,
32713298 } )
32723299
3273- // Process parent updates using shared helper
3274- executeBatchParentUpdate (
3275- selectedNodes ,
3276- potentialParentId ,
3277- 'Batch moved nodes to new parent'
3278- )
3300+ // Only reparent when an actual drag changed the target container.
3301+ // onNodeDragStart sets both potentialParentId and dragStartParentId to the
3302+ // clicked node's current parent; they only diverge when onNodeDrag detects
3303+ // the selection being dragged over a different container.
3304+ if ( potentialParentId !== dragStartParentId ) {
3305+ executeBatchParentUpdate (
3306+ selectedNodes ,
3307+ potentialParentId ,
3308+ 'Batch moved nodes to new parent'
3309+ )
3310+ }
32793311
32803312 // Clear drag start state
32813313 setDragStartPosition ( null )
@@ -3686,6 +3718,20 @@ const WorkflowContent = React.memo(
36863718 const handleNodeClick = useCallback (
36873719 ( event : React . MouseEvent , node : Node ) => {
36883720 const isMultiSelect = event . shiftKey || event . metaKey || event . ctrlKey
3721+
3722+ // Ignore shift-clicks on nodes at a different nesting level
3723+ if ( isMultiSelect ) {
3724+ const clickedContext = getNodeSelectionContextId ( node , blocks )
3725+ const currentlySelected = getNodes ( ) . filter ( ( n ) => n . selected )
3726+ if ( currentlySelected . length > 0 ) {
3727+ const selectionContext = getNodeSelectionContextId ( currentlySelected [ 0 ] , blocks )
3728+ if ( clickedContext !== selectionContext ) {
3729+ usePanelEditorStore . getState ( ) . clearCurrentBlock ( )
3730+ return
3731+ }
3732+ }
3733+ }
3734+
36893735 setDisplayNodes ( ( currentNodes ) => {
36903736 const updated = currentNodes . map ( ( currentNode ) => ( {
36913737 ...currentNode ,
@@ -3698,7 +3744,7 @@ const WorkflowContent = React.memo(
36983744 return resolveSelectionConflicts ( updated , blocks , isMultiSelect ? node . id : undefined )
36993745 } )
37003746 } ,
3701- [ blocks ]
3747+ [ blocks , getNodes ]
37023748 )
37033749
37043750 /** Handles edge selection with container context tracking and Shift-click multi-selection. */
0 commit comments