Compare commits
16 Commits
refactor/n
...
fix/spaceb
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
16ad2c5975 | Merge branch 'main' into fix/spacebar-panning-vue-nodes | ||
|
|
813f54bff4 | [automated] Update test expectations | ||
|
|
0f6fb90d04 | format files | ||
|
|
0972a46a15 | Merge remote-tracking branch 'origin/main' into fix/spacebar-panning-vue-nodes | ||
|
|
f5cdee82e9 |
fix: add capture flag to removeEventListener calls and reuse target variable
- Add capture:true to removeEventListener calls that were added with capture to prevent event listener leaks when unbinding - Reuse existing target variable in Delete/Backspace check instead of re-accessing e.target with @ts-expect-error - Add isContentEditable check to Delete/Backspace guard for consistency |
||
|
|
571bb51ab5 | [automated] Apply ESLint and Prettier fixes | ||
|
|
a47081c169 |
fix: skip text-editable elements in processKey
Broaden the target guard in processKey to skip all text-editable surfaces (input, textarea, contenteditable) before handling shortcuts. This prevents space/Ctrl+A/C from being blocked when typing in prompt textareas or other multiline fields in Vue nodes mode. |
||
|
|
0e47f9fb10 |
fix: always remove document keydown listener in unbindEvents
Remove the LiteGraph.vueNodesMode check when removing the document-level keydown listener in unbindEvents. If vueNodesMode was true during bindEvents but changed before unbindEvents, the listener would be left dangling. Now guarded only by existence of _key_callback. |
||
|
|
9427d7ed60 | Merge remote-tracking branch 'origin/main' into fix/spacebar-panning-vue-nodes | ||
|
|
d114b01a75 |
refactor: consolidate panning state in litegraph
Remove duplicate panning state management from useSlotLinkInteraction. Instead of Vue manually manipulating canvas.ds.offset, sync pointer.isDown with litegraph and let events bubble when in panning mode (read_only). - Remove isPanningDuringLinkDrag and lastPanningMouse local state - Remove manual offset manipulation in handlePointerMove - Conditionally stopPropagation only when NOT in panning mode - Set canvas.pointer.isDown so spacebar triggers litegraph panning |
||
|
|
53bdf7f6c3 |
fix: enable spacebar panning during slot link drag
Move spacebar detection to document-level listener in LGraphCanvas when vueNodesMode is enabled. Implement direct panning in useSlotLinkInteraction when spacebar is held during connection drag, bypassing litegraph event handling for smoother panning while maintaining link position updates. Fixes #7806 |
||
|
|
fc082d84b9 |
refactor: use read_only as signal, let events bubble to litegraph
- Set canvas.read_only directly when spacebar pressed - Skip stopPropagation when read_only is true in slot link handler - Remove manual dragging_canvas and processMouseMove management - Litegraph handles panning through its normal event handlers |
||
|
|
3cf19af173 |
fix: Explicitly pass null to isEditableElement when activeElement.value is falsy.
|
||
|
|
1d1e16b62d | refactor: simplify spacebar forwarding to module-level initialization | ||
|
|
2f8f5253b5 |
refactor: address review comments - use useMagicKeys and derive button state
- Refactored spacebar forwarding to use useMagicKeys from VueUse instead of raw event listeners - Added filtering for editable elements (input, textarea, contentEditable) - Changed panning check in useSlotLinkInteraction to derive button state from event.buttons instead of storing canvas.pointer.isDown statefully - Updated tests to work with the new useMagicKeys approach using vi.hoisted pattern |
||
|
|
f5ac48c5be |
fix: spacebar panning in vueNodes mode
Forward keydown events to litegraph's processKey when Vue nodes have focus. Litegraph only binds keydown to the canvas element, so spacebar panning didn't work when Vue nodes were focused. Also sync pointer state with litegraph canvas during link dragging to enable spacebar panning while dragging connections. Fixes #7806 |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 98 KiB |
|
|
@@ -1953,6 +1953,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
|||
this._key_callback = this.processKey.bind(this)
|
||||
|
||||
canvas.addEventListener('keydown', this._key_callback, true)
|
||||
// In Vue nodes mode, also listen on document for keydown since Vue elements may have focus
|
||||
if (LiteGraph.vueNodesMode) {
|
||||
document.addEventListener('keydown', this._key_callback, true)
|
||||
}
|
||||
// keyup event must be bound on the document
|
||||
document.addEventListener('keyup', this._key_callback, true)
|
||||
|
||||
|
|
@@ -1977,14 +1981,24 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
|||
const { canvas } = this
|
||||
|
||||
// Assertions: removing nullish is fine.
|
||||
canvas.removeEventListener('pointercancel', this._mousecancel_callback!)
|
||||
// Note: capture flag must match addEventListener for removal to work
|
||||
canvas.removeEventListener(
|
||||
'pointercancel',
|
||||
this._mousecancel_callback!,
|
||||
true
|
||||
)
|
||||
canvas.removeEventListener('pointerout', this._mouseout_callback!)
|
||||
canvas.removeEventListener('pointermove', this._mousemove_callback!)
|
||||
canvas.removeEventListener('pointerup', this._mouseup_callback!)
|
||||
canvas.removeEventListener('pointerdown', this._mousedown_callback!)
|
||||
canvas.removeEventListener('pointerup', this._mouseup_callback!, true)
|
||||
canvas.removeEventListener('pointerdown', this._mousedown_callback!, true)
|
||||
canvas.removeEventListener('wheel', this._mousewheel_callback!)
|
||||
canvas.removeEventListener('keydown', this._key_callback!)
|
||||
document.removeEventListener('keyup', this._key_callback!)
|
||||
canvas.removeEventListener('keydown', this._key_callback!, true)
|
||||
// Always remove document keydown listener - it may have been added if vueNodesMode
|
||||
// was true during bindEvents, even if vueNodesMode has since changed
|
||||
if (this._key_callback) {
|
||||
document.removeEventListener('keydown', this._key_callback, true)
|
||||
}
|
||||
document.removeEventListener('keyup', this._key_callback!, true)
|
||||
canvas.removeEventListener('contextmenu', this._doNothing)
|
||||
canvas.removeEventListener('dragenter', this._doReturnTrue)
|
||||
|
||||
|
|
@@ -3668,8 +3682,14 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
|||
if (!graph) return
|
||||
|
||||
let block_default = false
|
||||
// @ts-expect-error EventTarget.localName is not in standard types
|
||||
if (e.target.localName == 'input') return
|
||||
// Skip all text-editable surfaces to avoid blocking typing/selection/copy
|
||||
const target = e.target as HTMLElement | null
|
||||
if (
|
||||
target?.localName === 'input' ||
|
||||
target?.localName === 'textarea' ||
|
||||
target?.isContentEditable
|
||||
)
|
||||
return
|
||||
|
||||
if (e.type == 'keydown') {
|
||||
// TODO: Switch
|
||||
|
|
@@ -3705,9 +3725,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
|||
// paste
|
||||
this.pasteFromClipboard({ connectInputs: e.shiftKey })
|
||||
} else if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
// delete or backspace
|
||||
// @ts-expect-error EventTarget.localName is not in standard types
|
||||
if (e.target.localName != 'input' && e.target.localName != 'textarea') {
|
||||
// delete or backspace (but don't intercept when editing text)
|
||||
if (
|
||||
target?.localName !== 'input' &&
|
||||
target?.localName !== 'textarea' &&
|
||||
!target?.isContentEditable
|
||||
) {
|
||||
if (this.selectedItems.size === 0) {
|
||||
this.#noItemsSelected()
|
||||
return
|
||||
|
|
|
|||
|
|
@@ -2,11 +2,12 @@ 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()
|
||||
|
|
|
|||
|
|
@@ -6,8 +6,8 @@ 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'
|
||||
|
||||
export function useNodePointerInteractions(
|
||||
nodeIdRef: MaybeRefOrGetter<string>
|
||||
|
|
@@ -65,6 +65,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
|
||||
|
||||
|
|
|
|||
|
|
@@ -293,6 +293,10 @@ export function useSlotLinkInteraction({
|
|||
raf.cancel()
|
||||
dragContext.dispose()
|
||||
clearCompatible()
|
||||
// Reset litegraph pointer state
|
||||
if (app.canvas) {
|
||||
app.canvas.pointer.isDown = false
|
||||
}
|
||||
}
|
||||
|
||||
const updatePointerState = (event: PointerEvent) => {
|
||||
|
|
@@ -409,6 +413,11 @@ export function useSlotLinkInteraction({
|
|||
|
||||
const handlePointerMove = (event: PointerEvent) => {
|
||||
if (!pointerSession.matches(event)) return
|
||||
|
||||
// When in panning mode (read_only), let litegraph handle panning - don't stop propagation
|
||||
if (app.canvas?.read_only) return
|
||||
|
||||
// Not in panning mode - Vue handles link drag, stop propagation to prevent litegraph interference
|
||||
event.stopPropagation()
|
||||
|
||||
dragContext.pendingPointerMove = {
|
||||
|
|
@@ -539,7 +548,10 @@ export function useSlotLinkInteraction({
|
|||
}
|
||||
|
||||
const handlePointerUp = (event: PointerEvent) => {
|
||||
event.stopPropagation()
|
||||
// When in panning mode, let litegraph handle - but still cleanup our link drag state
|
||||
if (!app.canvas?.read_only) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
finishInteraction(event)
|
||||
}
|
||||
|
||||
|
|
@@ -584,6 +596,10 @@ export function useSlotLinkInteraction({
|
|||
if (event.button !== 0) return
|
||||
if (!nodeId) return
|
||||
if (pointerSession.isActive()) return
|
||||
|
||||
// Don't start link drag if in panning mode - let litegraph handle panning
|
||||
if (app.canvas?.read_only) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
|
|
@@ -703,6 +719,9 @@ export function useSlotLinkInteraction({
|
|||
)
|
||||
|
||||
pointerSession.begin(event.pointerId)
|
||||
// Sync pointer state with litegraph so spacebar panning works
|
||||
canvas.last_mouse = [event.clientX, event.clientY]
|
||||
canvas.pointer.isDown = true
|
||||
|
||||
toCanvasPointerEvent(event)
|
||||
updatePointerState(event)
|
||||
|
|
|
|||