Skip to content

Commit 8b64334

Browse files
authored
Merge pull request #110 from github/mac-alt-support
Add support for alt/option on Mac via symbol map
2 parents 49b6479 + 955474c commit 8b64334

File tree

3 files changed

+114
-5
lines changed

3 files changed

+114
-5
lines changed

src/hotkey.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {NormalizedSequenceString} from './sequence'
2+
import {macosSymbolLayerKeys} from './macos-symbol-layer'
23

34
const normalizedHotkeyBrand = Symbol('normalizedHotkey')
45

@@ -28,7 +29,10 @@ export type NormalizedHotkeyString = NormalizedSequenceString & {[normalizedHotk
2829
* if (eventToHotkeyString(event) === 'h') ...
2930
* })
3031
*/
31-
export function eventToHotkeyString(event: KeyboardEvent): NormalizedHotkeyString {
32+
export function eventToHotkeyString(
33+
event: KeyboardEvent,
34+
platform: string = navigator.platform
35+
): NormalizedHotkeyString {
3236
const {ctrlKey, altKey, metaKey, key} = event
3337
const hotkeyString: string[] = []
3438
const modifiers: boolean[] = [ctrlKey, altKey, metaKey, showShift(event)]
@@ -38,7 +42,8 @@ export function eventToHotkeyString(event: KeyboardEvent): NormalizedHotkeyStrin
3842
}
3943

4044
if (!modifierKeyNames.includes(key)) {
41-
hotkeyString.push(key)
45+
const nonOptionPlaneKey = matchApplePlatform.test(platform) ? macosSymbolLayerKeys[key] ?? key : key
46+
hotkeyString.push(nonOptionPlaneKey)
4247
}
4348

4449
return hotkeyString.join('+') as NormalizedHotkeyString

src/macos-symbol-layer.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Map of special symbols to the keys that would be pressed (while holding `Option`) to type them on MacOS on an
3+
* English layout. Most of these are standardized across most language layouts, so this won't work 100% in every
4+
* language but it should work most of the time.
5+
*/
6+
export const macosSymbolLayerKeys: Record<string, string> = {
7+
['¡']: '1',
8+
['™']: '2',
9+
['£']: '3',
10+
['¢']: '4',
11+
['∞']: '5',
12+
['§']: '6',
13+
['¶']: '7',
14+
['•']: '8',
15+
['ª']: '9',
16+
['º']: '0',
17+
['–']: '-',
18+
['≠']: '=',
19+
['`']: '~',
20+
['⁄']: '!',
21+
['€']: '@',
22+
['‹']: '#',
23+
['›']: '$',
24+
['fi']: '%',
25+
['fl']: '^',
26+
['‡']: '&',
27+
['°']: '*',
28+
['·']: '(',
29+
['‚']: ')',
30+
['—']: '_',
31+
['±']: '+',
32+
['œ']: 'q',
33+
['∑']: 'w',
34+
['®']: 'r',
35+
['†']: 't',
36+
['¥']: 'y',
37+
['ø']: 'o',
38+
['π']: 'p',
39+
['“']: '[',
40+
['‘']: ']',
41+
['«']: '\\',
42+
['Œ']: 'Q',
43+
['„']: 'W',
44+
['´']: 'E',
45+
['‰']: 'R',
46+
['ˇ']: 'T',
47+
['Á']: 'Y',
48+
['¨']: 'U',
49+
['ˆ']: 'I',
50+
['Ø']: 'O',
51+
['∏']: 'P',
52+
['”']: '{',
53+
['’']: '}',
54+
['»']: '|',
55+
['å']: 'a',
56+
['ß']: 's',
57+
['∂']: 'd',
58+
['ƒ']: 'f',
59+
['©']: 'g',
60+
['˙']: 'h',
61+
['∆']: 'j',
62+
['˚']: 'k',
63+
['¬']: 'l',
64+
['…']: ';',
65+
['æ']: "'",
66+
['Å']: 'A',
67+
['Í']: 'S',
68+
['Î']: 'D',
69+
['Ï']: 'F',
70+
['˝']: 'G',
71+
['Ó']: 'H',
72+
['Ô']: 'J',
73+
['']: 'K',
74+
['Ò']: 'L',
75+
['Ú']: ':',
76+
['Æ']: '"',
77+
['Ω']: 'z',
78+
['≈']: 'x',
79+
['ç']: 'c',
80+
['√']: 'v',
81+
['∫']: 'b',
82+
['µ']: 'm',
83+
['≤']: ',',
84+
['≥']: '.',
85+
['÷']: '/',
86+
['¸']: 'Z',
87+
['˛']: 'X',
88+
['Ç']: 'C',
89+
['◊']: 'V',
90+
['ı']: 'B',
91+
['˜']: 'N',
92+
['Â']: 'M',
93+
['¯']: '<',
94+
['˘']: '>',
95+
['¿']: '?'
96+
}

test/test.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,21 @@ describe('hotkey', function () {
276276
['S', {key: 'S', shiftKey: true, code: 'KeyS'}],
277277
['!', {key: '!', shiftKey: true, code: 'KeyS'}],
278278
['Control+Shift', {ctrlKey: true, shiftKey: true, key: 'Shift'}],
279-
['Control+Shift', {ctrlKey: true, shiftKey: true, key: 'Control'}]
279+
['Control+Shift', {ctrlKey: true, shiftKey: true, key: 'Control'}],
280+
['Alt+s', {altKey: true, key: 's'}],
281+
['Alt+s', {altKey: true, key: 'ß'}, 'mac'],
282+
['Alt+Shift+S', {altKey: true, shiftKey: true, key: 'S'}],
283+
['Alt+Shift+S', {altKey: true, shiftKey: true, key: 'Í'}, 'mac'],
284+
['Alt+ArrowLeft', {altKey: true, key: 'ArrowLeft'}],
285+
['Alt+ArrowLeft', {altKey: true, key: 'ArrowLeft'}, 'mac'],
286+
['Alt+Shift+ArrowLeft', {altKey: true, shiftKey: true, key: 'ArrowLeft'}],
287+
['Alt+Shift+ArrowLeft', {altKey: true, shiftKey: true, key: 'ArrowLeft'}, 'mac']
280288
]
281-
for (const [expected, keyEvent] of tests) {
289+
for (const [expected, keyEvent, platform = 'win / linux'] of tests) {
282290
it(`${JSON.stringify(keyEvent)} => ${expected}`, function (done) {
283291
document.body.addEventListener('keydown', function handler(event) {
284292
document.body.removeEventListener('keydown', handler)
285-
assert.equal(eventToHotkeyString(event), expected)
293+
assert.equal(eventToHotkeyString(event, platform), expected)
286294
done()
287295
})
288296
document.body.dispatchEvent(new KeyboardEvent('keydown', keyEvent))

0 commit comments

Comments
 (0)