@@ -307,9 +307,8 @@ const WorkflowContent = React.memo(() => {
307307
308308 const isAutoConnectEnabled = useAutoConnect ( )
309309 const autoConnectRef = useRef ( isAutoConnectEnabled )
310- useEffect ( ( ) => {
311- autoConnectRef . current = isAutoConnectEnabled
312- } , [ isAutoConnectEnabled ] )
310+ // Keep ref in sync with latest value for use in callbacks (no effect needed)
311+ autoConnectRef . current = isAutoConnectEnabled
313312
314313 // Panel open states for context menu
315314 const isVariablesOpen = useVariablesStore ( ( state ) => state . isOpen )
@@ -448,11 +447,14 @@ const WorkflowContent = React.memo(() => {
448447 )
449448
450449 /** Re-applies diff markers when blocks change after socket rehydration. */
451- const blocksRef = useRef ( blocks )
450+ const diffBlocksRef = useRef ( blocks )
452451 useEffect ( ( ) => {
453- if ( ! isWorkflowReady ) return
454- if ( hasActiveDiff && isDiffReady && blocks !== blocksRef . current ) {
455- blocksRef . current = blocks
452+ // Track if blocks actually changed (vs other deps triggering this effect)
453+ const blocksChanged = blocks !== diffBlocksRef . current
454+ diffBlocksRef . current = blocks
455+
456+ if ( ! isWorkflowReady || ! blocksChanged ) return
457+ if ( hasActiveDiff && isDiffReady ) {
456458 setTimeout ( ( ) => reapplyDiffMarkers ( ) , 0 )
457459 }
458460 } , [ blocks , hasActiveDiff , isDiffReady , reapplyDiffMarkers , isWorkflowReady ] )
@@ -2160,6 +2162,8 @@ const WorkflowContent = React.memo(() => {
21602162 // Local state for nodes - allows smooth drag without store updates on every frame
21612163 const [ displayNodes , setDisplayNodes ] = useState < Node [ ] > ( [ ] )
21622164
2165+ // Sync derivedNodes to displayNodes while preserving selection state
2166+ // This effect handles both normal sync and pending selection from paste/duplicate
21632167 useEffect ( ( ) => {
21642168 // Check for pending selection (from paste/duplicate), otherwise preserve existing selection
21652169 if ( pendingSelection && pendingSelection . length > 0 ) {
@@ -2186,7 +2190,7 @@ const WorkflowContent = React.memo(() => {
21862190 selected : selectedIds . has ( node . id ) ,
21872191 } ) )
21882192 } )
2189- } , [ derivedNodes , blocks , pendingSelection , clearPendingSelection ] )
2193+ } , [ derivedNodes , blocks , pendingSelection , clearPendingSelection , syncPanelWithSelection ] )
21902194
21912195 // Phase 2: When displayNodes updates, check if pending zoom blocks are ready
21922196 // (Phase 1 is located earlier in the file where pendingZoomBlockIdsRef is defined)
@@ -2380,40 +2384,6 @@ const WorkflowContent = React.memo(() => {
23802384 resizeLoopNodesWrapper ( )
23812385 } , [ derivedNodes , resizeLoopNodesWrapper , isWorkflowReady ] )
23822386
2383- /** Cleans up orphaned nodes with invalid parent references after deletion. */
2384- useEffect ( ( ) => {
2385- if ( ! isWorkflowReady ) return
2386-
2387- // Create a mapping of node IDs to check for missing parent references
2388- const nodeIds = new Set ( Object . keys ( blocks ) )
2389-
2390- // Check for nodes with invalid parent references and collect updates
2391- const orphanedUpdates : Array < {
2392- id : string
2393- position : { x : number ; y : number }
2394- parentId : string
2395- } > = [ ]
2396- Object . entries ( blocks ) . forEach ( ( [ id , block ] ) => {
2397- const parentId = block . data ?. parentId
2398-
2399- // If block has a parent reference but parent no longer exists
2400- if ( parentId && ! nodeIds . has ( parentId ) ) {
2401- logger . warn ( 'Found orphaned node with invalid parent reference' , {
2402- nodeId : id ,
2403- missingParentId : parentId ,
2404- } )
2405-
2406- const absolutePosition = getNodeAbsolutePosition ( id )
2407- orphanedUpdates . push ( { id, position : absolutePosition , parentId : '' } )
2408- }
2409- } )
2410-
2411- // Batch update all orphaned nodes at once
2412- if ( orphanedUpdates . length > 0 ) {
2413- batchUpdateBlocksWithParent ( orphanedUpdates )
2414- }
2415- } , [ blocks , batchUpdateBlocksWithParent , getNodeAbsolutePosition , isWorkflowReady ] )
2416-
24172387 /** Handles edge removal changes. */
24182388 const onEdgesChange = useCallback (
24192389 ( changes : any ) => {
0 commit comments