fix(tui): keep sidebar resize live during active turns
The loading mouse filter (should_drop_loading_mouse_motion) dropped all Drag events while app.is_loading unless a transcript selection or scrollbar drag was active. A sidebar resize started on the handle (Down passes the filter) then never received its Drag events, leaving the resize wedged mid-gesture during live runs (#3063, symptom of the #3096 subagent-runtime pressure on the TUI). - Allow Drag events through the loading filter while app.sidebar_resizing is set. - Clear last_sidebar_area / last_sidebar_handle_area and any in-flight resize when the sidebar is hidden or doesn't fit, so stale handle hit-areas can't capture clicks. - Tests: resize down/drag/up while loading, mouse-up outside the handle still ends the resize. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,11 @@ pub(crate) fn should_drop_loading_mouse_motion(app: &App, mouse: MouseEvent) ->
|
||||
!(over_sidebar || app.sidebar_hover_tooltip.is_some())
|
||||
}
|
||||
MouseEventKind::Drag(_) => {
|
||||
// Sidebar drag-to-resize must stay live during active turns —
|
||||
// dropping these events wedges the resize state mid-drag (#3063).
|
||||
!app.viewport.transcript_selection.dragging
|
||||
&& !app.viewport.transcript_scrollbar_dragging
|
||||
&& !app.sidebar_resizing
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
||||
@@ -7654,6 +7654,15 @@ fn render(f: &mut Frame, app: &mut App) {
|
||||
// hit-testing can route scroll events correctly.
|
||||
app.viewport.last_sidebar_area = sidebar_area;
|
||||
|
||||
// When the sidebar is hidden or doesn't fit, drop its stale mouse
|
||||
// hit areas and any in-flight resize so clicks on those columns
|
||||
// don't keep routing to an invisible handle (#3063).
|
||||
if sidebar_area.is_none() {
|
||||
app.last_sidebar_area = None;
|
||||
app.last_sidebar_handle_area = None;
|
||||
app.sidebar_resizing = false;
|
||||
}
|
||||
|
||||
let chat_widget = ChatWidget::new(app, chat_area);
|
||||
let buf = f.buffer_mut();
|
||||
chat_widget.render(chat_area, buf);
|
||||
|
||||
@@ -727,6 +727,53 @@ fn loading_mouse_filter_keeps_active_drags() {
|
||||
app.viewport.transcript_selection.dragging = false;
|
||||
app.viewport.transcript_scrollbar_dragging = true;
|
||||
assert!(!should_drop_loading_mouse_motion(&app, drag));
|
||||
|
||||
// Sidebar drag-to-resize must also survive the loading filter (#3063).
|
||||
app.viewport.transcript_scrollbar_dragging = false;
|
||||
app.sidebar_resizing = true;
|
||||
assert!(!should_drop_loading_mouse_motion(&app, drag));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loading_mouse_filter_allows_sidebar_resize_down_drag_up() {
|
||||
let mut app = create_test_app();
|
||||
app.is_loading = true;
|
||||
setup_resize_handle(&mut app, 80, 33, 120);
|
||||
|
||||
let down = MouseEvent {
|
||||
kind: MouseEventKind::Down(MouseButton::Left),
|
||||
column: 80,
|
||||
row: 5,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
};
|
||||
assert!(!should_drop_loading_mouse_motion(&app, down));
|
||||
handle_mouse_event(&mut app, down);
|
||||
assert!(app.sidebar_resizing, "down on handle starts resize");
|
||||
|
||||
let drag = MouseEvent {
|
||||
kind: MouseEventKind::Drag(MouseButton::Left),
|
||||
column: 76,
|
||||
row: 5,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
};
|
||||
assert!(
|
||||
!should_drop_loading_mouse_motion(&app, drag),
|
||||
"resize drag must not be dropped while loading"
|
||||
);
|
||||
handle_mouse_event(&mut app, drag);
|
||||
let expected = ((37u32 * 100) / 120) as u16;
|
||||
assert_eq!(app.sidebar_width_percent, expected);
|
||||
|
||||
let up = MouseEvent {
|
||||
kind: MouseEventKind::Up(MouseButton::Left),
|
||||
column: 76,
|
||||
row: 5,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
};
|
||||
assert!(!should_drop_loading_mouse_motion(&app, up));
|
||||
handle_mouse_event(&mut app, up);
|
||||
assert!(!app.sidebar_resizing);
|
||||
assert!(app.sidebar_width_dirty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3626,6 +3673,32 @@ fn sidebar_resize_up_ends_resizing_and_marks_dirty() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_resize_up_outside_handle_still_ends_resizing() {
|
||||
let mut app = create_test_app();
|
||||
setup_resize_handle(&mut app, 80, 33, 120);
|
||||
app.sidebar_resizing = true;
|
||||
app.sidebar_resize_anchor_x = 80;
|
||||
app.sidebar_resize_anchor_width = 33;
|
||||
|
||||
// Release far away from the handle and the sidebar entirely.
|
||||
handle_mouse_event(
|
||||
&mut app,
|
||||
MouseEvent {
|
||||
kind: MouseEventKind::Up(MouseButton::Left),
|
||||
column: 5,
|
||||
row: 20,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
},
|
||||
);
|
||||
|
||||
assert!(
|
||||
!app.sidebar_resizing,
|
||||
"mouse up must clear resize state even outside the handle"
|
||||
);
|
||||
assert!(app.sidebar_width_dirty);
|
||||
}
|
||||
|
||||
fn make_subagent(
|
||||
id: &str,
|
||||
status: crate::tools::subagent::SubAgentStatus,
|
||||
|
||||
Reference in New Issue
Block a user