mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-08 09:00:05 +00:00
[fix] Change keyboard event handling from event.key to event.code
This fixes keyboard shortcuts not working on non-English (non-Latin) keyboard layouts. Changes: - KeyComboImpl.fromEvent() now uses event.code instead of event.key - Updated isModifier check to use key codes for modifier keys - Added getDisplayKey() method to convert key codes to readable names - Updated Escape key handling in keybindingService to use event.code - Updated KeybindingPanel captureKeybinding to use event.code - Migrated all core keybindings from event.key to event.code format - Fixed tests to mock event.code instead of event.key Fixes #5252 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -237,7 +237,7 @@ async function removeKeybinding(commandData: ICommandData) {
|
||||
async function captureKeybinding(event: KeyboardEvent) {
|
||||
// Allow the use of keyboard shortcuts when adding keyboard shortcuts
|
||||
if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
||||
switch (event.key) {
|
||||
switch (event.code) {
|
||||
case 'Escape':
|
||||
cancelEdit()
|
||||
return
|
||||
|
||||
@@ -26,58 +26,58 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'r'
|
||||
key: 'KeyR'
|
||||
},
|
||||
commandId: 'Comfy.RefreshNodeDefinitions'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'q'
|
||||
key: 'KeyQ'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.queue'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'w'
|
||||
key: 'KeyW'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.workflows'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'n'
|
||||
key: 'KeyN'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.node-library'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm'
|
||||
key: 'KeyM'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.model-library'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 's',
|
||||
key: 'KeyS',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.SaveWorkflow'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'o',
|
||||
key: 'KeyO',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.OpenWorkflow'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'g',
|
||||
key: 'KeyG',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Graph.GroupSelectedNodes'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: ',',
|
||||
key: 'Comma',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.ShowSettingsDialog'
|
||||
@@ -85,7 +85,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
// For '=' both holding shift and not holding shift
|
||||
{
|
||||
combo: {
|
||||
key: '=',
|
||||
key: 'Equal',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomIn',
|
||||
@@ -93,7 +93,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '+',
|
||||
key: 'Equal',
|
||||
alt: true,
|
||||
shift: true
|
||||
},
|
||||
@@ -103,7 +103,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
// For number pad '+'
|
||||
{
|
||||
combo: {
|
||||
key: '+',
|
||||
key: 'NumpadAdd',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomIn',
|
||||
@@ -111,7 +111,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '-',
|
||||
key: 'Minus',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomOut',
|
||||
@@ -119,21 +119,21 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '.'
|
||||
key: 'Period'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.FitView',
|
||||
targetElementId: 'graph-canvas-container'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'p'
|
||||
key: 'KeyP'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelected.Pin',
|
||||
targetElementId: 'graph-canvas-container'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'c',
|
||||
key: 'KeyC',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Collapse',
|
||||
@@ -141,7 +141,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'b',
|
||||
key: 'KeyB',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Bypass',
|
||||
@@ -149,7 +149,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm',
|
||||
key: 'KeyM',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Mute',
|
||||
@@ -157,20 +157,20 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '`',
|
||||
key: 'Backquote',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Workspace.ToggleBottomPanelTab.logs-terminal'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'f'
|
||||
key: 'KeyF'
|
||||
},
|
||||
commandId: 'Workspace.ToggleFocusMode'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'e',
|
||||
key: 'KeyE',
|
||||
ctrl: true,
|
||||
shift: true
|
||||
},
|
||||
@@ -178,7 +178,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm',
|
||||
key: 'KeyM',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleMinimap'
|
||||
@@ -187,19 +187,19 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
combo: {
|
||||
ctrl: true,
|
||||
shift: true,
|
||||
key: 'k'
|
||||
key: 'KeyK'
|
||||
},
|
||||
commandId: 'Workspace.ToggleBottomPanel.Shortcuts'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'v'
|
||||
key: 'KeyV'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.Unlock'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'h'
|
||||
key: 'KeyH'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.Lock'
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ export const useKeybindingService = () => {
|
||||
if (keybinding && keybinding.targetElementId !== 'graph-canvas') {
|
||||
// Special handling for Escape key - let dialogs handle it first
|
||||
if (
|
||||
event.key === 'Escape' &&
|
||||
event.code === 'Escape' &&
|
||||
!event.ctrlKey &&
|
||||
!event.altKey &&
|
||||
!event.metaKey
|
||||
@@ -88,7 +88,7 @@ export const useKeybindingService = () => {
|
||||
}
|
||||
|
||||
// Escape key: close the first open modal found, and all dialogs
|
||||
if (event.key === 'Escape') {
|
||||
if (event.code === 'Escape') {
|
||||
const modals = document.querySelectorAll<HTMLElement>('.comfy-modal')
|
||||
for (const modal of modals) {
|
||||
const modalDisplay = window
|
||||
|
||||
@@ -44,7 +44,7 @@ export class KeyComboImpl implements KeyCombo {
|
||||
|
||||
static fromEvent(event: KeyboardEvent) {
|
||||
return new KeyComboImpl({
|
||||
key: event.key,
|
||||
key: event.code,
|
||||
ctrl: event.ctrlKey || event.metaKey,
|
||||
alt: event.altKey,
|
||||
shift: event.shiftKey
|
||||
@@ -75,7 +75,16 @@ export class KeyComboImpl implements KeyCombo {
|
||||
}
|
||||
|
||||
get isModifier(): boolean {
|
||||
return ['Control', 'Meta', 'Alt', 'Shift'].includes(this.key)
|
||||
return [
|
||||
'ControlLeft',
|
||||
'ControlRight',
|
||||
'MetaLeft',
|
||||
'MetaRight',
|
||||
'AltLeft',
|
||||
'AltRight',
|
||||
'ShiftLeft',
|
||||
'ShiftRight'
|
||||
].includes(this.key)
|
||||
}
|
||||
|
||||
get modifierCount(): number {
|
||||
@@ -106,9 +115,95 @@ export class KeyComboImpl implements KeyCombo {
|
||||
if (this.shift) {
|
||||
sequences.push('Shift')
|
||||
}
|
||||
sequences.push(this.key)
|
||||
sequences.push(this.getDisplayKey())
|
||||
return sequences
|
||||
}
|
||||
|
||||
getDisplayKey(): string {
|
||||
// Convert key codes to display names
|
||||
const keyMap: Record<string, string> = {
|
||||
// Letters
|
||||
KeyA: 'A',
|
||||
KeyB: 'B',
|
||||
KeyC: 'C',
|
||||
KeyD: 'D',
|
||||
KeyE: 'E',
|
||||
KeyF: 'F',
|
||||
KeyG: 'G',
|
||||
KeyH: 'H',
|
||||
KeyI: 'I',
|
||||
KeyJ: 'J',
|
||||
KeyK: 'K',
|
||||
KeyL: 'L',
|
||||
KeyM: 'M',
|
||||
KeyN: 'N',
|
||||
KeyO: 'O',
|
||||
KeyP: 'P',
|
||||
KeyQ: 'Q',
|
||||
KeyR: 'R',
|
||||
KeyS: 'S',
|
||||
KeyT: 'T',
|
||||
KeyU: 'U',
|
||||
KeyV: 'V',
|
||||
KeyW: 'W',
|
||||
KeyX: 'X',
|
||||
KeyY: 'Y',
|
||||
KeyZ: 'Z',
|
||||
// Numbers
|
||||
Digit0: '0',
|
||||
Digit1: '1',
|
||||
Digit2: '2',
|
||||
Digit3: '3',
|
||||
Digit4: '4',
|
||||
Digit5: '5',
|
||||
Digit6: '6',
|
||||
Digit7: '7',
|
||||
Digit8: '8',
|
||||
Digit9: '9',
|
||||
// Function keys
|
||||
F1: 'F1',
|
||||
F2: 'F2',
|
||||
F3: 'F3',
|
||||
F4: 'F4',
|
||||
F5: 'F5',
|
||||
F6: 'F6',
|
||||
F7: 'F7',
|
||||
F8: 'F8',
|
||||
F9: 'F9',
|
||||
F10: 'F10',
|
||||
F11: 'F11',
|
||||
F12: 'F12',
|
||||
// Special keys
|
||||
Space: 'Space',
|
||||
Enter: 'Enter',
|
||||
Tab: 'Tab',
|
||||
Escape: 'Escape',
|
||||
Backspace: 'Backspace',
|
||||
Delete: 'Delete',
|
||||
ArrowUp: '↑',
|
||||
ArrowDown: '↓',
|
||||
ArrowLeft: '←',
|
||||
ArrowRight: '→',
|
||||
Home: 'Home',
|
||||
End: 'End',
|
||||
PageUp: 'PageUp',
|
||||
PageDown: 'PageDown',
|
||||
Insert: 'Insert',
|
||||
// Punctuation
|
||||
Minus: '-',
|
||||
Equal: '=',
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Backslash: '\\',
|
||||
Semicolon: ';',
|
||||
Quote: "'",
|
||||
Backquote: '`',
|
||||
Comma: ',',
|
||||
Period: '.',
|
||||
Slash: '/'
|
||||
}
|
||||
return keyMap[this.key] || this.key
|
||||
}
|
||||
}
|
||||
|
||||
export const useKeybindingStore = defineStore('keybinding', () => {
|
||||
|
||||
@@ -66,7 +66,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
|
||||
it('should execute ExitSubgraph command when Escape is pressed', async () => {
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
@@ -83,7 +83,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
|
||||
it('should not execute command when Escape is pressed with modifiers', async () => {
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
ctrlKey: true,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
@@ -100,7 +100,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
it('should not execute command when typing in input field', async () => {
|
||||
const inputElement = document.createElement('input')
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
@@ -130,7 +130,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
document.body.appendChild(dialog)
|
||||
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
@@ -158,7 +158,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
)
|
||||
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
@@ -185,7 +185,7 @@ describe('keybindingService - Escape key handling', () => {
|
||||
keybindingService = useKeybindingService()
|
||||
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user