Skip to content

Commit df28670

Browse files
authored
Merge pull request #117 from github/fix-hotkey-tool
Fix hotkey mapper tool
2 parents 70c43ec + 36a9884 commit df28670

File tree

2 files changed

+85
-72
lines changed

2 files changed

+85
-72
lines changed

pages/hotkey_mapper.html

Lines changed: 84 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,105 @@
11
<!doctype html>
22
<html lang="en">
3-
4-
<head>
5-
<meta charset="utf-8">
6-
<meta name="viewport" content="width=device-width">
7-
<title>hotkey | Mapper Tool</title>
8-
<link href="https://unpkg.com/@primer/css@^21.0.8/dist/primer.css" rel="stylesheet" />
9-
<script type="module" src="https://unpkg.com/@github/clipboard-copy-element@latest?module"></script>
10-
</head>
11-
12-
<body>
13-
<div class="mx-auto my-3 col-12 col-md-8 col-lg-6">
14-
<h1 id="app-name">Hotkey Code</h1>
15-
<p id="hint">Press a key combination to see the corresponding hotkey string. Quickly press another combination to build a sequence.</p>
16-
<div class="position-relative">
17-
<input
18-
readonly
19-
role="application"
20-
aria-roledescription="Input Capture"
21-
autofocus
22-
aria-labelledby="app-name"
23-
aria-describedby="hint sequence-hint"
24-
aria-live="assertive"
25-
aria-atomic="true"
26-
id="hotkey-code"
27-
class="border rounded-2 mt-2 p-6 f1 text-mono"
28-
style="width: 100%"
29-
/>
30-
31-
<div class="position-absolute bottom-2 left-3 right-3 d-flex" style="align-items: center; gap: 8px">
32-
<!-- This indicates that the input is listening for a sequence press. Ideally we'd have a way to tell screen
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width" />
6+
<title>hotkey | Mapper Tool</title>
7+
<link crossorigin="anonymous" href="https://unpkg.com/@primer/css@^21.0.8/dist/primer.css" rel="stylesheet" />
8+
<script type="module" src="https://unpkg.com/@github/clipboard-copy-element@latest?module"></script>
9+
</head>
10+
11+
<body>
12+
<div class="mx-auto my-3 col-12 col-md-8 col-lg-6">
13+
<h1 id="app-name">Hotkey Code</h1>
14+
<p id="hint">
15+
Press a key combination to see the corresponding hotkey string. Quickly press another combination to build a
16+
sequence.
17+
</p>
18+
<div class="position-relative">
19+
<input
20+
readonly
21+
role="application"
22+
aria-roledescription="Input Capture"
23+
autofocus
24+
aria-labelledby="app-name"
25+
aria-describedby="hint sequence-hint"
26+
aria-live="assertive"
27+
aria-atomic="true"
28+
id="hotkey-code"
29+
class="border rounded-2 mt-2 p-6 f1 text-mono"
30+
style="width: 100%"
31+
/>
32+
33+
<div class="position-absolute bottom-2 left-3 right-3 d-flex" style="align-items: center; gap: 8px">
34+
<!-- This indicates that the input is listening for a sequence press. Ideally we'd have a way to tell screen
3335
readers this too, but if we make this live and add more text it will get annoying because it will conflict
3436
with the already-live input above. -->
35-
<p id="sequence-status" class="color-fg-subtle" style="margin: 0" aria-hidden hidden></p>
37+
<p id="sequence-status" class="color-fg-subtle" style="margin: 0" aria-hidden hidden></p>
3638

37-
<span style="flex: 1"></span>
39+
<span style="flex: 1"></span>
3840

39-
<button id="reset-button" class="btn">Reset</button>
41+
<button id="reset-button" class="btn">Reset</button>
4042

41-
<clipboard-copy for="hotkey-code" class="btn">
42-
Copy to clipboard
43-
</clipboard-copy>
43+
<clipboard-copy for="hotkey-code" class="btn"> Copy to clipboard </clipboard-copy>
44+
</div>
4445
</div>
4546
</div>
46-
</div>
4747

48-
<script type="module">
49-
import {eventToHotkeyString} from './hotkey/index.js'
50-
import {SEQUENCE_DELIMITER, SequenceTracker} from './hotkey/sequence.js'
48+
<script type="module">
49+
import {eventToHotkeyString} from './hotkey/index.js'
50+
import {SEQUENCE_DELIMITER, SequenceTracker} from './hotkey/sequence.js'
51+
52+
const hotkeyCodeElement = document.getElementById('hotkey-code')
53+
const sequenceStatusElement = document.getElementById('sequence-status')
54+
const resetButtonElement = document.getElementById('reset-button')
55+
56+
const sequenceTracker = new SequenceTracker({
57+
onReset() {
58+
sequenceStatusElement.hidden = true
59+
}
60+
})
61+
62+
const modifierKeyNames = ['Alt', 'Control', 'Meta', 'Shift']
5163

52-
const hotkeyCodeElement = document.getElementById('hotkey-code')
53-
const sequenceStatusElement = document.getElementById('sequence-status')
54-
const resetButtonElement = document.getElementById('reset-button')
64+
// we only want to add lone modifier keys to the sequence if they are lifted without pressing another key
65+
// otherwise, the chord Control+f would become "Control Control+f". But we do want to be able to display sequences
66+
// like "Control f", which is pressing and releasing control, then f.
67+
let lastKeyDownModifierEvent = null
5568

56-
const sequenceTracker = new SequenceTracker({
57-
onReset() {
58-
sequenceStatusElement.hidden = true
69+
const registerEvent = event => {
70+
sequenceTracker.registerKeypress(event)
71+
sequenceStatusElement.hidden = false
72+
73+
event.target.value = sequenceTracker.path.join(SEQUENCE_DELIMITER)
5974
}
60-
})
6175

62-
let currentsequence = null
76+
hotkeyCodeElement.addEventListener('keydown', event => {
77+
lastKeyDownModifierEvent = null
6378

64-
hotkeyCodeElement.addEventListener('keydown', event => {
65-
if (event.key === "Tab")
66-
return;
79+
// always leave Tab alone so focus can leave the input
80+
if (event.key === 'Tab') return
6781

68-
event.preventDefault();
69-
event.stopPropagation();
82+
event.preventDefault()
83+
event.stopPropagation()
7084

71-
currentsequence = eventToHotkeyString(event)
72-
event.currentTarget.value = [...sequenceTracker.path, currentsequence].join(SEQUENCE_DELIMITER);
73-
})
85+
if (modifierKeyNames.includes(event.key)) {
86+
lastKeyDownModifierEvent = event
87+
return
88+
}
7489

75-
hotkeyCodeElement.addEventListener('keyup', () => {
76-
// we don't just build the sequence from the keyup event because keyups don't necessarily map to keydowns - for
77-
// example, the keyup event for meta+b is just meta.
78-
if (currentsequence) {
79-
sequenceTracker.registerKeypress(currentsequence)
80-
sequenceStatusElement.hidden = false
81-
currentsequence = null
82-
}
83-
})
90+
registerEvent(event)
91+
})
8492

85-
resetButtonElement.addEventListener('click', () => {
86-
sequenceTracker.reset()
87-
hotkeyCodeElement.value = ''
88-
})
89-
</script>
90-
</body>
93+
hotkeyCodeElement.addEventListener('keyup', () => {
94+
// we still have to use the keydown event, not the keyup event, because the keyup event won't have the modifier
95+
// properties (ie, `ctrlKey` will be false).
96+
if (lastKeyDownModifierEvent) registerEvent(lastKeyDownModifierEvent)
97+
})
9198

99+
resetButtonElement.addEventListener('click', () => {
100+
sequenceTracker.reset()
101+
hotkeyCodeElement.value = ''
102+
})
103+
</script>
104+
</body>
92105
</html>

pages/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta charset="utf-8">
66
<meta name="viewport" content="width=device-width">
77
<title>hotkey</title>
8-
<link href="https://unpkg.com/@primer/css@^16.0.0/dist/primer.css" rel="stylesheet" />
8+
<link crossorigin="anonymous" href="https://unpkg.com/@primer/css@^16.0.0/dist/primer.css" rel="stylesheet" />
99
</head>
1010

1111
<body>

0 commit comments

Comments
 (0)