github OpenTodoApp/OpenTodo v0.19.0
OpenTodo v0.19.0

2 hours ago

OpenTodo 0.19.0 is about repeating tasks, the calendar and notes. A repeating task now shows its coming occurrences in Day, Week and Month; any one occurrence can be completed early, skipped, moved or edited alone; a series can change from one date onward without losing its history or a habit's streak; and All occurrences can fix the past ones too. Tasks can span days, the Month can stack several months and a new Year view shows the whole year at a glance. A new Notes page gathers task notes, project notes and standalone notes with search on the device, notes reach the server after a pause instead of after every keystroke pause, and the server removes old note versions so the database stops growing. Board columns fold and cards can show their notes, you can pick your own accent colour, and the Inbox shows only the Inbox. There is no migration; an older database is rewritten once in the background when the server is quiet (see the upgrade notes).

Highlights

  • Repeating tasks. Future occurrences in Day, Week, Month and optionally Upcoming (#550); act on one occurrence alone, with CalDAV exceptions (#551); This and following, with history, streaks and for N times counts carried across (#552; thanks to @pieros7, whose request #537 started this work); and Only future ones or Also past ones under All occurrences.
  • Calendar. Tasks that span days, drawn as bars from start to due and dragged by either end (#558); Months shown 2, 3 or 6 in the Month and a Year view (#557).
  • Notes. A Notes page with standalone notes, note search, ot notes, a REST collection and MCP tools (#655); a Formatting button beside notes on a computer (#654); notes sent after a pause (#653); compaction of superseded note versions, with a Database storage card for the owner (#652).
  • Boards and appearance. Fold a board column and show notes on cards (#561); a Custom accent colour with contrast guaranteed in light and dark (#563).

Upgrade notes

  • Replace the image or the binary and restart; upgrade the server before the devices. No migration and no new variable. New synced entity note (standalone notes) and new synced fields task.span, task.occurrence_date, task.split_from, the appends.<id> family of a task's or a note's notes, view.card_notes, project_prefs.card_notes, user_prefs.theme_accent_custom and user_prefs.formatting_toggle, all additive: older devices keep working and ignore them, but only an upgraded server stores and serves them. Back up first; there are no down-migrations.
  • An older database is rewritten once so it can shrink. A database created before this version is converted to SQLite's incremental vacuum mode by one full VACUUM, run by the new compaction job at a quiet moment (no change for 10 minutes, or at its next run once 7 days passed) and only with free disk space of the database's size plus 10 %. It takes about as long as a backup; reads continue, and a write that waits longer than 5 seconds fails as busy and is retried by the device. Without the space the server logs a warning, keeps removing old note versions and the file stops growing. New databases need nothing. No migration, no new variable. (Database size and compaction)

Added

  • Your own accent colour, with contrast guaranteed. Eight themes were the only choice, so a freelancer's brand colour or a team's colour could not be the app's accent. A ninth tile, Custom, in Settings → Appearance → Theme opens a picker (a bottom sheet on a phone): 18 swatches as a radio group (arrow keys move, Enter or Space picks) and a hex field (#1f6feb, 1f6feb or #f60; Enter or leaving the field applies it, anything else shows an error), with a preview of a button, a link, a selected row and a soft chip in light and dark while you choose. The device derives the light accent, the bright dark-mode accent and, when needed, a lighter dark-mode button from the one colour, keeping its hue and moving only its lightness until every accent contrast pair of the design system passes in both modes (a pale yellow becomes a darker yellow in light and a light yellow in dark); a colour close to the red of errors and priority 1 is turned away from it until it is as far as Rose is. When the colour was adjusted the picker says so and shows the picked and the used colour. Choosing another theme keeps the colour for next time; the browser and system bar uses the derived light accent; the first frame is already in your colour (the derived values are cached on the device, the derivation itself loads lazily). Synced as the additive user_prefs.theme_accent_custom beside theme_accent: "custom" and carried by the export and import; older apps show Emerald and write nothing. High contrast still shows black or white. (Your own accent colour, Design system, accent-colour, closes #563)
  • Fold a board column, and show notes on cards. A finished or parked column ("Done", "Later") took as much width as the one being worked on, and the line of context in a card's notes was a click away. Choose Fold column in a column's menu, or press z on one of its cards, and the column becomes a narrow strip with its colour or icon, its open-task count and its name written sideways; select the strip (or Enter / z on it) to unfold it. A folded column still takes cards: drop one on the strip or press Shift+→ / Shift+← into it, the card goes to the bottom, the count goes up, the move is announced and the column stays folded; h / l skip it, and on a phone it is a 48 px strip between the others with a dot per unfolded column. Folding is kept on this device only and is the same as collapsing that section in the list layout; saved views in the board layout fold their columns the same way, and a board of filters folds a panel to its header line. Show notes on cards in a project board's menu (your own choice for that project, synced to your devices; Notes on cards in a board view's display options) adds the first two lines of each card's notes as plain text under its title, read from the notes on the device, so it works offline and on encrypted accounts and never shows ciphertext (#561).
  • Several months in the Month, and a Year view. The calendar stopped at one month, so a term, a quarter of launches or a training block could not be seen at once. Choose Months shown: 2, 3 or 6 in the Month's page menu to stack that many months, each with its name and only its own days, so a day and its tasks appear once; the arrows and j / k run on across them, [ and ] still move by one month, and this device remembers the choice (a phone always shows one month). Year (g y, and in the Day | Week | Month | Plan | Year switch) shows the twelve months as small grids with a mark under each day in four steps (nothing, 1–2, 3–5, 6 or more) that also differ in length and in the weight of the number, a red dot where the estimates exceed your day length, and a tip with the day's count and first three titles. It counts open tasks, every day of a multi-day task and, while Show repeating occurrences is on, the coming occurrences of repeating tasks; events and deadlines don't count, and the calendar's filter and a project's Calendar layout apply. Click a day for Day and a month's name for Month; with the keyboard the twelve months are one grid (arrows across month edges, Page Up / Page Down a month, Home / End, [ / ] a year, Enter opens the day). On a phone a month is one touch target that opens it in Month. Month and Year now load on first use, which took 1 KB off the first load. Everything is read from the device and works offline (#557).
  • Tasks that span days. A trip, a conference or a week of writing showed as one dot on its last day, so the calendar never said that Tuesday to Thursday were taken. Turn on Multi-day in the When editor's Start tab (it sets the start to the day before when there is none) and the task is drawn from its start date to its do-on date: one bar in the Week's all-day strip in the project's colour, the day's piece in Day with "Day 2 of 3" and a due time as "until 17:00", a bar across each of Month's week rows that continues on the next, and a row in each Plan column with its position; on a phone the Week lists it under each day it covers and the Month counts it on each. Drag the bar to move start and due together, drag an end to change the first or last day, or drag the right end of a task without a time onto a later day to make it a span; with a span selected, ←/→ move it, Shift+←/Shift+→ move the last day and Alt+Shift+←/Alt+Shift+→ the first, each one change with Undo (Shift+→ on a task without a time makes it a two-day span). Upcoming lists a span once under its first day with its range ("3–5 Nov"), Today shows it during the span with "until 5 Nov", and every list row shows the range in the date's place. Its estimate counts on the last day only; a repeating span keeps its length, and its coming occurrences are drawn as dimmed bars. The flag is the additive boolean task.span: start dates keep meaning "defer until" for every other task, and a flag without a valid range draws as a plain task. REST and the export carry it, ot add/ot edit take --span/--no-span and ot list prints the range, AI tools read and write it, CalDAV serves X-OPENTODO-SPAN:TRUE (other CalDAV apps show the task on its last day), the calendar feed and calendar sync write a span as one all-day event and read a moved or resized event back into start and due, and the Markdown vault writes [span:: true]. (Tasks that span days, API, CalDAV, multi-day-tasks, closes #558)
  • A Notes page, standalone notes and note search. Notes in the sidebar's More group (g e, promotable to the main destinations) lists your task notes, project notes and a new kind of note that is not a task, newest change first, 50 at a time with Show more, filtered by source, project and label. Write meeting notes or a reference page with New note (q on the page), in a project and with labels; a note in a shared project is shared with it (editors write, viewers read). The search field finds every word you type in titles and text, ignoring case, accents and formatting, with the matching words highlighted, on your device only, so it works offline and with end-to-end encryption; the command palette (Ctrl+K) finds notes too, and the filter language gains notes, no notes and notes: text. Turn into task and Move note to a new note convert in both directions with one Undo, and notes go to the Trash like tasks. Scripts and assistants get ot notes and ot task note, the REST collection /api/v1/notes with POST …/append and …/to-task, and MCP tools to list, search, read, create, update, append to, trash and convert notes behind the write gate. Text added from outside is appended at the end without replacing what you wrote (the additive appends.<id> field), and replacing a note's text checks that nobody changed it since it was read (body_version, If-Match, 409 body_changed). Notes are not sent over CalDAV, webhooks, the activity feed, automations or the Markdown vault. (Notes, Notes, tasks and scripts, API, notes-overview, closes #655)
  • A Formatting button beside notes on a computer. The formatting row (bold, italic, link, bullet list, checklist and More) was shown only on touch screens; on a computer, a task's or a project's note now has a small Formatting button at its top corner, shown when you point at the note or move into it, that keeps the row above the note until you press it again. Whether the row is shown is remembered on each computer, separately for notes and for the comment box, whose Formatting button now remembers its row too. The row is a proper toolbar: one Tab stop, the arrow keys, Home and End move between its buttons, Down opens More, Escape returns to the text with its selection, and a button puts you back in the text. Formatting button in Settings → Account → Profile → How you work (on by default, synced as the additive user_prefs.formatting_toggle) hides the button in notes and comments; shortcuts, typed formatting and the / menu keep working, and phones are unchanged. (The formatting row, notes-toolbar-desktop, closes #654)
  • The server removes old note versions. Every save of a task's or a project's note stored the whole note, so the database kept every old copy and a new device downloaded them all. Once a day, after something changed, a background job deletes the superseded note documents the server received more than 30 days ago, in every scope, shared projects included, and keeps the last readable revision of each UTC day for older edits; the current note and every other field are never touched, and devices converge exactly as before (cursors stay valid, offline devices keep their edits). It works in short steps, so syncing is never held up, and returns the freed space to the file system. Settings → Server → Backups → Database storage shows the owner the database's size, the free space inside it and what the last compaction removed and reclaimed (GET /api/v1/settings/storage), and the History of a task or project says in one line that older note versions are kept one per day. On a host the job runs per tenant and lowers its operation count. (Sync, Backups, notes-compaction, closes #652)
  • Fix a repeating task's past occurrences too: "All occurrences" asks "Only future ones" or "Also past ones". All occurrences changed a series from its current occurrence on, so renaming "Spansh class" left every completed session, and the series before a change from a date on, with the typo. Now, when a series has a past (a done or skipped occurrence, or an earlier part from "This and following"), a second choice appears under the scope control in the task panel: Only future ones (the default, as before) or Also past ones. With Also past ones, a new title, notes, labels, project or priority also reaches the past occurrences that still held the old value and the earlier parts of the series, in one step with one Undo; past dates and completions never change, and a new time or rule only reaches the earlier parts' own dates. Works offline; on an encrypted account the device encrypts each occurrence itself. The API's PATCH of a series still changes that task only. See Recurring task rules.
  • Change a repeating task from one occurrence onward; history, streaks and counts carry across. Changing a routine from a date on (the Spanish class at 19:00 after the break, gym days from Monday to Tuesday) meant editing the whole series or ending it and starting again, which lost its history and a habit's streak. Open a coming occurrence and choose This and following between This occurrence and All occurrences: the first change there ends the series the day before (its rule gets until) and starts a new series on that date with the change, in one batch with one Undo ("Changed from Wednesday 14 October on"). Done, skipped and moved occurrences after the date stay as they are; open ones that still hold the old value take the change. The new series carries the additive task.split_from naming the series and date and a derived id, so two devices making the same split offline make one, and splits at two dates on two devices converge: each piece runs to the day before the next one, and any device writes that until into the rule. for N times counts over all pieces, so twenty sessions stay twenty; a habit's streak and month grid read every piece; the panel reads "Continues Study from Oct 14" and "Continues as Study from Oct 14". Delete this and following in a projection's menu ends the series the day before. Scripts use POST /api/v1/tasks/{id}/split and ot task split <task> --from <date>, and the occurrence list continues into the pieces with each date's series_id; CalDAV serves each piece as its own repeating VTODO with RELATED-TO;RELTYPE=SIBLING and the ended series with its UNTIL; the vault writes each as its own line. (Recurring task rules, API, CalDAV, series-split, closes #552; thanks to @pieros7 for the request in #537)
  • Act on one occurrence of a repeating task: complete it early, skip it, move or edit it alone; CalDAV exceptions. Only the current occurrence could be detached, and a skip always moved the whole series. Now any coming occurrence can be changed alone, as calendars do with "this event only": right-click, long-press or press Shift+F10 on a projected occurrence for Complete this occurrence, Skip this occurrence (Skip this day for a habit) or Move to…, or drag it with the mouse to another day or time in Day, Week, Month and Plan, with Undo. Opening one shows the panel on This occurrence · All occurrences, with the date, the task's values, its actions and Edit this occurrence; "This occurrence only" (Shift+O) now writes the same occurrence record. A changed occurrence is a task of its own with the additive task.occurrence_date naming the date it stands for; that date becomes an exception of the series, so no projection is drawn there and completing the series jumps over it, and two devices acting on different dates offline keep both (a device that rolled before it saw another's record moves the series on after the next sync). for N times counts done and open occurrences, never skipped ones; a moved occurrence gets copies of the series' relative reminders; renaming the series renames the moved ones that still had the old title; deleting an open occurrence skips it; a habit's grid counts a moved day on the day it stands for. CalDAV serves exceptions as EXDATE, moved occurrences as their own VTODOs and counted rules with UNTIL, and a client's EXDATE and RECURRENCE-ID overrides of any date (completed, cancelled or changed) become the matching occurrence; the Markdown vault jumps over exceptions too. Scripts use POST /api/v1/tasks/{id}/occurrences/{date}/complete and /skip, PATCH /api/v1/tasks/{id}/occurrences/{date} and ot task occurrence <task> <date> complete|skip|edit; the occurrence list reports open, done and skipped. On an encrypted account server-made records carry no title until an unlocked device fills them in. "This and following" comes with series-split. (Repeating tasks on the calendar, Recurring task rules, API, CalDAV, series-exceptions, closes #551)
  • Future occurrences of repeating tasks and habits in Day, Week and Month, and optionally in Upcoming. A repeating task stood on one date only, so next Tuesday's study session or Friday's habit was missing until the earlier one was completed. Day, Week and Month now draw the occurrences that follow the current one as faint blocks and rows with a dashed edge and the repeat icon, computed on the device from the rule (nothing is written or synced, offline included), stopped by until and for N times, and counted in each day's planned total ("45m repeating"). They can't be dragged, completed or selected with j/k; Tab reaches them and a click or Enter opens the task with the date named under its title. The calendar's filter, Colour by and a project's Calendar layout apply to them. Show repeating occurrences in the calendar's menu turns them off on a device (on by default; the Plan keeps them), and the same item in Upcoming's menu (off by default) lists the next 14 days' occurrences under their days. Scripts and assistants read the same dates with GET /api/v1/tasks/{id}/occurrences, ot task occurrences and the next_occurrences of a recurring task in the AI tools; the calculation is shared by the server and the app and pinned by web/src/lib/occurrences.vectors.json. Acting on one occurrence alone and "this and following" come with series-exceptions and series-split. (Repeating tasks on the calendar, Recurring task rules, API, series-occurrences, closes #550)
  • The OpenTodo account on X, from Settings → About and Help. A News and updates row in Settings → About opens OpenTodo on X, where each release and the work in progress are posted in a few lines; the settings search finds it under "twitter" and "news". A plain link in a new tab, like the Discord row: nothing in the app talks to X. The README and the Feedback and support guide name it too. (Feedback and support, x-account-link)

Changed

  • The Inbox shows only the Inbox. The folded Planned, no project group at the end of the Inbox is gone, with its Open as a new view action: dated tasks seemed to be in the Inbox while it said Inbox zero, and tasks without a project are too common to need a section. An empty Inbox shows its full Inbox zero picture again. Planned tasks without a project are where their dates put them, in Today and Upcoming, and Filter with no project lists them all. Quick add in the Inbox still says where a dated task went ("Added to Friday"). (Today and Upcoming, inbox-only-inbox)
  • Notes are sent after a pause, not after every keystroke pause. The note editor still saves on the device 400 ms after you stop typing, so a crash or a closed tab loses nothing, but it now sends the note to the server only 3 seconds after its last save, at once when you leave the note (it loses focus, the task or project closes, another task opens, the app navigates away, the page is hidden or closed), and at least every 60 seconds while you keep typing. Before, every short pause sent a full copy of the note, up to 256 KB each. Any other sync carries the waiting note too, the device keeps only the newest copy and its text, collaborators see your text about 3 seconds after you pause, offline writing and convergence are unchanged, and comments are not affected. (Sync, Project notes, notes-save-cadence, closes #653)

Fixed

  • An attachment uploaded right after another one is no longer refused as over the quota. An upload held its size against the attachment quota until a moment after its answer was sent, so a device that sent the next file at once could find the finished file counted twice and get "this upload would go over your quota" with room to spare. The room is now given back before the answer is sent; parallel uploads still share the free space.
  • "Added to the bottom of …" shows on a slow device too. After a quick add, the app waited only twelve frames for the new row before deciding whether it was out of sight, which a busy phone or computer could miss, so the toast with Show never came. It now waits for the row itself, for up to 3 seconds.
  • Offline, a part of the app that was not loaded yet no longer reloads the page. When a piece of the app loaded on first use (such as the "Added to the bottom of …" announcement after a quick add) could not be fetched because the device was offline, the app took it for a newer release and reloaded, which showed an empty page until the connection came back if the app was not installed yet. Offline, the app now stays on the page and does without that piece; online, a missing piece still reloads into the new release.

Install

  • Docker: docker run -d -p 8080:8080 -v opentodo:/data ghcr.io/opentodoapp/opentodo:0.19.0
  • Binary: download opentodo-<os>-<arch> below, then run it (data goes to ./data).
  • Command-line client: ot-<os>-<arch>. Browser extension: opentodo-browser-extension-chrome.zip and opentodo-browser-extension-firefox.zip.

Verify downloads with sha256sum -c SHA256SUMS. Self-hosting guides: docs/self-hosting.

Don't miss a new OpenTodo release

NewReleases is sending notifications on new releases.