Skip to content

v3.0.0

Latest

Choose a tag to compare

@atiladefreitas atiladefreitas released this 12 Aug 01:15

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):

Dooing classic UI

Modern (ui.style = "modern"):

Dooing modern UI

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 default links
  • window.dimensions accepting a table or a function of the editor size
  • nested_tasks.inherit_priority to 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 tomorrow or in 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, so zc on a
    parent collapses exactly its own subtree
  • Previously foldmethod=indent produced fold level 0 for both indent 2 and indent 4 at
    the default shiftwidth, 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 using vim.ui.input because 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 to false
  • 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, DooingQuickKey and
    DooingQuickDesc were referenced by the panel but never defined anywhere in the plugin
  • add_time_estimation could error. It indexed state.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() returns lnum as an index into state.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 statistics
  • lua/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 teardown
  • lua/dooing/ui/window.lua - progress title and footer, compact quick keys, per-style
    fold setup, sub-window cleanup
  • lua/dooing/ui/calendar.lua - layout table, centred positioning, roomier modern grid
  • lua/dooing/ui/components.lua - dispatch to the modern panels, scratchpad lookup
  • lua/dooing/ui/actions.lua - all cursor-to-todo lookups routed through one accessor,
    centred prompts, nil guards, inherit_priority
  • lua/dooing/ui/utils.lua - todo_index_at_line() / todo_index_at_cursor()
  • lua/dooing/ui/constants.lua - line_to_todo, primary_lines, fold_levels
  • lua/dooing/ui/highlights.lua - 16 new groups, plus the three quick-keys groups that
    were previously referenced but undefined
  • lua/dooing/ui/init.lua, lua/dooing/init.lua - focus_first on list open and swap
  • lua/dooing/config.lua - ui section, is_modern(), modern_feature(), ui_icon(),
    get_window_dimensions(), legacy dimension migration
  • README.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 resolution
  • utils.todo_index_at_line() / utils.todo_index_at_cursor() - line-to-todo lookup
  • modern.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

New Contributors

Full Changelog: v2.10.0...v3.0.0