From 923bc2f099c45e16bfeb3f955b0acb7bdac22085 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Wed, 20 Aug 2025 19:45:46 +0200 Subject: [PATCH] fix: virtual scroll fixes for tests --- internal/tui/exp/list/list.go | 66 +++++++++++++++++++++++++++--- internal/tui/exp/list/list_test.go | 14 +++++-- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/internal/tui/exp/list/list.go b/internal/tui/exp/list/list.go index 6266d2c8e6ebf6a047f52381c2f8123e688e1c0b..65db55cff5fa886cee31313ba456b395035c3b6e 100644 --- a/internal/tui/exp/list/list.go +++ b/internal/tui/exp/list/list.go @@ -217,7 +217,43 @@ func New[T Item](items []T, opts ...ListOption) List[T] { // Init implements List. func (l *list[T]) Init() tea.Cmd { - return l.render() + // Ensure we have width and height + if l.width <= 0 || l.height <= 0 { + // Can't calculate positions without dimensions + return nil + } + + // Set size for all items + var cmds []tea.Cmd + for _, item := range slices.Collect(l.items.Seq()) { + if cmd := item.SetSize(l.width, l.height); cmd != nil { + cmds = append(cmds, cmd) + } + } + + // For backward lists, we need to position at the bottom after initial render + if l.direction == DirectionBackward && l.offset == 0 && l.items.Len() > 0 { + // Calculate positions first + l.calculateItemPositions() + // Set offset to show the bottom of the list + if l.virtualHeight > l.height { + l.offset = 0 // In backward mode, offset 0 means bottom + } + // Select the last item + l.selectLastItem() + } + + // Scroll to the selected item for initial positioning + if l.focused { + l.scrollToSelection() + } + + renderCmd := l.render() + if renderCmd != nil { + cmds = append(cmds, renderCmd) + } + + return tea.Batch(cmds...) } // Update implements List. @@ -546,12 +582,10 @@ func (l *list[T]) scrollToSelection() { if l.direction == DirectionForward { l.offset = rItem.start } else { - if l.virtualHeight > 0 { - l.offset = l.virtualHeight - rItem.end - } else { + // For backward direction, we want to show the bottom of the item + // offset = 0 means bottom of list is visible l.offset = 0 } - } return } @@ -830,6 +864,26 @@ func (l *list[T]) renderVirtualScrolling() string { // Calculate viewport bounds viewStart, viewEnd := l.viewPosition() + // Debug: Check if viewport is valid + if viewEnd < viewStart { + // Return empty viewport + var lines []string + for i := 0; i < l.height; i++ { + lines = append(lines, "") + } + return strings.Join(lines, "\n") + } + + // Debug: Check if we have any rendered items + if l.renderedItems.Len() == 0 { + // No items have been calculated yet, return empty + var lines []string + for i := 0; i < l.height; i++ { + lines = append(lines, "") + } + return strings.Join(lines, "\n") + } + // Find which items are visible var visibleItems []struct { item T @@ -846,6 +900,7 @@ func (l *list[T]) renderVirtualScrolling() string { rItem, ok := l.renderedItems.Get(item.ID()) if !ok { + // Item not yet calculated, skip it continue } @@ -865,6 +920,7 @@ func (l *list[T]) renderVirtualScrolling() string { } if len(visibleItems) == 0 { + // No visible items found - this shouldn't happen if viewport is valid // Return empty lines to maintain height var lines []string for i := 0; i < l.height; i++ { diff --git a/internal/tui/exp/list/list_test.go b/internal/tui/exp/list/list_test.go index 63cfa599e8ce1c96aad1cae67243caa2b097ee0b..bf7b9f98e0b018f3604e5b8a17c2e3dcb8695e91 100644 --- a/internal/tui/exp/list/list_test.go +++ b/internal/tui/exp/list/list_test.go @@ -160,8 +160,11 @@ func TestList(t *testing.T) { for i := range 30 { expectedLines += (i + 1) * 1 } - assert.Equal(t, expectedLines, lipgloss.Height(l.rendered)) - assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline") + // With virtual scrolling, rendered height should be viewport height (10) + assert.Equal(t, 10, lipgloss.Height(l.rendered)) + if len(l.rendered) > 0 { + assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline") + } start, end := l.viewPosition() assert.Equal(t, 0, start) assert.Equal(t, 9, end) @@ -198,8 +201,11 @@ func TestList(t *testing.T) { for i := range 30 { expectedLines += (i + 1) * 1 } - assert.Equal(t, expectedLines, lipgloss.Height(l.rendered)) - assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline") + // With virtual scrolling, rendered height should be viewport height (10) + assert.Equal(t, 10, lipgloss.Height(l.rendered)) + if len(l.rendered) > 0 { + assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline") + } start, end := l.viewPosition() assert.Equal(t, expectedLines-10, start) assert.Equal(t, expectedLines-1, end)