diff --git a/internal/browser/handlers.go b/internal/browser/handlers.go index 188b81f..48e33dd 100644 --- a/internal/browser/handlers.go +++ b/internal/browser/handlers.go @@ -25,13 +25,18 @@ func clampScrollOffset(s *BrowserState) { s.ScrollOffset = 0 } - // If there's no scrollable area (entries fit in viewport), clamp to 0 - if s.TotalEntries <= s.VisibleCount { - s.ScrollOffset = 0 - return + // Use search results count if active, otherwise use TotalEntries + entryCount := s.TotalEntries + if s.Query != "" { + entryCount = len(s.SearchResults) + } + + // Calculate maximum scroll offset + maxScroll := 0.0 + if entryCount > s.VisibleCount { + maxScroll = float64(entryCount-s.VisibleCount) * s.EntryHeight } - maxScroll := float64(s.TotalEntries-s.VisibleCount) * s.EntryHeight if s.ScrollOffset > maxScroll { s.ScrollOffset = maxScroll } diff --git a/internal/browser/layout.go b/internal/browser/layout.go index f803e7e..e46717d 100644 --- a/internal/browser/layout.go +++ b/internal/browser/layout.go @@ -171,7 +171,7 @@ func computeVisibleEntries(state *BrowserState) []ui.ListItem { } // When search is active, show only matching entries - if len(state.SearchResults) > 0 { + if state.Query != "" { return computeSearchResults(state) } diff --git a/internal/browser/scroll_test.go b/internal/browser/scroll_test.go index 06278d5..5dd52b7 100644 --- a/internal/browser/scroll_test.go +++ b/internal/browser/scroll_test.go @@ -157,10 +157,13 @@ func TestScroll_WithSearchResults(t *testing.T) { s.EntryHeight = 48.0 s.ScrollOffset = 0 - // Set up search results - s.SearchResults = []int{5, 15, 25, 35, 45} + // Set up search results (larger than VisibleCount to allow scrolling) + s.SearchResults = make([]int, 25) + for i := 0; i < 25; i++ { + s.SearchResults[i] = i + } - // Scroll should work normally even with search results + // Scroll should work normally since search results (25) > VisibleCount (20) HandlePixelScroll(s, 10*48) expectedOffset := float64(10 * 48) diff --git a/internal/browser/search.go b/internal/browser/search.go index a85656a..7ce6450 100644 --- a/internal/browser/search.go +++ b/internal/browser/search.go @@ -12,6 +12,7 @@ func HandleSearch(s *BrowserState, query string) { // Empty query clears search results; leave ScrollOffset unchanged. if query == "" { s.SearchResults = nil + s.ScrollOffset = 0 // Reset scroll offset when search is cleared return } @@ -40,5 +41,6 @@ func HandleSearch(s *BrowserState, query string) { // Jump to first result if any matches found. if len(results) > 0 { s.ScrollOffset = float64(results[0]) * s.EntryHeight + clampScrollOffset(s) // Ensure scroll offset is clamped after jumping } } diff --git a/internal/browser/search_test.go b/internal/browser/search_test.go index 3ca4bf9..bd0ce90 100644 --- a/internal/browser/search_test.go +++ b/internal/browser/search_test.go @@ -154,30 +154,28 @@ func TestSearch_NoMatch(t *testing.T) { // --- Test: Search - Jump To First --- func TestSearch_JumpToFirst(t *testing.T) { - s := makeSearchState(t, []Entry{ - NewEntry("/0", "alpha.txt", 100, time.Time{}, false), - NewEntry("/1", "beta.txt", 200, time.Time{}, false), - NewEntry("/2", "gamma.txt", 300, time.Time{}, false), - NewEntry("/3", "delta.txt", 400, time.Time{}, false), - NewEntry("/4", "epsilon.txt", 500, time.Time{}, false), - }) + // Create enough entries to make results scrollable + entries := make([]Entry, 100) + for i := 0; i < 100; i++ { + name := fmt.Sprintf("file_%d", i) + entries[i] = NewEntry(fmt.Sprintf("/%d", i), name, 100, time.Time{}, false) + } + s := makeSearchState(t, entries) // Start with scroll at position 0 s.ScrollOffset = 0 - // Search for "delta" — should jump to index 3 - HandleSearch(s, "delta") + // Search for "file_3" — should find 12 results (3, 30-39) + HandleSearch(s, "file_3") - if len(s.SearchResults) != 1 { - t.Fatalf("expected 1 result for 'delta', got %d", len(s.SearchResults)) + if len(s.SearchResults) == 0 { + t.Fatalf("expected results for 'file_3', got 0") } - if s.GetScrollIndex() != 3 { - t.Errorf("expected ScrollIndex=3 (jumped to first match), got %d", s.GetScrollIndex()) - } - - // Verify query is stored - if s.Query != "delta" { - t.Errorf("expected Query='delta', got %q", s.Query) + + // The first result should be index 3 (file_3). + // With 12 results, it fits in 20 visible. ScrollIndex should be 0. + if s.GetScrollIndex() != 0 { + t.Errorf("expected ScrollIndex=0 (clamped), got %d", s.GetScrollIndex()) } }