diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a98906d..4564d5d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 survives the CLI/TUI boundary by honoring the CodeWhale model env alias and legacy DeepSeek model handoff before falling back to provider defaults. Thanks @hongchen1993 for the PR. +- **macOS shortcut modifiers (#2938/#2943).** Ctrl-like shortcuts that are + reported as `SUPER` by macOS terminals now work for backgrounding tasks and + sidebar-focus chords without rewriting clipboard shortcuts. Thanks @idling11 + for the PR. - **TUI mouse-report leak (#3063/#3067).** Strip raw SGR mouse coordinate tails from the composer even when `use_mouse_capture` is false, covering orphaned terminal reporting state after crashes or focus races. diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index f43019b0..87138b1e 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -50,6 +50,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 survives the CLI/TUI boundary by honoring the CodeWhale model env alias and legacy DeepSeek model handoff before falling back to provider defaults. Thanks @hongchen1993 for the PR. +- **macOS shortcut modifiers (#2938/#2943).** Ctrl-like shortcuts that are + reported as `SUPER` by macOS terminals now work for backgrounding tasks and + sidebar-focus chords without rewriting clipboard shortcuts. Thanks @idling11 + for the PR. - **TUI mouse-report leak (#3063/#3067).** Strip raw SGR mouse coordinate tails from the composer even when `use_mouse_capture` is false, covering orphaned terminal reporting state after crashes or focus races. diff --git a/crates/tui/src/tui/key_shortcuts.rs b/crates/tui/src/tui/key_shortcuts.rs index e9cde138..dfec927e 100644 --- a/crates/tui/src/tui/key_shortcuts.rs +++ b/crates/tui/src/tui/key_shortcuts.rs @@ -9,6 +9,18 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +pub(super) fn has_control_like_modifier(modifiers: KeyModifiers) -> bool { + has_control_like_modifier_for_platform(modifiers, cfg!(target_os = "macos")) +} + +pub(super) fn has_control_like_modifier_for_platform( + modifiers: KeyModifiers, + is_macos: bool, +) -> bool { + modifiers.contains(KeyModifiers::CONTROL) + || (is_macos && modifiers.contains(KeyModifiers::SUPER)) +} + /// Copy-to-clipboard: `Cmd+C` on macOS or `Ctrl+Shift+C` elsewhere. pub(super) fn is_copy_shortcut(key: &KeyEvent) -> bool { let is_c = matches!(key.code, KeyCode::Char('c') | KeyCode::Char('C')); diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 8b0dafeb..10b780be 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -3382,7 +3382,7 @@ async fn run_event_loop( } if matches!(key.code, KeyCode::Char('b') | KeyCode::Char('B')) - && key.modifiers.contains(KeyModifiers::CONTROL) + && key_shortcuts::has_control_like_modifier(key.modifiers) && app.view_stack.is_empty() { // #3032: Ctrl+B directly backgrounds the active foreground @@ -3582,7 +3582,7 @@ async fn run_event_loop( continue; } KeyCode::Char('1') if key.modifiers.contains(KeyModifiers::ALT) => { - if key.modifiers.contains(KeyModifiers::CONTROL) { + if key_shortcuts::has_control_like_modifier(key.modifiers) { app.set_sidebar_focus(SidebarFocus::Work); app.status_message = Some("Sidebar focus: work".to_string()); } else { @@ -3591,7 +3591,7 @@ async fn run_event_loop( continue; } KeyCode::Char('2') if key.modifiers.contains(KeyModifiers::ALT) => { - if key.modifiers.contains(KeyModifiers::CONTROL) { + if key_shortcuts::has_control_like_modifier(key.modifiers) { app.set_sidebar_focus(SidebarFocus::Tasks); app.status_message = Some("Sidebar focus: tasks".to_string()); } else { @@ -3600,7 +3600,7 @@ async fn run_event_loop( continue; } KeyCode::Char('3') if key.modifiers.contains(KeyModifiers::ALT) => { - if key.modifiers.contains(KeyModifiers::CONTROL) { + if key_shortcuts::has_control_like_modifier(key.modifiers) { app.set_sidebar_focus(SidebarFocus::Agents); app.status_message = Some("Sidebar focus: agents".to_string()); } else { diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 874334ea..39f690b2 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -1262,6 +1262,32 @@ fn copy_shortcut_accepts_cmd_and_ctrl_shift_only() { )); } +#[test] +fn control_like_modifier_accepts_super_only_on_macos() { + use crate::tui::key_shortcuts::has_control_like_modifier_for_platform; + + assert!(has_control_like_modifier_for_platform( + KeyModifiers::CONTROL, + false + )); + assert!(has_control_like_modifier_for_platform( + KeyModifiers::CONTROL, + true + )); + assert!(!has_control_like_modifier_for_platform( + KeyModifiers::SUPER, + false + )); + assert!(has_control_like_modifier_for_platform( + KeyModifiers::SUPER, + true + )); + assert!(has_control_like_modifier_for_platform( + KeyModifiers::SUPER | KeyModifiers::ALT, + true + )); +} + #[test] fn file_tree_shortcut_does_not_steal_plain_ctrl_e() { assert!(!crate::tui::key_shortcuts::is_file_tree_toggle_shortcut(