Skip to content

feat: add configurable keyboard shortcuts (#211) - #226

Merged
matheuswhite merged 1 commit into
mainfrom
feat/211-custom-shortcuts
Jul 24, 2026
Merged

feat: add configurable keyboard shortcuts (#211)#226
matheuswhite merged 1 commit into
mainfrom
feat/211-custom-shortcuts

Conversation

@matheuswhite

Copy link
Copy Markdown
Owner

What

Closes #211. Adds an optional [shortcuts] table to config.toml that lets users remap the action/navigation keyboard shortcuts, merged over the built-in per-platform defaults (an omitted action keeps its default).

[shortcuts]
record        = "Ctrl+G"   # move record off Ctrl+R
next_bookmark = "F2"
prev_bookmark = "F3"

15 remappable actions: copy, clear, save, record, search_toggle, toggle_case, toggle_regex, page_up, page_down, jump_start, jump_end, word_left, word_right, next_bookmark, prev_bookmark. Text-editing/control keys (typing, Enter, Esc, arrows, Home/End, Backspace/Delete, headless Ctrl+K/Ctrl+Q) stay fixed.

How

  • src/inputs/keymap.rs (new)Action / KeyBinding / Keymap own all key knowledge; config.rs stays a plain BTreeMap<String,String> and never enumerates the action set.
  • Logical modifiers — config combos ("Ctrl+Left", "Shift+Tab", "F2") are parsed once into the physical crossterm event of the build target, so a config file is portable: the macOS Ctrl+arrow → Alt+arrow terminal quirk is handled in resolve, and the Windows jump_* defaults are Ctrl+PageUp/Down (vs Alt elsewhere). Matching is exact on modifiers, case-insensitive on chars. A default_matches_current_arms test proves the default keymap reproduces the old hardcoded arms per platform.
  • handle_key_input resolves a remappable action before the intrinsic key match; a Tab-bound next_bookmark falls through to @tag autocomplete while the pop-up is up. The old CTRL_MODIFIER/ACTION_MODIFIER consts are gone (only ACTION_MODIFIER, for the fixed Alt+Enter arm, remains).
  • Validation — unknown action names, unparseable combos, reserved-key overrides and duplicate bindings are fatal config errors routed through main's existing [ERR] funnel.

Behavior change

An unbound Ctrl/Alt+letter is now swallowed rather than typed literally (e.g. Ctrl+C after copy is remapped away). AltGr (Ctrl+Alt together, the Windows international-layout signature) still types.

Note: terminals send Ctrl+I/Ctrl+J/Ctrl+M as Tab/Enter, so those combos can't be used as shortcuts (documented).

Tests

  • keymap.rs unit tests: parser round-trips & rejections, per-platform resolve, exact/case-fold matcher, merge/swap, unknown/reserved/duplicate errors, and the anti-regression default-parity table.
  • config.rs: [shortcuts] parsing.
  • inputs_task.rs: the char_is_literal_text typing/AltGr predicate.
  • tests/tui_e2e.rs: a config-driven remap (record → Ctrl+G, proving override + old-key disabled) and a headless Ctrl+F-swallow test. The e2e harness now isolates the config dir to a temp tree.

All 226 unit + 14 e2e tests pass (1 e2e ignored on macOS per the existing PTY-baud limitation); tree is warning-clean under #![deny(warnings)]. Docs updated (README shortcut table gains a "Config action" column + a "Custom shortcuts" section; CLAUDE.md).

🤖 Generated with Claude Code

Add an optional `[shortcuts]` table to `config.toml` that remaps the 15
action/navigation keys (copy, clear, save, record, search_toggle,
toggle_case, toggle_regex, page_up/down, jump_start/end, word_left/right,
next/prev_bookmark), merged over the built-in per-platform defaults.

A new `inputs/keymap.rs` (Action / KeyBinding / Keymap) owns all key
knowledge: config modifier names are logical and resolved once to the
physical crossterm event of the build target (macOS Ctrl+arrow -> Alt,
Windows jump defaults on Ctrl), and matching is exact. `handle_key_input`
resolves a remappable action before the intrinsic key match, with the
Tab-bound next_bookmark yielding to `@tag` autocomplete. Text-editing and
control keys stay fixed and are rejected as override targets. Unknown
actions, bad key strings, reserved keys and duplicate bindings are fatal
config errors. An unbound Ctrl/Alt+letter is now swallowed instead of typed
(AltGr, i.e. Ctrl+Alt together, still types).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@matheuswhite
matheuswhite merged commit 5c4d340 into main Jul 24, 2026
8 checks passed
@matheuswhite
matheuswhite deleted the feat/211-custom-shortcuts branch July 24, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom shortcuts file

1 participant