Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,58 @@ describe('resolveTerminalShortcutAction', () => {
).toBeNull()
})

it('translates alt+arrow to readline word-nav escapes on both platforms', () => {
// macOS: option+←/→ → \eb / \ef (readline backward-word / forward-word)
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowLeft', code: 'ArrowLeft', altKey: true }),
true
)
).toEqual({ type: 'sendInput', data: '\x1bb' })
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowRight', code: 'ArrowRight', altKey: true }),
true
)
).toEqual({ type: 'sendInput', data: '\x1bf' })

// Linux/Windows: alt+←/→ produces the same escapes (platform-agnostic chord)
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowLeft', code: 'ArrowLeft', altKey: true }),
false
)
).toEqual({ type: 'sendInput', data: '\x1bb' })
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowRight', code: 'ArrowRight', altKey: true }),
false
)
).toEqual({ type: 'sendInput', data: '\x1bf' })

// alt+shift+arrow is a different chord (select-word in some shells) — don't
// intercept, let xterm.js / the shell handle it.
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowLeft', code: 'ArrowLeft', altKey: true, shiftKey: true }),
true
)
).toBeNull()

// alt+ctrl+arrow is a different chord entirely — passthrough.
expect(
resolveTerminalShortcutAction(
event({ key: 'ArrowLeft', code: 'ArrowLeft', altKey: true, ctrlKey: true }),
true
)
).toBeNull()

// Regression guard: plain ArrowLeft must still pass through untouched.
expect(
resolveTerminalShortcutAction(event({ key: 'ArrowLeft', code: 'ArrowLeft' }), true)
).toBeNull()
})

it('keeps Cmd+D and Cmd+Shift+D for split on macOS', () => {
expect(
resolveTerminalShortcutAction(event({ key: 'd', code: 'KeyD', metaKey: true }), true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ export function resolveTerminalShortcutAction(
return { type: 'sendInput', data: '\x1b\x7f' }
}

if (
!event.metaKey &&
!event.ctrlKey &&
event.altKey &&
!event.shiftKey &&
(event.key === 'ArrowLeft' || event.key === 'ArrowRight')
) {
// Why: xterm.js would otherwise emit \e[1;3D / \e[1;3C for option/alt+arrow,
// which default readline (bash, zsh) does not bind to backward-word /
// forward-word — so word navigation silently doesn't work without a custom
// inputrc. Translate to \eb / \ef (readline's default word-nav bindings) so
// option+←/→ on macOS and alt+←/→ on Linux/Windows behave like they do in
// iTerm2's "Esc+" option-key mode. Platform-agnostic: both produce altKey.
return { type: 'sendInput', data: event.key === 'ArrowLeft' ? '\x1bb' : '\x1bf' }
}

// Why: the terminal shortcut layer is an explicit allowlist, not a generic
// "modifier means app shortcut" rule. Keeping this list narrow prevents Orca
// from swallowing readline/emacs control chords like Ctrl+R, Ctrl+U, Ctrl+E,
Expand Down
Loading