From 02e86e50628faee40dbba092f2fea024c22d3167 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Wed, 1 Apr 2026 09:59:32 -0700 Subject: [PATCH] feat(claudbg-wfoe): Ctrl+C quits TUI immediately without confirmation Co-Authored-By: Claude Sonnet 4.6 --- ...-immediately-quits-without-confirmation.md | 5 +- src/tui/modals/help_modal.rs | 5 +- src/tui/run.rs | 46 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/.beans/claudbg-wfoe--tui-ctrlc-immediately-quits-without-confirmation.md b/.beans/claudbg-wfoe--tui-ctrlc-immediately-quits-without-confirmation.md index d190b99..d9a4ee8 100644 --- a/.beans/claudbg-wfoe--tui-ctrlc-immediately-quits-without-confirmation.md +++ b/.beans/claudbg-wfoe--tui-ctrlc-immediately-quits-without-confirmation.md @@ -1,10 +1,11 @@ --- # claudbg-wfoe title: 'TUI: Ctrl+C immediately quits without confirmation' -status: todo +status: in-progress type: feature +priority: normal created_at: 2026-04-01T16:47:04Z -updated_at: 2026-04-01T16:47:04Z +updated_at: 2026-04-01T16:57:38Z --- Support Ctrl+C to immediately quit without confirmation. Currently quit requires a confirmation dialog; Ctrl+C should bypass this and exit immediately. diff --git a/src/tui/modals/help_modal.rs b/src/tui/modals/help_modal.rs index d9300a6..73979c4 100644 --- a/src/tui/modals/help_modal.rs +++ b/src/tui/modals/help_modal.rs @@ -16,7 +16,7 @@ use crate::tui::state::AppState; /// Dialog dimensions. const DIALOG_WIDTH: u16 = 36; -const DIALOG_HEIGHT: u16 = 37; +const DIALOG_HEIGHT: u16 = 38; /// Compute a centered [`Rect`] of the given size within `area`. fn centered_rect(width: u16, height: u16, area: Rect) -> Rect { @@ -69,7 +69,8 @@ const HELP_TEXT: &str = "\ Esc clear & close\n\ \n\ Global\n\ - q/Q quit\n\ + q/Q quit (confirm)\n\ + Ctrl+C quit immediately\n\ ? this help\n\ c toggle color\n\ Ctrl+L redraw screen"; diff --git a/src/tui/run.rs b/src/tui/run.rs index f5a3a11..b73ac02 100644 --- a/src/tui/run.rs +++ b/src/tui/run.rs @@ -119,6 +119,16 @@ fn render(frame: &mut ratatui::Frame, state: &AppState) { /// then falls back to global keybindings for any event not consumed by /// the screen. fn handle_event(event: Event, state: &mut AppState) { + // Ctrl+C quits immediately, bypassing any modal dialogs. + if let Event::Key(key) = &event + && key.kind == KeyEventKind::Press + && key.code == KeyCode::Char('c') + && key.modifiers.contains(KeyModifiers::CONTROL) + { + state.should_quit = true; + return; + } + // Modal dialogs intercept all input while visible. if state.show_quit_dialog { handle_quit_dialog_event(event, state); @@ -405,4 +415,40 @@ mod tests { handle_event(key_press(KeyCode::Char('c')), &mut state); assert!(state.color_enabled, "color toggled back on"); } + + fn ctrl_key_press(code: KeyCode) -> Event { + Event::Key(KeyEvent { + code, + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + state: KeyEventState::NONE, + }) + } + + /// Ctrl+C quits immediately without showing the quit dialog. + #[test] + fn ctrl_c_quits_immediately() { + let mut state = AppState::new(); + handle_event(ctrl_key_press(KeyCode::Char('c')), &mut state); + assert!(state.should_quit); + assert!(!state.show_quit_dialog); + } + + /// Ctrl+C quits immediately even when the quit dialog is already open. + #[test] + fn ctrl_c_quits_through_quit_dialog() { + let mut state = AppState::new(); + state.show_quit_dialog = true; + handle_event(ctrl_key_press(KeyCode::Char('c')), &mut state); + assert!(state.should_quit); + } + + /// Ctrl+C quits immediately even when the help modal is open. + #[test] + fn ctrl_c_quits_through_help_modal() { + let mut state = AppState::new(); + state.show_help = true; + handle_event(ctrl_key_press(KeyCode::Char('c')), &mut state); + assert!(state.should_quit); + } }