fix(ui): list: ensure the offset line does not go negative when scrolling up

Ayman Bagabas created

When scrolling up in the list, the offset line could become negative if
there was a gap between items. This change ensures that the offset line
is clamped to zero in such cases, preventing potential rendering issues.

This also adds a check to avoid unnecessary scrolling when already at
the bottom of the list. The calculation of item height has been
simplified by using strings.Count directly.

Change summary

internal/ui/list/list.go | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)

Detailed changes

internal/ui/list/list.go 🔗

@@ -158,7 +158,7 @@ func (l *List) getItem(idx int) renderedItem {
 
 	rendered := item.Render(l.width)
 	rendered = strings.TrimRight(rendered, "\n")
-	height := countLines(rendered)
+	height := strings.Count(rendered, "\n") + 1
 	ri := renderedItem{
 		content: rendered,
 		height:  height,
@@ -190,13 +190,18 @@ func (l *List) ScrollBy(lines int) {
 	}
 
 	if lines > 0 {
+		if l.AtBottom() {
+			// Already at bottom
+			return
+		}
+
 		// Scroll down
 		l.offsetLine += lines
 		currentItem := l.getItem(l.offsetIdx)
 		for l.offsetLine >= currentItem.height {
 			l.offsetLine -= currentItem.height
 			if l.gap > 0 {
-				l.offsetLine -= l.gap
+				l.offsetLine = max(0, l.offsetLine-l.gap)
 			}
 
 			// Move to next item
@@ -219,14 +224,13 @@ func (l *List) ScrollBy(lines int) {
 		// Scroll up
 		l.offsetLine += lines // lines is negative
 		for l.offsetLine < 0 {
-			if l.offsetIdx <= 0 {
+			// Move to previous item
+			l.offsetIdx--
+			if l.offsetIdx < 0 {
 				// Reached top
 				l.ScrollToTop()
 				break
 			}
-
-			// Move to previous item
-			l.offsetIdx--
 			prevItem := l.getItem(l.offsetIdx)
 			totalHeight := prevItem.height
 			if l.gap > 0 {
@@ -642,11 +646,3 @@ func (l *List) findItemAtY(_, y int) (itemIdx int, itemY int) {
 
 	return -1, -1
 }
-
-// countLines counts the number of lines in a string.
-func countLines(s string) int {
-	if s == "" {
-		return 1
-	}
-	return strings.Count(s, "\n") + 1
-}