From fdea0bbbf606fcd8cfec1613a3aaf610bfb9b3cf Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Mon, 6 Apr 2026 16:00:34 +0300 Subject: [PATCH] fix(ui): add sidebar section headers and align workspace drag slots --- src/app/input.rs | 27 ++++++++++++++++++++++++++- src/ui.rs | 22 +++++++++++++++++----- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/app/input.rs b/src/app/input.rs index fdb668c6..da38f1d7 100644 --- a/src/app/input.rs +++ b/src/app/input.rs @@ -3361,7 +3361,32 @@ mod tests { crate::ui::compute_view(&mut app.state, Rect::new(0, 0, 106, 20)); assert_eq!(app.state.workspace_drop_index_at_row(0), Some(0)); - assert_eq!(app.state.workspace_drop_index_at_row(1), Some(1)); + assert_eq!(app.state.workspace_drop_index_at_row(1), Some(0)); + assert_eq!(app.state.workspace_drop_index_at_row(2), Some(0)); + assert_eq!(app.state.workspace_drop_index_at_row(3), Some(1)); + } + + #[test] + fn bottom_drop_slot_stays_below_last_workspace_not_footer() { + let mut app = app_for_mouse_test(); + app.state.workspaces = vec![ + Workspace::test_new("a"), + Workspace::test_new("b"), + Workspace::test_new("c"), + ]; + crate::ui::compute_view(&mut app.state, Rect::new(0, 0, 106, 20)); + + let cards = &app.state.view.workspace_card_areas; + let bottom_slot = crate::ui::workspace_drop_indicator_row( + cards, + app.state.workspace_list_rect(), + cards.len(), + ) + .unwrap(); + + let last = cards.last().unwrap().rect; + assert_eq!(bottom_slot, last.y + last.height); + assert!(bottom_slot < app.state.sidebar_footer_rect().y.saturating_sub(1)); } #[test] diff --git a/src/ui.rs b/src/ui.rs index 7b5f25b7..d8322f6f 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -145,7 +145,7 @@ pub(crate) fn compute_workspace_card_areas( let ws_h = (total_h + 1) / 2; let ws_area = Rect::new(content.x, content.y, content.width, ws_h as u16); let list_bottom = ws_area.y + ws_area.height.saturating_sub(1); - let mut row_y = ws_area.y; + let mut row_y = ws_area.y.saturating_add(2); let mut cards = Vec::new(); for (ws_idx, ws) in app.workspaces.iter().enumerate() { @@ -431,7 +431,7 @@ pub(crate) fn workspace_drop_indicator_row( if insert_idx == 0 { return cards .first() - .map(|card| card.rect.y) + .and_then(|card| card.rect.y.checked_sub(1)) .filter(|y| *y < list_bottom); } if let Some(card) = cards.get(insert_idx) { @@ -439,7 +439,7 @@ pub(crate) fn workspace_drop_indicator_row( } cards .last() - .map(|_| list_bottom.saturating_sub(1)) + .map(|card| card.rect.y.saturating_add(card.rect.height)) .filter(|y| *y < list_bottom) } @@ -512,9 +512,21 @@ fn render_workspace_list(app: &AppState, frame: &mut Frame, area: Rect, is_navig _ => None, }; - // Reserve last row for "new" button + // Section header + reserve last row for "new" button let list_bottom = area.y + area.height.saturating_sub(1); - let mut row_y = area.y; + if area.height > 0 { + frame.render_widget( + Paragraph::new(Line::from(vec![Span::styled( + " spaces", + Style::default().fg(p.overlay0).add_modifier(Modifier::BOLD), + )])), + Rect::new(area.x, area.y, area.width, 1), + ); + } + let mut row_y = area.y.saturating_add(1); + + // Blank line for breathing room + row_y = row_y.saturating_add(1); for (i, ws) in app.workspaces.iter().enumerate() { if row_y + 1 >= list_bottom {