Skip to content

fix: preserve shift on ctrl+shift+letter for legacy panes - #4597

Merged
ogulcancelik merged 1 commit into
herdrdev:masterfrom
factory-ain3sh:ain3sh/gh-4581-ctrl-shift-c
Sep 25, 2026
Merged

ogulcancelik merged 1 commit into
herdrdev:masterfrom
factory-ain3sh:ain3sh/gh-4581-ctrl-shift-c

Conversation

@factory-ain3sh

Copy link
Copy Markdown
Contributor

Description

What

Ctrl+Shift+letter now reaches pane apps with Shift intact when the app has not enabled the kitty keyboard protocol. Herdr sends CSI 99;6u for Ctrl+Shift+C instead of 0x03, which is the same bytes bare Ghostty sends. Verified live: stable 0.9.1 delivers 03 03 for Ctrl+Shift+C then Ctrl+C; this branch delivers 1b 5b 39 39 3b 36 75 03.

Why

Every Char key goes through herdr's own encoder (ghostty_prefers_herdr_text_encoding). Its legacy path turned any Ctrl+letter into a C0 byte and dropped Shift, so apps could not tell Ctrl+Shift+C from Ctrl+C and treated it as an interrupt. Droid 0.226.2 is one such app: in a herdr pane it never pushes kitty flags or modifyOtherKeys, so it always gets legacy encoding.

How

In legacy mode, Ctrl+Shift+ASCII letter (with or without Alt) now goes through the same CSI-u branch that already preserves Super chords. This mirrors libghostty-vt's legacy encoder (ctrlSeq declines Ctrl+Shift+letter, and the fixterms branch emits CSI-u). Plain Ctrl+letter and Ctrl+Shift+punctuation keep their C0 bytes.

Repro Recipe

  1. In a herdr pane, run a raw byte reader that does not push kitty flags (for example the probe from the issue with the \x1b[>7u write removed).
  2. herdr pane send-keys <pane> ctrl+shift+c ctrl+c
  3. Before: GOT: 03 03. After: GOT: 1b 5b 39 39 3b 36 75 03.

Related Issue

refs #4581

Reviewer Guide

Review depth: Standard. One predicate in the encoder plus tests.
Read order:

  1. src/input/encode.rs: legacy_chord_needs_csi_u and its call site in encode_terminal_key.
  2. src/input/encode.rs tests: Ctrl+Shift+C (either case), Ctrl+Alt+Shift+C, the kitty host sequence from the issue, and Ctrl+Shift+_ keeping 0x1f.
  3. src/pane/terminal.rs: ghostty_char_keys_still_use_herdr_encoding now uses Ctrl+A. Ctrl+Shift+A no longer tells herdr's encoder and Ghostty's encoder apart, because both now emit CSI 97;6u. Ctrl+A still does (0x01 vs CSI 97;5u).

Risk & Impact

A legacy-mode app that relied on Ctrl+Shift+letter arriving as the plain Ctrl+letter byte will now receive a CSI-u sequence instead. Bare Ghostty already behaves this way, so apps that work in Ghostty outside herdr are unaffected. Kitty-mode panes, non-letter keys, and the wire protocol are unchanged. Reverting the single commit restores the old behavior.

Verification

Behavior verified @ 0e8c5926: Disposable herdr sessions on stable 0.9.1 and on this branch, byte probe in a non-kitty pane, send-keys ctrl+shift+c ctrl+c: 03 03 before, CSI 99;6u + 03 after.
Regression coverage: input::encode::tests::legacy_ctrl_shift_letter_* fail on master with [3] and pass here.
Not tested: A physical Ghostty keypress into Droid. The probe drives the same encode_terminal_key path through the API.
Standard validators: just ci passes (fmt, clippy -D warnings, 3802 nextest tests, maintenance tests).

Panes whose app has not enabled the kitty keyboard protocol received
ctrl+shift+letter as the same C0 byte as ctrl+letter. Encode the chord as
CSI-u instead, matching Ghostty's legacy encoder.

refs herdrdev#4581
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 210ce1c5-1ffc-4fad-afde-55b6cf047ddc

📥 Commits

Reviewing files that changed from the base of the PR and between 8d95e9b and 0e8c592.

📒 Files selected for processing (2)
  • src/input/encode.rs
  • src/pane/terminal.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Legacy encoding now uses CSI-u for non-release Super-modified character keys and ASCII letters with Control and Shift. Other Control+Shift characters, including punctuation, retain legacy encoding. Tests cover lowercase and uppercase letters, Alt-modified letters, Kitty-parsed host input, punctuation, and a terminal test input change.

Suggested reviewers: ogulcancelik

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 0e8c5

The intended legacy key encoding appears to reach pane input, with no actionable merge-blocking issue identified. Merge after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preserving Shift for Ctrl+Shift+letter input in legacy panes.
Description check ✅ Passed The description directly explains the legacy encoding change, its purpose, implementation, verification, and impact.
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.
  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@kangal-bot

Copy link
Copy Markdown
Collaborator

@coderabbitai review
@greptileai

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps

greptile-apps Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR should not merge until Ctrl+Shift+letter encoding respects panes using modifyOtherKeys level 2.

Findings

  1. P1 ModifyOtherKeys chords use wrong encoding ▶

Summary

This PR preserves Shift for Ctrl+Shift+ASCII-letter keys sent to legacy panes.

  • It routes those chords through CSI-u while retaining C0 encoding for plain Ctrl+letter and the tested punctuation chord.
  • It adds encoder regressions and changes the pane encoder-selection test to use Ctrl+A.
  • The new route does not account for panes that negotiated modifyOtherKeys level 2.

Reviews (1) · Last reviewed commit: "fix: preserve shift on ctrl+shift+letter..."

Comment thread src/input/encode.rs
&& key.kind != crossterm::event::KeyEventKind::Release
&& matches!(key.code, KeyCode::Char(_))
&& key.modifiers.contains(KeyModifiers::SUPER)
&& legacy_chord_needs_csi_u(&key)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 ModifyOtherKeys chords use wrong encoding When a pane enables modifyOtherKeys level 2 without Kitty flags, its protocol remains Legacy. This branch sends Ctrl+Shift+letter as CSI-u, but an app using the negotiated mode expects a CSI 27;...~ sequence and can miss the chord. Ghostty’s level-2 encoder, for example, sends CSI 27;6;72~ for Ctrl+Shift+H. This path needs to respect the pane’s modifyOtherKeys mode.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point, but this isn't a regression. on master those panes get a plain 03 for ctrl+shift+c. now they get CSI 99;6u, which modifyOtherKeys readers like vim and neovim also accept. herdr's encoder already ignores modifyOtherKeys for all letter keys, plain ctrl+letter included, so that's a separate follow-up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s fair. Since encode_terminal_key already treats all character chords—including plain Ctrl+letters—as legacy/CSI-u behavior without consulting modifyOtherKeys, this change does not introduce a new modifyOtherKeys-specific regression; it only changes Ctrl+Shift+ASCII letters from the old collapsed C0 byte to CSI-u. If vim/neovim’s readers accept that sequence, the concern I raised is a separate consistency/follow-up issue rather than a blocker for this PR. I’m withdrawing the finding.

@ogulcancelik
ogulcancelik merged commit 21d0ce6 into herdrdev:master Sep 25, 2026
8 checks passed

@davshn davshn left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

abhijit-s pushed a commit to abhijit-s/herdr that referenced this pull request Sep 30, 2026
Merges 46 upstream commits spanning stable releases v0.9.2 and v0.9.3:
multiple prefix keys (herdrdev#4653), self-reported agent resume commands
(herdrdev#4687), Go To workspace-grouped navigator rows (herdrdev#4384), clear_pane
keybinding (herdrdev#4383), non-interactive machine status/reconnect with SSH
MFA (herdrdev#3763), interactive machine add session picker (herdrdev#4204, herdrdev#4678),
mouse events on terminal session control (herdrdev#4685), staggered agent
restarts (herdrdev#4102), SSH compression (herdrdev#4340), row-shift scrolling
(herdrdev#4711), TERM_PROGRAM identity (herdrdev#4104), PowerShell 7 default on
Windows (herdrdev#4297), tab-close confirmation (herdrdev#4379, herdrdev#4409), full event
subscription bursts (herdrdev#4178, herdrdev#4225), SIGWINCH color refresh (herdrdev#4349),
saved-layout shutdown survival (herdrdev#4320), stray-Escape click fix
(herdrdev#3480), first-task-done detection (herdrdev#3338, herdrdev#3990, herdrdev#3916), CPU/perf
work across navigator/splits/named-targets/event-batching (herdrdev#4506,
herdrdev#4546, herdrdev#4426, herdrdev#4669, herdrdev#4670, herdrdev#4671, herdrdev#4672), synchronized-frame tearing
and cursor flicker fixes (herdrdev#2968, herdrdev#4303), API socket transient-error
resilience (herdrdev#4601), async worktree git lookups (herdrdev#4492), worktree-open
workspace-takeover fix (herdrdev#4293), SSH agent forwarding after reconnect
(herdrdev#1931, herdrdev#4252), OpenCode V1 subagent status (herdrdev#1362, herdrdev#4240), Codex
idle/mention/interrupt-key detection (herdrdev#4507, herdrdev#4196), Kiro/Grok
detection fixes (herdrdev#4203, herdrdev#4372, herdrdev#4333), Droid scrollback clears
(herdrdev#4432), copy-mode-stays-active fix (herdrdev#3812, herdrdev#4281), Ctrl+Shift+letter
fix (herdrdev#4581, herdrdev#4597), split mouse-report fix (herdrdev#4630), several Windows
paste/mouse-capture/clipboard/detection fixes (herdrdev#4251, herdrdev#4284, herdrdev#4470,
herdrdev#4314, herdrdev#4313, herdrdev#4579, herdrdev#4309), navigator search/highlight fixes (herdrdev#4300,
herdrdev#4408, herdrdev#4273, herdrdev#4535, herdrdev#4355), session-attach-without-terminal error
(herdrdev#4393), socket error request-ID preservation (herdrdev#4344), plugin install
option ordering (herdrdev#4446), Windows host color detection (herdrdev#1530, herdrdev#4369),
post-update saved-machine summary (herdrdev#4722), a hotfix for Escape-prefixed
terminal key bindings (herdrdev#4759) and pinned Windows ConPTY bundling
(herdrdev#4760), Windows same-user cross-elevation attach (herdrdev#4777), and the
libghostty-vt binding extraction into a ghostty-vt workspace crate
(herdrdev#4661).

Also fixes two merge seam breaks: overlays.rs combines the fork's
rounded_borders threading with upstream's per-client prefix-label
onboarding overlay, and the fork's command_palette test file is
updated for the new Vec<KeyCombo> prefix representation and the
BindingConfig::one constructor introduced by the multi-prefix-key
feature.

refs upstream herdrdev/herdr@331775c3
maciejul pushed a commit to maciejul/herdr-supercharged that referenced this pull request Oct 28, 2026
)

Panes whose app has not enabled the kitty keyboard protocol received
ctrl+shift+letter as the same C0 byte as ctrl+letter. Encode the chord as
CSI-u instead, matching Ghostty's legacy encoder.

refs herdrdev#4581

Co-authored-by: ain3sh <ainesh.chatterjee@gmail.com>
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.

5 participants