| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | package spin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/charmbracelet/bubbles/spinner" | 
					
						
							|  |  |  | 	tea "github.com/charmbracelet/bubbletea" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type model struct { | 
					
						
							|  |  |  | 	spinner  spinner.Model | 
					
						
							|  |  |  | 	text     string | 
					
						
							|  |  |  | 	quitting bool | 
					
						
							|  |  |  | 	err      error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type errMsg error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | type textMessage string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-24 14:27:19 -05:00
										 |  |  | func newModel(text string) *model { | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	s := spinner.New() | 
					
						
							|  |  |  | 	s.Spinner = spinner.Dot | 
					
						
							| 
									
										
										
										
											2025-04-24 14:27:19 -05:00
										 |  |  | 	return &model{spinner: s, text: text} | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-24 14:27:19 -05:00
										 |  |  | func (m *model) Init() tea.Cmd { | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	return m.spinner.Tick | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-24 14:27:19 -05:00
										 |  |  | func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	switch msg := msg.(type) { | 
					
						
							|  |  |  | 	case errMsg: | 
					
						
							|  |  |  | 		m.err = msg | 
					
						
							|  |  |  | 		return m, nil | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 	case textMessage: | 
					
						
							|  |  |  | 		m.text = string(msg) | 
					
						
							|  |  |  | 		return m, nil | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		var cmd tea.Cmd | 
					
						
							|  |  |  | 		m.spinner, cmd = m.spinner.Update(msg) | 
					
						
							|  |  |  | 		return m, cmd | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-24 14:27:19 -05:00
										 |  |  | func (m *model) View() string { | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | 	if m.err != nil { | 
					
						
							|  |  |  | 		return m.err.Error() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	str := fmt.Sprintf("%s %s\n", m.spinner.View(), m.text) | 
					
						
							|  |  |  | 	if m.quitting { | 
					
						
							|  |  |  | 		return str + "\n" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return str | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | type Spinner interface { | 
					
						
							|  |  |  | 	Wait() error | 
					
						
							|  |  |  | 	SetMessage(string) | 
					
						
							|  |  |  | 	Err() error | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | var _ Spinner = new(spin) | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | type spin struct { | 
					
						
							|  |  |  | 	p        *tea.Program | 
					
						
							|  |  |  | 	err      error | 
					
						
							|  |  |  | 	finished chan struct{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *spin) Wait() error { | 
					
						
							|  |  |  | 	<-s.finished | 
					
						
							|  |  |  | 	return s.Err() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *spin) SetMessage(msg string) { | 
					
						
							|  |  |  | 	s.p.Send(textMessage(msg)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *spin) Err() error { | 
					
						
							| 
									
										
										
										
											2024-10-28 20:01:03 -05:00
										 |  |  | 	switch { | 
					
						
							|  |  |  | 	case errors.Is(s.err, context.Canceled): | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2024-10-28 20:01:03 -05:00
										 |  |  | 	case errors.Is(s.err, context.DeadlineExceeded): | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2024-10-28 20:01:03 -05:00
										 |  |  | 	case errors.Is(s.err, tea.ErrProgramKilled): | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return s.err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func Spin(ctx context.Context, message string) Spinner { | 
					
						
							| 
									
										
										
										
											2024-10-29 11:28:47 -05:00
										 |  |  | 	p := tea.NewProgram( | 
					
						
							|  |  |  | 		newModel(message), | 
					
						
							|  |  |  | 		tea.WithContext(ctx), | 
					
						
							|  |  |  | 		tea.WithoutSignalHandler(), | 
					
						
							|  |  |  | 		tea.WithInput(nil), | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 	s := &spin{p: p, finished: make(chan struct{}, 1)} | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 		_, err := s.p.Run() | 
					
						
							|  |  |  | 		s.err = err | 
					
						
							|  |  |  | 		s.finished <- struct{}{} | 
					
						
							| 
									
										
										
										
											2024-10-24 20:02:55 -05:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2024-10-25 13:17:35 -05:00
										 |  |  | 	return s | 
					
						
							| 
									
										
										
										
											2024-10-24 18:18:37 -05:00
										 |  |  | } |