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}