diff --git a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts index c3410051a..06de292e3 100644 --- a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts +++ b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts @@ -2,16 +2,33 @@ import { setActivePinia } from 'pinia' import { beforeEach, describe, expect, it, vi } from 'vitest' import { nextTick, ref } from 'vue' -import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions' -import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers' import { createTestingPinia } from '@pinia/testing' + import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import type { NodeLayout } from '@/renderer/core/layout/types' +import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers' +import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions' import { useNodeDrag } from '@/renderer/extensions/vueNodes/layout/useNodeDrag' const forwardEventToCanvasMock = vi.fn() const selectedItemsState: { items: Array<{ id?: string }> } = { items: [] } +const mockCanvas = vi.hoisted(() => { + const canvasElement = document.createElement('canvas') + return { + canvas: canvasElement, + processKey: vi.fn() + } +}) + +vi.mock('@/scripts/app', () => ({ + app: { + get canvas() { + return mockCanvas + } + } +})) + // Mock the dependencies vi.mock('@/renderer/core/canvas/useCanvasInteractions', () => ({ useCanvasInteractions: () => ({ @@ -324,4 +341,42 @@ describe('useNodePointerInteractions', () => { true ) }) + + describe('keydown forwarding for spacebar panning', () => { + it('forwards keydown events to canvas.processKey when target is not the canvas', () => { + // Initialize the composable to set up keydown forwarding + useNodePointerInteractions('test-node-123') + + // Create a div to simulate a Vue node element + const vueNodeElement = document.createElement('div') + document.body.appendChild(vueNodeElement) + + // Dispatch keydown event on the Vue node element (not the canvas) + const keydownEvent = new KeyboardEvent('keydown', { + key: ' ', + bubbles: true + }) + vueNodeElement.dispatchEvent(keydownEvent) + + // Should forward to canvas.processKey + expect(mockCanvas.processKey).toHaveBeenCalledWith(keydownEvent) + + document.body.removeChild(vueNodeElement) + }) + + it('does not forward keydown events when target is the canvas itself', () => { + useNodePointerInteractions('test-node-123') + mockCanvas.processKey.mockClear() + + // Dispatch keydown event directly on the canvas element + const keydownEvent = new KeyboardEvent('keydown', { + key: ' ', + bubbles: true + }) + mockCanvas.canvas.dispatchEvent(keydownEvent) + + // Should NOT forward (canvas handles it directly) + expect(mockCanvas.processKey).not.toHaveBeenCalled() + }) + }) }) diff --git a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts index 0dfdae51b..4bc74adda 100644 --- a/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts +++ b/src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.ts @@ -6,12 +6,34 @@ import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle' import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions' import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers' -import { isMultiSelectKey } from '@/renderer/extensions/vueNodes/utils/selectionUtils' import { useNodeDrag } from '@/renderer/extensions/vueNodes/layout/useNodeDrag' +import { isMultiSelectKey } from '@/renderer/extensions/vueNodes/utils/selectionUtils' +import { app } from '@/scripts/app' + +// Forward keydown events to litegraph's processKey when Vue nodes have focus +let keydownForwardingInitialized = false + +function initKeydownForwarding() { + if (keydownForwardingInitialized) return + keydownForwardingInitialized = true + + document.addEventListener( + 'keydown', + (e) => { + const canvas = app.canvas + if (!canvas) return + if (e.target === canvas.canvas) return + canvas.processKey(e) + }, + true + ) +} export function useNodePointerInteractions( nodeIdRef: MaybeRefOrGetter ) { + initKeydownForwarding() + const { startDrag, endDrag, handleDrag } = useNodeDrag() // Use canvas interactions for proper wheel event handling and pointer event capture control const { forwardEventToCanvas, shouldHandleNodePointerEvents } = @@ -65,6 +87,12 @@ export function useNodePointerInteractions( function onPointermove(event: PointerEvent) { if (forwardMiddlePointerIfNeeded(event)) return + // Don't handle pointer events when canvas is in panning mode - forward to canvas instead + if (!shouldHandleNodePointerEvents.value) { + forwardEventToCanvas(event) + return + } + // Don't activate drag while resizing if (layoutStore.isResizingVueNodes.value) return diff --git a/src/renderer/extensions/vueNodes/composables/useSlotLinkInteraction.ts b/src/renderer/extensions/vueNodes/composables/useSlotLinkInteraction.ts index 6e11902d8..d827c3890 100644 --- a/src/renderer/extensions/vueNodes/composables/useSlotLinkInteraction.ts +++ b/src/renderer/extensions/vueNodes/composables/useSlotLinkInteraction.ts @@ -293,6 +293,9 @@ export function useSlotLinkInteraction({ raf.cancel() dragContext.dispose() clearCompatible() + if (app.canvas?.pointer) { + app.canvas.pointer.isDown = false + } } const updatePointerState = (event: PointerEvent) => { @@ -409,6 +412,12 @@ export function useSlotLinkInteraction({ const handlePointerMove = (event: PointerEvent) => { if (!pointerSession.matches(event)) return + + const canvas = app.canvas + if (canvas?.read_only && canvas.dragging_canvas) { + canvas.processMouseMove(event) + } + event.stopPropagation() dragContext.pendingPointerMove = { @@ -703,6 +712,10 @@ export function useSlotLinkInteraction({ ) pointerSession.begin(event.pointerId) + if (canvas.pointer) { + canvas.pointer.isDown = true + } + canvas.last_mouse = [event.clientX, event.clientY] toCanvasPointerEvent(event) updatePointerState(event)