1package common
2
3import (
4 "strings"
5
6 "github.com/charmbracelet/crush/internal/ui/styles"
7)
8
9// Scrollbar renders a vertical scrollbar based on content and viewport size.
10// Returns an empty string if content fits within viewport (no scrolling needed).
11func Scrollbar(s *styles.Styles, height, contentSize, viewportSize, offset int) string {
12 if height <= 0 || contentSize <= viewportSize {
13 return ""
14 }
15
16 // Calculate thumb size (minimum 1 character).
17 thumbSize := max(1, height*viewportSize/contentSize)
18
19 // Calculate thumb position.
20 maxOffset := contentSize - viewportSize
21 if maxOffset <= 0 {
22 return ""
23 }
24
25 // Calculate where the thumb starts.
26 trackSpace := height - thumbSize
27 thumbPos := 0
28 if trackSpace > 0 && maxOffset > 0 {
29 thumbPos = min(trackSpace, offset*trackSpace/maxOffset)
30 }
31
32 // Build the scrollbar.
33 var sb strings.Builder
34 for i := range height {
35 if i > 0 {
36 sb.WriteString("\n")
37 }
38 if i >= thumbPos && i < thumbPos+thumbSize {
39 sb.WriteString(s.Dialog.ScrollbarThumb.Render(styles.ScrollbarThumb))
40 } else {
41 sb.WriteString(s.Dialog.ScrollbarTrack.Render(styles.ScrollbarTrack))
42 }
43 }
44
45 return sb.String()
46}