github ZenNotes/zennotes v2.44.0
ZenNotes v2.44.0

4 hours ago

ZenNotes 2.44.0: Cloud conflicts become clear choices, and Harper checks your writing

ZenNotes now combines safe edits automatically and asks for help only when two devices changed the same words. Every version stays safe until you choose, and sync no longer creates surprise (cloud conflict) notes. This release also adds Harper, an offline grammar and spell checker for the editor, off by default.

✨ New

  • Review sync changes where you already work. (#683, reported by @uNyanda) A persistent 1 file needs review · Review now action appears in the workspace status bar, with the same queue available in Settings → Cloud. The resolver lists every file waiting for a decision and moves to the next one automatically. While files are waiting, the command palette entry Review Cloud Sync Conflicts and the Vim leader binding Space r open the same queue.

  • Resolve only the ambiguous part. ZenNotes three-way merges edits made in different places without interrupting you. For a real overlap, the resolver labels the versions This device, Other device, and Last synced, shows the suggested combined note, and asks which wording to keep for each ambiguous change. You can edit the combined note directly or choose one complete version instead.

  • An honest first-sync choice. When there is no earlier shared version, ZenNotes does not pretend it can infer a merge. It shows both complete notes, explains why it cannot know which is newer, and offers clear choices to keep either version, keep both under a name you choose, or combine them yourself. Replacing one complete version always requires a separate confirmation.

  • Finish later without losing progress. Leaving an unresolved note requires an explicit confirmation. Your local note remains in the vault; its Cloud comparison, last-synced version, and current draft stay in private app storage. The conflicted path waits while unrelated notes continue syncing, and reopening the queue restores the draft.

  • No task or vault pollution. New conflicts never become numbered conflict files. While a note waits for your decision, its tasks are withheld from the app's own task surfaces: the Tasks view (list, calendar, and Kanban modes) and the calendar panel. Tools that read the vault straight from disk, such as MCP, the zn CLI, and the self-hosted server, still report that note's tasks as the local file has them. Existing conflict copies from older releases are left untouched and can be opened or moved to Trash explicitly.

  • Safe choices for every file type. Text, binary, delete, move, and filename collisions use the same durable queue. Before saving, ZenNotes verifies both the local file and current Cloud revision. A local multi-file decision rolls back newly created destinations if a later write fails.

  • Grammar and spelling with Harper. Turn on Settings, Editor, Grammar and spelling with Harper and the editor underlines misspellings, typos, and grammar slips as you write. Harper (writewithharper.com) runs entirely on your device; no text leaves the app. Hover an underline, or press z= in Vim mode, for a card with the fixes: a digit or Enter applies one, j and k move the highlight, ]s and [s walk the problems, zg teaches the vault's dictionary a word, and zG ignores one suggestion. Choose the English dialect below the toggle. Code, links, and frontmatter are never checked, and the dictionary and ignored suggestions live in vault.json, so they travel and sync with the vault. Off by default; the command palette and :harper on|off toggle it too. Harper is a desktop and web feature: the phone apps keep the system keyboard's own spelling and grammar help and never load it.

🧰 For contributors

  • The shared coordinator now owns durable conflict-only snapshots, automatic line-based three-way merging, per-path sync pauses, stale-choice protection, and auto-next summaries. Desktop, iOS, and Android expose the same inspect, draft, and resolve bridge operations.
  • ZenNotes Cloud adds an authorized historical-revision read so upgraded clients can recover the last agreed text when it is still retained. Older servers and expired revisions fall back safely to a two-version choice.
  • Captioned real-app demo: media/cloud-conflict-resolution-683.mp4 plus media/cloud-conflict-resolution-683.vtt (18 seconds, H.264, 1280×800). The clip uses the built Electron app against an isolated local Cloud fixture and was assembled with FFmpeg.
  • Review of the resolver before release found and fixed three data-loss cases: the three-way merge glued a line onto an unterminated last line and pushed the result as the agreed revision; a move conflict could never be resolved because its Cloud snapshot was built from a field nothing wrote, and loosening the check would have deleted the note; and Use this device on a file above the 5 MB inline limit wrote it back as 0 bytes. Conflict snapshots above 256 KB now keep only metadata in state, an automatic merge that loses to a save queues the conflict instead of failing the run, and the cloud sync writer follows symlinked notes and keeps file modes through the electron-free apps/desktop/src/main/atomic-write.ts it shares with the vault writer.
  • The resolver dialog is now in every global key-handler bail list, ui/Modal gained the focus trap and restore the design system had promised, the Settings resolver is keyed per conflict, Save combined note requires every hunk, and the queue opens from the palette and Space r through an app-wide host so it works in zen mode. The unreachable bootstrap resolver panel was deleted; the bridge methods stay because the mobile shells implement them.
  • Harper ships as a 15.6 MB WebAssembly asset fetched by Harper's worker through a zen-harper:// scheme that shares the Typst asset handler, loaded only once the toggle is on and never on the boot path (the entry chunk only dynamic-imports Harper's chunk). The wasm reaches both renderer builds through one shared Vite plugin, tooling/vite/harper-wasm-asset.ts, because harper.js hides its dist/ behind an exports map. The session, editor extension, and runtime glue live in packages/app-core/src/lib/harper-*.ts and cm-harper.ts; dialects, rule config, and the vault state normalizer are in packages/shared-domain/src/harper-settings.ts; the dictionary and ignore hashes are a harper field on VaultSettings, mirrored in desktop main and the Go server. Ignore hashes are unsigned 64-bit integers carried as digit strings, since JSON.parse would round them. Real-engine tests run Harper under Node: spans are UTF-16, code and link targets are skipped, and one word can carry two rules, so an ignore is per rule.
  • Harper is gated on a new optional host capability, supportsHarper, true on desktop and web and absent on the phones, which hides the setting, the commands, and the editor extension there. The iOS and Android repos carry matching edits for their next source-pin bump: the settings normalizer keeps the harper field so a phone-side save cannot erase the vault dictionary, and a Vite stub resolves Harper's two imports to an empty module so the phone bundles never contain the binary.
  • Captioned demo: media/harper-grammar-check.mp4 plus media/harper-grammar-check.vtt (38 seconds, H.264, 1280×800), recorded from the built app.
  • Verification, this pass: monorepo typecheck clean; forced, uncached test runs green (shared-domain 1556, app-core 1888, desktop 706, Go server); desktop and web production builds; the built desktop app driven over CDP with isolated userData, config, and vault through the Harper flow (underlines, hover card, ]s, z=, j/k/Enter across a background re-lint, zG, zg writing vault.json, :harper off and on); the Harper card checked in all eleven theme families in light and dark; both phone shells typechecked, built, and tested against this source (30 iOS and 35 Android tests). Earlier in the cycle: 621 Cloud API tests, the Xcode simulator build, and the Gradle debug APK. Not repeated after these changes: the packaged-app launch check (npm run pack plus a CDP page target) and npm run perf:desktop-runtime, which currently stops before measuring because the app opens on the Home view. Dependency audits report no known vulnerabilities.

Local-first and keyboard-first, as always.

Don't miss a new zennotes release

NewReleases is sending notifications on new releases.