master
  1// modified from <https://github.com/maaslalani/pom/blob/main/main.go>
  2package timer
  3
  4import (
  5	"fmt"
  6	"strings"
  7	"time"
  8
  9	"github.com/charmbracelet/bubbles/progress"
 10	"github.com/charmbracelet/lipgloss"
 11
 12	tea "github.com/charmbracelet/bubbletea"
 13)
 14
 15// init
 16const (
 17	titleColor   = "#2EF8BB"
 18	tickInterval = time.Second / 2
 19)
 20
 21var (
 22	currentTime    = time.Now().Format("15:04PM")
 23	startTimeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(titleColor)).MarginRight(1).SetString(currentTime)
 24	helpStyle      = lipgloss.NewStyle().Foreground(lipgloss.Color("240")).MarginTop(2)
 25	baseTimerStyle = lipgloss.NewStyle().Padding(1, 2)
 26)
 27
 28type tickMsg time.Time
 29
 30// model
 31type Model struct {
 32	quitting  bool
 33	startTime time.Time
 34	duration  time.Duration
 35	progress  progress.Model
 36}
 37
 38// bubbletea
 39func (m Model) Init() tea.Cmd {
 40	return tea.Tick(tickInterval, tickCmd)
 41}
 42
 43func tickCmd(t time.Time) tea.Msg {
 44	return tickMsg(t)
 45}
 46
 47func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 48	var cmds []tea.Cmd
 49
 50	switch msg := msg.(type) {
 51	case tickMsg:
 52		cmds = append(cmds, tea.Tick(tickInterval, tickCmd))
 53	case tea.KeyMsg:
 54		switch msg.String() {
 55		case "q":
 56			m.startTime = time.Now()
 57
 58			m.quitting = true
 59			return m, tea.Quit
 60		case "ctrl+c":
 61			m.quitting = true
 62			return m, tea.Quit
 63		default:
 64			m.startTime = time.Now()
 65		}
 66	}
 67
 68	// Update timer
 69	if m.startTime.IsZero() {
 70		m.startTime = time.Now()
 71		cmds = append(cmds, tea.Tick(tickInterval, tickCmd))
 72	}
 73
 74	if time.Since(m.startTime) > m.duration {
 75		m.startTime = time.Now()
 76
 77		m.quitting = true
 78		return m, tea.Quit
 79	}
 80
 81	return m, tea.Batch(cmds...)
 82}
 83
 84func (m Model) View() string {
 85	if m.quitting {
 86		return ""
 87	}
 88
 89	var s strings.Builder
 90
 91	elapsed := time.Since(m.startTime)
 92	timeLeft := m.duration - elapsed
 93
 94	percent := float64(elapsed) / float64(m.duration)
 95	s.WriteString(startTimeStyle.String())
 96	s.WriteString("- " + timeLeft.Round(time.Second).String())
 97	s.WriteString("\n\n")
 98	s.WriteString(m.progress.ViewAs(percent))
 99	s.WriteString(helpStyle.Render("Press 'q' to quit"))
100
101	return baseTimerStyle.Render(s.String())
102}
103
104func NewModel() Model {
105	progressBar := progress.New()
106	progressBar.FullColor = titleColor
107	progressBar.SetSpringOptions(1, 1)
108
109	return Model{
110		progress: progressBar,
111	}
112}
113
114func Timer(args []string) error {
115	// check if input exists
116	if len(args) == 0 {
117		return fmt.Errorf("please provide a duration")
118	}
119	// parse input
120	duration, err := time.ParseDuration(args[0])
121	if err != nil {
122		return fmt.Errorf("error parsing duration: %w", err)
123	}
124
125	// timer
126	m := NewModel()
127	m.duration = duration
128
129	if _, err = tea.NewProgram(&m).Run(); err != nil {
130		return fmt.Errorf("error running timer: %w", err)
131	}
132	return nil
133}