package animatedtext import ( "sync" "time" tea "github.com/charmbracelet/bubbletea" ) var ( lastID int mutex sync.Mutex ) func nextID() int { mutex.Lock() defer mutex.Unlock() lastID++ return lastID } type AnimatedText struct { FullText string PrintPauseTime time.Duration } type Model struct { AnimatedText AnimatedText id int currentCharIdx int finished bool } func New(text string, pauseTime time.Duration) Model { m := Model{ AnimatedText: AnimatedText{FullText: text, PrintPauseTime: pauseTime}, id: nextID(), currentCharIdx: 0, } return m } type TickMsg struct { Time time.Time ID int } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { if m.finished { return m, nil } switch msg := msg.(type) { case TickMsg: if msg.ID > 0 && msg.ID != m.id { return m, nil } if m.currentCharIdx + 1 >= len(m.AnimatedText.FullText) { m.finished = true return m, nil } m.currentCharIdx++ return m, m.tick(m.id) default: return m, nil } } func (m Model) View() string { if (m.finished) { return m.AnimatedText.FullText } return m.AnimatedText.FullText[0:m.currentCharIdx] } func (m Model) tick(id int) tea.Cmd { return tea.Tick(m.AnimatedText.PrintPauseTime, func(t time.Time) tea.Msg { return TickMsg{ Time: t, ID: id, } }) } func Tick() tea.Msg { return TickMsg{Time: time.Now()} }