From db8039ae465a2a1aba2a7c21de249aaa7ad88ff0 Mon Sep 17 00:00:00 2001 From: Hunter B Date: Fri, 12 Jun 2026 06:03:07 -0700 Subject: [PATCH] fix(tui): keep sidebar hover live while loading --- CHANGELOG.md | 3 ++ crates/tui/CHANGELOG.md | 3 ++ crates/tui/src/tui/mouse_ui.rs | 5 ++- crates/tui/src/tui/ui/tests.rs | 56 +++++++++++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c2897ba..f2bb75eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Sidebar context menus (#3065).** Right-clicking the sidebar no longer shows `Paste`; clickable sidebar rows now offer their row command as the first context action. +- **Sidebar hover popovers (#3088).** Streaming turns now keep sidebar hover + popovers responsive while continuing to throttle transcript/body mouse + motion. - **Cursor-style activity metadata rows (#3146).** Dense successful tool-run summaries now render as a single muted `Explored ...` / `Updated metadata` row, include short command-family labels for successful generic verifier diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 04ccbdd9..acce0059 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Sidebar context menus (#3065).** Right-clicking the sidebar no longer shows `Paste`; clickable sidebar rows now offer their row command as the first context action. +- **Sidebar hover popovers (#3088).** Streaming turns now keep sidebar hover + popovers responsive while continuing to throttle transcript/body mouse + motion. - **Cursor-style activity metadata rows (#3146).** Dense successful tool-run summaries now render as a single muted `Explored ...` / `Updated metadata` row, include short command-family labels for successful generic verifier diff --git a/crates/tui/src/tui/mouse_ui.rs b/crates/tui/src/tui/mouse_ui.rs index ea197275..c523b303 100644 --- a/crates/tui/src/tui/mouse_ui.rs +++ b/crates/tui/src/tui/mouse_ui.rs @@ -33,7 +33,10 @@ pub(crate) fn should_drop_loading_mouse_motion(app: &App, mouse: MouseEvent) -> } match mouse.kind { - MouseEventKind::Moved => true, + MouseEventKind::Moved => { + let over_sidebar = mouse_hits_rect(mouse, app.viewport.last_sidebar_area); + !(over_sidebar || app.sidebar_hover_tooltip.is_some()) + } MouseEventKind::Drag(_) => { !app.viewport.transcript_selection.dragging && !app.viewport.transcript_scrollbar_dragging diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index ca75e80c..66ec4eb4 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -6,7 +6,7 @@ use crate::config::{ use crate::config_ui::{self, WebConfigSession, WebConfigSessionEvent}; use crate::core::engine::mock_engine_handle; use crate::tui::active_cell::ActiveCell; -use crate::tui::app::ToolDetailRecord; +use crate::tui::app::{SidebarHoverRow, SidebarHoverSection, ToolDetailRecord}; use crate::tui::file_mention::{ apply_mention_menu_selection, find_file_mention_completions, partial_file_mention_at_cursor, try_autocomplete_file_mention, user_request_with_file_mentions, visible_mention_menu_entries, @@ -700,6 +700,60 @@ fn loading_mouse_filter_keeps_active_drags() { assert!(!should_drop_loading_mouse_motion(&app, drag)); } +#[test] +fn loading_mouse_filter_allows_sidebar_hover_popovers() { + let mut app = create_test_app(); + app.is_loading = true; + app.viewport.last_sidebar_area = Some(Rect::new(60, 4, 20, 6)); + app.sidebar_hover.sections.push(SidebarHoverSection { + content_area: Rect::new(60, 4, 20, 6), + lines: vec!["Visible row".to_string()], + rows: vec![SidebarHoverRow { + row_y: 5, + display_text: "Truncated".to_string(), + full_text: "Full sidebar task label".to_string(), + detail: Some("Detailed context".to_string()), + is_truncated: true, + click_action: None, + }], + }); + let moved = MouseEvent { + kind: MouseEventKind::Moved, + column: 65, + row: 5, + modifiers: KeyModifiers::NONE, + }; + + assert!(!should_drop_loading_mouse_motion(&app, moved)); + handle_mouse_event(&mut app, moved); + + assert_eq!( + app.sidebar_hover_tooltip.as_deref(), + Some("Full sidebar task label\nDetailed context") + ); + assert_eq!(app.last_mouse_pos, Some((65, 5))); +} + +#[test] +fn loading_mouse_filter_allows_sidebar_hover_to_clear() { + let mut app = create_test_app(); + app.is_loading = true; + app.viewport.last_sidebar_area = Some(Rect::new(60, 4, 20, 6)); + app.sidebar_hover_tooltip = Some("Stale sidebar tooltip".to_string()); + let moved = MouseEvent { + kind: MouseEventKind::Moved, + column: 12, + row: 5, + modifiers: KeyModifiers::NONE, + }; + + assert!(!should_drop_loading_mouse_motion(&app, moved)); + handle_mouse_event(&mut app, moved); + + assert_eq!(app.sidebar_hover_tooltip, None); + assert_eq!(app.last_mouse_pos, Some((12, 5))); +} + #[test] fn jump_to_latest_button_click_scrolls_to_tail() { let mut app = create_test_app();