A CLI/TUI Pomodoro timer and todo (coming soon) application for keyboard addicts and terminal fans that makes you more productive.
- Features
- Installing
- Usages
- Configuration
- Integrating with other apps
- Privacy Policy
- Known Issues
- Tech Stack
- Todo
- Development
- Conclusion
This application is 20k+ lines of code (including tests) terminal madness and packed with features. It
- can be be used entirely from the CLI (you don't have to switch to another window to interact with a Pomodoro app anymore)
- has a sleek and (almost) responsive TUI
- plays alarm beep after every non-break timer ends
- can show notifications
- can show comprehensive stats (in the TUI)
- has automatic database backup system
- is reactive to changes. Changes from any TUI instance or CLI is reflected everywhere.
If you don't have Node.js installed then go ahead and install an LTS version from here and run the following command in your terminal or command prompt.
npm i -g productivity-timer
After installing run pt -v
to verify, it should print the current version of
the app.
To update the app run the following command:
npm i -g productivity-timer@latest
To uninstall the app run:
npm un -g productivity-timer
Note: You have to remove the config file ~/ptrc.json
and the data
directories specified in the config manually.
Productivity Timer depends on the mplayer CLI for playing audio. It
should be accessible globally in your shell as the mplayer
command. If it's
not globally available in the shell then you can manually provide the mplayer
binary path in the config.
On Arch based systems
sudo pacman -S mplayer
On Debian based systems:
sudo apt install mplayer
On MacOS with homebrew:
brew install mplayer
On Windows and other OSs: Please refer to the official mplayer website or your package manager for installation instructions.
Productivity Timer doesn’t enforce a strict Pomodoro sequence (work -> break -> work). It’s entirely up to you when you want to start a timer, though you can configure the app to automatically start a break after each work session.
If a timer has a reference to a task category or project, it’ll be saved and show up in your stats. Otherwise, it’ll be considered a break timer and not be logged. It is only your hard work that counts!
Kindly read the help page (with pt --help
) to learn about all the
commands.
Note: In the help page of the CLI you'll see commands like pause|p
. Here
|p
means an alias named p
.
# boot up the server
pt bootup
# create a category or project
pt create category --name Study # or use alias: pt c c -n Study
# start a 25m timer for the "Study" category
pt start --category --name study -d 25m # or: pt start -cn study -d 20m
# see the current status
pt info # or: pt i
# after the above timer times up start a short break (5m)
pt break -s # or: pt b -s
# shortcut to start the study timer again
pt start --last # or: pt s -l
Tip:
- When the timer is beeping you can mute it by issuing the mute command. e.g.,
pt mute
. - Don't worry about writing long commands. Almost every command has a single-character alias.
# before starting the TUI start the server
pt bootup
# start the TUI
pt tui
When in the TUI press F1 to see the help page.
This app depends on a config file named ~/.ptrc.json
.
Tip:
- Here
~/
means your home directory/folder. If you're not sure what is your home directory then runnode -p 'os.homedir()'
in your terminal/command prompt. - The config JSON file supports comments and trailing commas thanks to JSON5.
Example Configuration:
{
// timer
"BEEP_DURATION_MS": "10s",
"DEFAULT_TIMER_DURATION_MS": "10m",
"SHOW_TIMER_NOTIFICATIONS": true,
"AUTO_START_BREAK": false,
"AUTO_START_BREAK_DURATION": 300000,
// speaker
"SPEAKER_VOLUME": 40,
"MPLAYER_PATH": "mplayer",
"MPLAYER_AUDIO_PATH": "/home/muhammad/alarm.mp3",
// database
"DB_BACKUP_INTERVAL_MS": 3600000,
"DATA_DIR": "/home/muhammad/.p-timer",
"DB_BACKUP_DIR": "/home/muhammad/.p-timer-bak",
// tui
"FIRST_DAY_OF_WEEK": "Mon",
// other
"CHECK_UPDATE": true
}
Descriptions:
-
BEEP_DURATION_MS
: for how long the beep should be played when a timer times up. -
DEFAULT_TIMER_DURATION_MS
: this value will be used to start a timer when no explicit duration is provided. -
SHOW_TIMER_NOTIFICATIONS
: whether the app should show a notification when a timer ends. -
AUTO_START_BREAK
: whether the app should automatically start the break timer. -
AUTO_START_BREAK_DURATION
: the timer duration of automatically started breaks.
-
MPLAYER_PATH
: this app uses themplayer
audio player to play the alarm. If themplayer
command is not available in your shell then you can manually specify themplayer
binary path in this field. -
MPLAYER_AUDIO_PATH
: path to a custom audio file. -
SPEAKER_VOLUME
: An integer value for the speaker volume. 0 for mute and 100 for maximum.
Note: All path must be absolute, i.e. should start from your root directory.
Example: /home/muhammad/alarm.mp3
-
DATA_DIR
: the directory where the sqlite database and error logs should be stored. -
DB_BACKUP_DIR
: the database backup directory. -
DB_BACKUP_INTERVAL_MS
: database backup interval timer. I recommend setting this to1h
to be on the safe side.
FIRST_DAY_OF_WEEK
: The TUI depends on this field to render calendar. Example day names:"Saturday"
,"Sat"
or"Sa"
.
CHECK_UPDATE
: Whether the app should check for updates and show notification.
Tip: All the duration fields can either take a milliseconds number value or
a descriptive duration string value (e.g., "20m"
, "1h30m"
etc.).
The pt_plugin
is a lightweight CLI provided by Productivity Timer for easy
integration with other applications that can show status by invoking shell
commands (e.g., Polybar). Right now, it has only one command named info
that
can either take a template to format status message or output raw JSON.
Run pt_plugin info --help
to see the command structure.
Example
pt_plugin info -t "[ref] | [ed]/[td] | [state]"
# will print something like: 'p(2)/Timer | 00:00:06/01:00:00 | RUNNING'
Syntax
You can write anything in the template and to interpolate a variable, write it
inside []
. To escape the [
and ]
characters use %
and to write a single
%
write %%
.
Examples
pt_plugin info -t "Elapsed time: [ed]"
# example output: 'Elapsed time: 01:23'
pt_plugin info -t "Elapsed time%% %[[ed]%]"
# example output: 'Elapsed time% [01:23]'
Available Variables
-
ref
: the timer reference. It will be formatted as[pc](id)/name
. Herec
andp
means task category and project respectively. If ref isnull
i.e. it's a break timer then it'll be set to the string"Break"
. Also thename
field is truncated to 15 characters. -
state
: timer states:ENDED
,PAUSED
,RUNNING
,TIMED_UP
, andNOT_STARTED
. -
td
: target duration. -
ed
: elapsed duration. -
rd
: remaining duration.
Tip: Checkout executor if you're using Gnome. It can show the output of any shell command in your top panel.
If the template doesn't suit your needs then you can provide the --json
flag
to get the raw JSON data.
pt_plugin info --json
The response data has the following interface:
type InfoCommandResponse =
| { error: null; data: any }
| { error: { message: string; code: string }; data: null };
You can programmatically communication with Productivity Timer through IPC socket.
- Install express-ipc
- Copy the config from
src/config/other.ts
module and the client services fromsrc/client/services
directory.
Example: see the docs/api-integration.ts
file.
I have the utmost respect for your privacy and zero interest in your personal data. Whatever data you generate with this app lives in your own machine and it doesn't make any network requests except for update check (you can turn it off). But the sqlite database is unencrypted though and is accessible to every application running on your system!
1. The app shows the background color of my terminal!
The library I'm using to build the TUI is called blessed. It's a very old library and it hasn't been updated in the last seven years. It doesn't support inheritance for background color styling. So to fix this I've to manually assign a background color to every element. Even if I assign a value of "#000" it doesn't become as black as my terminal background. So yeah, it's a feature!
2. The line-chart in the stats page of TUI is not responsive.
It's probably intended by blessed-contrib as drawing that line is probably expensive. Not sure if I should create a new line-chart element after every resize event.
3. The key bindings doesn't work or app is not responding to key press.
You probably clicked somewhere in the app that removed the focus from the selected element. Try pressing ctrl-j or ctrl-k to bring back the focus or move to another tab and come back to the current one.
This project has been made possible with help of following open source libraries and frameworks. I'm just a lousy developer who have stitched together all this technologies. Long live open source!
-
Backend Service
- Database: better-sqlite3
- Validation: zod and handy-types
- IPC Server: express-ipc
- Notification: node-notifier
-
CLI (commander)
- IPC Client: express-ipc
- Text formatting: ansi-colors
- Table: cli-table
- Box: boxen
-
TUI (blessed + blessed-contrib)
- Pie chart: cli-pie
- Fuzzy search: fuse.js
- State management: redux
- Terminal Canvas: drawille-canvas
- Tree printing: flexible-tree-printer
-
Other
- Utils: lodash
- Date utilities: date-fns
This app is far from being complete. I still have to implement so many features. If you think you can help me to implement some features then please do so. It's becoming very tiresome for me to do everything singlehandedly.
- Add todo feature.
- Write code documentation.
- Replace nasty validation logic with zod schemas.
- Remove the overengineered sub-process database layer.
- Don't refresh stats in the TUI for unrelated changes. Use
redux-watch
to selectively update stats.
# running tests
npm test
npm run test:watch # in watch mode
npm run test:coverage # to see test coverage
# building
npm run build:dev:watch # run build in watch mode
npm run build:dev
npm run build:prod # the debug log won't be visible in the TUI
# formatting
npm run format
My main inspiration in building this app was that, I didn't like to move my hands from the keyboard to start a Pomodoro timer. Now I can be more productive and never leave VIM and my terminal. Though, ironically I've spent more than five months building this app. I guess that's productivity at the peak 😅. But that's not the point. I actually learned a lot (sqlite, child process management, blessed, redux ...) while building this app and also built an entire IPC server framework (express-ipc) from scratch.
If you like this app, give it a ⭐ on Github and share it with others. You can also buy me a coffee if you want, I would really appreciate that 💝.