2026-04-11 23:55:43 +08:00
|
|
|
package internal
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"charm.land/bubbles/v2/spinner"
|
|
|
|
|
"charm.land/lipgloss/v2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SpinnerState int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
StateThinking SpinnerState = iota
|
|
|
|
|
StateAnswering
|
|
|
|
|
StateDone
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Spinner struct {
|
|
|
|
|
text string
|
|
|
|
|
state SpinnerState
|
|
|
|
|
spinner spinner.Model
|
|
|
|
|
stopCh chan struct{}
|
|
|
|
|
doneCh chan struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSpinner(text string) *Spinner {
|
|
|
|
|
s := spinner.New(
|
|
|
|
|
spinner.WithSpinner(spinner.MiniDot),
|
|
|
|
|
spinner.WithStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("243"))),
|
|
|
|
|
)
|
|
|
|
|
return &Spinner{
|
|
|
|
|
text: text,
|
|
|
|
|
state: StateThinking,
|
|
|
|
|
spinner: s,
|
|
|
|
|
stopCh: make(chan struct{}),
|
|
|
|
|
doneCh: make(chan struct{}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) Start() {
|
|
|
|
|
go s.run()
|
|
|
|
|
s.tick()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) Update(text string) {
|
|
|
|
|
s.text = text
|
|
|
|
|
s.state = StateAnswering
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) Stop() {
|
|
|
|
|
close(s.stopCh)
|
|
|
|
|
<-s.doneCh
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) tick() {
|
|
|
|
|
msg := s.spinner.Tick()
|
|
|
|
|
if msg, ok := msg.(spinner.TickMsg); ok {
|
|
|
|
|
s.spinner, _ = s.spinner.Update(msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) run() {
|
|
|
|
|
defer close(s.doneCh)
|
|
|
|
|
|
|
|
|
|
ticker := time.NewTicker(time.Second / 12)
|
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-ticker.C:
|
|
|
|
|
s.tick()
|
|
|
|
|
s.render()
|
|
|
|
|
case <-s.stopCh:
|
|
|
|
|
s.clear()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) render() {
|
2026-04-15 06:13:52 +08:00
|
|
|
fmt.Printf("\r %s %s", s.spinner.View(), s.text)
|
2026-04-11 23:55:43 +08:00
|
|
|
os.Stdout.Sync()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Spinner) clear() {
|
2026-04-15 06:13:52 +08:00
|
|
|
fmt.Printf("\r %s 思考完成.\n", s.spinner.View())
|
2026-04-11 23:55:43 +08:00
|
|
|
os.Stdout.Sync()
|
|
|
|
|
}
|