Skip to content

Commit 22f1900

Browse files
committed
feat: add spinner for project creation
1 parent 2e82374 commit 22f1900

File tree

2 files changed

+93
-5
lines changed

2 files changed

+93
-5
lines changed

pkg/cmd/init.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,14 @@ func askCreateProject(ctx context.Context, cmd *cli.Command, client stainless.Cl
333333
},
334334
}
335335

336-
_, err = client.Projects.New(
337-
ctx,
338-
params,
339-
debugMiddlewareOption,
340-
)
336+
err = group.Spinner("Creating project...", func() error {
337+
_, err = client.Projects.New(
338+
ctx,
339+
params,
340+
debugMiddlewareOption,
341+
)
342+
return err
343+
})
341344
if err != nil {
342345
return "", nil, err
343346
}

pkg/console/print.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"os"
66
"strings"
77

8+
"github.com/charmbracelet/bubbles/spinner"
9+
tea "github.com/charmbracelet/bubbletea"
810
"github.com/charmbracelet/huh"
911
"github.com/charmbracelet/lipgloss"
1012
"github.com/charmbracelet/x/ansi"
@@ -181,3 +183,86 @@ func (g Group) Confirm(cmd *cli.Command, flagName, title, description string, de
181183
func (g Group) Field(field huh.Field) error {
182184
return huh.NewForm(huh.NewGroup(field)).WithTheme(GetFormTheme(g.indent)).Run()
183185
}
186+
187+
// spinnerModel handles the spinner UI while executing an operation
188+
type spinnerModel struct {
189+
spinner spinner.Model
190+
message string
191+
indent int
192+
execute func() error
193+
err error
194+
done bool
195+
}
196+
197+
type operationCompleteMsg struct {
198+
err error
199+
}
200+
201+
func (m spinnerModel) Init() tea.Cmd {
202+
return tea.Batch(m.spinner.Tick, m.runOperation)
203+
}
204+
205+
func (m spinnerModel) runOperation() tea.Msg {
206+
err := m.execute()
207+
return operationCompleteMsg{err: err}
208+
}
209+
210+
func (m spinnerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
211+
switch msg := msg.(type) {
212+
case tea.KeyMsg:
213+
if msg.String() == "ctrl+c" {
214+
return m, tea.Quit
215+
}
216+
case operationCompleteMsg:
217+
m.done = true
218+
m.err = msg.err
219+
return m, tea.Quit
220+
case spinner.TickMsg:
221+
var cmd tea.Cmd
222+
m.spinner, cmd = m.spinner.Update(msg)
223+
return m, cmd
224+
}
225+
return m, nil
226+
}
227+
228+
func (m spinnerModel) View() string {
229+
if m.done {
230+
return ""
231+
}
232+
indentStr := strings.Repeat(" ", m.indent)
233+
return indentStr + m.spinner.View() + " " + m.message
234+
}
235+
236+
// Spinner runs the given operation with a spinner and message
237+
func Spinner(message string, operation func() error) error {
238+
return spinnerWithIndent(0, message, operation)
239+
}
240+
241+
// Spinner runs the given operation with a spinner and message, respecting the group's indent
242+
func (g Group) Spinner(message string, operation func() error) error {
243+
return spinnerWithIndent(g.indent, message, operation)
244+
}
245+
246+
func spinnerWithIndent(indent int, message string, operation func() error) error {
247+
s := spinner.New()
248+
s.Spinner = spinner.MiniDot
249+
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("14"))
250+
251+
model := spinnerModel{
252+
spinner: s,
253+
message: message,
254+
indent: indent,
255+
execute: operation,
256+
}
257+
258+
finalModel, err := tea.NewProgram(model).Run()
259+
if err != nil {
260+
return err
261+
}
262+
263+
if m, ok := finalModel.(spinnerModel); ok && m.err != nil {
264+
return m.err
265+
}
266+
267+
return nil
268+
}

0 commit comments

Comments
 (0)