diff --git a/src/extensions/core/index.ts b/src/extensions/core/index.ts index d4b5c83f2..37afd26f3 100644 --- a/src/extensions/core/index.ts +++ b/src/extensions/core/index.ts @@ -6,7 +6,6 @@ import './groupNode' import './groupNodeManage' import './groupOptions' import './invertMenuScrolling' -import './keybinds' import './maskeditor' import './nodeTemplates' import './noteNode' diff --git a/src/extensions/core/keybinds.ts b/src/extensions/core/keybinds.ts deleted file mode 100644 index 0505773d2..000000000 --- a/src/extensions/core/keybinds.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { app } from '../../scripts/app' -import { KeyComboImpl, useKeybindingStore } from '@/stores/keybindingStore' -import { useCommandStore } from '@/stores/commandStore' - -app.registerExtension({ - name: 'Comfy.Keybinds', - init() { - const keybindListener = async function (event: KeyboardEvent) { - // Ignore keybindings for legacy jest tests as jest tests don't have - // a Vue app instance or pinia stores. - if (!app.vueAppReady) return - - const keyCombo = KeyComboImpl.fromEvent(event) - if (keyCombo.isModifier) { - return - } - - // Ignore non-modifier keybindings if typing in input fields - const target = event.composedPath()[0] as HTMLElement - if ( - !keyCombo.hasModifier && - (target.tagName === 'TEXTAREA' || - target.tagName === 'INPUT' || - (target.tagName === 'SPAN' && - target.classList.contains('property_value'))) - ) { - return - } - - const keybindingStore = useKeybindingStore() - const commandStore = useCommandStore() - const keybinding = keybindingStore.getKeybinding(keyCombo) - if (keybinding && keybinding.targetSelector !== '#graph-canvas') { - // Prevent default browser behavior first, then execute the command - event.preventDefault() - await commandStore.execute(keybinding.commandId) - return - } - - // Only clear dialogs if not using modifiers - if (event.ctrlKey || event.altKey || event.metaKey) { - return - } - - // Escape key: close the first open modal found, and all dialogs - if (event.key === 'Escape') { - const modals = document.querySelectorAll('.comfy-modal') - for (const modal of modals) { - const modalDisplay = window - .getComputedStyle(modal) - .getPropertyValue('display') - - if (modalDisplay !== 'none') { - modal.style.display = 'none' - break - } - } - - for (const d of document.querySelectorAll('dialog')) d.close() - } - } - - window.addEventListener('keydown', keybindListener) - } -}) diff --git a/src/services/keybindingService.ts b/src/services/keybindingService.ts new file mode 100644 index 000000000..30d92e491 --- /dev/null +++ b/src/services/keybindingService.ts @@ -0,0 +1,60 @@ +import { useCommandStore } from '@/stores/commandStore' +import { KeyComboImpl, useKeybindingStore } from '@/stores/keybindingStore' + +export const useKeybindingService = () => { + const keybindingStore = useKeybindingStore() + const commandStore = useCommandStore() + + const keybindHandler = async function (event: KeyboardEvent) { + const keyCombo = KeyComboImpl.fromEvent(event) + if (keyCombo.isModifier) { + return + } + + // Ignore non-modifier keybindings if typing in input fields + const target = event.composedPath()[0] as HTMLElement + if ( + !keyCombo.hasModifier && + (target.tagName === 'TEXTAREA' || + target.tagName === 'INPUT' || + (target.tagName === 'SPAN' && + target.classList.contains('property_value'))) + ) { + return + } + + const keybinding = keybindingStore.getKeybinding(keyCombo) + if (keybinding && keybinding.targetSelector !== '#graph-canvas') { + // Prevent default browser behavior first, then execute the command + event.preventDefault() + await commandStore.execute(keybinding.commandId) + return + } + + // Only clear dialogs if not using modifiers + if (event.ctrlKey || event.altKey || event.metaKey) { + return + } + + // Escape key: close the first open modal found, and all dialogs + if (event.key === 'Escape') { + const modals = document.querySelectorAll('.comfy-modal') + for (const modal of modals) { + const modalDisplay = window + .getComputedStyle(modal) + .getPropertyValue('display') + + if (modalDisplay !== 'none') { + modal.style.display = 'none' + break + } + } + + for (const d of document.querySelectorAll('dialog')) d.close() + } + } + + return { + keybindHandler + } +} diff --git a/src/views/GraphView.vue b/src/views/GraphView.vue index 219f30b76..ae43ef229 100644 --- a/src/views/GraphView.vue +++ b/src/views/GraphView.vue @@ -42,6 +42,8 @@ import { SERVER_CONFIG_ITEMS } from '@/constants/serverConfig' import { useMenuItemStore } from '@/stores/menuItemStore' import { useCommandStore } from '@/stores/commandStore' import { useCoreCommands } from '@/hooks/coreCommandHooks' +import { useEventListener } from '@vueuse/core' +import { useKeybindingService } from '@/services/keybindingService' setupAutoQueueHandler() @@ -160,6 +162,8 @@ onBeforeUnmount(() => { executionStore.unbindExecutionEvents() }) +useEventListener(window, 'keydown', useKeybindingService().keybindHandler) + const onGraphReady = () => { requestIdleCallback( () => {