diff --git a/README.md b/README.md index 3f5c6c5..30b83df 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,5 @@ go build -o pomodoro main.go ## Requirements -- Go 1.18+ +- [Go](https://go.dev) 1.18+ +- [terminal-notifier](https://github.com/julienXX/terminal-notifier) diff --git a/go.sum b/go.sum index 6cacb45..9b11f9a 100644 --- a/go.sum +++ b/go.sum @@ -4,20 +4,14 @@ github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/ansi v0.9.2 h1:92AGsQmNTRMzuzHEYfCdjQeUzTrgE1vfO5/7fEVoXdY= github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= @@ -43,17 +37,13 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= diff --git a/main.go b/main.go index 7f6855b..7b7f9d6 100644 --- a/main.go +++ b/main.go @@ -4,21 +4,12 @@ import ( "flag" "fmt" "log" - "strings" "time" "github.com/charmbracelet/bubbles/progress" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" ) -const ( - padding = 2 - maxWidth = 80 -) - -var helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render - type ( model struct { name string @@ -52,57 +43,3 @@ func run() error { } return nil } - -func initialModel(name string, duration time.Duration) model { - return model{ - name: name, - totalTime: duration, - progress: progress.New(progress.WithDefaultGradient()), - } -} - -func tickCmd() tea.Cmd { - return tea.Tick(time.Second, func(t time.Time) tea.Msg { - return TickMsg(t) - }) -} - -func (m model) Init() tea.Cmd { - return tickCmd() -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "q": - return m, tea.Quit - } - case tea.WindowSizeMsg: - m.progress.Width = min(msg.Width-padding*2-4, maxWidth) - return m, nil - case TickMsg: - if m.progress.Percent() == 1.0 { - return m, tea.Quit - } - m.elapsedTime += time.Second - cmd := m.progress.SetPercent(float64(m.elapsedTime) / float64(m.totalTime)) - - return m, tea.Batch(tickCmd(), cmd) - - case progress.FrameMsg: - progressModel, cmd := m.progress.Update(msg) - m.progress = progressModel.(progress.Model) - return m, cmd - } - return m, nil -} - -func (m model) View() string { - pad := strings.Repeat(" ", padding) - return "\n" + - pad + m.name + " for " + m.totalTime.String() + "\n\n" + - pad + m.elapsedTime.String() + "\n\n" + - pad + m.progress.View() + "\n\n" + - pad + helpStyle("Press Q to quit") -} diff --git a/tui.go b/tui.go new file mode 100644 index 0000000..d46b2dd --- /dev/null +++ b/tui.go @@ -0,0 +1,77 @@ +package main + +import ( + "log" + "os/exec" + "strings" + "time" + + "github.com/charmbracelet/bubbles/progress" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +const ( + padding = 2 + maxWidth = 80 +) + +var helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render + +func initialModel(name string, duration time.Duration) model { + return model{ + name: name, + totalTime: duration, + progress: progress.New(progress.WithDefaultGradient()), + } +} + +func tickCmd() tea.Cmd { + return tea.Tick(time.Second, func(t time.Time) tea.Msg { + return TickMsg(t) + }) +} + +func (m model) Init() tea.Cmd { + return tickCmd() +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q": + return m, tea.Quit + } + case tea.WindowSizeMsg: + m.progress.Width = min(msg.Width-padding*2-4, maxWidth) + return m, nil + case TickMsg: + if m.progress.Percent() == 1.0 { + cmd := exec.Command("terminal-notifier", "-title", "Timer Complete", "-message", m.name, "-sound", "Crystal") + if err := cmd.Run(); err != nil { + log.Fatal(err) + } + return m, tea.Quit + } + m.elapsedTime += time.Second + cmd := m.progress.SetPercent(float64(m.elapsedTime) / float64(m.totalTime)) + + return m, tea.Batch(tickCmd(), cmd) + + case progress.FrameMsg: + progressModel, cmd := m.progress.Update(msg) + m.progress = progressModel.(progress.Model) + return m, cmd + } + return m, nil +} + +func (m model) View() string { + pad := strings.Repeat(" ", padding) + return "\n" + + pad + m.name + " for " + m.totalTime.String() + "\n\n" + + pad + m.elapsedTime.String() + "\n\n" + + pad + m.progress.View() + "\n\n" + + pad + helpStyle("Press Q to quit") +}