aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-08-01 05:08:17 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-08-01 05:08:17 +0300
commit26a7bc860e4af516ef687d90b9f9dd2830f88d2a (patch)
treec8d2332222917b9fd4128609fb157729eace919c
parentb76adc155d979705a353fd6dc87c702f9dfc6c22 (diff)
fix(windows): preserve semantic escape key taps
-rw-r--r--docs/next/CHANGELOG.md1
-rw-r--r--src/platform/windows.rs46
2 files changed, 47 insertions, 0 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index 50632738..17ee1a24 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -28,6 +28,7 @@
- OMP integration install, status, and uninstall now respect `PI_CONFIG_DIR` when `PI_CODING_AGENT_DIR` is not set, and installation refuses extension-directory collisions with Pi. (#1696)
- Physical Escape key records on native Windows now bypass raw VT report framing, so pane applications receive Escape immediately and reliably. (#1736)
- Native Windows key presses, grouped repeats, and releases now preserve their physical lifecycle and stay with the pane that received the initial press. (#2077)
+- Windows `pane send-keys` and `agent send-keys` now deliver semantic Escape as a complete key tap, preventing a following key from being interpreted as an Alt chord.
- Windows now shows `system` notifications and completes MP3 notification sounds without leaving PowerShell players waiting for a timeout. (#1330)
## [0.7.5] - 2026-07-21
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index 6cc642eb..f384ea02 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -76,6 +76,12 @@ pub(crate) fn encode_windows_conpty_fallback(key: &crate::input::TerminalKey) ->
record.unicode,
record.control_key_state,
)
+ } else if key.code == KeyCode::Esc
+ && key.modifiers.is_empty()
+ && key.kind == KeyEventKind::Press
+ && key.vt_bytes().is_none()
+ {
+ return Some(b"\x1b[27;1;27;1;0;1_\x1b[27;1;27;0;0;1_".to_vec());
} else if key.code == KeyCode::Enter && key.modifiers == KeyModifiers::SHIFT {
(13, 28, 13, 16)
} else {
@@ -1435,6 +1441,46 @@ mod tests {
}
#[test]
+ fn windows_conpty_native_encoder_preserves_semantic_escape_fallback() {
+ let escape = crate::input::TerminalKey::new(
+ crossterm::event::KeyCode::Esc,
+ crossterm::event::KeyModifiers::empty(),
+ );
+
+ assert_eq!(
+ super::encode_windows_conpty_fallback(&escape),
+ Some(b"\x1b[27;1;27;1;0;1_\x1b[27;1;27;0;0;1_".to_vec())
+ );
+ assert_eq!(
+ super::encode_windows_conpty_fallback(
+ &escape
+ .clone()
+ .with_kind(crossterm::event::KeyEventKind::Repeat),
+ ),
+ None
+ );
+ assert_eq!(
+ super::encode_windows_conpty_fallback(
+ &escape
+ .clone()
+ .with_kind(crossterm::event::KeyEventKind::Release),
+ ),
+ None
+ );
+ assert_eq!(
+ super::encode_windows_conpty_fallback(&escape.clone().with_vt_bytes(vec![27])),
+ None
+ );
+ assert_eq!(
+ super::encode_windows_conpty_fallback(&crate::input::TerminalKey::new(
+ crossterm::event::KeyCode::Esc,
+ crossterm::event::KeyModifiers::ALT,
+ ),),
+ None
+ );
+ }
+
+ #[test]
fn windows_conpty_native_encoder_preserves_semantic_shift_enter_fallback() {
let shift_enter = crate::input::TerminalKey::new(
crossterm::event::KeyCode::Enter,