Compare commits

..

No commits in common. "309277a1154fb1660a276982a00363261474794b" and "1cac8904da4e58bee4d460359ef8102bb5094cf0" have entirely different histories.

11 changed files with 21 additions and 285 deletions

View File

@ -10,8 +10,7 @@ LDFLAGS=\
test \ test \
docs \ docs \
pomo-build \ pomo-build \
readme \ readme
bin/pomo
default: bin/pomo test default: bin/pomo test
@ -29,11 +28,6 @@ test:
install: install:
go install ./cmd/... go install ./cmd/...
man/pomo.1: man/pomo.1.scd
scdoc < $< > $@
manpages: man/pomo.1
docs: www/data/readme.json docs: www/data/readme.json
cd www && hugo -d ../docs cd www && hugo -d ../docs

View File

@ -49,7 +49,7 @@ pomo start -t my-project "write some codes"
## Configuration ## Configuration
Pomo has a few configuration options which can be read from a JSON file in Pomo's config directory `~/.config/pomo/config.json`. Pomo has a few configuration options which can be read from a JSON file in Pomo's state directory `~/.pomo/config.json`.
### colors ### colors
@ -65,33 +65,6 @@ Example:
} }
``` ```
### Execute command on state change
Pomo will execute an arbitrary command specified in the array argument `onEvent`
when the state changes. The first element of this array should be the
executable to run while the remaining elements are space delimited arguments.
The new state will be exported as an environment variable `POMO_STATE` for this
command. Possible state values are `RUNNING`, `PAUSED`, `BREAKING`, or
`COMPLETE`.
For example, to trigger a terminal bell when a session completes, add the
following to `config.json`:
```json
...
"onEvent": ["/bin/sh", "/path/to/script/my_script.sh"],
...
```
where the contents of `my_script.sh` are
```bash
#!/bin/sh
if [ "$POMO_STATE" == "COMPLETE" ] ; then
echo -e '\a'
fi
```
See the `contrib` directory for user contributed scripts for use with `onEvent`.
## Integrations ## Integrations
By default pomo will setup a Unix socket and serve it's status there. By default pomo will setup a Unix socket and serve it's status there.

View File

View File

@ -1,3 +0,0 @@
#!/bin/sh
echo -e '\e'

View File

@ -1,5 +0,0 @@
#!/bin/sh
if [ "$POMO_STATE" == "BREAKING" ] ; then
swaynag -m "It's time to take a break!"
fi

View File

@ -1,89 +0,0 @@
.\" Generated by scdoc 1.11.2
.\" Complete documentation for this program is not available as a GNU info page
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "pomo" "1" "2022-05-30"
.P
.SH NAME
.P
\fBPomo\fR is a simple CLI for using the Pomodoro Technique.\&
.P
.SH SYNOPSIS
.P
\fBpomo\fR [OPTIONS] COMMAND [arg.\&.\&.\&]
.P
.SH DESCRIPTION
.P
\fBpomo\fR helps you track what you did, how long it took you to do it,
and how much effort you expect it to take.\&
.P
The Pomodoro Technique is simple and effective:
.P
.RS 4
\fB\fR Decide on a task you want to accomplish
\fB\fR Break the task into timed intervals (pomodoros), [approx.\& 25 min]
\fB\fR After each pomodoro take a short break [approx.\& 3 - 5 min]
\fB\fR Once all pomodoros are completed take a longer break [approx 15 - 20 min]
\fB\fR Repeat
.P
.RE
.SH SUBCOMMANDS
.P
See --help for the complete command usage
.P
.nf
.RS 4
start, s start a new task
init initialize the sqlite database
config, cf display the current configuration
create, c create a new task without starting
begin, b begin requested pomodoro
list, l list historical tasks
delete, d delete a stored task
status, st output the current status
.fi
.RE
.P
.SH CONFIGURATION
.P
Pomo has a configuration file that is stored in \fB~/.\&config/pomo/config.\&json\fR.\&
.P
.nf
.RS 4
{
"colors": null,
"dateTimeFmt": "2006-01-02 15:04",
"publish": false,
"publishJson": false,
"publishSocketPath": ""
}
.fi
.RE
.P
.SH EXAMPLES
.P
.SS GETTING STARTED
.P
.nf
.RS 4
# ensure your database has been initialized
pomo init
# run a new pomodoro
pomo start -t my-project "write some code"
# once finished view previously completed pomodoros
pomo list
.fi
.RE
.P
.SH SEE ALSO
.P
See the pomo source repository on Github at https://github.\&com/kevinschoon/pomo for complete documentation.\&
.P
.SH AUTHORS
.P
Written by Kevin Schoon <me@kevinschoon.\&com> with help from the open source
community.\&

View File

@ -1,74 +0,0 @@
pomo(1)
# NAME
*Pomo* is a simple CLI for using the Pomodoro Technique.
# SYNOPSIS
*pomo* [OPTIONS] COMMAND [arg...]
# DESCRIPTION
*pomo* helps you track what you did, how long it took you to do it,
and how much effort you expect it to take.
The Pomodoro Technique is simple and effective:
** Decide on a task you want to accomplish
** Break the task into timed intervals (pomodoros), [approx. 25 min]
** After each pomodoro take a short break [approx. 3 - 5 min]
** Once all pomodoros are completed take a longer break [approx 15 - 20 min]
** Repeat
# SUBCOMMANDS
See --help for the complete command usage
```
start, s start a new task
init initialize the sqlite database
config, cf display the current configuration
create, c create a new task without starting
begin, b begin requested pomodoro
list, l list historical tasks
delete, d delete a stored task
status, st output the current status
```
# CONFIGURATION
Pomo has a configuration file that is stored in *~/.config/pomo/config.json*.
```
{
"colors": null,
"dateTimeFmt": "2006-01-02 15:04",
"publish": false,
"publishJson": false,
"publishSocketPath": ""
}
```
# EXAMPLES
## GETTING STARTED
```
# ensure your database has been initialized
pomo init
# run a new pomodoro
pomo start -t my-project "write some code"
# once finished view previously completed pomodoros
pomo list
```
# SEE ALSO
See the pomo source repository on Github at https://github.com/kevinschoon/pomo for complete documentation.
# AUTHORS
Written by Kevin Schoon <me@kevinschoon.com> with help from the open source
community.

View File

@ -23,7 +23,6 @@ type Config struct {
DBPath string `json:"dbPath"` DBPath string `json:"dbPath"`
SocketPath string `json:"socketPath"` SocketPath string `json:"socketPath"`
IconPath string `json:"iconPath"` IconPath string `json:"iconPath"`
OnEvent []string `json:"onEvent"`
// Publish pushes updates to the configured // Publish pushes updates to the configured
// SocketPath rather than listening for requests // SocketPath rather than listening for requests
Publish bool `json:"publish"` Publish bool `json:"publish"`

View File

@ -2,10 +2,6 @@ package pomo
import ( import (
"database/sql" "database/sql"
"fmt"
"os"
"os/exec"
"sync"
"time" "time"
) )
@ -23,8 +19,6 @@ type TaskRunner struct {
toggle chan bool toggle chan bool
notifier Notifier notifier Notifier
duration time.Duration duration time.Duration
mu sync.Mutex
onEvent []string
} }
func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) { func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) {
@ -34,7 +28,7 @@ func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunn
nPomodoros: task.NPomodoros, nPomodoros: task.NPomodoros,
origDuration: task.Duration, origDuration: task.Duration,
store: store, store: store,
state: CREATED, state: State(0),
pause: make(chan bool), pause: make(chan bool),
toggle: make(chan bool), toggle: make(chan bool),
notifier: notifier, notifier: notifier,
@ -59,7 +53,6 @@ func NewTaskRunner(task *Task, config *Config) (*TaskRunner, error) {
toggle: make(chan bool), toggle: make(chan bool),
notifier: NewXnotifier(config.IconPath), notifier: NewXnotifier(config.IconPath),
duration: task.Duration, duration: task.Duration,
onEvent: config.OnEvent,
} }
return tr, nil return tr, nil
} }
@ -78,34 +71,6 @@ func (t *TaskRunner) TimePauseDuration() time.Duration {
func (t *TaskRunner) SetState(state State) { func (t *TaskRunner) SetState(state State) {
t.state = state t.state = state
// execute onEvent command if variable is set
if t.onEvent != nil {
go t.runOnEvent()
}
}
// execute script command specified by `onEvent` on state change
func (t *TaskRunner) runOnEvent() error {
var cmd *exec.Cmd
// parse command arguments
numArgs := len(t.onEvent)
app := t.onEvent[0]
if numArgs > 1 {
args := t.onEvent[1:(numArgs + 1)]
cmd = exec.Command(app, args...)
} else {
cmd = exec.Command(app)
}
// set state in command environment
cmd.Env = append(os.Environ(),
fmt.Sprintf("POMO_STATE=%s", t.state),
)
// run command
err := cmd.Run()
if err != nil {
return err
}
return nil
} }
func (t *TaskRunner) run() error { func (t *TaskRunner) run() error {
@ -125,6 +90,7 @@ func (t *TaskRunner) run() error {
loop: loop:
select { select {
case <-timer.C: case <-timer.C:
t.SetState(BREAKING)
t.stopped = time.Now() t.stopped = time.Now()
t.count++ t.count++
case <-t.toggle: case <-t.toggle:
@ -160,7 +126,7 @@ func (t *TaskRunner) run() error {
if t.count == t.nPomodoros { if t.count == t.nPomodoros {
break break
} }
t.SetState(BREAKING)
t.notifier.Notify("Pomo", "It is time to take a break!") t.notifier.Notify("Pomo", "It is time to take a break!")
// Reset the duration incase it // Reset the duration incase it
// was paused. // was paused.
@ -175,25 +141,15 @@ func (t *TaskRunner) run() error {
} }
func (t *TaskRunner) Toggle() { func (t *TaskRunner) Toggle() {
t.mu.Lock() t.toggle <- true
defer t.mu.Unlock()
if t.state == BREAKING {
t.toggle <- true
}
} }
func (t *TaskRunner) Pause() { func (t *TaskRunner) Pause() {
t.mu.Lock() t.pause <- true
defer t.mu.Unlock()
if t.state == PAUSED || t.state == RUNNING {
t.pause <- true
}
} }
func (t *TaskRunner) Status() *Status { func (t *TaskRunner) Status() *Status {
return &Status{ return &Status{
TaskID: t.taskID,
TaskMessage: t.taskMessage,
State: t.state, State: t.state,
Count: t.count, Count: t.count,
NPomodoros: t.nPomodoros, NPomodoros: t.nPomodoros,

View File

@ -12,8 +12,6 @@ type State int
func (s State) String() string { func (s State) String() string {
switch s { switch s {
case CREATED:
return "CREATED"
case RUNNING: case RUNNING:
return "RUNNING" return "RUNNING"
case BREAKING: case BREAKING:
@ -27,8 +25,7 @@ func (s State) String() string {
} }
const ( const (
CREATED State = iota RUNNING State = iota + 1
RUNNING
BREAKING BREAKING
COMPLETE COMPLETE
PAUSED PAUSED
@ -105,8 +102,6 @@ func (p Pomodoro) Duration() time.Duration {
// Status is used to communicate the state // Status is used to communicate the state
// of a running Pomodoro session // of a running Pomodoro session
type Status struct { type Status struct {
TaskID int `json:"task_id"`
TaskMessage string `json:"task_message"`
State State `json:"state"` State State `json:"state"`
Remaining time.Duration `json:"remaining"` Remaining time.Duration `json:"remaining"`
Pauseduration time.Duration `json:"pauseduration"` Pauseduration time.Duration `json:"pauseduration"`

View File

@ -14,8 +14,6 @@ func setContent(wheel *Wheel, status *Status, par *widgets.Paragraph) {
par.Text = fmt.Sprintf( par.Text = fmt.Sprintf(
`[%d/%d] Pomodoros completed `[%d/%d] Pomodoros completed
Current Task: %s
%s %s remaining %s %s remaining
@ -23,7 +21,6 @@ func setContent(wheel *Wheel, status *Status, par *widgets.Paragraph) {
`, `,
status.Count, status.Count,
status.NPomodoros, status.NPomodoros,
status.TaskMessage,
wheel, wheel,
status.Remaining, status.Remaining,
) )
@ -32,29 +29,26 @@ func setContent(wheel *Wheel, status *Status, par *widgets.Paragraph) {
par.Text = fmt.Sprintf( par.Text = fmt.Sprintf(
`It is time to take a break! `It is time to take a break!
Once you are ready, press [Enter] Once you are ready, press [Enter]
to begin the next Pomodoro to begin the next Pomodoro
%s %s break duration %s %s pause duration
[q] - quit [q] - quit [p] - pause
`, `,
wheel, wheel,
status.Pauseduration, status.Pauseduration,
) )
case PAUSED: case PAUSED:
par.Text = fmt.Sprintf(`Pomo is suspended. par.Text = `Pomo is suspended.
Current Task: %s
Press [p] to continue. Press [p] to continue.
[q] - quit [p] - unpause [q] - quit [p] - unpause
`, `
status.TaskMessage,
)
case COMPLETE: case COMPLETE:
par.Text = `This session has concluded. par.Text = `This session has concluded.
@ -89,20 +83,16 @@ func StartUI(runner *TaskRunner) {
resize := func() { resize := func() {
termWidth, termHeight := ui.TerminalDimensions() termWidth, termHeight := ui.TerminalDimensions()
// for RUNNING and PAUSED states
x1 := (termWidth - 50) / 2 x1 := (termWidth - 50) / 2
x2 := x1 + 50 x2 := x1 + 50
y1 := (termHeight - 10) / 2 y1 := (termHeight - 8) / 2
y2 := y1 + 10 y2 := y1 + 8
switch runner.state { switch runner.state {
case BREAKING: case BREAKING:
y1 = (termHeight - 11) / 2 y1 = (termHeight - 12) / 2
y2 = y1 + 11 y2 = y1 + 12
case COMPLETE:
y1 = (termHeight - 8) / 2
y2 = y1 + 8
} }
par.SetRect(x1, y1, x2, y2) par.SetRect(x1, y1, x2, y2)