UP | HOME

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

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 file
  • Alt-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 headline
  • Alt-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