Skip to content

ZenNotes v2.22.0

Latest

Choose a tag to compare

@github-actions github-actions released this 04 Aug 17:36

ZenNotes 2.22.0: the board remembers your order

Kanban cards stay where you put them, archived notes retire their tasks from the boards, a narrow sidebar keeps its footer readable, Cmd+L turns any line into a checkbox, and notes created on a remote vault carry their title again. Installers for macOS, Windows, and Linux (x64 + arm64) are attached below.

✨ New

  • Cmd+L turns any line into a checkbox, and toggles it on repeat. The Obsidian habit, asked for on Discord in exactly its terms: "turning a note into a task, and soon later marking it done." Press it on plain text and the line becomes - [ ] text. Press it again and the box checks; again and it unchecks. An existing bullet or numbered item keeps its marker (* note becomes * [ ] note), indentation and blockquote prefixes survive, and a multi-line selection converts every line in one press and one undo step.

    The states with their own meaning keep it. An in-progress - [/] checks off to [x], since finishing started work is the natural next step. A forwarded - [>] or cancelled - [-] line is left alone: both are records, both have their own commands, and silently flipping them would erase what they say.

    The binding is Cmd+L on macOS and Ctrl+L elsewhere, works with Vim mode on or off, and is remappable as editor.toggleCheckbox under Settings → Keymaps or [keymaps] in config.toml. It is also in the command palette as "Toggle Checkbox".

    How to test locally: put the cursor on any plain line and press Cmd+L three times: - [ ], then - [x], then back. Try a * bullet, a > quote, and a selection spanning several lines.

  • Archiving a note retires its tasks. Finish a project, archive its notes, and every - [x] it ever held used to keep standing in the Tasks Done group and the Kanban Done column, forever. Years of finished work crowding out this week's. Now a note that moves to the Archive takes its tasks with it: off the list, the boards, and the calendars, with the folder board's Archive column stepping aside too. Nothing is written to your files, the checkboxes stay in the markdown, and un-archiving a note brings its tasks straight back.

    Open tasks are the risky half, so archiving a note that still has some asks first ("This note still has 2 open tasks. Archive anyway?"), on every path in: the command palette, the note list, and both sidebar menus, with a bulk archive asking once for the whole set instead of once per note. Live work never disappears silently.

    This changes a default, deliberately: an archive that keeps shouting is not an archive. If you relied on archived tasks staying visible, Settings → Tasks → "Show tasks from archived notes" (or show_archived_tasks in config.toml) restores the old behavior everywhere, Archive column included. (Asked for by @naingyeminn in #540, whose write-up already contained the two right design calls: filter the views, never touch the files, and warn on open tasks.)

    How to test locally: put - [x] done and - [ ] open in a note, archive it from the sidebar menu, and answer the prompt. Open Tasks: neither task is there, and the folder board has no Archive column. Un-archive the note and both return. Flip the toggle in Settings → Tasks and archived tasks stay visible instead.

🐛 Fixes

  • The Kanban board keeps the order you drag cards into. Prioritize the Today column by hand, open a note, come back, and the column was sorted by due date again as if nothing had happened. The arrangement lived exactly as long as the board was on screen: dragging a card within its column deliberately writes nothing into the note (position in a note is line order, and a same-column drag should not rewrite your markdown), but the resulting order was kept only in the component's memory, which is discarded the moment you leave. The columns' own left-to-right arrangement already persisted, which is what made the missing half feel like data loss.

    Card order is now a real preference, saved the same way the column arrangement is: it survives switching views, switching Tasks sub-views, and quitting the app. Every board keeps its own order per column. It degrades on purpose rather than misplacing anything: a saved entry names a task by its note and position, so if an outside editor rewrites the note, the entry simply stops matching and that card returns to the default due-date sort. New tasks appear at the bottom of the column in their built order, and each drop rewrites the board's saved lists, so completed and deleted tasks fall out on their own. (Reported by a user who noticed their Today prioritization did not hold.)

    How to test locally: open Tasks, press 3 for Kanban, drag a Today card to the top, open any note, and come back. Before this the column reverted to due-date order; now the card is where you left it, including after a full restart.

  • A narrow sidebar keeps Files, Help, and Settings readable. The sidebar resizes down to 160px, but the footer's three labeled buttons need roughly 300px, and the layout neither clipped nor shrank them: the file counter painted straight over the word "Help", and "Settings" pushed out through the panel border. The search row had the same flaw, its icon strip overflowing instead of yielding. Reported from a Wayland tiling setup where narrow sidebars are a way of life, but nothing platform-specific was involved: any sidebar dragged near its minimum showed it.

    The footer now gives way in steps as the sidebar narrows. First the file count folds into the Files tooltip, then the labels drop and the three actions stand as icons, with tooltips and screen-reader labels carrying the names and the Settings update badge staying visible in every mode. The search row's icon strip wraps onto its own line under the search field instead of overflowing. Widen the sidebar and everything comes back. (Reported by @Johnny-Taake in #539.)

    How to test locally: drag the sidebar's right edge all the way left. Before this, the counter stamped over "Help" and "Settings" clipped through the border; now labels give way to icons and everything stays inside the panel. Widen past ~310px and the labels and count return.

  • A new note on a remote vault carries its title, the way a local one always has. Open Today's Daily Note from the palette or Leader d against a self-hosted server and the note arrived blank: no # 2026-08-04 heading, nothing, while the same command on a local vault seeds the heading. The desktop app writes # Title into every note it creates; the server's mirror of that operation wrote an empty file. Every surface that creates notes remotely was affected: the daily note, plain New note from the desktop app in remote mode, the web client, and the CLI pointed at a server.

    The server now seeds the same body, built from the final on-disk name, so a deduplicated "Note 2" heads itself correctly too, matching the desktop's behavior byte for byte. A daily-note template still wins where one is configured, since the app overwrites the seeded body with the rendered template right after creating. (Reported by @naingyeminn on Discord.)

    How to test locally: connect the app to a self-hosted server, run "Open Today's Daily Note". Before this the note opened empty; now it opens with the date heading, same as a local vault.

🧰 For contributors

  • kanbanCardOrder mirrors kanbanColumnOrder's plumbing exactly: normalized on load, saved to prefs, mirrored to the per-vault view override, round-tripped through the desktop settings normalizer. It is deliberately not in PORTABLE_PREF_KEYS: the entries name vault-local note paths, which have no meaning in a synced config.toml. applyColumnOrder in TasksKanban is the replay rule (listed cards first, unlisted keep built order) and is now exported and tested.
  • The sidebar footer's degrade thresholds are derived from content widths (~306px with the count badge, ~260px without), keyed off the sidebarWidth pref, which is exactly the rendered width because the aside is fixed-width and shrink-0. No resize observer. SidebarFooterAction also clips its own content now (min-w-0 + overflow-hidden), so no future label can paint over a neighbor even if the thresholds drift.
  • cm-toggle-checkbox.ts emits one minimal change per line (a single state character, or one marker insertion), never a whole-line replace, so cursors map through the edit naturally and a multi-line toggle is one undo step. The forwarded/cancelled skip matches the live-preview checkbox widget, which refuses to toggle a forwarded marker for the same reason.
  • Mod+L was audited before binding: it is the only registry binding on that chord, CodeMirror's defaults only bind Mod+a i u y z (selectLine is Alt+L), the vim extension maps no C-l at all, and no Electron menu accelerator uses it. In the self-hosted web app some browsers reserve Ctrl/Cmd+L for the address bar; it is rebindable there like every action.
  • The Go server's CreateNote is a mirror of desktop createNote (main vault.ts) and the MCP copy in vault-ops.ts, and all three now agree on the seeded body. TestCreateNoteSeedsTheTitleHeadingLikeTheDesktopApp pins it; if the desktop seed ever changes shape, change the mirror and the test with it.
  • filterTasksForDisplay (shared-domain tasks.ts) is the one rule for which tasks the live surfaces show; apply it where a surface reads vaultTasks, never in the scanners (the folder board and the toggle both need the archived tasks to exist in the index). Every archive entry point goes through confirmArchiveNotes in the store; a new archive path that calls window.zen.archiveNote directly reintroduces the silent-open-tasks hole, and there are four existing call sites to crib from.

Keyboard-first and local-first, as always.