Skip to content

ZenNotes v2.76.0

Latest

Choose a tag to compare

@github-actions github-actions released this 23 Oct 21:45
· 4 commits to main since this release

ZenNotes 2.76.0 is a Tasks release. Any task can take any due date from its right-click menu, a task without a due date waits under its own No due date group instead of in Today, and the Tasks list can put high priority first while staying grouped by date. It also fixes Cloud conflicts that a phone could not settle, one of which could delete a note it kept, and names a note's folder the same way in note search, the buffer switcher and the [[ picker.

Released from 61628218 through #994 on 2026-10-23. All installers are verified.

Features

  • Choose any due date from the task menu (#991, thanks to @davi-jorge-art). The menu every task surface shares stopped at Due next week, so a day further out meant finding it on a calendar and typing due:2026-11-04 on the line, which is the most work on a phone. Choose a date… now follows the presets in the Tasks list, on the board, in both calendars, and on the phones a long-press on a task. It opens the month calendar that typing @date opens in a note, on the task's due date (today without one), and a click on a day sets it. In Vim mode, @ opens it on the selected task in the list, on the board and in the Tasks calendar. A daily-note task picked for its own note's date gets that date written out, so it stays when the task moves.
  • No due date has its own place (#990, thanks to @davi-jorge-art). An open task without a due date sat in Today, so Today mixed what is due with everything undated. It now waits under No due date, after Upcoming, in the Tasks list; the Status board gains a No due date column between Upcoming and In progress, and dropping a card there (or Shift+L / Shift+H into it) clears its due:. Home's Today keeps only what is due today or overdue. A task written in a daily note still counts as due on that note's date while "Tasks are due on the note's date" is on, so it stays in Today, and the No due date column refuses it, since its date is its note's.
  • Order the Tasks list by priority (#989, thanks to @davi-jorge-art). The list kept each group in its notes' line order, so a high-priority task could sit under five low ones. A new Order menu above the list switches each group between Note order and Priority: high, then medium, low and none, ties by due date, still grouped by Today, Upcoming and the rest. s switches it in Vim mode, :order priority and :order note on the ex line, and the command palette, Settings → Tasks and tasks_list_order under [view] in config.toml set the same thing. Shift+J/K and dragging reorder only in Note order; in Priority order they say how to switch back. zn 0.11.0 reads the same key (O in its task list).

Fixes

  • Cloud conflicts settle cleanly, and none deletes a note (reported by Dave on Discord). A phone whose sync had been stuck came back to two conflicts on one Untitled.md: keeping the other device's version said the file already existed, the second conflict then refused both choices as "changed again", and taking the other device's version of a note this phone never had could delete the note it kept, on both devices. Three causes, reproduced with two devices against a local Cloud server. A note met at a file this device had edited was queued as a conflict against it; such a conflict now settles after each pull once Cloud has moved that note on, landing where Cloud keeps it and leaving this device's file alone. The other device's version could not take its place while any file of this device held the path; that file now moves beside it to the next free name (Untitled 2.md and on). And a Cloud path taken by another file now says so instead of "changed again".
  • One folder label for a note everywhere. 2.75.0's note search showed the whole vault path (inbox/Projects/Atlas), the [[ picker showed Projects/Atlas/, and the buffer switcher the raw parent folder, while the picker made an archived 2025/Atlas look like an inbox folder. All three now name a note's folder the same way: inside the main notes area, the folder within it; anywhere else, the path from the vault root, area first (archive/2025/Atlas).
  • Move asset takes a folder like v1..2. The Move asset prompt refused any path with two dots in a row, and promised that an empty answer moves the asset to the vault root while every host files it in assets/. It now refuses only a whole .. segment, says an empty answer goes to assets/, and does that: an asset loose at the vault root moves there.
  • zn 0.11.0 is bundled. On macOS and Linux, the zn that ZenNotes installs is now 0.11.0, the terminal side of No due date and the priority order (ZenNotes/tui#16).

For contributors

  • Due date picker (1bf79403): chooseTaskDueDate in lib/task-context-menu.ts opens the @date DatePickerModal and writes through the same due-date mutation as the presets; the @ key is Vim-gated like x, i, c and > in TasksView, TasksKanban and TasksCalendar. Tests in task-context-menu.test.ts.
  • No due date (032e5da2): VaultTaskGroups.undated in shared-domain tasks.ts; the Status board's undated column and dropMutationsFor('undated') (refused for dueInferred tasks) in task-column-mutations.ts; Home and getTodayTasks now apply inferDailyTaskDueDates as the Tasks view does. Drag-reorder inside the No due date group came with 712a0e7a.
  • List order (712a0e7a): applyPriorityOrder and the order parameter of computeTasksRender in lib/tasks-filter.ts; tasksListOrder in the store, PORTABLE_PREF_KEYS and the desktop's TOML map ([view] tasks_list_order).
  • Cloud (5a34aea7): settleMovedArrivals in shared-domain's cloud-sync-coordinator.ts runs after each pull and on resolve; cloudSyncFreePath and displace_existing make room for the other device's version; PATH_CONFLICT has its own message.
  • Folder label (d825b149): noteFolderLabel in vault-layout.ts, used by SearchPalette, BufferPalette and the [[ picker. Move asset (0b3c8ce9): one prompt in lib/move-asset.ts.
  • zod 4.6.6 (61628218), the first release fixing the advisory that failed npm audit --omit=dev --audit-level=high; only that package moves in the lockfile. What's New 2.76.0 (efe5592f); bundled CLI 0.11.0 (36b8edbf); version bump (49304a02).

Demos

Screenshots of this release on the release build, attached to this release:

Choose a date… from a task's right-click menu opens the month calendar on its due date; a click on a day sets it.

The month calendar opened from a task's menu

No due date waits after Upcoming instead of in Today.

The Tasks list with a No due date group

Priority order: high first inside each group, still grouped by date.

The Tasks list ordered by priority

Verification

How to test locally. Build once with npm run build --workspace @zennotes/desktop and launch with isolated stores: ZENNOTES_USER_DATA_PATH=$(mktemp -d) ZENNOTES_CONFIG_DIR=$(mktemp -d) npx electron apps/desktop/out/main/index.js. Put a note with these lines in the vault (use today's date for the first):

- [ ] Call the bank !med due:2026-10-23
- [ ] Renew passport !high due:2026-10-20
- [ ] Book museum tickets due:2026-11-04
- [ ] Read the manual
- [ ] Review contract !high due:2026-10-23
  1. No due date. Open Tasks (Cmd+Shift+P, "Open Tasks"): Read the manual sits under No due date after Upcoming. Before: it was in Today. Switch to Kanban: the Status board has a No due date column; drag Book museum tickets there and its due: is gone from the line.
  2. Choose a date. Right-click Call the bank and pick Choose a date…: a month calendar opens on its due date; click a day and the line's due: changes. With Vim mode on, select a task and press @ for the same calendar.
  3. Priority order. Set the Order menu above the list to Priority (or press s in Vim mode): Today reads Renew passport, Review contract, Call the bank. Press Shift+J: a message says reordering works in Note order. :order note brings the line order back, and tasks_list_order = "note" is under [view] in config.toml.
  4. Cloud conflicts. Needs two devices on one Cloud vault. Close the first and edit a note there by hand (say Daily Notes/Untitled.md). On the second, move that note to the trash, make a new note with the same name in the same folder, and trash that one too. Open the first again and let it sync: the new note lands in its trash on its own, and Review sync changes (Space r) asks only about the note you edited. Either choice goes through; with the other device's version, a file of this device already at its path moves to Untitled 2.md instead of refusing. Before: a second conflict claimed the edited file and refused both choices, and taking the other device's version on it could delete the edited note on both devices.

Distribution channels

  • GitHub release: installers for macOS (Apple silicon and Intel DMG and zip), Windows (x64 and ARM64 installer) and Linux (x64 and arm64 AppImage, deb, rpm, pacman, tar.gz), with five update manifests; the three screenshots in Demos are attached too.
  • AUR: zennotes-bin 2.76.0-1 (AUR clone 1831d7a, repo efebd2fe), pinned to ZenNotes-2.76.0-linux-x64.tar.gz SHA-256 49b6fe70…7a27, after the tarball check confirmed the bundled CLI folder is world-readable.
  • Nix: packaging/nix/release-data.json lifted from the update workflow (run 40030644133, bot PR #995 closed) as 2107af3e, and nix-build on main passed.
  • nixpkgs: NixOS/nixpkgs#575797 now proposes 2.70.0 -> 2.76.0 (48ba825f), rebased on master; the source hash comes from the tag tarball, and the same command reproduces master's 2.70.0 hash.
  • Homebrew: zennotes 2.76.0 in the tap (e9e4832), mirrored in 62cdd219; both DMGs' SHA-256 match the cask and GitHub's digests; brew style clean.
  • zn CLI: ZenNotes/tui v0.11.0 (ZenNotes/tui#16), its signed manifest verified against zn-release-1.pub and pinned byte for byte (36b8edbf); the Homebrew formula is on 0.11.0 (tap f400799).
  • Website: ZenNotes/website#90 merged with the 2.76.0 entry and its three screenshots, the docs for Choose a date…, No due date, the list's Order and where the other device's version of a Cloud conflict goes, and the CLI pages on 0.11.0. It also carries d403396: livewire 4.4.8 for an advisory published during this release that failed composer audit, which gates the website's deploy. Laravel Cloud deployed it at ee152467, and the live check passed: /releases leads with 2.76.0, all three screenshots match the local files byte for byte, and /docs and the CLI manual say 0.11.0.
  • Server and Docker: ZenNotes/znserver v2.76.0 pins the browser app web-2.76.0-web.h5b1c11f768454e10 (b46e2d3, ZenNotes/znserver#44); all five binaries match SHA256SUMS, and the released darwin-arm64 binary served the 2.76.0 app on a scratch vault and answered 401 without its token. adibhanna/zennotes:2.76.0, 2.76 and latest are published from the tag.
  • Boundary packages: core-2.76.0-core.h076cc71aa1ce0bbe and web-2.76.0-web.h5b1c11f768454e10, built by CI from 61628218; web is published as a pre-release, core once both phones shipped on it. No viewer this time: nothing in 2.76.0 changes how a published note renders. "Latest" still names v2.76.0.
  • Phones: iPhone 1.32.0 (build 48, ZenNotes/zennotesios#75, on core 2.76.0) is Waiting for Review in App Store Connect, set to release automatically. Android 1.1.46 (versionCode 49, ZenNotes/zennotesandroid#132) is in Google Play review at 100% rollout with no devices lost, and its GitHub release carries Play's signed universal APK. Both carry the Cloud conflict fixes, Dave's navigation-bar and home-indicator fixes, and two fixes found while preparing them: the Tasks list's Order row no longer stretches on a phone, and the date calendar drops its keyboard help on touch.

Release validation

Every CI failure since 2.75.0 was read before this release: ci-sweep.py v2.75.0 found none outside bot branches (two unmerged Dependabot PRs and the Nix bot's zero-job run, which fails the same way every release). hazard-scan.py --since v2.75.0 found no hazards. Two problems turned up during the release and were fixed before it shipped: npm audit --omit=dev --audit-level=high had started failing on zod 4.3.6 through the MCP SDK (fixed in 61628218, zod 4.6.6, before the cut), and on phones the Tasks list's new Order row stretched to a third of the screen while the date calendar showed keyboard help (fixed in both phone apps, below).

Release-time gates, in a clean worktree with its own npm ci at 61628218: typecheck 8/8 with no turbo cache, test:run with no cache (shared-domain 2,065, app-core 3,435, desktop 1,192, quicklook 15 and the rest), and a fresh build. Pack: Developer ID signed; packaged launch check: page target and drawn app in 1.6 s, version 2.76.0. Smoke suites vim-editor, sidebar-vim and editor-improvements pass on the first run. The bundled MCP server answered over stdio on zod 4.6.6 (34 tools, list_tasks, bad arguments refused). #991, #990 and #989 were driven in that build over CDP with isolated stores, which is where the screenshots come from; zn 0.11.0 was driven in tmux.

Release PR #994: all 10 checks passed on the first run. CI on main for 61628218 (run 40027088057): all six jobs green before the tag, and CodeQL too. Release run 40029084913: all six jobs green (Windows ARM64 51 minutes, macOS 44); verify-installers.py ALL GOOD by SHA-512 on the five manifests; the app in both DMGs is Notarized Developer ID with a valid stapled ticket. npm run verify:channels -- 2.76.0: every channel serves v2.76.0 (Homebrew, AUR, Nix and the website's eight download buttons).

On the phones, Dave's Review sync changes confirm step was rendered from core 2.76.0's own dialog and measured on the Android 1.1.46 build on an API 37 emulator (WebView 145) with 3-button navigation: Saving… and Go back end at 723 px with the bar from 752, and both landscape rotations are clear. The iPhone 1.32.0 build was walked from a fresh install on the simulator: a new task under No due date, Choose a date… from its long-press menu, a tap on a day moving it under Upcoming.

After the tag, CI on main for the packaging commits: the AUR commit's run (efebd2fe) was cancelled by the Nix push that followed it a minute later, and the Nix commit's run (2107af3e) passed all six jobs; the Homebrew commit's run (62cdd219) failed only its production dependency audit, on a body-parser advisory (GHSA-27r3-jxmh-p828, high) published after the cut. body-parser reaches the app only through express inside the MCP SDK, whose HTTP transports the app never starts (its MCP server speaks stdio), so 2.76.0 never runs it; 2e87b125 moves it to 2.4.0 on main, and that run passed all six jobs.