A Pomodoro and deep-work timer that lives inside Neovim. Structured focus/break intervals, distraction blocking, session stats, and a dashboard.
:q / :qa during active focus sessions.vim.notify during focus. Queued notifications flush when focus ends.<Nop> during sessions.FocusLost, resumes on FocusGained. Optional idle timeout.vim.ui.select to choose a focus mode.achievement-tracker.nvim for XP and daily achievements.{
"raymondware/focusmode.nvim",
config = function()
require("focusmode").setup({
keymaps = {
toggle = "<leader>ft",
start = "<leader>fs",
stop = "<leader>fx",
pause = "<leader>fp",
dashboard = "<leader>fd",
},
})
end,
}
| Plugin | Unlocks |
|---|---|
nvim-telescope/telescope.nvim |
Rich mode picker with preview |
ibhagwan/fzf-lua |
Alternative mode picker |
nvim-lualine/lualine.nvim |
Statusline timer component |
raymondware/achievement-tracker.nvim |
XP and daily achievements |
| Command | Description |
|---|---|
:FocusStart [mode] |
Start a focus session. Mode name autocompletes from available modes. |
:FocusStop |
Stop the current session. |
:FocusPause |
Pause the current session. |
:FocusResume |
Resume a paused session. |
:FocusToggle |
Smart toggle -- idle starts timer, running pauses, paused resumes. |
:FocusSkip |
Skip to the next phase (focus to break, break to focus). |
:FocusDashboard |
Open the stats dashboard. |
:FocusStatus |
Show current state in a notification. |
:FocusPick |
Open the mode picker. |
:FocusReset |
Reset all persisted data (prompts for confirmation). |
No default mappings. Set keymaps in your config to register the following:
| Config key | Action |
|---|---|
keymaps.toggle |
Toggle focus: idle -> start, running -> pause, paused -> resume |
keymaps.start |
Start a focus session |
keymaps.stop |
Stop the current session |
keymaps.pause |
Pause or resume the current session |
keymaps.dashboard |
Open the stats dashboard |
Dashboard keymaps (always active):
| Key | Action |
|---|---|
q / <Esc> |
Close dashboard |
local focus = require("focusmode")
focus.start("deep_work") -- Start a session with a named mode
focus.stop() -- Stop current session
focus.pause() -- Pause current session
focus.resume() -- Resume paused session
focus.toggle() -- Smart toggle
focus.skip() -- Skip to next phase
focus.dashboard() -- Toggle stats dashboard
focus.status() -- Returns { phase, mode, remaining_seconds, ... }
focus.pick() -- Open mode picker
Add the component to your lualine config:
require("lualine").setup({
sections = {
lualine_x = { require("focusmode").lualine() },
},
})
Full defaults shown below. Pass overrides to setup().
require("focusmode").setup({
-- Timing
focus_duration = 25, -- minutes
break_duration = 5,
long_break_duration = 15,
long_break_interval = 4, -- sessions before a long break
auto_start_break = true,
auto_start_focus = false,
default_mode = "pomodoro",
-- Mode presets (focus, break, long_break, long_break_interval in minutes)
modes = {
pomodoro = { focus = 25, break = 5, long_break = 15, long_break_interval = 4 },
deep_work = { focus = 50, break = 10, long_break = 20, long_break_interval = 2 },
review = { focus = 15, break = 3, long_break = 10, long_break_interval = 4 },
learning = { focus = 30, break = 10, long_break = 15, long_break_interval = 3 },
},
-- Floating timer window
timer_ui = {
enabled = true,
position = "top_right", -- top_left, bottom_right, bottom_left, center_top, center_bottom
row_offset = 0,
col_offset = 0,
width = 22,
border = "rounded",
zindex = 50,
blend = 10,
show_mode = true,
show_session_count = true,
},
-- Toast notifications
toast = {
enabled = true,
duration_ms = 4000,
width = 40,
position = "bottom_right", -- bottom_left, top_right, top_left
},
-- Sound alerts
sound = {
enabled = false,
on_focus_end = true,
on_break_end = true,
bell = true, -- terminal bell (\a); set false and provide command for custom sound
command = nil, -- shell command, e.g. "afplay /path/to/sound.wav"
},
-- Distraction blocking
keybind_blocking = {
enabled = false,
blocked_keys = {}, -- keys to disable during focus, e.g. { "<C-n>", "<C-p>" }
allow_override = true,
},
-- Auto-pause
auto_pause = {
enabled = false,
on_focus_lost = true, -- pause when Neovim loses focus
on_idle = false,
idle_timeout = 120, -- seconds
},
-- Daily/weekly goals
goals = {
daily_minutes = 120,
weekly_minutes = 600,
},
-- Achievement tracker integration
achievements = {
enabled = true,
xp_per_focus_minute = 4,
bonus_streak_multiplier = 1.5,
daily_achievement_id = "deep_focus_session",
},
-- Do Not Disturb
dnd = {
enabled = false,
suppress_notify = true, -- block vim.notify during focus
suppress_diagnostics = false,
},
-- Quit protection
focus_lock = {
enabled = false,
severity = "warn", -- "warn" shows confirm dialog, "block" prevents quit
message = "You're in a focus session! Are you sure you want to quit?",
},
-- Mode picker
picker = {
backend = "auto", -- "auto", "telescope", "fzf_lua", "select"
},
-- Keymap bindings (set to nil to skip)
keymaps = {
toggle = nil,
start = nil,
stop = nil,
pause = nil,
dashboard = nil,
},
-- Data directory for persistence
data_dir = vim.fn.stdpath("data") .. "/focusmode",
-- Lualine component
lualine = {
icon_focus = "",
icon_break = "",
icon_paused = "",
icon_idle = "",
show_progress = true,
show_session_count = true,
show_mode = true,
progress_width = 6,
format_focus = "{icon} {time} {bar} ({mode} {session})",
format_break = "{icon} {time} {bar}",
format_paused = "{icon} PAUSED {time}",
},
})