From c9ce6c920b4144bbf9e3d82f6cdc3817e967fc82 Mon Sep 17 00:00:00 2001 From: Hunter B Date: Sat, 6 Jun 2026 10:32:18 -0700 Subject: [PATCH] fix(tui): harden hotbar action dispatch --- crates/tui/src/tui/hotbar/actions.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/tui/src/tui/hotbar/actions.rs b/crates/tui/src/tui/hotbar/actions.rs index 92f65a76..d80d196a 100644 --- a/crates/tui/src/tui/hotbar/actions.rs +++ b/crates/tui/src/tui/hotbar/actions.rs @@ -60,7 +60,7 @@ impl HotbarActionRegistry { .insert(action.id().to_string(), Arc::new(action)); } - pub fn register_builtins(&mut self) { + pub(crate) fn register_builtins(&mut self) { self.register(AppHotbarAction::new( "voice.toggle", "voice", @@ -184,7 +184,9 @@ impl HotbarAction for AppHotbarAction { AppHotbarKind::VoiceToggle => false, AppHotbarKind::SessionCompact => app.is_compacting, AppHotbarKind::Mode(mode) => app.mode == mode, - AppHotbarKind::ReasoningCycle => false, + AppHotbarKind::ReasoningCycle => { + !app.auto_model && app.reasoning_effort != crate::tui::app::ReasoningEffort::Off + } AppHotbarKind::SidebarToggle => app.sidebar_focus != SidebarFocus::Hidden, AppHotbarKind::FileTreeToggle => app.file_tree.is_some(), AppHotbarKind::PaletteOpen => false, @@ -200,6 +202,10 @@ impl HotbarAction for AppHotbarAction { Ok(HotbarDispatch::Handled) } AppHotbarKind::SessionCompact => { + if app.is_compacting { + app.status_message = Some("Compaction is already running.".to_string()); + return Ok(HotbarDispatch::Handled); + } Ok(HotbarDispatch::AppAction(AppAction::CompactContext)) } AppHotbarKind::Mode(mode) => { @@ -390,6 +396,16 @@ mod tests { ); app.is_compacting = true; assert!(compact.is_active(&app)); + assert_eq!( + compact + .dispatch(&mut app) + .expect("dispatch compact while busy"), + HotbarDispatch::Handled + ); + assert_eq!( + app.status_message.as_deref(), + Some("Compaction is already running.") + ); } #[test] @@ -405,10 +421,15 @@ mod tests { HotbarDispatch::AppAction(AppAction::UpdateCompaction(_)) )); assert_eq!(app.reasoning_effort, ReasoningEffort::High); + assert!(reasoning.is_active(&app)); assert_eq!( app.status_message.as_deref(), Some("Reasoning effort: high") ); + + app.auto_model = true; + assert!(!reasoning.is_active(&app)); + assert!(reasoning.dispatch(&mut app).is_err()); } #[test]