package main import ( "strings" "time" "github.com/charmbracelet/bubbles/progress" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render const ( padding = 2 maxWidth = 80 ) type progressMsg float64 type progressErrMsg struct{ err error } func finalPause() tea.Cmd { return tea.Tick(time.Millisecond*750, func(_ time.Time) tea.Msg { return nil }) } type model struct { url, path string pw *progressWriter progress progress.Model err error } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: return m, tea.Quit case tea.WindowSizeMsg: m.progress.Width = msg.Width - padding*2 - 4 if m.progress.Width > maxWidth { m.progress.Width = maxWidth } return m, nil case progressErrMsg: m.err = msg.err return m, tea.Quit case progressMsg: var cmds []tea.Cmd if msg >= 1.0 { cmds = append(cmds, tea.Sequence(finalPause(), tea.Quit)) } cmds = append(cmds, m.progress.SetPercent(float64(msg))) return m, tea.Batch(cmds...) // FrameMsg is sent when the progress bar wants to animate itself case progress.FrameMsg: progressModel, cmd := m.progress.Update(msg) m.progress = progressModel.(progress.Model) return m, cmd default: return m, nil } } func (m model) View() string { if m.err != nil { return "Error downloading: " + m.err.Error() + "\n" } pad := strings.Repeat(" ", padding) return "\n" + pad + m.progress.View() + "\n\n" + pad + helpStyle("Press any key to quit") }