parent
39ba6b7a02
commit
d190c1f55e
|
|
@ -18,6 +18,7 @@
|
|||
- Bumped the client/server protocol version to 15 for socket API placement mutation event and response compatibility.
|
||||
|
||||
### Fixed
|
||||
- Windows clients now send shifted punctuation such as `!`, `?`, and `:` as literal text to Kitty-keyboard-mode pane apps, fixing Kiro CLI TUI prompts while preserving modified key chords. (#1105)
|
||||
- `herdr --remote` now prints clean remote attach failures and SSH authentication guidance instead of Rust Debug-formatted I/O errors when SSH authentication is denied. (#1034)
|
||||
- `herdr server stop` now stops Windows named-pipe servers instead of failing with `named pipes do not support I/O timeouts`. (#1113)
|
||||
- `herdr server stop` now waits until both server sockets are unreachable before returning, avoiding an immediate first-start failure when restarting right after replacing the binary.
|
||||
|
|
|
|||
|
|
@ -2012,6 +2012,35 @@ mod tests {
|
|||
assert!(rx.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn api_pane_send_keys_sends_shifted_punctuation_as_text_in_kitty_mode() {
|
||||
let (mut app, pane_id) = app_with_test_workspace();
|
||||
let internal_pane_id = app.state.workspaces[0].tabs[0].root_pane;
|
||||
let (runtime, mut rx) =
|
||||
crate::terminal::TerminalRuntime::test_with_channel_and_scrollback_bytes(
|
||||
80,
|
||||
24,
|
||||
0,
|
||||
b"\x1b[>7u",
|
||||
1,
|
||||
);
|
||||
app.state.insert_test_runtime(internal_pane_id, runtime);
|
||||
|
||||
let response = app.handle_api_request(crate::api::schema::Request {
|
||||
id: "req".into(),
|
||||
method: crate::api::schema::Method::PaneSendKeys(PaneSendKeysParams {
|
||||
pane_id,
|
||||
keys: vec!["shift+?".into()],
|
||||
}),
|
||||
});
|
||||
|
||||
let success: SuccessResponse = serde_json::from_str(&response).unwrap();
|
||||
assert_eq!(success.id, "req");
|
||||
assert_eq!(success.result, ResponseResult::Ok {});
|
||||
assert_eq!(rx.try_recv().unwrap(), bytes::Bytes::from_static(b"?"));
|
||||
assert!(rx.try_recv().is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn api_pane_send_input_keys_accept_key_combo_chords() {
|
||||
let (mut app, pane_id, mut rx) = app_with_send_key_runtime(1);
|
||||
|
|
|
|||
|
|
@ -379,9 +379,39 @@ fn shifted_text_char(key: &TerminalKey, ch: char) -> Option<char> {
|
|||
return Some(ch.to_ascii_uppercase());
|
||||
}
|
||||
|
||||
if is_shifted_ascii_punctuation(ch) {
|
||||
return Some(ch);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_shifted_ascii_punctuation(ch: char) -> bool {
|
||||
matches!(
|
||||
ch,
|
||||
'!' | '@'
|
||||
| '#'
|
||||
| '$'
|
||||
| '%'
|
||||
| '^'
|
||||
| '&'
|
||||
| '*'
|
||||
| '('
|
||||
| ')'
|
||||
| '_'
|
||||
| '+'
|
||||
| '{'
|
||||
| '}'
|
||||
| '|'
|
||||
| ':'
|
||||
| '"'
|
||||
| '<'
|
||||
| '>'
|
||||
| '?'
|
||||
| '~'
|
||||
)
|
||||
}
|
||||
|
||||
fn canonical_kitty_char(ch: char, mods: KeyModifiers) -> char {
|
||||
if mods.contains(KeyModifiers::SHIFT) && ch.is_ascii_uppercase() {
|
||||
ch.to_ascii_lowercase()
|
||||
|
|
@ -848,6 +878,56 @@ mod tests {
|
|||
assert_eq!(encode_key(key, KeyboardProtocol::Kitty { flags: 7 }), b"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_shifted_punctuation_literals_send_text() {
|
||||
for ch in "!@#$%^&*()_+{}|:\"<>?~".chars() {
|
||||
let key = TerminalKey::new(KeyCode::Char(ch), KeyModifiers::SHIFT);
|
||||
let encoded = encode_terminal_key(key, KeyboardProtocol::Kitty { flags: 7 });
|
||||
assert_eq!(encoded, ch.to_string().into_bytes(), "ch={ch}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_shifted_punctuation_release_does_not_emit_text() {
|
||||
let key = TerminalKey::new(KeyCode::Char('?'), KeyModifiers::SHIFT)
|
||||
.with_kind(crossterm::event::KeyEventKind::Release);
|
||||
assert_eq!(
|
||||
encode_terminal_key(key, KeyboardProtocol::Kitty { flags: 7 }),
|
||||
b""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_shifted_punctuation_does_not_infer_layout() {
|
||||
let key = TerminalKey::new(KeyCode::Char('1'), KeyModifiers::SHIFT);
|
||||
assert_eq!(
|
||||
encode_terminal_key(key, KeyboardProtocol::Kitty { flags: 7 }),
|
||||
b"\x1b[49;2:1u"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_modified_shifted_punctuation_stays_modified_key() {
|
||||
for (modifiers, expected) in [
|
||||
(
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
b"\x1b[33;6:1u".as_slice(),
|
||||
),
|
||||
(
|
||||
KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||
b"\x1b[33;4:1u".as_slice(),
|
||||
),
|
||||
(
|
||||
KeyModifiers::SUPER | KeyModifiers::SHIFT,
|
||||
b"\x1b[33;10:1u".as_slice(),
|
||||
),
|
||||
] {
|
||||
let key = TerminalKey::new(KeyCode::Char('!'), modifiers);
|
||||
let encoded = encode_terminal_key(key, KeyboardProtocol::Kitty { flags: 7 });
|
||||
assert_eq!(encoded, expected, "modifiers={modifiers:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn release_bytes_gated_on_report_event_types() {
|
||||
for code in [KeyCode::Enter, KeyCode::Backspace] {
|
||||
|
|
|
|||
Loading…
Reference in New Issue