Skip to content

Commit 1fd540a

Browse files
committed
Settings: Fix ⌥+key shortcuts showed "Dead" on mac
1 parent 47050e0 commit 1fd540a

2 files changed

Lines changed: 51 additions & 2 deletions

File tree

apps/desktop/src/lib/shortcuts/key-capture.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,30 @@ describe('formatKeyCombo', () => {
118118
const result = formatKeyCombo(makeKeyEvent({ altKey: true, key: 'F2' }))
119119
expect(result).toBe('Alt+F2')
120120
})
121+
122+
it('resolves Dead key via event.code for ⌥+letter on macOS', () => {
123+
setMacOS(true)
124+
const result = formatKeyCombo(makeKeyEvent({ altKey: true, key: 'Dead', code: 'KeyH' }))
125+
expect(result).toBe('⌥H')
126+
})
127+
128+
it('resolves Dead key via event.code for ⌥+digit on macOS', () => {
129+
setMacOS(true)
130+
const result = formatKeyCombo(makeKeyEvent({ altKey: true, key: 'Dead', code: 'Digit6' }))
131+
expect(result).toBe('⌥6')
132+
})
133+
134+
it('resolves Dead key via event.code for ⌥+punctuation on macOS', () => {
135+
setMacOS(true)
136+
const result = formatKeyCombo(makeKeyEvent({ altKey: true, key: 'Dead', code: 'BracketLeft' }))
137+
expect(result).toBe('⌥[')
138+
})
139+
140+
it('resolves Dead key with multiple modifiers on macOS', () => {
141+
setMacOS(true)
142+
const result = formatKeyCombo(
143+
makeKeyEvent({ metaKey: true, altKey: true, key: 'Dead', code: 'KeyE' }),
144+
)
145+
expect(result).toBe('⌘⌥E')
146+
})
121147
})

apps/desktop/src/lib/shortcuts/key-capture.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,34 @@ const nonMacKeyNames: Record<string, string> = {
4646
End: 'End',
4747
}
4848

49+
/** Maps event.code to a display character for physical keys (used when event.key is "Dead") */
50+
const codeToKey: Record<string, string> = {
51+
Minus: '-',
52+
Equal: '=',
53+
BracketLeft: '[',
54+
BracketRight: ']',
55+
Backslash: '\\',
56+
Semicolon: ';',
57+
Quote: "'",
58+
Backquote: '`',
59+
Comma: ',',
60+
Period: '.',
61+
Slash: '/',
62+
}
63+
4964
/**
5065
* Normalize a key name for display.
5166
* Single characters are uppercased, special keys are mapped.
5267
*/
53-
export function normalizeKeyName(key: string): string {
68+
export function normalizeKeyName(key: string, code?: string): string {
69+
// On macOS, Option+key often produces "Dead" — fall back to the physical key via event.code
70+
if (key === 'Dead' && code) {
71+
const match = /^Key([A-Z])$/.exec(code) ?? /^Digit(\d)$/.exec(code)
72+
if (match) return match[1]
73+
if (code in codeToKey) return codeToKey[code]
74+
return code // last resort: raw code name
75+
}
76+
5477
// Single printable characters are uppercased
5578
if (key.length === 1 && key !== ' ') {
5679
return key.toUpperCase()
@@ -89,7 +112,7 @@ export function formatKeyCombo(event: KeyboardEvent): string {
89112

90113
// Don't include modifier keys themselves as the main key
91114
if (!isModifierKey(event.key)) {
92-
const key = normalizeKeyName(event.key)
115+
const key = normalizeKeyName(event.key, event.code)
93116
parts.push(key)
94117
}
95118

0 commit comments

Comments
 (0)