diff --git a/browser_tests/tests/vueNodes/interactions/node/resize.spec.ts b/browser_tests/tests/vueNodes/interactions/node/resize.spec.ts new file mode 100644 index 000000000..7ea5bf965 --- /dev/null +++ b/browser_tests/tests/vueNodes/interactions/node/resize.spec.ts @@ -0,0 +1,54 @@ +import { + comfyExpect as expect, + comfyPageFixture as test +} from '../../../../fixtures/ComfyPage' + +test.describe('Vue Node Resizing', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.VueNodes.Enabled', true) + await comfyPage.vueNodes.waitForNodes() + }) + + test('should resize node without position drift after selecting', async ({ + comfyPage + }) => { + // Get a Vue node fixture + const node = await comfyPage.vueNodes.getFixtureByTitle('Load Checkpoint') + const initialBox = await node.boundingBox() + if (!initialBox) throw new Error('Node bounding box not found') + + // Select the node first (this was causing the bug) + await node.header.click() + await comfyPage.page.waitForTimeout(100) // Brief pause after selection + + // Get position after selection + const selectedBox = await node.boundingBox() + if (!selectedBox) + throw new Error('Node bounding box not found after select') + + // Verify position unchanged after selection + expect(selectedBox.x).toBeCloseTo(initialBox.x, 1) + expect(selectedBox.y).toBeCloseTo(initialBox.y, 1) + + // Now resize from bottom-right corner + const resizeStartX = selectedBox.x + selectedBox.width - 5 + const resizeStartY = selectedBox.y + selectedBox.height - 5 + + await comfyPage.page.mouse.move(resizeStartX, resizeStartY) + await comfyPage.page.mouse.down() + await comfyPage.page.mouse.move(resizeStartX + 50, resizeStartY + 30) + await comfyPage.page.mouse.up() + + // Get final position and size + const finalBox = await node.boundingBox() + if (!finalBox) throw new Error('Node bounding box not found after resize') + + // Position should NOT have changed (the bug was position drift) + expect(finalBox.x).toBeCloseTo(initialBox.x, 1) + expect(finalBox.y).toBeCloseTo(initialBox.y, 1) + + // Size should have increased + expect(finalBox.width).toBeGreaterThan(initialBox.width) + expect(finalBox.height).toBeGreaterThan(initialBox.height) + }) +}) diff --git a/src/renderer/core/layout/store/layoutStore.ts b/src/renderer/core/layout/store/layoutStore.ts index aeb21f32b..b6e139e6b 100644 --- a/src/renderer/core/layout/store/layoutStore.ts +++ b/src/renderer/core/layout/store/layoutStore.ts @@ -138,6 +138,8 @@ class LayoutStoreImpl implements LayoutStore { // Vue dragging state for selection toolbox (public ref for direct mutation) public isDraggingVueNodes = ref(false) + // Vue resizing state to prevent drag from activating during resize + public isResizingVueNodes = ref(false) constructor() { // Initialize Yjs data structures diff --git a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts index 5108a781b..eb96ff8c8 100644 --- a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts +++ b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts @@ -92,12 +92,14 @@ const mockData = vi.hoisted(() => { vi.mock('@/renderer/core/layout/store/layoutStore', () => { const isDraggingVueNodes = ref(false) + const isResizingVueNodes = ref(false) const fakeNodeLayoutRef = ref(mockData.fakeNodeLayout) const getNodeLayoutRef = vi.fn(() => fakeNodeLayoutRef) const setSource = vi.fn() return { layoutStore: { isDraggingVueNodes, + isResizingVueNodes, getNodeLayoutRef, setSource } diff --git a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts index 33369bc06..26d2b8a4b 100644 --- a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts +++ b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts @@ -63,6 +63,9 @@ export function useNodePointerInteractions( function onPointermove(event: PointerEvent) { if (forwardMiddlePointerIfNeeded(event)) return + // Don't activate drag while resizing + if (layoutStore.isResizingVueNodes.value) return + const nodeId = toValue(nodeIdRef) if (nodeManager.value?.getNode(nodeId)?.flags?.pinned) { diff --git a/src/renderer/extensions/vueNodes/interactions/resize/useNodeResize.ts b/src/renderer/extensions/vueNodes/interactions/resize/useNodeResize.ts index ebc8a3f01..75749e812 100644 --- a/src/renderer/extensions/vueNodes/interactions/resize/useNodeResize.ts +++ b/src/renderer/extensions/vueNodes/interactions/resize/useNodeResize.ts @@ -2,6 +2,7 @@ import { useEventListener } from '@vueuse/core' import { ref } from 'vue' import type { Point, Size } from '@/renderer/core/layout/types' +import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import { useNodeSnap } from '@/renderer/extensions/vueNodes/composables/useNodeSnap' import { useShiftKeySync } from '@/renderer/extensions/vueNodes/composables/useShiftKeySync' import { useTransformState } from '@/renderer/core/layout/transform/useTransformState' @@ -55,6 +56,8 @@ export function useNodeResize( // Capture pointer to ensure we get all move/up events target.setPointerCapture(event.pointerId) + // Mark as resizing to prevent drag from activating + layoutStore.isResizingVueNodes.value = true isResizing.value = true resizeStartPointer.value = { x: event.clientX, y: event.clientY } resizeStartSize.value = startSize @@ -93,6 +96,7 @@ export function useNodeResize( const handlePointerUp = (upEvent: PointerEvent) => { if (isResizing.value) { isResizing.value = false + layoutStore.isResizingVueNodes.value = false resizeStartPointer.value = null resizeStartSize.value = null