v3.0.0 Modern UI, Adaptive Windows & Inherited Priorities π¨πͺ
New Feature Overview
This release introduces the Modern UI - a complete redesign of every surface Dooing
draws, available behind a single opt-in switch. Todos are grouped into status sections
with counts, priority becomes a coloured marker instead of tinting whole rows, metadata is
dimmed and right-aligned, nested tasks get real tree guides, and a todo's notes now
preview inline. Every sub-window - help, tags, search, the calendar and all text prompts -
is centred on the editor and sized from its own content.
The classic style remains the default and renders exactly as it always has, so updating
the plugin does not change your interface until you ask it to.
Alongside it, adaptive window sizing lets the todo window size itself against the
current editor dimensions, sub-tasks can inherit their parent's priority, and deleting
an unfinished task now asks for confirmation.
Classic (default, unchanged):
Modern (ui.style = "modern"):
Key capabilities:
- Opt-in
ui.style = "modern"with a per-feature toggle for every part of it - Status sections (
IN PROGRESS/PENDING/DONE) with counts, keeping subtrees intact - Priority shown as a coloured marker rather than colouring the entire row
- Right-aligned dimmed metadata, overflowing to its own line only when needed
- Tree connectors for nested tasks, and folding that finally works on nesting depth
- Inline preview of the first line of a todo's notes
- Due dates that distinguish overdue / today / tomorrow / this week from distant dates
- Completion progress bar in the window title, overdue count in the footer
- Centred, content-sized help, tags, search, calendar and input prompts
- 16 new highlight groups, all overridable
defaultlinks window.dimensionsaccepting a table or a function of the editor sizenested_tasks.inherit_priorityto skip the priority picker for sub-tasks- Confirmation prompt before deleting an unfinished task
What's Changed
Modern UI: the todo list
New renderer (lua/dooing/ui/modern.lua):
- Status sections with per-section counts. Only top-level todos choose a section;
descendants follow their parent, so a nested tree is never split across headings - Priority marker (
β) carries the priority colour, leaving the task text in the normal
foreground instead of tinting the whole row - Estimate, due date and age are dimmed and right-aligned, dropping onto their own
continuation line only when the row would otherwise be squeezed - Tree connectors (
ββ/ββ/β) for nested tasks, replacing plain-space indentation - Note previews: the first line of a todo's notes, dimmed and aligned under the task text,
truncated with an ellipsis when it does not fit - Due dates read
σ°¦ overdue 3d,due today,due tomorroworin 5d, each with its own
accent, degrading to a plain localized date further out - Tags are highlighted as structural segments rather than by re-matching the rendered line
Window chrome (lua/dooing/ui/window.lua):
- Completion progress bar in the title:
Global to-dos 3/14 β°β°β±β±β±β±β±β± - Overdue count in the footer:
[?] for help Β· 2 overdue - Quick keys collapse from a fixed 8-line panel into a width-adaptive strip that packs the
keys into the fewest balanced rows that fit
Folding:
- The modern style folds on each todo's real nesting depth via
foldexpr, sozcon a
parent collapses exactly its own subtree - Previously
foldmethod=indentproduced fold level 0 for both indent 2 and indent 4 at
the defaultshiftwidth, meaning nested folding never worked at all. The classic style
keeps its existing behaviour
Modern UI: sub-windows
New module (lua/dooing/ui/panels.lua):
- Every panel is centred on the editor and sized from its own content, rather than being
positioned against a hard-coded assumption about the main window's width - Centred input box replaces the cmdline for new to-do, sub-task, edit, time
estimation, tag rename and search, opening straight into insert mode. Import and export
deliberately keep usingvim.ui.inputbecause they rely on filename completion - Help is grouped by what the keys actually do (
TODOS,METADATA,VIEW,
IMPORT / EXPORT,TAGS WINDOW,CALENDAR), aligns the key column across all groups,
and omits any keymap you have set tofalse - Tags shows a usage count beside each tag
- Search prompts in the centred input, highlights the matched substring in each result
and reports the count in the footer. Pressing<CR>jumps to the todo, first clearing an
active tag filter if it would hide the match
Calendar (lua/dooing/ui/calendar.lua):
- Centred on the editor with the navigation keys spelled out in the footer, instead of
being anchored to wherever the cursor happened to be - Roomier grid in the modern style (32x10 rather than 26x9): wider day cells, with the
weekday names separated from the days - The grid is now driven by a single layout table (padding, cell width, number offset,
header rows) shared by rendering, cursor placement, the reverse position-to-day lookup
and the highlight pass, so the four cannot drift out of sync
Modern UI: configuration and theming
New configuration section:
ui = {
style = "classic", -- "classic" | "modern"
sections = true, -- group top-level todos under status headings
priority_bar = true, -- coloured marker instead of colouring the whole row
tree_connectors = true, -- draw tree guides for nested tasks
note_preview = true, -- first line of a todo's notes, dimmed, beneath it
progress = true, -- progress bar in the title, summary in the footer
compact_quick_keys = true, -- single strip instead of the tall quick keys panel
section_titles = {
in_progress = "IN PROGRESS",
pending = "PENDING",
done = "DONE",
},
icons = {
priority_bar = "β",
overdue = "σ°¦",
progress_on = "β°",
progress_off = "β±",
},
}Every sub-toggle additionally requires ui.style = "modern", so the classic layout can
never be affected by one.
New highlight groups, all defined as default links so a colorscheme or your own
nvim_set_hl call always wins:
| Group | Default link | Used for |
|---|---|---|
DooingText |
Normal |
Todo text |
DooingMeta |
Comment |
Estimates, ages, note previews, distant dates |
DooingTag |
Type |
#tags |
DooingSectionTitle |
Title |
Section headings and window title |
DooingSectionCount |
Comment |
Section counts, progress numbers |
DooingSectionRule |
NonText |
Section separator rule |
DooingTreeGuide |
NonText |
Nested task connectors |
DooingOverdue |
DiagnosticError |
Overdue due dates |
DooingDueToday |
DiagnosticWarn |
Due today |
DooingDueSoon |
DiagnosticInfo |
Due tomorrow / within a week |
DooingProgressOn |
DiagnosticOk |
Filled progress bar cells |
DooingProgressOff |
NonText |
Empty progress bar cells |
CalendarWeekdayHeader |
DooingSectionCount |
Calendar weekday row |
DooingQuickTitle, DooingQuickKey and DooingQuickDesc are also now actually defined -
see Bug Fixes below.
Adaptive window sizing
window.width and window.height are replaced by a single window.dimensions, which
accepts either a table or a function returning one. The function form is evaluated on
every window creation, so the todo window can size itself against the current editor:
require("dooing").setup({
window = {
dimensions = function()
return {
width = math.max(40, math.floor(vim.o.columns * 0.4)),
height = math.max(10, math.floor(vim.o.lines * 0.6)),
}
end,
},
})config.get_window_dimensions() is the single resolution point: it evaluates the function
form under pcall, accepts named and positional tables, floors and clamps values to the
available editor space, and falls back to 55x20 with a warning on invalid input.
Sub-tasks can inherit their parent's priority
nested_tasks = {
inherit_priority = false, -- when true, sub-tasks copy the parent's priorities
}With inherit_priority = true, creating a sub-task skips the "Select Priorities" window
entirely and the todo is created immediately with a copy of its parent's priorities.
Deletion confirmation
Deleting an unfinished task now shows a confirmation dialog with the task text and its due
date. Completed tasks are still deleted immediately without a prompt.
Bug Fixes
- Quick keys panel was entirely unstyled.
DooingQuickTitle,DooingQuickKeyand
DooingQuickDescwere referenced by the panel but never defined anywhere in the plugin add_time_estimationcould error. It indexedstate.todos[todo_index]with no nil
guard, throwing whenever the cursor was not on a todo. Guarded here and in the due-date
and remaining estimate actions- The tags window was orphaned on screen when the main window closed; only the help
and search windows were being cleaned up - Nested folding never worked at the default
shiftwidth(see Folding above) - Modern search resolves matches through the line map before moving the cursor.
state.search_todos()returnslnumas an index intostate.todos, not a buffer line,
so using it directly lands on the wrong row whenever the two diverge
Breaking Changes
window.width / window.height are deprecated. Existing configurations keep working:
setup() folds them into window.dimensions and emits a deprecation warning. However,
the legacy keys are then removed from config.options.window, so anything reading
config.options.window.width at runtime now sees nil. Use
config.get_window_dimensions() instead.
Migration:
-- before
window = { width = 80, height = 25 }
-- after
window = { dimensions = { width = 80, height = 25 } }Nothing else changes for existing users: the modern UI is entirely opt-in, and the classic
style renders exactly as it did in v2.10.0.
Technical Details
Files Added:
lua/dooing/ui/modern.lua- modern list renderer: sections, tree guides, metadata
layout, note previews, fold levels, progress statisticslua/dooing/ui/panels.lua- modern sub-windows: centred input, help, tags, search
Files Modified:
lua/dooing/ui/rendering.lua- renderer dispatch, line/fold map publication, shared
render teardownlua/dooing/ui/window.lua- progress title and footer, compact quick keys, per-style
fold setup, sub-window cleanuplua/dooing/ui/calendar.lua- layout table, centred positioning, roomier modern gridlua/dooing/ui/components.lua- dispatch to the modern panels, scratchpad lookuplua/dooing/ui/actions.lua- all cursor-to-todo lookups routed through one accessor,
centred prompts, nil guards,inherit_prioritylua/dooing/ui/utils.lua-todo_index_at_line()/todo_index_at_cursor()lua/dooing/ui/constants.lua-line_to_todo,primary_lines,fold_levelslua/dooing/ui/highlights.lua- 16 new groups, plus the three quick-keys groups that
were previously referenced but undefinedlua/dooing/ui/init.lua,lua/dooing/init.lua-focus_firston list open and swaplua/dooing/config.lua-uisection,is_modern(),modern_feature(),ui_icon(),
get_window_dimensions(), legacy dimension migrationREADME.md,doc/dooing.txt,CLAUDE.md- documentation for all of the above
Internal: one line-to-todo mapping. Resolving a cursor line back to a todo was
previously duplicated positional arithmetic in three variants - find_todo_index(),
cursor - (active_filter and 3 or 1), and a filter-ignoring cursor - 1 in the
scratchpad. None of those survive a layout containing section headers or multi-line rows.
Both renderers now publish constants.line_to_todo for every line a todo occupies and
constants.primary_lines for each todo's canonical line, and all call sites resolve
through ui/utils.todo_index_at_cursor(), which returns nil on non-todo lines. Every
line a todo occupies - its row, an overflowed metadata line, the note preview - responds
to keys, so there are no dead zones.
New Functions:
config.is_modern()/config.modern_feature()/config.ui_icon()- style resolutionutils.todo_index_at_line()/utils.todo_index_at_cursor()- line-to-todo lookupmodern.build()/modern.stats()/modern.progress_chunks()/modern.foldexpr()panels.centered()/panels.input()/panels.prompt()/panels.help()/
panels.tags()/panels.search()window.build_title()/window.build_footer()/window.create_compact_keys_window()
What's Changed
- fix: allow tasks to be deleted with confirmation by @adaviloper in #84
- Update FUNDING.yml by @atiladefreitas in #85
- feat(config): combine window size into adaptive
dimensionsoption by @ImmanuelHaffner in #87 - feat(nested): optionally inherit parent priority for sub-tasks by @atiladefreitas (closes #86, reported by @bkrusz)
- feat(ui): add opt-in modern interface style by @atiladefreitas
New Contributors
- @adaviloper made their first contribution in #84
Full Changelog: v2.10.0...v3.0.0

