@@ -8,11 +8,9 @@ import (
88 "net/url"
99 "path"
1010 "strings"
11- "sync"
1211 "time"
1312
1413 bubblesprogress "charm.land/bubbles/v2/progress"
15- tea "charm.land/bubbletea/v2"
1614 "charm.land/lipgloss/v2"
1715 "github.com/charmbracelet/x/term"
1816 "github.com/lets-cli/lets/internal/fetch"
@@ -30,9 +28,7 @@ type Observer struct {
3028 writer io.Writer
3129 width int
3230 noColor bool
33- animate bool
3431 throttle time.Duration
35- finalPause time.Duration
3632 fillColor color.Color
3733 emptyColor color.Color
3834 now func () time.Time
@@ -73,12 +69,6 @@ func WithThrottle(throttle time.Duration) Option {
7369 }
7470}
7571
76- func WithFinalPause (finalPause time.Duration ) Option {
77- return func (observer * Observer ) {
78- observer .finalPause = finalPause
79- }
80- }
81-
8272func WithNow (now func () time.Time ) Option {
8373 return func (observer * Observer ) {
8474 observer .now = now
@@ -87,12 +77,10 @@ func WithNow(now func() time.Time) Option {
8777
8878func New (writer io.Writer , options ... Option ) * Observer {
8979 observer := & Observer {
90- writer : writer ,
91- width : detectWidth (writer ),
92- throttle : 100 * time .Millisecond ,
93- finalPause : 750 * time .Millisecond ,
94- animate : isTerminal (writer ),
95- now : time .Now ,
80+ writer : writer ,
81+ width : detectWidth (writer ),
82+ throttle : 100 * time .Millisecond ,
83+ now : time .Now ,
9684 }
9785
9886 for _ , option := range options {
@@ -111,10 +99,6 @@ func New(writer io.Writer, options ...Option) *Observer {
11199}
112100
113101func (o * Observer ) Start (info fetch.ProgressInfo ) fetch.ProgressTracker { //nolint:ireturn // Implements fetch.ProgressObserver.
114- if info .TotalBytes > 0 && o .animate {
115- return newAnimatedTracker (o , info )
116- }
117-
118102 tracker := & manualTracker {
119103 observer : o ,
120104 info : info ,
@@ -243,226 +227,9 @@ func (t *manualTracker) progressModel(width int) bubblesprogress.Model {
243227 return model
244228}
245229
246- type animatedTracker struct {
247- observer * Observer
248- program * tea.Program
249- done chan struct {}
250- label string
251- read int64
252- total int64
253- lastUpdate time.Time
254- }
255-
256- func newAnimatedTracker (observer * Observer , info fetch.ProgressInfo ) * animatedTracker {
257- ready := make (chan struct {})
258- done := make (chan struct {})
259- label := downloadLabel (info .URL )
260- _ , _ = fmt .Fprintln (observer .writer , labelLine ("Downloading" , label , observer .width ))
261- model := newProgressModel (observer , label , info .TotalBytes , ready )
262- program := tea .NewProgram (
263- model ,
264- tea .WithInput (nil ),
265- tea .WithOutput (observer .writer ),
266- tea .WithoutSignals (),
267- )
268-
269- tracker := & animatedTracker {
270- observer : observer ,
271- program : program ,
272- done : done ,
273- label : label ,
274- total : info .TotalBytes ,
275- }
276-
277- go func () {
278- _ , _ = program .Run ()
279-
280- close (done )
281- }()
282-
283- <- ready
284-
285- return tracker
286- }
287-
288- func (t * animatedTracker ) Add (n int64 ) {
289- t .read += n
290-
291- now := t .observer .now ()
292- if t .observer .throttle > 0 && ! t .lastUpdate .IsZero () && now .Sub (t .lastUpdate ) < t .observer .throttle {
293- return
294- }
295-
296- t .program .Send (progressMsg {read : t .read , total : t .total })
297- t .lastUpdate = now
298- }
299-
300- func (t * animatedTracker ) Done (err error ) {
301- if err != nil {
302- t .program .Send (progressErrMsg {})
303- } else {
304- t .program .Send (progressDoneMsg {read : t .read , total : t .total })
305- }
306-
307- <- t .done
308-
309- if err == nil {
310- _ , _ = fmt .Fprintf (t .observer .writer , "%s\n " , t .progressLine ())
311- }
312- }
313-
314- func (t * animatedTracker ) progressLine () string {
315- bar := t .progressModel ().ViewAs (1 )
316- return fmt .Sprintf ("%s 100%% %s/%s" , bar , formatBytes (t .total ), formatBytes (t .total ))
317- }
318-
319- func (t * animatedTracker ) progressModel () bubblesprogress.Model {
320- model := bubblesprogress .New (
321- bubblesprogress .WithWidth (barWidthForTerminal (t .observer .width )),
322- bubblesprogress .WithoutPercentage (),
323- bubblesprogress .WithFillCharacters ('#' , '-' ),
324- )
325- if t .observer .noColor {
326- model .FullColor = nil
327- model .EmptyColor = nil
328- } else {
329- applyProgressColors (& model , t .observer .fillColor , t .observer .emptyColor )
330- }
331-
332- return model
333- }
334-
335- type progressMsg struct {
336- read int64
337- total int64
338- }
339-
340- type progressDoneMsg struct {
341- read int64
342- total int64
343- }
344-
345- type progressErrMsg struct {}
346-
347- type progressQuitMsg struct {}
348-
349- type progressModel struct {
350- label string
351- read int64
352- total int64
353- width int
354- finalPause time.Duration
355- ready chan struct {}
356- readyOnce * sync.Once
357- progress bubblesprogress.Model
358- }
359-
360- func newProgressModel (observer * Observer , label string , total int64 , ready chan struct {}) progressModel {
361- model := bubblesprogress .New (
362- bubblesprogress .WithWidth (barWidthForTerminal (observer .width )),
363- bubblesprogress .WithoutPercentage (),
364- bubblesprogress .WithFillCharacters ('#' , '-' ),
365- )
366- if observer .noColor {
367- model .FullColor = nil
368- model .EmptyColor = nil
369- } else {
370- applyProgressColors (& model , observer .fillColor , observer .emptyColor )
371- }
372-
373- return progressModel {
374- label : label ,
375- total : total ,
376- width : observer .width ,
377- finalPause : observer .finalPause ,
378- ready : ready ,
379- readyOnce : & sync.Once {},
380- progress : model ,
381- }
382- }
383-
384- func (m progressModel ) Init () tea.Cmd {
385- m .readyOnce .Do (func () {
386- close (m .ready )
387- })
388-
389- return nil
390- }
391-
392- func (m progressModel ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) { //nolint:ireturn // Required by Bubble Tea's model interface.
393- switch msg := msg .(type ) {
394- case tea.WindowSizeMsg :
395- m .width = msg .Width
396- m .progress .SetWidth (barWidthForTerminal (msg .Width ))
397-
398- return m , nil
399-
400- case progressMsg :
401- m .read = msg .read
402- m .total = msg .total
403-
404- return m , m .progress .SetPercent (m .percent ())
405-
406- case progressDoneMsg :
407- m .read = msg .read
408- m .total = msg .total
409-
410- return m , tea .Batch (m .progress .SetPercent (1 ), m .quitAfterFinalPause ())
411-
412- case progressErrMsg :
413- return m , tea .Quit
414-
415- case progressQuitMsg :
416- return m , tea .Quit
417-
418- case bubblesprogress.FrameMsg :
419- var cmd tea.Cmd
420-
421- m .progress , cmd = m .progress .Update (msg )
422-
423- return m , cmd
424-
425- default :
426- return m , nil
427- }
428- }
429-
430- func (m progressModel ) View () tea.View {
431- return tea .NewView (m .progressLine ())
432- }
433-
434- func (m progressModel ) progressLine () string {
435- return fmt .Sprintf ("%s %3.0f%% %s/%s" , m .progress .View (), m .percent ()* 100 , formatBytes (m .read ), formatBytes (m .total ))
436- }
437-
438- func (m progressModel ) percent () float64 {
439- if m .total <= 0 {
440- return 0
441- }
442-
443- return clamp (float64 (m .read )/ float64 (m .total ), 0 , 1 )
444- }
445-
446- func (m progressModel ) quitAfterFinalPause () tea.Cmd {
447- return tea .Tick (m .finalPause , func (time.Time ) tea.Msg {
448- return progressQuitMsg {}
449- })
450- }
451-
452- func barWidthForTerminal (width int ) int {
453- suffixWidth := lipgloss .Width (" 100% 1023.9 KiB/1023.9 KiB" )
454-
455- spaceForBar := width - 1 - suffixWidth
456- if spaceForBar < minBarWidth {
457- return minBarWidth
458- }
459-
460- return min (maxBarWidth , max (minBarWidth , spaceForBar ))
461- }
462-
463230func detectWidth (writer io.Writer ) int {
464231 file , ok := writer .(term.File )
465- if ! ok || ! isTerminal (writer ) {
232+ if ! ok || ! util . IsTerminalWriter (writer ) {
466233 return defaultWidth
467234 }
468235
@@ -474,10 +241,6 @@ func detectWidth(writer io.Writer) int {
474241 return width
475242}
476243
477- func isTerminal (writer io.Writer ) bool {
478- return util .IsTerminalWriter (writer )
479- }
480-
481244func applyProgressColors (model * bubblesprogress.Model , fill , empty color.Color ) {
482245 if fill != nil {
483246 model .FullColor = fill
0 commit comments