Skip to content

fix: handle held-key repeat in modal dispatch - #1972

Open
mudlej wants to merge 1 commit into
herdrdev:masterfrom
mudlej:fix/modal-key-repeat
Open

fix: handle held-key repeat in modal dispatch#1972
mudlej wants to merge 1 commit into
herdrdev:masterfrom
mudlej:fix/modal-key-repeat

Conversation

@mudlej

@mudlej mudlej commented Jul 28, 2026

Copy link
Copy Markdown

Current behavior

Holding a key in any non-terminal mode acts once. Hold Down in the keybinds list or the navigator and the selection moves a single row. Hold Backspace in either search box and one character is deleted. Copy mode has the same problem (#1341, #1387).

Reproduces in Ghostty and Alacritty on macOS and Linux.

Cause

Herdr requests REPORT_EVENT_TYPES, so kitty-protocol terminals report auto-repeat as KeyEventKind::Repeat. But the Repeat arm only reaches a pane or the terminal-mode handler. Nothing routes it into the modal dispatch, so every modal drops it.

Introduced by 10b17a6, which correctly stopped repeats leaking into a pane after a held key changed the mode, but scoped the whole repeat path to terminal mode.

Fix

  • suppressed_repeat_keys: HashSet<_>
  • becomes
  • held_key_repeats: HashMap<_, HeldKeyRepeat> (Blocked | InMode(Mode))

separating the clipboard interception's deliberate block from a press having been handled in a modal. Repeat gains a branch that dispatches to the modal handler when the recorded mode still equals the current one.

Tests

Eleven new tests in src/app/mod.rs covering both input paths, the mode guard, each exclusion, and input-source cleanup. All fail before the change and pass after. The existing repeat-suppression and clipboard Ctrl-C tests are unchanged.

Also checked by hand against 0.7.5 does not move on the repeats, this branch does.

Checks

cargo fmt --check, cargo clippy --all-targets --locked -- -D warnings, just integration-assets-test and just plugin-marketplace-test are clean.

cargo nextest run --locked is 3093/3097. The four failures are auto_detect_default_socket_path_from_config_dir, auto_detect_writes_client_and_server_logs_to_separate_files, no_session_writes_startup_logs_to_monolith_file and server_start_restores_legacy_session_through_api_identity. All four fail identically on an unmodified checkout of the same commit, and none of them send a key event.

Note: Very cool app!

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: df0b2e19-0db4-4c51-895a-2bd77c45519e

📥 Commits

Reviewing files that changed from the base of the PR and between 9bb38db and 176ff61.

📒 Files selected for processing (4)
  • src/app/input/clipboard.rs
  • src/app/input/terminal.rs
  • src/app/mod.rs
  • src/app/runtime.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/input/clipboard.rs
  • src/app/input/terminal.rs
  • src/app/runtime.rs
  • src/app/mod.rs

📝 Walkthrough

Walkthrough

Key repeat handling now uses a mode-aware held-key map instead of a suppression set. Headless and runtime paths share modal repeat gating, while clipboard actions and input-source cleanup update the new state. Tests cover navigation, modal behavior, typing, detachment, and unrecorded repeats.

Changes

Mode-aware key repeat tracking

Layer / File(s) Summary
Repeat state model and modal policy
src/app/mod.rs
App stores HeldKeyRepeat values, initializes the map, and uses modal_repeat_allowed to gate modal repeats.
Headless repeat routing and cleanup
src/app/mod.rs, src/app/input/clipboard.rs, src/app/input/terminal.rs
Headless press, repeat, and release handling records and clears held keys; retained-selection copying blocks repeats and source detachment removes related state.
Runtime repeat routing and validation
src/app/runtime.rs, src/app/mod.rs
Runtime repeat decisions use mode-aware state, and tests cover navigation, modal behavior, typing, source detachment, and repeats without a recorded press.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant InputSource
  participant App
  participant Terminal
  InputSource->>App: key press
  App->>App: record HeldKeyRepeat
  InputSource->>App: key repeat
  App->>App: check modal_repeat_allowed and held state
  App->>Terminal: forward terminal-owned repeat
  InputSource->>App: key release
  App->>App: clear held repeat state
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing held-key repeat handling in modal dispatch.
Description check ✅ Passed The description matches the PR and explains the repeat-handling fix, tests, and affected modes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@kangal-bot kangal-bot added coderabbit-review greptile-review Trigger Greptile review for contributor-approved pull requests labels Jul 28, 2026
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

Routes held-key kitty-protocol Repeat events into modal handlers when the press was recorded in the same mode.

  • Replaces suppressed_repeat_keys with held_key_repeats (Blocked vs InMode(Mode)).
  • On Repeat in non-terminal modes, dispatches via handle_key / handle_non_terminal_key_headless when mode matches and modal_repeat_allowed.
  • Excludes Enter, Esc, paste shortcuts, and Space outside text inputs; clears state on release and input-source detach.
  • Adds headless and monolithic coverage for navigator, search, mode-guard, exclusions, and cleanup.

Confidence Score: 5/5

Safe to merge; no remaining blocking failures were identified from prior threads or this follow-up pass.

No blocking failure remains. Prior Greptile inline threads were not available in this environment, and the follow-up eligibility gate therefore admits no new comments; the held-key repeat routing and mode/exclusion guards are covered by the new tests.

Important Files Changed

Filename Overview
src/app/mod.rs Introduces HeldKeyRepeat, modal_repeat_allowed, headless Repeat routing, and regression tests.
src/app/runtime.rs Mirrors held-key Repeat routing for the monolithic input path.
src/app/input/clipboard.rs Clipboard interception records HeldKeyRepeat::Blocked instead of a bare suppress set.
src/app/input/terminal.rs Input-source detach clears held_key_repeats for that source.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Press[KeyEventKind::Press] --> TermCheck{popup or Terminal?}
  TermCheck -->|yes| Clear[held_key_repeats.remove]
  TermCheck -->|no| Record[insert InMode current mode]
  Clear --> HandlePress[handle_key / terminal path]
  Record --> HandleModal[handle_key / non-terminal]
  Repeat[KeyEventKind::Repeat] --> TermR{popup or Terminal?}
  TermR -->|yes| Blocked{key in held_key_repeats?}
  Blocked -->|yes| DropT[ignore]
  Blocked -->|no| FwdT[forward to terminal]
  TermR -->|no| ModalOK{modal_repeat_allowed and InMode matches?}
  ModalOK -->|yes| HandleR[handle modal again]
  ModalOK -->|no| DropM[ignore]
  Release[KeyEventKind::Release] --> Cleared[held_key_repeats.remove]
Loading

Reviews (3): Last reviewed commit: "fix: handle held-key repeat in modal dis..." | Re-trigger Greptile

@mudlej
mudlej force-pushed the fix/modal-key-repeat branch from 360ab51 to 9bb38db Compare July 28, 2026 17:45
@mudlej
mudlej force-pushed the fix/modal-key-repeat branch from 9bb38db to 176ff61 Compare July 28, 2026 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coderabbit-review greptile-review Trigger Greptile review for contributor-approved pull requests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants