+
+
+
+
+
+
+
+
diff --git a/src/components/topbar/CommandMenubar.vue b/src/components/topbar/CommandMenubar.vue
index 49bfb17c4..e1ac7a1a5 100644
--- a/src/components/topbar/CommandMenubar.vue
+++ b/src/components/topbar/CommandMenubar.vue
@@ -21,7 +21,7 @@
v-if="item?.comfyCommand?.keybinding"
class="ml-auto border border-surface rounded text-muted text-xs p-1 keybinding-tag"
>
- {{ item.comfyCommand.keybinding.combo.toString() }}
+ {{ item.comfyCommand.keybinding.currentCombo?.toString() || '-' }}
diff --git a/src/constants/coreKeybindings.ts b/src/constants/coreKeybindings.ts
index 023a63ac9..4a9973432 100644
--- a/src/constants/coreKeybindings.ts
+++ b/src/constants/coreKeybindings.ts
@@ -173,5 +173,22 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
key: 'f'
},
commandId: 'Workspace.ToggleFocusMode'
+ },
+ {
+ combo: {
+ key: 'z',
+ ctrl: true
+ },
+ commandId: 'Comfy.Undo',
+ targetSelector: '#graph-canvas'
+ },
+ {
+ combo: {
+ key: 'z',
+ ctrl: true,
+ shift: true
+ },
+ commandId: 'Comfy.Redo',
+ targetSelector: '#graph-canvas'
}
]
diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts
index 96e0fde6e..1cc9a0a74 100644
--- a/src/constants/coreSettings.ts
+++ b/src/constants/coreSettings.ts
@@ -416,6 +416,15 @@ export const CORE_SETTINGS: SettingParams[] = [
versionAdded: '1.3.5'
},
{
+ //new array that saves all modified keybindings, no distinction between modified and unset -> unified in object
+ id: 'Comfy.Keybinding.ModifiedBindings',
+ name: 'Keybindings changed by the user',
+ type: 'hidden',
+ defaultValue: [] as Keybinding[],
+ versionAdded: '1.5.7'
+ },
+ {
+ //deprecated
id: 'Comfy.Keybinding.UnsetBindings',
name: 'Keybindings unset by the user',
type: 'hidden',
@@ -423,6 +432,7 @@ export const CORE_SETTINGS: SettingParams[] = [
versionAdded: '1.3.7'
},
{
+ //deprecated
id: 'Comfy.Keybinding.NewBindings',
name: 'Keybindings set by the user',
type: 'hidden',
diff --git a/src/extensions/core/keybinds.ts b/src/extensions/core/keybinds.ts
index 0505773d2..980469bd0 100644
--- a/src/extensions/core/keybinds.ts
+++ b/src/extensions/core/keybinds.ts
@@ -2,64 +2,99 @@ 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
+//this class is responsible for handling key events and executing commands
+class KeyboardManager {
+ private modifiers: string[] = []
+ private context: string = 'global'
- const keyCombo = KeyComboImpl.fromEvent(event)
- if (keyCombo.isModifier) {
- return
- }
+ constructor() {
+ this.addListeners()
+ }
- // Ignore non-modifier keybindings if typing in input fields
- const target = event.composedPath()[0] as HTMLElement
+ addListeners() {
+ window.addEventListener('keydown', (event) => this.handleKeyDown(event))
+ window.addEventListener('keyup', (event) => this.handleKeyUp(event))
+ window.addEventListener('blur', () => this.clearKeys())
+
+ app.extensionManager.setting.set('Comfy.KeybindContext', 'global')
+ }
+
+ private clearKeys() {
+ this.modifiers = []
+ }
+
+ private handleKeyUp(event: KeyboardEvent) {
+ this.modifiers = this.modifiers.filter((key) => key !== event.key)
+ }
+
+ private setContext(event?: KeyboardEvent) {
+ if (!event) return
+ event.preventDefault()
+ const context = app.extensionManager.setting.get('Comfy.KeybindContext')
+ this.context = context
+ }
+
+ private async handleKeyDown(event: KeyboardEvent) {
+ if (!app.vueAppReady) return
+
+ if (event.key === 'Escape' && this.modifiers.length === 0) {
+ this.handleEscapeKey()
+ return
+ }
+
+ if (event.key === 'F12') return // prevent opening dev tools
+
+ this.setContext(event)
+
+ const target = event.composedPath()[0] as HTMLElement
+ const excludedTags = ['TEXTAREA', 'INPUT', 'SPAN']
+
+ if (this.context === 'global') {
if (
- !keyCombo.hasModifier &&
- (target.tagName === 'TEXTAREA' ||
- target.tagName === 'INPUT' ||
- (target.tagName === 'SPAN' &&
- target.classList.contains('property_value')))
+ excludedTags.includes(target.tagName) ||
+ 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