github OpenTodoApp/OpenTodo v0.22.0
OpenTodo v0.22.0

5 hours ago

OpenTodo 0.22.0 is about speed at scale and switching in. Big accounts are much faster: in a 2,000-task project completing a task went from about 4 seconds to a tenth of one, a new device's first sync of a 10,000-task account is about ten times faster, and a year of note history is compacted in minutes while a new device downloads only the latest notes. Lists, label pages and saved views can now sort by custom fields, and on Android Back closes what is open. You can now switch from OmniFocus, Vikunja, Notion, Super Productivity and Tududi, chosen from a new Switching from… list on the Import screen. And there is a written stability promise for 1.0, checked on every change: the v1 API and data formats only grow, and upgrades from every earlier release are tested.

Upgrade notes

  • Replace the image or the binary and restart; upgrade the server before the devices. One migration runs at start (0055, an index for listing a project's tasks); there are no down-migrations, so back up first. No new variable and no new synced field.
  • Sync gains an optional latest_docs feature that upgraded apps use for a first sync; older apps keep getting the full history and keep working.
  • The first notes compaction after upgrading may run for a minute or two on a large, old database, in short steps that never hold writes for more than about 20 ms.

Added

  • A written stability promise for 1.0, checked on every change. Stability and compatibility says what every 1.x release keeps: the /api/v1/ routes with their fields, statuses and error codes, the sync protocol and op schema, the export format, the MCP tools and the ot commands, flags, exit codes and --json output. Within 1.x these only grow; a route that will be replaced is deprecated for at least 12 months and two minor releases first and answers with Deprecation and Sunset headers. Any release from 0.1.0 on upgrades directly to the latest, and every 1.x server works with every 1.x client and imports every export an earlier version wrote. Tests enforce it: a snapshot of the whole surface that may only grow, an upgrade of a data directory written by every released version from 0.1 to 0.21, the recorded calls of the web app, ot and the extension replayed against the current server, and every released version's export imported. From 1.0 on the image also gets a 1 tag that follows the 1.x line (#587).
  • Switching from… on the Import screen, and a migration matrix. Choose the app you use now at the top of Settings → Data → Import data: its export steps appear right there, the format is set for you, and Full guide opens its switch guide. Switch from another app lists all fifteen apps (Todoist, TickTick, Things, OmniFocus, Any.do, Microsoft To Do, Google Tasks, Apple Reminders, Vikunja, Trello, Asana, Notion, Super Productivity, Tududi and Linear) with what comes with you, what stays behind and when each import was last checked against a real export; a repository check keeps every row backed by an importer, a guide and a fixture, and release preparation for 1.0 refuses a check older than 120 days. ot import --help lists every format.
  • Import from Vikunja. Upload the ZIP from Vikunja's Settings → Export your data and your projects, sub-projects, Kanban buckets (as sections), tasks, labels, repeats, reminders, subtasks, "blocked by" relations, comments and attached files come with you. Verified against exports of Vikunja 0.22.1 and 2.7.1; see Switch from Vikunja.
  • Import from Tududi. Upload a copy of Tududi's database file (production.sqlite3) and your areas, projects with their notes, tasks, subtasks, tags, priorities, dates and repeats come with you, read from a private read-only copy that is deleted afterwards. Verified against Tududi 1.14; see Switch from Tududi.
  • Import from Super Productivity, tracked time included. Upload the backup from Super Productivity's Settings → Sync & Backup → Export data and your projects, tags, tasks, subtasks, archived tasks, due dates, deadlines, estimates and repeats come with you. The time you tracked arrives as one time entry per day, so your totals stay the same. Verified against Super Productivity 19.3.1; see Switch from Super Productivity.
  • Import from OmniFocus. Upload OmniFocus's CSV export (File → Export → CSV, or CSV UTF-16) and your projects, actions and action groups as subtasks, tags, due, defer and planned dates, estimates, flags and notes come with you. A newer export of the same database skips what you already imported. Repeat rules are not in OmniFocus's CSV, and the preview says so; see Switch from OmniFocus.
  • Import from Notion, with your own columns. Upload the ZIP from Notion's Export → Markdown & CSV: each task database becomes a project, each row a task with its page as the description, and sub-items stay under their parent. Because Notion's columns are your own, the preview proposes which column holds the status, due date, labels, priority, project and notes, and you can change each one (or leave it out) before importing. The API takes the same choice as the columns option; see Switch from Notion.
  • The import preview names the format a file really looks like. When you choose a format and the file is another app's export, the preview says which one (detected in the API).
  • GET /api/v1/tasks?project_id=<id> lists one project's tasks. The parameter used to be ignored, so the call listed every task of the account. It now lists only that project's items, on every collection whose items have a project, and reads only that project's rows: about 150 ms for a 2,000-task project in a 10,000-task account. An empty value or a collection without projects answers 400 invalid_input (API).
  • Performance budgets for a 10,000-task account. Performance budgets lists how fast OpenTodo is held to be on a computer, in a phone profile and on a small server, with a test account built by go run ./cmd/perfseed and a benchmark you can run with make perf (#590).
  • Sort lists by custom fields. Project lists, label pages and saved views can sort by Number or Date fields. Same-named fields match across projects, including inherited fields; a sort follows the field's stable definition ID after a rename and falls back to the page default when that definition is deleted. Sync carries the ID rather than the encrypted field name. (Group and order, Custom fields)
  • Suggest after adding a task. A new per-device switch, Settings → Integrations → Assistant → Suggest after adding (off by default), makes the assistant propose a clearer title, a project and up to four subtasks right after you add a task from a quick-add line. The proposal is the usual reviewable change set: nothing is written before Apply (even with Apply small changes without review on), and a task added while the assistant is busy gets no suggestion. Undo puts it back. (Suggest after adding, assistant-suggest-after-add)

Changed

  • Getting a chore's turn because someone left now tells you. When the person whose turn it is leaves a shared project, is removed or deletes their account, the next person in the rotation now gets the same It's your turn notification as after a completion, in the app, by push and on their notification channels. Before, the task moved to them silently (Take turns).

  • A new device's first sync of a large account is about ten times faster. Signing in to a 10,000-task account took over two minutes before everything was on the device; it now takes about 12 seconds on a laptop. The device keeps the newest 100 changes of each project in its local history after such a sync; the Activity tab and Updates load older history from the server, as before.

  • Long lists stay quick. In a 2,000-task project, completing a task used to take about 4 seconds on a computer and 15 on a phone before the tick showed; it now shows in the same frame as the click, and opening the project takes about 0.1 second instead of 2. Moving with j and k, an edit arriving from another device and typing in the command bar no longer redraw every row. Rows below the screen are drawn a moment after the top ones and stay in the page, so find in page, drag and drop and screen readers reach all of them as before. Ticking several tasks in a row on a phone no longer makes each tick wait for the list to work out all its rows again: a write redraws only the rows it changed, and the counts of saved views, workspaces and the app icon badge follow a moment after the ticks stop. The command bar prepares its search while the app is idle, so its first keystroke is as quick as the rest.

  • A year of note history is compacted in minutes, and a new device downloads only the latest notes. With 500 notes saved 300 times each over a year (2.6 GB), the compaction of old note versions never finished a pass and could hold a sync back for seconds; it now finishes in a few minutes of background work, works in steps of about 20 ms so a change saved meanwhile waits a moment at most, and returns the space as it goes (2.6 GB to 240 MB). A device signing in for the first time asks only for each device's latest copy of a note instead of every saved version: on that account it downloads 43 MB instead of the 2 GB a new device pulled before. Older apps keep working and download the whole history as before (Sync protocol, #590).

  • The assistant gets 12 steps for a multi-step request. A turn that keeps planning ("then break each of these down") reached round_limit after 8 model calls, so the loop now allows 12 on the server and on a device, and the fixed instructions ask the model to read what it needs in one step and send independent tool calls together. A tool-use answer on the 12th call still stops the turn with round_limit and no change set; an end_turn on the 12th call keeps its change set. (API, assistant-suggest-after-add)

Changed

  • Getting a chore's turn because someone left now tells you. When the person whose turn it is leaves a shared project, is removed or deletes their account, the next person in the rotation now gets the same It's your turn notification as after a completion, in the app, by push and on their notification channels. Before, the task moved to them silently (Take turns).

  • A new device's first sync of a large account is about ten times faster. Signing in to a 10,000-task account took over two minutes before everything was on the device; it now takes about 12 seconds on a laptop. The device keeps the newest 100 changes of each project in its local history after such a sync; the Activity tab and Updates load older history from the server, as before.

  • Long lists stay quick. In a 2,000-task project, completing a task used to take about 4 seconds on a computer and 15 on a phone before the tick showed; it now shows in the same frame as the click, and opening the project takes about 0.1 second instead of 2. Moving with j and k, an edit arriving from another device and typing in the command bar no longer redraw every row. Rows below the screen are drawn a moment after the top ones and stay in the page, so find in page, drag and drop and screen readers reach all of them as before. Ticking several tasks in a row on a phone no longer makes each tick wait for the list to work out all its rows again: a write redraws only the rows it changed, and the counts of saved views, workspaces and the app icon badge follow a moment after the ticks stop. The command bar prepares its search while the app is idle, so its first keystroke is as quick as the rest.

  • A year of note history is compacted in minutes, and a new device downloads only the latest notes. With 500 notes saved 300 times each over a year (2.6 GB), the compaction of old note versions never finished a pass and could hold a sync back for seconds; it now finishes in a few minutes of background work, works in steps of about 20 ms so a change saved meanwhile waits a moment at most, and returns the space as it goes (2.6 GB to 240 MB). A device signing in for the first time asks only for each device's latest copy of a note instead of every saved version: on that account it downloads 43 MB instead of the 2 GB a new device pulled before. Older apps keep working and download the whole history as before (Sync protocol, #590).

  • The assistant gets 12 steps for a multi-step request. A turn that keeps planning ("then break each of these down") reached round_limit after 8 model calls, so the loop now allows 12 on the server and on a device, and the fixed instructions ask the model to read what it needs in one step and send independent tool calls together. A tool-use answer on the 12th call still stops the turn with round_limit and no change set; an end_turn on the 12th call keeps its change set. (API, assistant-suggest-after-add)

Fixed

  • The task's Comments, Activity, History and Time tabs on a phone in a long translation. In German and French the row was wider than a 360 px screen and its last tab sat cut at the edge. The row still scrolls sideways inside itself, now fades out at the edge where more tabs are hidden, and keeps the selected tab clear of the fade; this holds for every tab row. Safari also draws every list row again: its engine sometimes left the rows on screen undrawn after the faster list rendering of this release, so a list could open blank.
  • The floating add button no longer covers fields on Import and other screens without quick add. The 0.21.0 fix left it out of the Settings overview only; on a phone it still sat over the import options. Each screen now says whether it offers quick add, and the button stays off Settings and its sub-pages, the theme editor, Import, Help, Updates, Statistics, Trash, Routines and the sign-in and share links. The layout check fails when the button covers a form field.
  • ot vault sync and watch no longer overwrite a file saved while a sync runs. Each sync reads a project file or task note again right before writing or removing it; a file saved meanwhile is left as saved, and the next sync merges it with what changed in the app in between. New lines in it still get their 🆔, so no task is created twice.
  • Labels that were cut with room to spare. A repeating task's This occurrence / All occurrences switch read "This occurren…" even on a wide screen: its options split the row evenly, so the longer label lost. The options of every two- or three-way switch are now as wide as the longest label and only shorten when the row itself is out of room. In the Week calendar beside the planning list, a narrow day header put the load next to the day's name and cut the name to "T…"; a narrow header now puts the load under the name. The layout check fails when a switch option or a calendar day name is cut.
  • The first screen no longer downloads Settings, the Calendar or Import. Those screens, and a project's Calendar layout, now load when first opened, with the app's row skeleton meanwhile; the first load fell from 745 KB to 648 KB gzip (the entry chunk from 312 KB to 135 KB), and every screen still opens offline because its chunk is precached. (OpenSpec)
  • A server with calendar feeds and calendar sync off no longer logs a 404 on every app start. The app now reads the two owner switches before asking for events, so the default install makes no request to the disabled events route and drops any events cached while the feature was on; once either switch is on, the next refresh fetches them. (OpenSpec)
  • A long panel title shows its start again after editing. A two-line title scrolled inside its field while it was edited kept that position after the edit, so the beginning stayed hidden. Leaving the field now scrolls the title back to its start.
  • The installed app's splash is the dark canvas. The manifest's splash background was a slate #0f172a that matches no part of the app; it is now the dark canvas #0d0e10. A manifest has one splash colour, so a light device sees a dark splash before the light page, which is the lesser flash compared with a bright splash on a phone in dark mode.
  • The installed app's status bar keeps its colour from the first frame. The static theme-colour metas, the boot script's light fallback and the manifest still said #0f172a, while the default emerald appearance uses #047857 in light and #0d0e10 in dark, so Android changed the tint after the app loaded. They now use the default bars, and a test keeps the three files in step.
  • The floating add button no longer covers Settings rows on a phone. Settings has no quick-add action, and the button sat over the chevrons at the end of its rows. On a phone the tab bar stays, and the button returns on the next view that offers quick add.
  • Undo a big delete. A bulk Trash offer held Undo for only ten seconds, and Delete on a large ticked set trashed it without asking. The offer now holds Undo for fifteen seconds and offers Show Trash, and Delete on more than twenty ticked tasks asks first with the count; smaller sets stay immediate (OpenSpec trash-undo-toasts).
  • Deleting a task always offers Undo. The command palette, Vim's dd, the Filter view and saved views wrote a bare tombstone and skipped the Undo toast the main list shows. They now use the same undoable trash, so every delete restores the task and its subtasks the same way (OpenSpec trash-undo-toasts).
  • A failed Focus time entry is no longer silent. When a session could not start or stop its time entry, the minutes were lost with only a console warning. The session now keeps running and a toast says whether it is not being tracked or its minutes may not be logged, with the underlying error (OpenSpec trash-undo-toasts).
  • A failure toast never covers the Undo offer. A focus time-entry failure could render a second toast region over the offer's Undo control. It now waits while an offer shows, takes the region when the offer leaves, starts its five-second window then, and pauses on hover or focus like the offer itself (OpenSpec trash-undo-toasts).
  • A failure toast keeps clear of the focused field. The focus failure message could sit over the field the person was typing in, and when its close button had focus, removing it left the next message held open. It now moves to the opposite corner like the Undo offer, and its hover or focus hold resets when the message changes or leaves (OpenSpec trash-undo-toasts).
  • Escape closes a failure toast and returns focus. The focus failure message had no keyboard dismissal: Escape with focus on its close button left the toast open with its timer paused. It now closes on Escape and returns focus to the element that had it before (OpenSpec trash-undo-toasts).
  • Move to project from a task's right-click menu moves the task again. Picking a project closed the menu and nothing else happened: the review of the move belonged to the menu, which the right-click menu, the row's More button and . remove as they close, so the review never appeared and the task stayed where it was ("Pick a date…" lost its date dialog the same way). The review, and the date, now appear however the menu was opened, and Apply moves the task as from the selection bar (#839).
  • Clearer feedback for a missing project, an empty Projects view and a sync error. Known local refusals such as a project deleted on another device now read in the interface language instead of English. The empty Projects view shows the shared empty state with one line distinguishing projects from labels and a New project action. A sync error keeps the status label and shows its message beside it (over two lines instead of cut short), and in the narrow icon sidebar tapping the status shows the full message in a toast, instead of hiding it in a tooltip.
  • Back on Android closes what is open, instead of the page behind it. The back gesture and button dismissed nothing: a sheet, a menu, the command palette or the phone drawer stayed over whatever page the browser had moved to. On a touch device (not in Safari or other iOS browsers, which have no such Back) every overlay now owns one history entry at the address it was opened from, so Back closes the topmost overlay and leaves the page where it is; a second Back navigates as it always did, and closing an overlay with its own control or Escape removes its entry so a later Back never reopens it. A destination chosen from the drawer takes the drawer's entry, so Back returns to the page the drawer was opened from (OpenSpec phone-interaction).
  • The closed phone drawer no longer takes the tab order or the screen reader. Below 768 px the drawer sat off screen but stayed in the document, so Tab walked through an invisible find field, destinations and account footer between the menu button and the page, and a screen reader read them on every screen. Closed, it is now inert and hidden from assistive technology; open, it is an overlay: focus moves into it, Tab stays inside it, the page behind it is inert and does not scroll, Escape or Back closes it, and focus returns to the menu button. From 768 px the sidebar stays the column it is, never inert and never hidden. phone-interaction in OpenSpec.
  • A tap on a row in Select mode ticks it instead of opening the task. "Select tasks" on a phone showed a checkbox on every row, but a tap on the title, or on the space beside it, still opened the task while the person was building a selection. With Select mode on, a tap anywhere on the row now ticks or unticks it, exactly as Shift and Ctrl/⌘ clicks do with a pointer; the checkbox, the details control, drag and swipe keep their own behaviour, and with Select mode off a tap opens the task as before (OpenSpec phone-interaction).
  • The quick add no longer covers the list on a phone held in landscape. From 768 px the quick add row is pinned under the page header, and a phone on its side is wider than that, so the pinned row and the header took most of the short viewport. The row now stays unpinned on a coarse pointer under 500 px tall and scrolls with the list, as it does on a narrow phone, leaving the floating Add button as the way to add; a keyboard cursor scrolled to a row no longer counts it as pinned there either (OpenSpec phone-interaction).

Install

  • Docker: docker run -d -p 8080:8080 -v opentodo:/data ghcr.io/opentodoapp/opentodo:0.22.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.