Skip to content

fix(input): match Shift+letter bindings when terminal omits SHIFT modifier#2001

Open
sinelaw wants to merge 1 commit into
masterfrom
claude/jolly-archimedes-Z0ZUq
Open

fix(input): match Shift+letter bindings when terminal omits SHIFT modifier#2001
sinelaw wants to merge 1 commit into
masterfrom
claude/jolly-archimedes-Z0ZUq

Conversation

@sinelaw
Copy link
Copy Markdown
Owner

@sinelaw sinelaw commented May 16, 2026

Summary

Fixes #1899.

Without kitty keyboard protocol, terminals typically encode Shift+letter by sending the uppercase character alone — no SHIFT modifier accompanies it. The previous normalize_key lowercased the char but kept the empty modifier set, so a binding stored as (Char('p'), SHIFT) never matched the incoming (Char('P'), NONE) event.

As reported in #1899, this made Shift+letter shortcuts only fire while Caps Lock was on (which causes some terminals to invert the case and re-introduce the SHIFT modifier on lowercase letters). The same root cause meant the keybinding editor's add-binding dialog captured Shift+P as plain p, losing the shift information at save time.

Approach

  • normalize_key now infers SHIFT when an uppercase letter arrives with no other modifiers. The CapsLock+Ctrl+letterCtrl+letter collapse is preserved (uppercase + CONTROL is intentionally not given a SHIFT because that case is ambiguous between caps lock and a real shift, and the existing behavior is to fold caps lock away).
  • The add-binding dialog (view/keybinding_editor.rs) and the record-key search (app/keybinding_editor/editor.rs) route the recorded event through normalize_key so they capture Shift+P consistently — no matter whether the terminal reported SHIFT.
  • The fallback to InsertChar is unaffected: it uses the original event, so typing capital Z in a buffer still inserts Z.

Tests

  • test_shift_letter_binding_works_without_terminal_shift_modifier — the core regression: Char('P') with no modifier now matches a Shift+P binding.
  • test_capslock_ctrl_letter_still_matches_ctrl_letter_binding — guards the existing CapsLock+Ctrl+A → Ctrl+A behavior.
  • test_uppercase_without_binding_falls_through_to_insert_char — confirms typing capital letters still inserts them when no binding exists.
  • record_search_key_normalizes_uppercase_letter_to_shift — the record-key search captures the SHIFT modifier even when the terminal omits it.
  • add_dialog_records_shift_when_terminal_omits_shift_modifier / add_dialog_records_shift_when_terminal_includes_shift_modifier / add_dialog_preserves_ctrl_when_capturing_upper_letter — full end-to-end dialog drive: send the simulated event through handle_keybinding_editor_input and assert the captured (key_code, modifiers).

Full lib test suite: 2468 passed, 0 failed.

Manual validation

Reproduced and verified the fix end-to-end:

  1. Added a Shift+P → save binding in ~/.config/fresh/config.json.
  2. Opened a modified buffer (Xinitial content) in fresh under tmux.
  3. Pressed P. The status bar transitioned to Saved, the unsaved-changes marker [+] and the buffer's * cleared, and the file on disk reflects the new content.

Test plan

  • cargo test -p fresh-editor --lib
  • cargo check --all-targets
  • cargo check --all-targets --features gui
  • cargo fmt --check
  • Manual smoke test: Shift+P triggers save via custom binding.

https://claude.ai/code/session_01H6FpwGdLrCA7zquAbVrhX9


Generated by Claude Code

…ifier

Without kitty keyboard protocol, terminals typically encode Shift+letter
by sending the uppercase character alone — no SHIFT modifier accompanies
it. The previous normalization lowercased the char but kept the empty
modifier set, so a binding stored as `(Char('p'), SHIFT)` never matched
the incoming `(Char('P'), NONE)` event. As reported in #1899, this made
Shift+letter shortcuts only fire while Caps Lock was on (which causes
some terminals to invert the case and re-introduce the SHIFT modifier).

When an uppercase letter arrives with no other modifiers, infer SHIFT
during normalization. Leave the existing CapsLock+Ctrl+letter path alone
(uppercase + CONTROL) so it continues to fold to Ctrl+letter as before.

The same fix is applied to the keybinding editor recording paths — the
"Add binding" dialog and the record-key search — so that pressing
Shift+P in a non-kitty terminal saves a `Shift+P` binding rather than a
plain `p` one.

https://claude.ai/code/session_01H6FpwGdLrCA7zquAbVrhX9
@sinelaw
Copy link
Copy Markdown
Owner Author

sinelaw commented May 16, 2026

In my terminal (xfce terminal) when enabling caps lock the editor receives only modifier "shift", i.e. "shift"+"A"

@sinelaw sinelaw added the question Further information is requested label May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

question Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keybindings utilizing SHIFT don't work + keybinding editor fails to record/assign hotkeys utilizing the SHIFT key unless caps lock is on

2 participants