fix(desktop): restore mod-chord keybinds while typing in inputs
#86586 replaced the combo-based input gate (any Cmd/Ctrl chord fires while typing) with an action allowlist that dropped session.new and every other mod-chord not explicitly listed. ⌘N/⌘T/⌘⇧N and friends became dead keys whenever focus was in the composer. Restore the pre-regression rule: primary-modifier chords stay global even in text fields; the allowlist now gates only bare/Shift/Alt combos, so rebound letter keys can never hijack typing. Text-navigation chords (Ctrl+Arrow/PgUp/PgDn) still stay with the input.
This commit is contained in:
@@ -137,16 +137,36 @@ describe('formatCombo — honest Control labels', () => {
|
||||
})
|
||||
|
||||
describe('actionAllowedInInput', () => {
|
||||
it('keeps only explicit text-entry-safe global actions active while typing', async () => {
|
||||
it('keeps primary-modifier chords global while typing, gating bare/Shift combos to the allowlist', async () => {
|
||||
const { actionAllowedInInput } = await loadCombo('MacIntel')
|
||||
|
||||
// Mod/Ctrl chords are deliberate two-key gestures — they fire even with
|
||||
// focus in the composer (⌘N, ⌘T, ⌘⇧N, ⌘K, ⌃Tab, …), matching every browser
|
||||
// and chat app and the pre-#86586 behavior.
|
||||
expect(actionAllowedInInput('session.new', 'mod+n')).toBe(true)
|
||||
expect(actionAllowedInInput('session.newTab', 'mod+t')).toBe(true)
|
||||
expect(actionAllowedInInput('session.newWindow', 'mod+shift+n')).toBe(true)
|
||||
expect(actionAllowedInInput('session.next', 'ctrl+tab')).toBe(true)
|
||||
expect(actionAllowedInInput('session.prev', 'ctrl+shift+tab')).toBe(true)
|
||||
expect(actionAllowedInInput('nav.commandPalette', 'mod+k')).toBe(true)
|
||||
expect(actionAllowedInInput('view.findInPage', 'mod+f')).toBe(true)
|
||||
expect(actionAllowedInInput('nav.skills', 'mod+k')).toBe(false)
|
||||
expect(actionAllowedInInput('view.showTerminal', 'ctrl+`')).toBe(false)
|
||||
expect(actionAllowedInInput('profile.next', 'mod+shift+]')).toBe(false)
|
||||
expect(actionAllowedInInput('nav.skills', 'mod+k')).toBe(true)
|
||||
expect(actionAllowedInInput('view.showTerminal', 'ctrl+`')).toBe(true)
|
||||
expect(actionAllowedInInput('profile.next', 'mod+shift+]')).toBe(true)
|
||||
|
||||
// A global action rebound onto a text-editing chord (Ctrl+A select-all,
|
||||
// Ctrl+E line-end, …) fires deliberately while typing — mod-chords are
|
||||
// two-key gestures, the pre-#86586 semantic. Unbound chords (the shipped
|
||||
// default for a/e/u/backspace) never reach this function: the dispatcher
|
||||
// returns before the gate when no action is bound, so the input keeps its
|
||||
// native editing behavior out of the box. An editing-chord exclusion set
|
||||
// would have to dodge shipped defaults (⌘K, ⌘W, ⌘D, ⌘F, ⌘B) or re-break them.
|
||||
expect(actionAllowedInInput('session.new', 'ctrl+a')).toBe(true)
|
||||
|
||||
// Bare/Shift-only combos must never hijack typing: a rebound ⌘N → 'n'
|
||||
// (or the pre-#76185 'shift+n') must not fire while the user types N.
|
||||
expect(actionAllowedInInput('session.new', 'n')).toBe(false)
|
||||
expect(actionAllowedInInput('session.new', 'shift+n')).toBe(false)
|
||||
})
|
||||
|
||||
it('leaves text navigation chords with the focused input even when rebound to an allowed action', async () => {
|
||||
|
||||
@@ -260,9 +260,13 @@ const INPUT_SAFE_ACTIONS = new Set([
|
||||
|
||||
const TEXT_NAVIGATION_KEYS = new Set(['up', 'down', 'left', 'right', 'home', 'end', 'pageup', 'pagedown'])
|
||||
|
||||
// Only explicit text-entry-safe actions fire while typing. Editing/navigation
|
||||
// chords such as Ctrl+Arrow/PageUp must stay with the input even if a user
|
||||
// rebinds them to a global navigation action.
|
||||
// Only explicit text-entry-safe actions fire while typing. A primary-modifier
|
||||
// chord (Cmd/Ctrl) is a deliberate two-key gesture that every browser and chat
|
||||
// app fires even with focus in a text field (⌘N, ⌘T, ⌘K, ⌃Tab…), so those stay
|
||||
// global — restoring the pre-#86586 behavior. Editing/navigation chords such
|
||||
// as Ctrl+Arrow/PageUp must stay with the input even if a user rebinds them to
|
||||
// a global navigation action, and bare/Shift-only combos (typed letters) are
|
||||
// gated by the allowlist so they never hijack normal typing.
|
||||
export function actionAllowedInInput(actionId: string, combo: string): boolean {
|
||||
const base = combo.split('+').pop()
|
||||
|
||||
@@ -270,5 +274,9 @@ export function actionAllowedInInput(actionId: string, combo: string): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if (/^(?:mod|ctrl)(?:\+|$)/.test(combo)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return INPUT_SAFE_ACTIONS.has(actionId)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user