-
Notifications
You must be signed in to change notification settings - Fork 0
Fix/stale key state #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,12 +80,24 @@ const maybeHandleMacOsCommandKeyReleased = (event: KeyboardEvent) => { | |
|
|
||
| const activeKeyEvents = new Map<string, KeyboardEvent>() | ||
|
|
||
| // Keyed by the physical key rather than the reported label as this not stable. See README. | ||
| const keyEventIdentity = (event: KeyboardEvent) => event.code || event.key | ||
|
|
||
| // Composition keydowns (IME) and the placeholder keys some autofill | ||
| // implementations emit never receive a matching keyup, so tracking them could | ||
| // only ever strand a key. Note we never filter keyups: those can only ever | ||
| // remove state, and dropping one is what strands a key in the first place. | ||
| const cannotBeReleased = (event: KeyboardEvent) => | ||
| event.isComposing === true || | ||
| event.key === 'Process' || | ||
| event.key === 'Unidentified' | ||
|
|
||
| const addActiveKeyEvent = (event: KeyboardEvent) => { | ||
| activeKeyEvents.set(event.key, event) | ||
| activeKeyEvents.set(keyEventIdentity(event), event) | ||
| } | ||
|
|
||
| const removeActiveKeyEvent = (event: KeyboardEvent) => { | ||
| activeKeyEvents.delete(event.key) | ||
| activeKeyEvents.delete(keyEventIdentity(event)) | ||
| } | ||
|
|
||
| const dispatchKeyUpForAllActiveKeys = () => { | ||
|
|
@@ -118,8 +130,24 @@ export const browserOnInactiveBinder: OnActiveEventBinder = (handler) => { | |
| handler() | ||
| } | ||
|
|
||
| // blur does not cover every way a page can stop receiving key events, and any | ||
| // keyup delivered elsewhere in the meantime is lost. | ||
| const visibilityHandler = () => { | ||
| const doc = getDoc() as Partial<Document> | ||
| if (doc.visibilityState === 'hidden') handlerWrapper() | ||
| } | ||
|
|
||
| addEventListener('blur', handlerWrapper) | ||
| return () => removeEventListener('blur', handlerWrapper) | ||
| addEventListener('pagehide', handlerWrapper) | ||
| // visibilitychange is fired at the document, but it bubbles, so listening on | ||
| // the window keeps the document listener order untouched. | ||
| addEventListener('visibilitychange', visibilityHandler) | ||
|
|
||
| return () => { | ||
| removeEventListener('blur', handlerWrapper) | ||
| removeEventListener('pagehide', handlerWrapper) | ||
| removeEventListener('visibilitychange', visibilityHandler) | ||
| } | ||
|
Comment on lines
+133
to
+150
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new clean up handlers to reset recorded keys when page is left / blurred |
||
| } catch {} | ||
| } | ||
|
|
||
|
|
@@ -129,12 +157,15 @@ export const browserOnKeyPressedBinder: OnKeyEventBinder< | |
| > = (handler) => { | ||
| try { | ||
| const handlerWrapper = (e: KeyboardEvent) => { | ||
| if (cannotBeReleased(e)) return | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. guard against autofill / ime events |
||
|
|
||
| addActiveKeyEvent(e) | ||
| maybeHandleMacOsCommandKeyPressed(e) | ||
|
|
||
| handler({ | ||
| key: e.key, | ||
| aliases: [`@${e.code}`], | ||
| identity: e.code || undefined, | ||
| originalEvent: e, | ||
| composedPath: () => e.composedPath(), | ||
| preventDefault: () => e.preventDefault(), | ||
|
|
@@ -157,6 +188,7 @@ export const browserOnKeyReleasedBinder: OnKeyEventBinder< | |
| handler({ | ||
| key: e.key, | ||
| aliases: [`@${e.code}`], | ||
| identity: e.code || undefined, | ||
| originalEvent: e, | ||
| composedPath: () => e.composedPath(), | ||
| preventDefault: () => e.preventDefault(), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,10 @@ export const checkKey: typeof globalKeystrokes.checkKey = (...args) => | |
| export const checkKeyCombo: typeof globalKeystrokes.checkKeyCombo = (...args) => | ||
| getGlobalKeystrokes().checkKeyCombo(...args) | ||
|
|
||
| export const releaseAllKeys: typeof globalKeystrokes.releaseAllKeys = ( | ||
| ...args | ||
| ) => getGlobalKeystrokes().releaseAllKeys(...args) | ||
|
|
||
|
Comment on lines
+76
to
+79
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just let the lib expose releaseAllKeys for WK to be able to execute this if useful / needed |
||
| export const normalizeKeyCombo = KeyComboState.normalizeKeyCombo | ||
| export const stringifyKeyCombo = KeyComboState.stringifyKeyCombo | ||
| export const parseKeyCombo = KeyComboState.parseKeyCombo | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guarding IME / browser autofill inputs