package main import ( "fmt" "os" "time" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/stopwatch" tea "github.com/charmbracelet/bubbletea" ) type model struct { stopwatch stopwatch.Model keymap keymap help help.Model quitting bool } type keymap struct { start key.Binding stop key.Binding reset key.Binding quit key.Binding } func (m model) Init() tea.Cmd { return m.stopwatch.Init() } func (m model) View() string { // Note: you could further customize the time output by getting the // duration from m.stopwatch.Elapsed(), which returns a time.Duration, and // skip m.stopwatch.View() altogether. s := m.stopwatch.View() + "\n" if !m.quitting { s = "Elapsed: " + s s += m.helpView() } return s } func (m model) helpView() string { return "\n" + m.help.ShortHelpView([]key.Binding{ m.keymap.start, m.keymap.stop, m.keymap.reset, m.keymap.quit, }) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch { case key.Matches(msg, m.keymap.quit): m.quitting = true return m, tea.Quit case key.Matches(msg, m.keymap.reset): return m, m.stopwatch.Reset() case key.Matches(msg, m.keymap.start, m.keymap.stop): m.keymap.stop.SetEnabled(!m.stopwatch.Running()) m.keymap.start.SetEnabled(m.stopwatch.Running()) return m, m.stopwatch.Toggle() } } var cmd tea.Cmd m.stopwatch, cmd = m.stopwatch.Update(msg) return m, cmd } func main() { m := model{ stopwatch: stopwatch.NewWithInterval(time.Millisecond), keymap: keymap{ start: key.NewBinding( key.WithKeys("s"), key.WithHelp("s", "start"), ), stop: key.NewBinding( key.WithKeys("s"), key.WithHelp("s", "stop"), ), reset: key.NewBinding( key.WithKeys("r"), key.WithHelp("r", "reset"), ), quit: key.NewBinding( key.WithKeys("ctrl+c", "q"), key.WithHelp("q", "quit"), ), }, help: help.New(), } m.keymap.start.SetEnabled(false) if _, err := tea.NewProgram(m).Run(); err != nil { fmt.Println("Oh no, it didn't work:", err) os.Exit(1) } }