Orgonomic
Table of Contents
Orgonomic
Orgonomic connects YALMS with nvim-orgmode to give you fast navigation, time tracking, and capture workflows — all from the Snacks picker. It does not reimplement orgmode; instead it gives you better tools to work with your existing org files.
It provides:
- A time clock with break tracking and session summaries
- Snacks picker sources for org files and headlines
- A floating capture window that replaces orgmode's default split
Getting Started
Prerequisites
- nvim-orgmode installed and configured
- Snacks.nvim installed
Quick Install
Enable Orgonomic through the main YALMS entry point:
require('yalms').setup({
orgonomics = true, -- enables pickers, capture window, and clock with defaults
})
Passing true enables all three features with sensible defaults. To configure each feature
individually, pass a table:
require('yalms').setup({
orgonomics = {
pickers = true,
capture_win = true,
clock = {
breaks_file = "~/org/breaks.org",
break_types = { "Time-Off", "Meeting" },
},
},
})
You can also set up Orgonomic directly without going through YALMS:
-- Enable everything with defaults
require('yalms.orgonomics').setup(true)
-- Or configure each feature individually
require('yalms.orgonomics').setup({
pickers = { files = true, headlines = true },
capture_win = true,
clock = {
breaks_file = "~/org/breaks.org",
},
})
When true is passed to setup() it is equivalent to passing:
{ pickers = true, capture_win = true, clock = true }
Daily Workflows
Tracking Time with the Clock
The clock manager helps you track what you work on and when you take breaks. It stores everything in your org files, so your time log is just another org document.
Start working on a task
Call clock_in() to clock into the default check-in task:
local clock = require('yalms.orgonomics.clock').get()
clock:clock_in()
-- You are now clocked into "No active task" (or whatever you set as checkin_task)
To pick a specific task from your org headlines instead:
clock:clock_in({ pick = true })
-- A picker opens showing all tasks. Select one and press Enter.
To write a quick memo and clock into it at the same time:
clock:clock_in({ memo = true })
-- A capture window opens. Write your task description and save.
You can route memos to a specific capture template and file:
clock:clock_in({
memo = true,
template = "my_template", -- use a custom org-capture template
file = "~/org/work.org", -- save to a specific file
})
Take a break
Clock into a break type (e.g. "Time-Off" or "Meeting"):
clock:clock_in({ is_break = true, memo = true })
-- Captures a break note and starts the break clock.
-- When you clock back in, it returns you to your previous task.
Break headlines are auto-created in your breaks_file when you first set up the clock.
You can configure which break types exist:
clock = {
break_types = { "Time-Off", "Meeting", "Sick", "Errand" },
}
Each break type gets its own headline in the breaks file. The check-in task headline is
created automatically with the default tag and a BREAK todo keyword.
See what you're working on
Check the currently active task at any time:
local task = clock:get_checked_in_task()
if task then
print("Currently working on: " .. task.title)
end
Or retrieve the check-in task headline programmatically:
clock:get_checkin_task():next(function(headline)
print("Check-in task: " .. headline.title)
end)
Edit the current task
While clocked in, you can open the active task in a floating editor window to update notes or add details:
clock:edit_checked_in_task() -- Opens a floating window with the task content. -- Edit and close the window to save changes back to the source file.
The editor window has on_close handling: when you close it, the content is written back
to the original org file and saved. Changes are applied immediately.
Stop the clock
Clock out of the current task:
clock:clock_out() -- The org clock is stopped. Duration is recorded in the headline.
View session summaries
Generate a summary of time spent for a given period:
clock:summarize("today")
-- or: "week", "month", "yesterday"
You can also pass a custom window configuration to control how the summary is displayed:
clock:summarize("week", { position = "float", width = 80 })
Navigating Org Files
Browse files
Open the file picker to see all org files in your configured org directories:
:Snacks picker orgfiles
Each entry shows the file title and its path. The picker supports fuzzy search across filenames and titles.
Search headlines
Open the headline picker to search across all headlines in your org files:
:Snacks picker orgheadlines
Headlines are indexed from every file in your org-agenda directories. The picker shows a preview of the surrounding context for each match.
Use the picker to quickly jump to any headline without navigating the file tree.
Change a task's status from the picker
With a headline selected in the picker, use these keys to change its todo state:
| Key | Action |
|---|---|
| Alt-t | Set to TODO |
| Alt-p | Set to PROG (in progress) |
| Alt-d | Set to DONE |
| Alt-c | Set to CLOSED |
Change priority
| Key | Action |
|---|---|
| Alt-+ | Increase priority (#A) |
| Alt-- | Decrease priority |
The picker updates the headline immediately and the change is reflected in the preview.
Refile a task
Two refile actions are available:
Alt-r— Refile to a file: opens the file picker so you can choose a destination fileAlt-Shift-R— Refile to a headline: first picks a destination file, then lets you choose a specific headline within it
Both actions close the picker automatically after the refile completes.
You can attach custom logic that runs after a refile:
require('yalms.orgonomics').setup({
pickers = {
headlines = {
hooks = {
after = {
refile = function(item, picker, action)
-- e.g., log the refile or trigger a notification
end,
},
},
},
},
})
Toggle the clock from the picker
While browsing headlines, you can start or stop the org clock without leaving the picker:
Alt-Shift-C— Toggle clock on the selected headlineAlt-x— Cancel the active clock
Drill through headlines
Navigate the headline hierarchy from within the picker:
| Key | Action |
|---|---|
| > | Show child headlines (drill down) |
| < | Go up to parent level |
| = | Reset to top-level view |
| Alt-s | Cycle through sort orders |
Capturing Notes
The capture window overrides orgmode's default window-split behaviour and opens captures in a floating Snacks window instead.
Enable it:
require('yalms.orgonomics').setup({
capture_win = true,
})
The default window is a floating, rounded-border window with max width 92 and max height 24. To customise it:
capture_win = {
width = 80,
height = 24,
border = "single",
title = "Quick Note",
}
Any key accepted by snacks.win.Config can be passed here.
When you trigger an org capture (e.g. via org-capture), the capture buffer opens in this
floating window instead of splitting your current pane.
Reference
Setup Options
The full OrgonomicsOpts table accepted by setup():
| Option | Type | Default | Description | |
|---|---|---|---|---|
pickers |
=boolean | PickersOpts= | true |
Enable or configure org file/headline pickers |
capture_win |
=boolean | snacks.win.Config= | true |
Enable or configure capture window |
clock |
=boolean | ClockOpts= | true |
Enable or configure clock manager |
When any option is true, its defaults are used. Pass a table to override specific values.
Picker options
pickers = {
files = {
-- Enable file picker. Accepts boolean or snacks.picker.files.Config.
true,
-- or
{
title_format = "both", -- "title" | "filename" | "both"
},
},
headlines = {
-- Enable headline picker. Accepts boolean or table.
true,
-- or
{
level = 1, -- only show headlines at this depth
dirs = { "~/org" }, -- restrict to specific directories
keywords = { "TODO", "PROG" }, -- filter by todo keyword
orgtags = { "work" }, -- filter by tag
hooks = {
after = {
refile = function(item, picker, action) end,
},
},
},
},
}
Clock options
clock = {
breaks_file = "~/.nixvim/breaks.org", -- file that stores break headlines
checkin_task = "No active task", -- title of the default check-in headline
break_types = { "Time-Off" }, -- list of break headline types to auto-create
tag = "vnix", -- tag applied to managed headlines
breaks_tag = "breaks", -- tag applied to break headlines
}
Capture window options
When capture_win = true, the defaults are:
| Property | Default |
|---|---|
| position | "float" |
| border | "rounded" |
| maxwidth | 92 |
| maxheight | 24 |
| title | "Org Capture" |
Pass any snacks.win.Config property to override.
Keyboard Shortcuts
The headline picker (triggered with :Snacks picker orgheadlines) supports these key
bindings:
| Key | Action |
|---|---|
| > | Drill down to child headlines |
| < | Go up to parent level |
| = | Reset to top level |
| Alt-s | Cycle sort order |
| Alt-+ | Increase priority |
| Alt-- | Decrease priority |
| Alt-t | Set status to TODO |
| Alt-p | Set status to PROG |
| Alt-d | Set status to DONE |
| Alt-c | Set status to CLOSED |
| Alt-r | Refile to another file |
| Alt-Shift-R | Refile to a specific headline |
| Alt-Shift-C | Toggle clock on headline |
| Alt-x | Cancel active clock |
Developer Guide
Extended Org API
Orgonomic adds several methods to the standard orgmode API. These are useful when writing scripts or integrations that need to manipulate org files programmatically.
Headline API
Each method returns an OrgPromise. Chain .next() to run code after completion:
local helpers = require('yalms.orgonomics.helpers')
local headline, file = helpers.resolve_headline_api(node)
-- Clock operations
headline:clock_in():next(function()
print("Clock started")
end)
headline:clock_out():next(function()
print("Clock stopped")
end)
headline:toggle_clock()
headline:is_clocked_in():next(function(is_in)
print("Clocked in:", is_in)
end)
headline:cancel_active_clock()
-- Content access
headline:get_closest_headline():next(function(hl)
-- Returns the underlying OrgHeadline object
end)
headline:get_lines():next(function(lines)
-- Returns the headline content as an array of lines
end)
headline:get_content():next(function(content)
-- Returns the headline content as a single string
end)
File API
Insert a new headline into an org file:
local file = helpers.resolve_file_api("path/to/file.org")
local headline = file:insert_headline({
text = "New Task",
level = 1,
})
-- Or insert after a specific existing headline:
file:insert_headline({ text = "Sub-task", level = 2 }, existing_headline)
Utility functions
| Function | Use |
|---|---|
resolve_file_api(filename) |
Load an org file and return its file API object |
resolve_headline_api(node) |
Get the headline API wrapper for an orgmode node |
is_same_headline(a, b) |
Check if two headline references point to the same headline |
is_break(headline, title) |
Test whether a headline is a break entry |
priority_to_integer(priority) |
Convert a priority letter (e.g. #A) to a number |
Clock utilities
-- Get the currently active clock (if any)
helpers.get_active_clock():next(function(clock)
if clock then
print("Clocked into: " .. clock.headline:get_title())
end
end)
-- Clock out of all running clocks, optionally keeping one
helpers.clock_out_all(keep_headline)
API Reference
Full type signatures and function documentation: Orgonomic API