11import { storeToRefs } from 'pinia'
22import { toValue } from 'vue'
33
4+ import type { LGraphGroup } from '@/lib/litegraph/src/LGraphGroup'
45import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
56import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
67import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
@@ -13,13 +14,14 @@ import type {
1314import { useNodeSnap } from '@/renderer/extensions/vueNodes/composables/useNodeSnap'
1415import { useShiftKeySync } from '@/renderer/extensions/vueNodes/composables/useShiftKeySync'
1516import { useTransformState } from '@/renderer/core/layout/transform/useTransformState'
17+ import { isLGraphGroup } from '@/utils/litegraphUtil'
1618import { createSharedComposable } from '@vueuse/core'
1719
1820export const useNodeDrag = createSharedComposable ( useNodeDragIndividual )
1921
2022function useNodeDragIndividual ( ) {
2123 const mutations = useLayoutMutations ( )
22- const { selectedNodeIds } = storeToRefs ( useCanvasStore ( ) )
24+ const { selectedNodeIds, selectedItems } = storeToRefs ( useCanvasStore ( ) )
2325
2426 // Get transform utilities from TransformPane if available
2527 const transformState = useTransformState ( )
@@ -37,6 +39,10 @@ function useNodeDragIndividual() {
3739 let rafId : number | null = null
3840 let stopShiftSync : ( ( ) => void ) | null = null
3941
42+ // For groups: track the last applied canvas delta to compute frame delta
43+ let lastCanvasDelta : Point | null = null
44+ let selectedGroups : LGraphGroup [ ] | null = null
45+
4046 function startDrag ( event : PointerEvent , nodeId : NodeId ) {
4147 const layout = toValue ( layoutStore . getNodeLayoutRef ( nodeId ) )
4248 if ( ! layout ) return
@@ -67,6 +73,10 @@ function useNodeDragIndividual() {
6773 otherSelectedNodesStartPositions = null
6874 }
6975
76+ // Capture selected groups (filter from selectedItems which only contains selected items)
77+ selectedGroups = toValue ( selectedItems ) . filter ( isLGraphGroup )
78+ lastCanvasDelta = { x : 0 , y : 0 }
79+
7080 mutations . setSource ( LayoutSource . Vue )
7181 }
7282
@@ -127,6 +137,21 @@ function useNodeDragIndividual() {
127137 mutations . moveNode ( otherNodeId , newOtherPosition )
128138 }
129139 }
140+
141+ // Move selected groups using frame delta (difference from last frame)
142+ // This matches LiteGraph's behavior which uses delta-based movement
143+ if ( selectedGroups && selectedGroups . length > 0 && lastCanvasDelta ) {
144+ const frameDelta = {
145+ x : canvasDelta . x - lastCanvasDelta . x ,
146+ y : canvasDelta . y - lastCanvasDelta . y
147+ }
148+
149+ for ( const group of selectedGroups ) {
150+ group . move ( frameDelta . x , frameDelta . y , true )
151+ }
152+ }
153+
154+ lastCanvasDelta = canvasDelta
130155 } )
131156 }
132157
@@ -195,6 +220,8 @@ function useNodeDragIndividual() {
195220 dragStartPos = null
196221 dragStartMouse = null
197222 otherSelectedNodesStartPositions = null
223+ selectedGroups = null
224+ lastCanvasDelta = null
198225
199226 // Stop tracking shift key state
200227 stopShiftSync ?.( )
0 commit comments