@@ -2,11 +2,22 @@ package ui
22
33import (
44 "fmt"
5+ "os"
56 "strings"
67 "sync"
78 "time"
89
910 "github.com/charmbracelet/lipgloss"
11+ "golang.org/x/term"
12+ )
13+
14+ const (
15+ minBarWidth = 20
16+ defaultBarWidth = 40
17+ minPkgWidth = 10
18+ defaultPkgWidth = 25
19+ statusWidth = 16 // " 48/48 (100%)"
20+ etaWidth = 8 // "~10m30s "
1021)
1122
1223var (
@@ -30,7 +41,8 @@ type ProgressTracker struct {
3041 total int
3142 completed int
3243 active map [string ]bool
33- width int
44+ barWidth int
45+ pkgWidth int
3446 startTime time.Time
3547 mu sync.Mutex
3648 spinnerIdx int
@@ -40,10 +52,21 @@ type ProgressTracker struct {
4052
4153var spinnerFrames = []string {"⠋" , "⠙" , "⠹" , "⠸" , "⠼" , "⠴" , "⠦" , "⠧" , "⠇" , "⠏" }
4254
55+ func getTerminalWidth () int {
56+ if w , _ , err := term .GetSize (int (os .Stdout .Fd ())); err == nil && w > 0 {
57+ return w
58+ }
59+ return 80
60+ }
61+
4362func NewProgressTracker (total int ) * ProgressTracker {
63+ termWidth := getTerminalWidth ()
64+ barWidth , pkgWidth := calculateWidths (termWidth )
65+
4466 p := & ProgressTracker {
4567 total : total ,
46- width : 40 ,
68+ barWidth : barWidth ,
69+ pkgWidth : pkgWidth ,
4770 startTime : time .Now (),
4871 active : make (map [string ]bool ),
4972 spinnerStop : make (chan bool ),
@@ -70,6 +93,28 @@ func NewProgressTracker(total int) *ProgressTracker {
7093 return p
7194}
7295
96+ func calculateWidths (termWidth int ) (barWidth , pkgWidth int ) {
97+ fixed := statusWidth + etaWidth
98+ available := termWidth - fixed - 2
99+
100+ if available < minBarWidth + minPkgWidth {
101+ return minBarWidth , minPkgWidth
102+ }
103+
104+ barWidth = available * 55 / 100
105+ pkgWidth = available - barWidth
106+
107+ if barWidth > defaultBarWidth {
108+ barWidth = defaultBarWidth
109+ pkgWidth = available - barWidth
110+ }
111+ if pkgWidth > defaultPkgWidth + 10 {
112+ pkgWidth = defaultPkgWidth + 10
113+ }
114+
115+ return barWidth , pkgWidth
116+ }
117+
73118func (p * ProgressTracker ) SetCurrent (pkgName string ) {
74119 p .mu .Lock ()
75120 defer p .mu .Unlock ()
@@ -88,10 +133,20 @@ func (p *ProgressTracker) Complete(pkgName string) {
88133 p .render ()
89134}
90135
136+ func truncate (s string , maxLen int ) string {
137+ if len (s ) <= maxLen {
138+ return s
139+ }
140+ if maxLen <= 3 {
141+ return s [:maxLen ]
142+ }
143+ return s [:maxLen - 3 ] + "..."
144+ }
145+
91146func (p * ProgressTracker ) render () {
92147 percent := float64 (p .completed ) / float64 (p .total )
93- filled := int (percent * float64 (p .width ))
94- empty := p .width - filled
148+ filled := int (percent * float64 (p .barWidth ))
149+ empty := p .barWidth - filled
95150
96151 bar := progressBarStyle .Render (strings .Repeat ("█" , filled )) +
97152 progressBgStyle .Render (strings .Repeat ("░" , empty ))
@@ -115,13 +170,13 @@ func (p *ProgressTracker) render() {
115170 break
116171 }
117172 }
118- if len (activeDisplay ) > 20 {
119- activeDisplay = activeDisplay [:17 ] + "..."
173+ suffixLen := 0
174+ if activeCount > 1 {
175+ suffixLen = len (fmt .Sprintf (" +%d" , activeCount - 1 ))
120176 }
177+ activeDisplay = truncate (activeDisplay , p .pkgWidth - suffixLen )
121178 if activeCount > 1 {
122- activeDisplay = fmt .Sprintf ("%-20s +%d" , activeDisplay , activeCount - 1 )
123- } else {
124- activeDisplay = fmt .Sprintf ("%-20s" , activeDisplay )
179+ activeDisplay = fmt .Sprintf ("%s +%d" , activeDisplay , activeCount - 1 )
125180 }
126181 }
127182
0 commit comments