From 453be36dcf94558eb5fc18dce8d00cf4e1cf9be8 Mon Sep 17 00:00:00 2001 From: cyq <15000851237@163.com> Date: Sun, 24 May 2026 02:33:51 +0800 Subject: [PATCH] Fix mode-switch toast dedupe during tab cycling --- crates/tui/src/tui/app.rs | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 85b018b8..096cbb53 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -2838,6 +2838,10 @@ impl App { (StatusToastLevel::Info, Some(4_000), false) } + fn is_mode_switch_status_message(message: &str) -> bool { + message.starts_with("Switched to ") && message.ends_with(" mode") + } + pub fn sync_status_message_to_toasts(&mut self) { let current = self.status_message.clone(); if self.last_status_message_seen == current { @@ -2864,6 +2868,10 @@ impl App { { self.clear_sticky_status(); } + if Self::is_mode_switch_status_message(&message) { + self.status_toasts + .retain(|toast| !Self::is_mode_switch_status_message(&toast.text)); + } self.push_status_toast(message, level, ttl_ms); } } @@ -5325,6 +5333,74 @@ mod tests { assert_eq!(app.mode, AppMode::Plan); } + #[test] + fn test_mode_switch_toasts_replace_previous_mode_switch_toast() { + let mut app = App::new(test_options(false), &Config::default()); + let first_mode = match app.mode { + AppMode::Plan => AppMode::Agent, + AppMode::Agent => AppMode::Yolo, + AppMode::Yolo => AppMode::Plan, + }; + let second_mode = match first_mode { + AppMode::Plan => AppMode::Yolo, + AppMode::Agent => AppMode::Plan, + AppMode::Yolo => AppMode::Agent, + }; + let third_mode = match second_mode { + AppMode::Plan => AppMode::Yolo, + AppMode::Agent => AppMode::Yolo, + AppMode::Yolo => AppMode::Plan, + }; + + app.set_mode(first_mode); + app.sync_status_message_to_toasts(); + assert_eq!(app.status_toasts.len(), 1); + assert_eq!( + app.status_toasts.back().expect("mode toast").text, + format!("Switched to {} mode", first_mode.label()) + ); + + app.set_mode(second_mode); + app.sync_status_message_to_toasts(); + assert_eq!(app.status_toasts.len(), 1); + assert_eq!( + app.status_toasts.back().expect("mode toast").text, + format!("Switched to {} mode", second_mode.label()) + ); + + app.set_mode(third_mode); + app.sync_status_message_to_toasts(); + assert_eq!(app.status_toasts.len(), 1); + assert_eq!( + app.status_toasts.back().expect("mode toast").text, + format!("Switched to {} mode", third_mode.label()) + ); + } + + #[test] + fn test_mode_switch_toasts_do_not_disrupt_non_mode_toasts() { + let mut app = App::new(test_options(false), &Config::default()); + app.status_message = Some("Task queued".to_string()); + app.sync_status_message_to_toasts(); + + app.set_mode(AppMode::Agent); + app.sync_status_message_to_toasts(); + app.set_mode(AppMode::Yolo); + app.sync_status_message_to_toasts(); + + assert_eq!(app.status_toasts.len(), 2); + assert!( + app.status_toasts + .iter() + .any(|toast| toast.text == "Task queued") + ); + assert!( + app.status_toasts + .iter() + .any(|toast| toast.text == "Switched to YOLO mode") + ); + } + #[test] fn test_clear_input() { let mut app = App::new(test_options(false), &Config::default());