From 28801d0a3650955ab83150d3c0928d35eb5928e3 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 20 May 2026 10:56:50 +0200 Subject: [PATCH 1/2] optimize session browser rendering to only render visible window --- pkg/tui/dialog/session_browser.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/pkg/tui/dialog/session_browser.go b/pkg/tui/dialog/session_browser.go index 61190256f..f012e2a38 100644 --- a/pkg/tui/dialog/session_browser.go +++ b/pkg/tui/dialog/session_browser.go @@ -253,6 +253,9 @@ func (d *sessionBrowserDialog) filterSessions() { if d.selected >= len(d.filtered) { d.selected = max(0, len(d.filtered)-1) } + // Keep the scrollview's totalHeight in sync so EnsureLineVisible and the + // scrollbar clamp correctly even before View() runs. + d.scrollview.SetContent(nil, len(d.filtered)) d.scrollview.SetScrollOffset(0) } @@ -285,13 +288,6 @@ func (d *sessionBrowserDialog) View() string { dialogWidth, _, contentWidth := d.dialogSize() d.textInput.SetWidth(contentWidth) - // Build all session lines - var allLines []string - for i, sess := range d.filtered { - allLines = append(allLines, d.renderSession(sess, i == d.selected, contentWidth)) - } - - // Configure scrollview and let it handle slicing + rendering regionWidth := contentWidth + d.scrollview.ReservedCols() visibleLines := d.scrollview.VisibleHeight() @@ -299,10 +295,16 @@ func (d *sessionBrowserDialog) View() string { dialogRow, dialogCol := d.Position() d.scrollview.SetPosition(dialogCol+3, dialogRow+sessionBrowserListStartY) - d.scrollview.SetContent(allLines, len(allLines)) + // Tell the scrollview the total content height and re-clamp the offset + // against it. Pass nil for lines because we render only the visible + // window below — rendering every row on every keystroke is the dominant + // cost when there are many sessions. + total := len(d.filtered) + d.scrollview.SetContent(nil, total) + d.scrollview.SetScrollOffset(d.scrollview.ScrollOffset()) var scrollableContent string - if len(d.filtered) == 0 { + if total == 0 { // Empty state: render manually so "No sessions found" is centered emptyLines := []string{"", styles.DialogContentStyle. Italic(true).Align(lipgloss.Center).Width(contentWidth). @@ -312,7 +314,13 @@ func (d *sessionBrowserDialog) View() string { } scrollableContent = d.scrollview.ViewWithLines(emptyLines) } else { - scrollableContent = d.scrollview.View() + offset := d.scrollview.ScrollOffset() + end := min(offset+visibleLines, total) + windowLines := make([]string, 0, end-offset) + for i := offset; i < end; i++ { + windowLines = append(windowLines, d.renderSession(d.filtered[i], i == d.selected, contentWidth)) + } + scrollableContent = d.scrollview.ViewWithLines(windowLines) } // Build title with session count and optional star-filter indicator. From 61fbb2ccd082c795a4490bdb35be2533f6c92ca5 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Wed, 20 May 2026 11:02:44 +0200 Subject: [PATCH 2/2] clarify SetScrollOffset re-clamps against updated content height --- pkg/tui/dialog/session_browser.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/tui/dialog/session_browser.go b/pkg/tui/dialog/session_browser.go index f012e2a38..2e1feab6f 100644 --- a/pkg/tui/dialog/session_browser.go +++ b/pkg/tui/dialog/session_browser.go @@ -295,10 +295,11 @@ func (d *sessionBrowserDialog) View() string { dialogRow, dialogCol := d.Position() d.scrollview.SetPosition(dialogCol+3, dialogRow+sessionBrowserListStartY) - // Tell the scrollview the total content height and re-clamp the offset - // against it. Pass nil for lines because we render only the visible - // window below — rendering every row on every keystroke is the dominant - // cost when there are many sessions. + // Tell the scrollview the total content height; pass nil for lines + // because we render only the visible window below. Rendering every row + // on every keystroke is the dominant cost when there are many sessions. + // The follow-up SetScrollOffset call re-clamps the offset against the + // (possibly shrunk) total — it is intentionally not a no-op. total := len(d.filtered) d.scrollview.SetContent(nil, total) d.scrollview.SetScrollOffset(d.scrollview.ScrollOffset())