Pomo.nvim
:new: :stopwatch: A simple, customizable pomodoro timer for Neovim, written in Lua, with nvim-notify, lualine, and telescope integrations
Install / Use
/learn @epwalsh/Pomo.nvimREADME
A simple, customizable pomodoro timer plugin for Neovim, written in Lua.
In pomo.nvim, most of the functionality is surfaced through the Notifier instances you configure. A timer can have any number of notifiers, which are essentially callbacks that fire on every tick of the timer (determined by update_interval) and each significant event, such as when the timer starts, completes, is stopped, or is hidden. pomo.nvim comes with several notifiers out-of-the-box, but it's also easy to create your own.
Features
- 🪶 Lightweight and asynchronous
- 💻 Written in Lua
- ⚙️ Easily customizable and extendable
- ⏱️ Run multiple concurrent timers and repeat timers, show/hide, pause/resume
- ➕ Integrate with nvim-notify, lualine, telescope, and more
Commands
-
:TimerStart TIMELIMIT [NAME]to start a new timer.The time limit can be specified in hours, minutes, seconds, or a combination of those, and shouldn't include any spaces. For example:
:TimerStart 25m Workto start a timer for 25 minutes called "Work".:TimerStart 10sto start a timer for 10 seconds.:TimerStart 1h30mto start a timer for an hour and a half.
pomo.nvim will recognize multiple forms of the time units, such as "m", "min", "minute", or "minutes" for minutes.
-
:TimerStop [TIMERID]to stop a running timer, e.g.:TimerStop 1. If no ID is given, the latest timer is stopped. -
:TimerRepeat TIMELIMIT REPETITIONS [NAME]to start a repeat timer, e.g.:TimerRepeat 10s 2to repeat a 10 second timer twice. -
:TimerHide [TIMERID]to hide the notifiers of a running timer, if the notifiers support that. If no ID is given, the latest timer's notifiers are hidden. -
:TimerShow [TIMERID]the opposite of:TimerHide. -
:TimerPause [TIMERID]pause a timer. If no ID is given, the latest timer is paused. -
:TimerResume [TIMERID]the opposite of:TimerPause. -
:TimerSession [SESSION_NAME]to start a predefined Pomodoro session.Example session configuration in your setup:
require("pomo").setup({ sessions = { pomodoro = { { name = "Work", duration = "25m" }, { name = "Short Break", duration = "5m" }, { name = "Work", duration = "25m" }, { name = "Short Break", duration = "5m" }, { name = "Work", duration = "25m" }, { name = "Long Break", duration = "15m" }, }, }, })To start the above session, use:
:TimerSession pomodoro.
💡 Tip: You can pass -1 as the TIMERID to apply the command to all active timers.
Setup
To setup pomo.nvim you just need to call require("pomo").setup({ ... }) with the desired options. Here are some examples using different plugin managers. The full set of configuration options are listed below.
Using lazy.nvim
return {
"epwalsh/pomo.nvim",
version = "*", -- Recommended, use latest release instead of latest commit
lazy = true,
cmd = { "TimerStart", "TimerRepeat", "TimerSession" },
dependencies = {
-- Optional, but highly recommended if you want to use the "Default" timer
"rcarriga/nvim-notify",
},
opts = {
-- See below for full list of options 👇
},
}
Using packer.nvim
use({
"epwalsh/pomo.nvim",
tag = "*", -- Recommended, use latest release instead of latest commit
requires = {
-- Optional, but highly recommended if you want to use the "Default" timer
"rcarriga/nvim-notify",
},
config = function()
require("pomo").setup({
-- See below for full list of options 👇
})
end,
})
Configuration options
This is a complete list of all of the options that can be passed to require("pomo").setup(). The values represent reasonable defaults, but please read each option carefully and customize it to your needs:
{
-- How often the notifiers are updated.
update_interval = 1000,
-- Configure the default notifiers to use for each timer.
-- You can also configure different notifiers for timers given specific names, see
-- the 'timers' field below.
notifiers = {
-- The "Default" notifier uses 'vim.notify' and works best when you have 'nvim-notify' installed.
{
name = "Default",
opts = {
-- With 'nvim-notify', when 'sticky = true' you'll have a live timer pop-up
-- continuously displayed. If you only want a pop-up notification when the timer starts
-- and finishes, set this to false.
sticky = true,
-- Configure the display icons:
title_icon = "",
text_icon = "",
-- Replace the above with these if you don't have a patched font:
-- title_icon = "⏳",
-- text_icon = "⏱️",
},
},
-- The "System" notifier sends a system notification when the timer is finished.
-- Available on MacOS and Windows natively and on Linux via the `libnotify-bin` package.
{ name = "System" },
-- You can also define custom notifiers by providing an "init" function instead of a name.
-- See "Defining custom notifiers" below for an example 👇
-- { init = function(timer) ... end }
},
-- Override the notifiers for specific timer names.
timers = {
-- For example, use only the "System" notifier when you create a timer called "Break",
-- e.g. ':TimerStart 2m Break'.
Break = {
{ name = "System" },
},
},
-- You can optionally define custom timer sessions.
sessions = {
-- Example session configuration for a session called "pomodoro".
pomodoro = {
{ name = "Work", duration = "25m" },
{ name = "Short Break", duration = "5m" },
{ name = "Work", duration = "25m" },
{ name = "Short Break", duration = "5m" },
{ name = "Work", duration = "25m" },
{ name = "Long Break", duration = "15m" },
},
},
}
Defining custom notifiers
To define your own notifier you need to create a pomo.Notifier Lua class along with a factory init function to construct your notifier. Your Notifier class needs to have the following methods
Notifier.start(self)- Called when the timer starts.Notifier.tick(self, time_left)- Called periodically (e.g. every second) while the timer is active. Thetime_leftargument is the number of seconds left on the timer.Notifier.done(self)- Called when the timer finishes.Notifier.stop(self)- Called when the timer is stopped before finishing.
You can also provide optionally Notifier.show(self) and Notifier.hide(self) methods to respond to :TimerShow and :TimerHide.
The factory init function takes 1 or 2 arguments, the timer (a pomo.Timer) and optionally a table of options from the opts field in the notifier's config.
For example, here's a simple notifier that just uses print:
local PrintNotifier = {}
PrintNotifier.new = function(timer, opts)
local self = setmetatable({}, { __index = PrintNotifier })
self.timer = timer
self.hidden = false
self.opts = opts -- not used
return self
end
PrintNotifier.start = function(self)
print(string.format("Starting timer #%d, %s, for %ds", self.timer.id, self.timer.name, self.timer.time_limit))
end
PrintNotifier.tick = function(self, time_left)
if not self.hidden then
print(string.format("Timer #%d, %s, %ds remaining...", self.timer.id, self.timer.name, time_left))
end
end
PrintNotifier.done = function(self)
print(string.format("Timer #%d, %s, complete", self.timer.id, self.timer.name))
end
PrintNotifier.stop = function(self) end
PrintNotifier.show = function(self)
self.hidden = false
end
PrintNotifier.hide = function(self)
self.hidden = true
end
And then in the notifiers field of your pomo.nvim config, you'd add the following entry:
{ init = PrintNotifier.new, opts = {} }
Integrations
nvim-notify
The "Default" notifier integrates seamlessly with nvim-notify, you just need to have nvim-notify installed.
[lualine.nvim](https://github.com/nvim-lualine/lual
Related Skills
node-connect
336.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
336.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.9kCommit, push, and open a PR
