dms-todoman-plugin

August 28, 2026 · View on GitHub

A DankMaterialShell (DMS) DankBar widget for your CalDAV to-dos, backed by todoman. It puts your open-task count in the bar and opens a popout where you can browse tasks grouped by due date, complete them, and add or edit tasks inline — all driven by the todo CLI, so your .ics files stay the single source of truth.

AI disclaimer: The QML frontend was built with the help of AI, following DankMaterialShell's Material Design scheme so it matches the rest of the shell.

Important

todoman is required. This plugin is a front-end for the todo command; it does not read or write .ics files on its own (except a tiny Python helper for renaming/clearing fields). Without a working todo on PATH the widget shows an error. See Requirements.

Requirements

RequirementWhyNotes
todoman (todo on PATH)Every action shells out to todoNeeds a working ~/.config/todoman/config.py pointing at your .ics task dirs
DankMaterialShell ≥ 1.5Host shell / plugin APIDeclared as requires_dms in plugin.json
python3 on PATHics_edit.py renames a task and clears due/priorityOnly invoked when you edit those fields

todoman itself reads CalDAV VTODOs from local .ics directories — typically kept in sync with a CalDAV server via vdirsyncer. Setting that up is out of scope here; if todo list works in your terminal, this plugin will work.

Features

  • Bar pill with a checklist icon and a live open-task count. Optionally (setting Show next task in the bar) it also carries the next due task and a countdown — the soonest task still ahead, falling back to the most recently missed one when nothing is upcoming. The title is truncated so the pill cannot push the rest of the bar around; a vertical bar shows the countdown only.

  • Due-date grouping in the popout — tasks are split into fixed sections, and empty sections are hidden:

    GroupContains
    OverdueDue before today
    Due todayDue today
    Due tomorrowDue tomorrow
    UpcomingDue in 2+ days
    No due dateUndated tasks
  • One-click completetodo done <id>, with an optimistic checked state.

  • Inline create — summary plus optional list, due date/time, and priority (todo new).

  • Inline edit — change summary, list (todo move), due date, and priority; clearing due/priority is handled by the bundled ics_edit.py.

  • Delete with an inline confirm step (todo delete --yes).

  • Each row shows a priority dot, the due date, and the list name — the list name is hidden while the view is already narrowed to one list.

  • Per-list view — an All lists / per-list dropdown in the popout header filters what you see instantly. It filters client-side, so switching lists spawns no process, cannot fail on a stale list name, and leaves the bar's task count global. It is view state, not a setting: it resets with the shell.

  • Configurable watched lists, sort field, refresh interval, and a show-completed toggle (see Settings).

How it works

  • The widget runs todo --porcelain list and renders the JSON. Task ids are todoman's own — the same ones todo list / todo done print — so completing or editing a task uses ids that stay valid. The list is refetched after every create/complete/edit so ids never go stale.
  • List names come from todo lists, which only exists in todoman ≥ 4.7. On older versions that call fails silently, so the names are recovered from the tasks themselves; the only thing lost is a list that currently holds no tasks. Filtering by list happens inside the widget, never by refetching with todo list <name> — that would make the bar's count follow the filter and would hard-fail on a list that vanished between two refreshes.
  • All mutations go through the todo CLI, except renaming a summary and clearing a due date or priority, which the CLI can't do directly; those go through ics_edit.py, a small pass over the task's .ics file.

Settings

KeyMeaningDefault
listFilterComma-separated list names the widget watches (empty = all)""
defaultListDefault list for new tasks (empty = todoman default)""
sortFieldOrder within each group: due | priority | created_at | summarydue
showCompletedInclude done/cancelled tasksfalse
showNearestTaskAppend the next due task + countdown to the bar pillfalse
refreshIntervalReload interval, in minutes5

Grouping by due date is always on; sortField controls the order of tasks within each group.

listFilter and the header dropdown are different scopes and never overlap: listFilter decides which lists the widget fetches at all — it is passed straight to todo list — while the dropdown narrows the view within them. A list that is dropped from listFilter, deleted, or renamed resets the view to All lists rather than leaving you staring at an empty popout.

Installation

Nix flake (declarative)

Add the repo as a flake input and hand it to the DMS plugins option — the attribute name becomes the plugin directory under ~/.config/DankMaterialShell/plugins/, and it must match the id in plugin.json (dankTodoman):

# flake.nix
inputs.dms-todoman-plugin = {
  url = "github:Shochraos/dms-todoman-plugin";
  flake = false;
};
# home-manager module (where you configure DMS)
programs.dank-material-shell.plugins.dankTodoman = {
  enable = true;
  src = inputs.dms-todoman-plugin;
  # settings = { sortField = "priority"; showCompleted = false; };
};

Manual / local path

Point a home.file / xdg.configFile entry (or just copy the folder) at your DMS plugins directory:

xdg.configFile."DankMaterialShell/plugins/dankTodoman".source = ./.;

Either way, restart DMS (or its systemd user service) after installing so it picks the plugin up.

Files

FilePurpose
plugin.jsonPlugin manifest (id, capabilities, requirements)
TodomanWidget.qmlThe bar pill + popout UI and all todo interaction
TodomanSettings.qmlSettings page shown in DMS
ics_edit.pyRename summary / clear due / clear priority in an .ics

License

MIT © 2026 Shochraos