diff --git a/src/composables/canvas/useSelectionToolboxPosition.ts b/src/composables/canvas/useSelectionToolboxPosition.ts index 963f9c7d2..e98d4d5ef 100644 --- a/src/composables/canvas/useSelectionToolboxPosition.ts +++ b/src/composables/canvas/useSelectionToolboxPosition.ts @@ -1,4 +1,4 @@ -import { onUnmounted, ref, watch } from 'vue' +import { computed, onUnmounted, ref, watch } from 'vue' import type { Ref } from 'vue' import { useCanvasTransformSync } from '@/composables/canvas/useCanvasTransformSync' @@ -170,50 +170,75 @@ export function useSelectionToolboxPosition( } ) - // Watch for dragging state - watch( - () => canvasStore.canvas?.state?.draggingItems, - (dragging) => { - if (dragging) { - visible.value = false - - if (moreOptionsOpen.value) { - const currentSig = buildSelectionSignature(canvasStore) - if (currentSig !== moreOptionsSelectionSignature) { - moreOptionsSelectionSignature = null - } - moreOptionsWasOpenBeforeDrag = true - moreOptionsOpen.value = false - moreOptionsRestorePending.value = !!moreOptionsSelectionSignature - if (moreOptionsRestorePending.value) { - forceCloseMoreOptionsSignal.value++ - } else { - moreOptionsWasOpenBeforeDrag = false - } - } else { - moreOptionsRestorePending.value = false - moreOptionsWasOpenBeforeDrag = false - } - } else { - requestAnimationFrame(() => { - updateSelectionBounds() - const selectionMatches = currentSelectionMatchesSignature(canvasStore) - const shouldRestore = - moreOptionsWasOpenBeforeDrag && - visible.value && - moreOptionsRestorePending.value && - selectionMatches - - if (shouldRestore) { - restoreMoreOptionsSignal.value++ - } else { - moreOptionsRestorePending.value = false - } - moreOptionsWasOpenBeforeDrag = false - }) - } + const handleDragStateChange = (dragging: boolean) => { + if (dragging) { + handleDragStart() + return } - ) + + handleDragEnd() + } + + const handleDragStart = () => { + visible.value = false + + // Early return if more options wasn't open + if (!moreOptionsOpen.value) { + moreOptionsRestorePending.value = false + moreOptionsWasOpenBeforeDrag = false + return + } + + // Handle more options cleanup + const currentSig = buildSelectionSignature(canvasStore) + const selectionChanged = currentSig !== moreOptionsSelectionSignature + + if (selectionChanged) { + moreOptionsSelectionSignature = null + } + moreOptionsOpen.value = false + moreOptionsWasOpenBeforeDrag = true + moreOptionsRestorePending.value = !!moreOptionsSelectionSignature + + if (moreOptionsRestorePending.value) { + forceCloseMoreOptionsSignal.value++ + return + } + + moreOptionsWasOpenBeforeDrag = false + } + + const handleDragEnd = () => { + requestAnimationFrame(() => { + updateSelectionBounds() + + const selectionMatches = currentSelectionMatchesSignature(canvasStore) + const shouldRestore = + moreOptionsWasOpenBeforeDrag && + visible.value && + moreOptionsRestorePending.value && + selectionMatches + + // Single point of assignment for each ref + moreOptionsRestorePending.value = + shouldRestore && moreOptionsRestorePending.value + moreOptionsWasOpenBeforeDrag = false + + if (shouldRestore) { + restoreMoreOptionsSignal.value++ + } + }) + } + + // Unified dragging state - combines both LiteGraph and Vue node dragging + const isDragging = computed((): boolean => { + const litegraphDragging = canvasStore.canvas?.state?.draggingItems ?? false + const vueNodeDragging = + shouldRenderVueNodes.value && layoutStore.isDraggingVueNodes.value + return litegraphDragging || vueNodeDragging + }) + + watch(isDragging, handleDragStateChange) onUnmounted(() => { resetMoreOptionsState() diff --git a/src/renderer/core/layout/store/layoutStore.ts b/src/renderer/core/layout/store/layoutStore.ts index 7daa4523c..254b27a2c 100644 --- a/src/renderer/core/layout/store/layoutStore.ts +++ b/src/renderer/core/layout/store/layoutStore.ts @@ -5,7 +5,7 @@ * CRDT ensures conflict-free operations for both single and multi-user scenarios. */ import log from 'loglevel' -import { type ComputedRef, type Ref, computed, customRef } from 'vue' +import { type ComputedRef, type Ref, computed, customRef, ref } from 'vue' import * as Y from 'yjs' import { ACTOR_CONFIG } from '@/renderer/core/layout/constants' @@ -134,6 +134,9 @@ class LayoutStoreImpl implements LayoutStore { private slotSpatialIndex: SpatialIndexManager // For slots private rerouteSpatialIndex: SpatialIndexManager // For reroutes + // Vue dragging state for selection toolbox (public ref for direct mutation) + public isDraggingVueNodes = ref(false) + constructor() { // Initialize Yjs data structures this.ynodes = this.ydoc.getMap('nodes') diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 60e946bf0..808c21ca1 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -148,6 +148,7 @@ import { useErrorHandling } from '@/composables/useErrorHandling' import { LiteGraph } from '@/lib/litegraph/src/litegraph' import { SelectedNodeIdsKey } from '@/renderer/core/canvas/injectionKeys' import { TransformStateKey } from '@/renderer/core/layout/injectionKeys' +import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import { useNodeExecutionState } from '@/renderer/extensions/vueNodes/execution/useNodeExecutionState' import { useNodeLayout } from '@/renderer/extensions/vueNodes/layout/useNodeLayout' import { LODLevel, useLOD } from '@/renderer/extensions/vueNodes/lod/useLOD' @@ -363,6 +364,10 @@ const handlePointerDown = (event: PointerEvent) => { // Start drag using layout system isDragging.value = true + + // Set Vue node dragging state for selection toolbox + layoutStore.isDraggingVueNodes.value = true + startDrag(event) lastY.value = event.clientY lastX.value = event.clientX @@ -378,6 +383,9 @@ const handlePointerUp = (event: PointerEvent) => { if (isDragging.value) { isDragging.value = false void endDrag(event) + + // Clear Vue node dragging state for selection toolbox + layoutStore.isDraggingVueNodes.value = false } // Emit node-click for selection handling in GraphCanvas const dx = event.clientX - lastX.value