From 151ed9dcd6a0b87b84ae1d16c67eef6faba92ece Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sat, 8 Aug 2026 22:38:02 +0700 Subject: [PATCH] fix(sftp): give the transfers list its scrollbar too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last scroll area in the app without one. It caps at 200px and scrolls past it, so eight queued downloads run off the bottom edge with nothing to say so. The shared helper grows into a flex parent, which is wrong for a box that already carries its own height — taking flex_1 there would stretch it past its cap or collapse it, which is why this one had been left alone. `over_vertical_scroll` lays the same bar over an area that measured itself, so the layout is exactly what it was without it. Verified over a real SSH connection to localhost: queued eight downloads, confirmed the list still scrolls through them and the bar renders. --- src/ui/scrollbar.rs | 27 +++++++++++++++++++++++++++ src/ui/sftp.rs | 20 +++++++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/ui/scrollbar.rs b/src/ui/scrollbar.rs index d3be8413..89e1b75f 100644 --- a/src/ui/scrollbar.rs +++ b/src/ui/scrollbar.rs @@ -41,3 +41,30 @@ pub(crate) fn with_vertical_scrollbar( ) .into_any_element() } + +/// The same bar, for a scroll area that already carries its own height — +/// a `max_h` box, say. +/// +/// `with_vertical_scrollbar` grows into a flex parent, which is wrong for a box +/// that is already the size it wants to be: taking `flex_1` there would either +/// stretch it past its cap or collapse it. This wrapper only lays the bar over +/// whatever the area measured, so the layout is exactly what it was without it. +pub(crate) fn over_vertical_scroll( + id: impl Into, + scroll_area: impl IntoElement, + handle: &ScrollHandle, +) -> AnyElement { + div() + .relative() + .child(scroll_area) + .child( + div() + .absolute() + .top_0() + .left_0() + .right_0() + .bottom_0() + .child(Scrollbar::vertical(handle).id(id)), + ) + .into_any_element() +} diff --git a/src/ui/sftp.rs b/src/ui/sftp.rs index 479a8adb..60974196 100644 --- a/src/ui/sftp.rs +++ b/src/ui/sftp.rs @@ -151,6 +151,7 @@ pub(crate) struct SftpPanelState { editing_path_sub: Vec, pub(crate) poll_gen: u64, scroll: gpui::ScrollHandle, + transfers_scroll: gpui::ScrollHandle, _subs: Vec, } @@ -184,6 +185,7 @@ impl SftpPanelState { editing_path_sub: Vec::new(), poll_gen: 0, scroll: gpui::ScrollHandle::new(), + transfers_scroll: gpui::ScrollHandle::new(), _subs: vec![sub], } } @@ -1554,11 +1556,19 @@ impl Tty7App { } list }; - div() - .id("sftp-transfers-list") - .max_h(px(200.)) - .overflow_y_scroll() - .child(inner) + // The list caps at 200px and scrolls past it; it was the last + // scroll area in the app with nothing to say so. This wrapper only + // overlays the bar, so the box keeps the height it already had. + crate::ui::scrollbar::over_vertical_scroll( + "sftp-transfers-scrollbar", + div() + .id("sftp-transfers-list") + .max_h(px(200.)) + .overflow_y_scroll() + .track_scroll(&self.sftp_panel.transfers_scroll) + .child(inner), + &self.sftp_panel.transfers_scroll, + ) }); Some(