ZenNotes v2.44.0
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 ropen 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
znCLI, 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,jandkmove the highlight,]sand[swalk the problems,zgteaches the vault's dictionary a word, andzGignores one suggestion. Choose the English dialect below the toggle. Code, links, and frontmatter are never checked, and the dictionary and ignored suggestions live invault.json, so they travel and sync with the vault. Off by default; the command palette and:harper on|offtoggle 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.mp4plusmedia/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.tsit shares with the vault writer. - The resolver dialog is now in every global key-handler bail list,
ui/Modalgained 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 andSpace rthrough 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 itsdist/behind an exports map. The session, editor extension, and runtime glue live inpackages/app-core/src/lib/harper-*.tsandcm-harper.ts; dialects, rule config, and the vault state normalizer are inpackages/shared-domain/src/harper-settings.ts; the dictionary and ignore hashes are aharperfield onVaultSettings, 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 theharperfield 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.mp4plusmedia/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,zgwritingvault.json,:harper offandon); 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 packplus a CDP page target) andnpm 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.