feat: TUI timeline view (vk timeline) #31

Merged
jmarya merged 5 commits from feat/timeline into main 2026-09-29 17:17:29 +00:00
Owner

Closes #3.

Adds vk timeline: a full-screen TUI that lays tasks out by every date field they carry.

 vk timeline · Aug 31 2026 – Dec 26 2026 · 1 column = day · 142 tasks
                                Aug 31 Sep 7  Sep 14 Sep 21 Sep 28 Oct 5
                                ╵      ╵      ╵      ╵      ╵▼     ╵
#1280 Browse Stadt Berlin leak…   ··▶······················•·┊
#1293 Setup Authentik                                •·······┊
#1299 job and worker queue                                 •·┊

Layers, bottom to top:

  • · lifeline from created until done (or now, while open). Every task gets a row, so backlog age is visible
  • ┊ today. It sits just above the lifeline so it never hides a marker
  • • updated
  • ━ start_date–end_date bar in the task's (or project's) colour, with ▶/■ for a lone start or end
  • ◇ projected repeats (repeat_after, or monthly mode), ◷ reminders, ◆ due (red when overdue), ✓ done_at

Navigation: zoom between 6 hours / day / week / month per column. The header shows the visible range. Keys: j/k select, h/l scroll, H/L page, +/- zoom, t today, c center on the selected task, ⏎ quit and print vk info for it, q quit. Rows whose dates are all off-screen show ◂/▸.

Flags: -p project, -l label, -d include done, -s only tasks with a start, end, due date or reminder.

The layout is pure functions (Viewport, row_cells, axis_labels, repeats_between) with unit tests, and the TUI loop only renders their output.

Verified: build, clippy and fmt are clean and 62 tests pass. I also ran it against a live instance (142 tasks) in tmux at every zoom level, with and without -s. Enter restores the terminal and prints the task.

🤖 Generated with Claude Code

Closes #3. Adds `vk timeline`: a full-screen TUI that lays tasks out by every date field they carry. ``` vk timeline · Aug 31 2026 – Dec 26 2026 · 1 column = day · 142 tasks Aug 31 Sep 7 Sep 14 Sep 21 Sep 28 Oct 5 ╵ ╵ ╵ ╵ ╵▼ ╵ #1280 Browse Stadt Berlin leak… ··▶······················•·┊ #1293 Setup Authentik •·······┊ #1299 job and worker queue •·┊ ``` **Layers, bottom to top:** - `·` lifeline from `created` until done (or now, while open). Every task gets a row, so backlog age is visible - `┊` today. It sits just above the lifeline so it never hides a marker - `•` `updated` - `━` `start_date`–`end_date` bar in the task's (or project's) colour, with `▶`/`■` for a lone start or end - `◇` projected repeats (`repeat_after`, or monthly mode), `◷` reminders, `◆` due (red when overdue), `✓` `done_at` **Navigation:** zoom between 6 hours / day / week / month per column. The header shows the visible range. Keys: `j/k` select, `h/l` scroll, `H/L` page, `+/-` zoom, `t` today, `c` center on the selected task, `⏎` quit and print `vk info` for it, `q` quit. Rows whose dates are all off-screen show `◂`/`▸`. **Flags:** `-p` project, `-l` label, `-d` include done, `-s` only tasks with a start, end, due date or reminder. The layout is pure functions (`Viewport`, `row_cells`, `axis_labels`, `repeats_between`) with unit tests, and the TUI loop only renders their output. **Verified:** build, clippy and fmt are clean and 62 tests pass. I also ran it against a live instance (142 tasks) in tmux at every zoom level, with and without `-s`. Enter restores the terminal and prints the task. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Lays tasks out on a horizontal time axis by their date fields. Each task
with at least one date gets a row: start_date..end_date is drawn as a
bar, due_date and done_at as markers on top of it, and a rule marks the
current time. Tasks with no dates are left out and counted in the header.

- Zoom between 6 hours, a day, a week and a month per column; columns
  snap to midnights, Mondays or month starts so the axis ticks line up
- Scroll, jump to today, center on the selected task, and press enter
  to leave and print `vk info` for it
- Rows whose dates are all off-screen point left or right instead of
  going blank
- Filters: --project, --label, and --done to include finished tasks;
  done_at is ignored on reopened tasks, where it is stale

Layout lives in pure functions (Viewport, row_cells, axis_labels) with
unit tests; the TUI loop only renders their output.

Closes #3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The timeline only placed tasks by start, end, due and done dates, which
left most tasks off it: on a real instance 140 of 142 open tasks had
none of those. It now draws every date field a task carries, in layers:

- A dim lifeline from `created` until the task was done (or now, while
  open), so every task gets a row and backlog age is visible at a glance
- `updated` as a marker on the lifeline
- Each reminder at its absolute time
- Projected future due dates for repeating tasks: every `repeat_after`,
  or monthly in month mode. From-current-date mode is projected from the
  due date, the best guess before the task is actually done

The today rule now sits just above the lifeline, so at coarse zoom it
no longer hides a marker sharing its column. Rows are sorted by their
first planned date, falling back to creation, and the view opens on the
first task ahead of today. The details panel gains a history line and
the footer a legend. `-s/--scheduled` restores the planned-only view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sorting by time makes the backlog of unscheduled tasks form one long
staircase of lifelines. Rows can now be ordered five ways, via --sort
or by cycling with `s` in the TUI:

- time: first planned date, else creation (the previous order)
- newest: most recently created first
- project: grouped by project, by time within each
- due: by due date, undated last
- activity: most recently updated first

The selected task stays selected across a re-sort. The initial selection
is now the task anchored closest to today, which works for every order
rather than assuming ascending time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dotted lifeline was too faint to read as a line. Lifelines are now a
grey half block and start–end bars a taller block in the task's colour,
so both read as filled shapes with the bar still the stronger mark, and
the lower blocks leave a gap between stacked rows. The last update is
the same half block in a brighter grey instead of a bullet, so it no
longer breaks the line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every lifeline was the same grey, so a screen of backlog read as one
block. Rows now take their project's colour: a darker shade for the
lifeline, full strength for the start–end bar and a lighter tint at the
last update. The task id in the name column carries the same colour so
rows can be matched to projects at a glance.

Projects without a colour in Vikunja get one from a fixed palette,
picked by project id so it is stable between runs. The palette avoids
the yellow, red, green and magenta used by the markers. Done tasks stay
grey.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jmarya merged commit d78dd9a675 into main 2026-09-29 17:17:29 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jmarya/vk!31
No description provided.