From 27e46b4fb910b85e928ecdc30ef63432ebe25f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Sun, 5 Jul 2026 18:57:52 +0200 Subject: [PATCH 1/3] chore: release ratatui-ratty 0.4.0 --- widget/Cargo.lock | 2 +- widget/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/Cargo.lock b/widget/Cargo.lock index bdaba62..7209cfd 100644 --- a/widget/Cargo.lock +++ b/widget/Cargo.lock @@ -1697,7 +1697,7 @@ dependencies = [ [[package]] name = "ratatui-ratty" -version = "0.3.1" +version = "0.4.0" dependencies = [ "base64", "crossterm", diff --git a/widget/Cargo.toml b/widget/Cargo.toml index 035be06..6c0249f 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratatui-ratty" -version = "0.3.1" +version = "0.4.0" description = "A Ratatui widget for rendering inline 3D graphics via Ratty Graphics Protocol" authors = ["Orhun Parmaksız "] license = "MIT" From 6f8bf9305e637b9e53ef20b9274264a9baad5e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Sun, 5 Jul 2026 19:04:45 +0200 Subject: [PATCH 2/3] chore: update the version on the website --- website/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/index.html b/website/index.html index c23a3c0..7c18fe6 100644 --- a/website/index.html +++ b/website/index.html @@ -130,7 +130,7 @@ > Download Date: Tue, 14 Jul 2026 16:56:59 +0200 Subject: [PATCH 3/3] fix(keyboard): pass alt arrow keys to alternate screen apps --- src/keyboard.rs | 137 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 6 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 8b5fb3f..4f9dca0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -338,6 +338,7 @@ pub fn handle_keyboard_input( let modifiers = current_modifiers(¶ms.keys).union(keyboard.modifiers()); if event.state == ButtonState::Pressed && let Some(action) = params.bindings.action_for(binding_key_code, modifiers) + && !(is_scroll_action(action) && params.runtime.parser.screen().alternate_screen()) { if event.repeat && !matches!( @@ -530,6 +531,16 @@ pub fn handle_keyboard_input( } } +fn is_scroll_action(action: BindingAction) -> bool { + matches!( + action, + BindingAction::ScrollPageUp + | BindingAction::ScrollPageDown + | BindingAction::ScrollUp + | BindingAction::ScrollDown + ) +} + fn current_modifiers(keys: &ButtonInput) -> BindingModifiers { BindingModifiers { control: keys.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]), @@ -682,17 +693,22 @@ fn translate_key(key_code: KeyCode, ctx: KeyTranslationContext<'_>) -> Vec { return bytes; } - if ctx.alt_pressed { - bytes.push(0x1b); - } - let navigation_key = NavigationKey::from_key_code(key_code) .or_else(|| NavigationKey::from_logical_key(ctx.logical_key)); if let Some(key) = navigation_key { - bytes.extend_from_slice(&key.encode(ctx.ctrl_pressed, ctx.application_cursor)); + bytes.extend_from_slice(&key.encode( + ctx.ctrl_pressed, + ctx.alt_pressed, + ctx.shift_pressed, + ctx.application_cursor, + )); return bytes; } + if ctx.alt_pressed { + bytes.push(0x1b); + } + match key_code { KeyCode::Enter | KeyCode::NumpadEnter => bytes.push(b'\r'), KeyCode::Tab => bytes.push(b'\t'), @@ -794,7 +810,44 @@ impl NavigationKey { } } - fn encode(self, ctrl_pressed: bool, application_cursor: bool) -> Vec { + fn encode( + self, + ctrl_pressed: bool, + alt_pressed: bool, + shift_pressed: bool, + application_cursor: bool, + ) -> Vec { + let modifier_code = + 1 + shift_pressed as u8 + (alt_pressed as u8 * 2) + (ctrl_pressed as u8 * 4); + + if modifier_code != 1 { + let arrow_suffix = match self { + Self::ArrowUp => Some('A'), + Self::ArrowDown => Some('B'), + Self::ArrowRight => Some('C'), + Self::ArrowLeft => Some('D'), + Self::Home => Some('H'), + Self::End => Some('F'), + _ => None, + }; + + if let Some(suffix) = arrow_suffix { + return format!("\x1b[1;{modifier_code}{suffix}").into_bytes(); + } + + let tilde_code = match self { + Self::PageUp => Some(5), + Self::PageDown => Some(6), + Self::Insert => Some(2), + Self::Delete => Some(3), + _ => None, + }; + + if let Some(code) = tilde_code { + return format!("\x1b[{code};{modifier_code}~").into_bytes(); + } + } + match self { Self::ArrowUp => { if ctrl_pressed { @@ -1035,6 +1088,78 @@ fn ctrl_keycode_byte(key: KeyCode) -> Option { } } +#[cfg(test)] +mod keyboard_translation_tests { + use super::*; + + fn translate_navigation_key( + key_code: KeyCode, + logical_key: Key, + ctrl_pressed: bool, + alt_pressed: bool, + shift_pressed: bool, + application_cursor: bool, + ) -> Vec { + translate_key( + key_code, + KeyTranslationContext { + logical_key: &logical_key, + text: None, + ctrl_pressed, + alt_pressed, + alt_gr_pressed: false, + shift_pressed, + application_cursor, + kitty_keyboard_flags: 0, + modify_other_keys: None, + }, + ) + } + + #[test] + fn encodes_alt_arrow_keys_as_modified_csi_sequences() { + assert_eq!( + translate_navigation_key(KeyCode::ArrowUp, Key::ArrowUp, false, true, false, false), + b"\x1b[1;3A" + ); + assert_eq!( + translate_navigation_key( + KeyCode::ArrowDown, + Key::ArrowDown, + false, + true, + false, + false + ), + b"\x1b[1;3B" + ); + } + + #[test] + fn modified_navigation_keys_do_not_use_application_cursor_mode() { + assert_eq!( + translate_navigation_key(KeyCode::ArrowUp, Key::ArrowUp, false, true, false, true), + b"\x1b[1;3A" + ); + assert_eq!( + translate_navigation_key(KeyCode::ArrowUp, Key::ArrowUp, false, false, false, true), + b"\x1bOA" + ); + } + + #[test] + fn encodes_modified_page_keys_with_tilde_sequences() { + assert_eq!( + translate_navigation_key(KeyCode::PageUp, Key::PageUp, false, true, false, false), + b"\x1b[5;3~" + ); + assert_eq!( + translate_navigation_key(KeyCode::PageDown, Key::PageDown, false, true, false, false), + b"\x1b[6;3~" + ); + } +} + #[cfg(test)] mod tests { use super::*;