Repository navigation
ZenNotes 2.68.0 lets you review what an AI assistant changed in a note, one change at a time, and keep or undo each. It runs natively on Windows on ARM, makes zennotes:// links work on Windows, which lets Windows finish connecting ZenNotes Cloud, and stops Tab from turning a heading into a code block.
Released from 881baa6a through #930 on 2026-10-12. All installers are verified.
New
- Review an assistant's edits. When an assistant you connected through MCP (Claude Code, Codex and others) changes a note, ZenNotes keeps what the note said before. The note shows a banner, "Claude Code changed this note 4 minutes ago", and Review changes shows every change right in the note: the removed lines above the new ones, with Accept and Reject on each, and Accept all, Reject all and Done on top. Reject puts the old text back. What you typed after the assistant is yours and stays out of the review. Move between changes with Cmd+Option+↓ and ↑ (Ctrl+Alt on Windows and Linux), accept with Cmd+Shift+Enter and reject with Cmd+Shift+Backspace; in Vim mode, Vim's own diff keys:
]cand[c,dpto keep a change anddoto put back what it replaced. The command palette lists every note waiting for review. To tell the assistant why, comment on the change before you decide it: assistants read comments through MCP. The kept copy lives beside the note in.zennotes/agent-edits/, syncs with your vault, and is deleted once every change is decided. Settings → MCP → Review agent edits turns it off. Not covered yet: vaults on a self-hosted server, new notes an assistant creates, and moves, renames and deletions (Trash keeps those). Thanks to Kevin for the idea. - Windows on ARM (#928). ZenNotes now has a native build for ARM PCs (Snapdragon X and other Windows on ARM machines):
ZenNotes-2.68.0-win-arm64.exe, and a zip. Until now those PCs ran the x64 build through Windows' emulation, which costs speed and battery. The download section on zennotes.org lists both, and Edge or Chrome on an ARM PC offers the ARM64 installer by itself. An ARM64 copy updates itself on its own build. An x64 copy that finds itself on an ARM PC moves to the native build with its next update after 2.68.0; to move today, run the ARM64 installer over it (your notes and settings stay, as with any update). Thanks to @redanthrax for the request. - What’s New after every update. The first launch after an update opens a short window: the version’s highlights, ZenNotes Cloud (left out once you use Cloud), and Full release notes on zennotes.org. Escape or Close dismisses it, and What’s New in ZenNotes in the command palette (or
:whatsnewin Vim mode) brings it back. - An anonymous count of installs and updates. So we know how many copies of ZenNotes are in use, the desktop app tells zennotes.org, once when it is installed and once per update: installed or updated, the version, the operating system (macOS, Windows or Linux), the architecture (x64 or arm64) and how it was installed (dmg, installer, AppImage, deb and so on). That is the whole message: no identifier, nothing about your notes, vaults or account, no cookies, no language, and the site keeps only daily totals. It is on by default; Settings → About → Count this copy anonymously turns it off (
[privacy] usage_count = falsein config.toml). The phone apps send nothing of the kind.
Fixes
zennotes://links open ZenNotes on Windows, and connecting ZenNotes Cloud finishes there (#509). No Windows install of ZenNotes could open azennotes://link: the installer and the zip never registered the link type, and the app registered it itself only on macOS. Links from an AI answer (the MCP server's note links) or a web page did nothing, Windows offered to "Get an app to open this 'zennotes' link", and connecting ZenNotes Cloud stalled at its last step, which hands the sign-in back to the app through that link. ZenNotes now registerszennotes://for your Windows user every time it starts, so links open the note whether the app is running or not (the browser asks once whether to open ZenNotes, as it does for any app). Connecting ZenNotes Cloud on Windows and Linux no longer needs the link at all: after you sign in, the browser returns to ZenNotes through a one-time page on127.0.0.1that says "ZenNotes is connected". Thanks to @JohannesSterba for the report.- Indenting no longer turns a heading into a code block (#925). Tab indented a heading line like any other line, and in Markdown a heading indented four spaces is a code block, so one press at the default tab size of 4 (two at a tab size of 2) silently turned a heading into code: it lost its styling, its place in the outline and its fold arrow, in Edit and in Preview. Indenting now leaves heading lines at the margin:
## Titleheadings, titles underlined with===or---, and headings inside list items. The rest of a selection still indents as before. When a press moves nothing, a short notice says why. The same goes for Vim's>>,>and insert-mode Ctrl+T, and the indent button on the phones' editing toolbar. Shift+Tab is unchanged, so a heading indented by hand can still come back. Thanks to @uNyanda for the detailed report. - Table column widths carry into Preview (#920). The widths you set by dragging a column border in the editor did reach Preview, but Preview squeezed the table into the reading column whenever the widths added up to more than it, so every column came out narrower than you set it and the cells wrapped again (Reading width tops out below Editor width, so a table that fit the editor could overflow Preview). A table with set widths now keeps them in Preview and split view, and scrolls sideways when it is wider than the column, the way the editor's table does. The editor had the same squeeze once a table outgrew it, and now scrolls there too. A PDF still shrinks a wide table to fit the page. Reading width now goes up to 1600 px, like Editor width. Thanks to @theintjengineer for the report and the videos.
- Plainer wording, and the demo tour's notes renamed. The app's own text (Settings, the command palette, the in-app manual, notices, the welcome note, the comments in
config.toml, theznCLI's help and what the MCP server tells your assistant) now uses commas, colons and parentheses instead of long dashes. Two names change with it: the demo tour's notes are now00 Start Here,01 Markdown Basicsand so on, and the Meeting Notes and 1:1 templates name new notesMeeting 2026-10-12and1-1 2026-10-12. A demo tour made by an older version is replaced when you regenerate it (no second copy) and removed by Remove Demo Tour Notes; notes you already made from a template keep their names.
For contributors
- Windows link registration:
registerAppDeepLinkProtocolnow runs on Windows as well as macOS (registersDeepLinkSchemeAtLaunchinapps/desktop/src/main/deep-links.ts) and writesHKCU\Software\Classes\zennoteswith the command"<exe>" "%1"at every launch, which covers the zip wherever it was unpacked and follows the app when it moves. electron-builder'sprotocolsoption is macOS only, which is why no Windows build had it. Linux keeps the package's.desktopfile (MimeType=x-scheme-handler/zennotes): setting the default at launch would override the user's ownxdg-mimechoice. Not done: the NSIS uninstaller leaves the per-user key behind. - Cloud sign-in return path: packaged Windows and Linux builds pass
callback_url=http://127.0.0.1:<port>/auth/callback(RFC 8252's loopback redirect,CloudAuthLoopbackServer, five-minute lifetime, one use), as development builds already did (cloudSignInUsesLoopback). Packaged macOS keeps thezennotes://authcallback its signed bundle always registers. zennotes.org already accepted loopback callbacks in production. - Heading indent guard (#925):
headingIndentGuard()inpackages/app-core/src/lib/cm-heading-indent-guard.tsis a transaction filter on theinput.indentuser event, which @codemirror/commands'indentMoresets, and every indent path usesindentMore: Tab (indentWithTab), the Vim adapter's>>,>and Ctrl-T, and the phone toolbar'sindentcommand. It drops the insertions on lines whose first non-blank character belongs to anATXHeading*orSetextHeading*node, read from the syntax tree, so a#line in fenced code or frontmatter still indents, and a list item that opens with a heading (- ## Topic) still nests. It ships insidenoteMarkdown(), so every note editor has it; the template editor builds its ownmarkdown()and adds it by hand. The notice is aninfotoast, added only when none is already showing. Not changed: ZenNotes' outline, folding, level chips and heading motions read only headings at the margin (^#{1,6}\s), although CommonMark accepts up to three spaces, so a heading indented by hand is still a heading in Preview but not in those. - Agent edit review (design and decisions:
docs/ideas/agent-edit-review.md):- Records:
packages/shared-domain/src/agent-edits.ts(format,beginAgentEdit/finishAgentEdit, and the merges:agentEditOrigin,agentEditBaseline,reviewedAgentEdit). Writers stay at string comparisons because two engines write:apps/desktop/src/mcp/agent-edit-recorder.ts(TypeScript MCP) and ZenNotes/tuiinternal/mcp/agentedits.gowithinternal/vault/agent_edits.go(the Gozn mcpa managed install runs). The record reaches disk before the edit; edits to one note take turns. - Files:
apps/desktop/src/main/agent-edit-files.ts(Electron-free, shared by main and MCP). Records travel with notes inrelocateNote, folder renames, deletes, both trashes and creation, in desktop main, the TypeScript MCP and the Go vault; Cloud sync carries.zennotes/agent-edits/*.json; the watcher reports them as a newagent-editschange scope (path = the note). - Bridge:
supportsAgentEditReviewplus optionallistAgentEdits/readAgentEdit/writeAgentEdit(desktop only for now; IPCvault:list-agent-editsand friends, vault root from main as always). - Review mode:
packages/app-core/src/lib/cm-agent-review.tson@codemirror/merge(new dependency, MIT), imported on demand throughagent-review-runtime.ts; the renderer chunk rule keeps@codemirror/mergeout ofvendor-editor, so nothing is added to launch. The pane side iscomponents/AgentEditReview.tsx(banner, bar,useAgentEditReview). Vim'sdp/docannot be codemirror-vim mappings (a full match on thedoperator always wins), so review mode reads normal-mode sequences ahead of Vim. - Settings: portable pref
reviewAgentEdits,[mcp] review_agent_editsinconfig.toml(newmcpsection), read directly by both MCP engines. - Keys:
editor.reviewNextChange,editor.reviewPreviousChange,editor.reviewAcceptChange,editor.reviewRejectChangeandvim.review*in the keymap registry and catalog.
- Records:
- MCP no longer writes into
.zennotes:write_note,append_to_note,prepend_to_note,insert_at_lineandreplace_in_notewrote the file before checking it was in a note folder, so a call on.zennotes/...created the file and then failed. Both engines now check first (noteForWriteinapps/desktop/src/mcp/vault-ops.ts,Vault.WriteNotein the Go vault). The self-hosted server's own write path (ZenNotes/znserver) was not checked here. - Windows ARM64 (#928):
release.ymlgains a "Windows arm64" job on GitHub'swindows-11-armrunner (free for public repos, likeubuntu-24.04-arm), running the newnpm run dist:win:arm64 --workspace @zennotes/desktop(a named script, because PowerShell'snpm.ps1shim can swallow a--before--arm64). electron-builder names every Windows feedlatest.ymlwhatever the architecture, so the collect step renames the ARM64 job's tolatest-arm64.yml, and either Windows job fails if its output carries the other architecture's file names (the two jobs upload to one release). The app reads that feed through electron-updater's channel:windowsUpdateChannel()inapps/desktop/src/main/updater.tsanswerslatest-arm64on Windows whenprocess.archisarm64orapp.runningUnderARM64Translationis true (an x64 copy under emulation, the Windows twin of what electron-updater does for Rosetta on a Mac), andallowDowngradeis put back to false because setting a channel turns it on. Nothing native ships in the app (no.nodefiles; font-list shells out to scripts), and Windows bundles nozn, so the package needed no other change. CI's build matrix addswindows-11-arm, which also packages the ARM64 app and runs the newtooling/scripts/packaged-launch-check.mjs --expect-arch arm64(a CDP page target, the app drawn, and AArch64 in the executable's PE header): no maintainer has an ARM64 Windows machine, so that step is the proof the build starts. Website (website/docs-2.68):/download/windows-arm64(andwin-arm64), a second row in the Windows panel, and the ARM detection throughnavigator.userAgentData.getHighEntropyValues(['architecture'])(the user-agent string reads x64 on ARM too; browsers without the API keep x64, which also runs on ARM). README lists both installers. - Table widths (#920):
rehypeTableColWidths(packages/app-core/src/lib/markdown.ts) now wraps a table that has azen:colshint indiv.zen-table-scrolland moves itsdata-source-linestamp to the wrapper (split scroll sync reads[data-preview-content] > [data-source-line], direct children only). CSS: the wrapper scrolls sideways, the table iswidth: max-contentwith fixed layout, and@media printrestores the shrink-to-fit for PDF. In the editor,.cm-table-wrapper:has(> table.cm-table-fixed)drops the wrapper'smax-width: 100%(from v2.4.0, older than #294) andtable.cm-table-fixediswidth: max-content, so the add-column rail stays at the table's edge and.cm-table-widgetscrolls: #294's own comment promised "scrolls if wide", which that older cap prevented. The Reading width slider goes to 1600 (the store already clamped at 1600). A column leftautois still sized by its content, so it can differ slightly between the editor and Preview fonts; a drag sets every column, so resized tables match exactly. Docs: a new in-app help topic, "Set the width of a table column", mirrored on the website's GFM tables row. - What’s New:
packages/app-core/src/lib/whats-new.tsholds the bundled highlights (one entry per release, written in the release PR; Adib chose bundled over fetched), the permanent Cloud card, and the rules (whatsNewAtLaunch: a fresh install records its version and opens nothing; a returning user’s first launch of a new version opens the window, with every entry since the version last seen; a downgrade records and opens nothing). The machine-local preflastSeenWhatsNewVersionmakes it once per version, recorded before the window opens. Whether this launch is a returning user’s is read with the first prefs load, so skipping the onboarding quickly does not count. The store’sshowWhatsNewAfterUpdateruns 1.2 s after the main window mounts;whatsNewOpenjoins VimNav’s skip list andCOMMAND_OVERLAY_FLAGS, so j/k scroll the window instead of the notes behind it. Full release notes openszennotes.org/releases#v<version>. Desktop only (hostKind). - Anonymous count: the same first launch decides the event (
installorupdate, never a relaunch or a downgrade) and calls the optionalsendUsageCountbridge method when the portable prefusageCount([privacy] usage_count) is on.apps/desktop/src/main/usage-count.tsbuilds the body (event,version,platform,arch,install), refuses development builds,ZEN_PERFandCIruns, other architectures and prerelease versions, and sends each event at most once per run withcredentials: 'omit',user-agent: ZenNotesandaccept-language: *(Chromium otherwise adds the system language). The server side (POST /api/v1/app/usage, daily totals, the owner console’s Usage page with GitHub download counts) is on the website branchwebsite/app-usage, and the privacy page says what is sent. - No long dashes (
aa24d748): the 325 in user-facing strings were rewritten by hand; code comments, test names and third-party text (Excalidraw, Mermaid, KaTeX) are untouched, and a scan of the builtout/finds none from our code. Older demo tour names are recognized bycurrentDemoTourPath/isDemoTourStartPathinpackages/shared-domain/src/demo-tour.ts, an exact match on the old separator becausegenerateDemoTourandremoveDemoTour(desktopvault.ts) delete what it matches;demo-tour.test.tsresolves every tour wikilink against the notes the tour ships. Synced copies: zn's MCP instructions, tool descriptions and templates change word for word in ZenNotes/tui9650f8d; the self-hosted server'sinternal/vault/demo-tour.json(ZenNotes/znserver) writes the old names until its next release, which the app handles. - Bundled CLI: zn 0.7.0 (
89798b6c), itsterminal-release.jsoncopied byte for byte after it verified againstzn-release-1(source commit9650f8d, protocol 1, all four archives matchingchecksums.txt); the staged darwin-arm64 binary reportszn v0.7.0. Itszn mcp, which a desktop-managed install runs for assistants, records edits for review the same way as the TypeScript server; a managedznupdates itself to 0.7.0 within a day on older desktops too. - Docs: the in-app manual's "Tune indentation and switch notes quickly" section, mirrored on the website (the same section and the Tab row of the keyboard table), and a new "Review an assistant’s edits" section, mirrored in the website's MCP docs, both on
website/docs-2.68.
Demos
In docs/releases/v2.68.0/media/ (local), 1080p with captions burned in and a .vtt beside each, recorded on the 2.68.0 build with a scratch vault and a real MCP client named claude-code driving the app's own MCP server:
- agent-edit-review.mp4 (23 s): an assistant edits a note through MCP; the banner says which one and when; Review changes shows each change with the old text above the new; Reject puts one back (Cmd+Shift+Backspace), and Accept keeps the others, from the keyboard and with the buttons.
- agent-edit-review-vim.mp4 (28 s): the same review with Vim mode on:
doputs the old text back,]cmoves to the next change anddpkeeps it; Escape leaves with one change still waiting, and the palette's list of notes to review picks it back up.
Verification
How to test locally (#509 needs Windows; the beelink VM zn-win11 has ZenNotes 2.67.0 installed without a handler, see ~/windows-vm/README.md there):
- Links, before and after. On 2.67.0,
Start-Process 'zennotes://open?path=Welcome.md'shows "Get an app to open this 'zennotes' link", and clicking such a link in Edge or Chrome does nothing (the browser console says "the scheme does not have a registered handler"). Install 2.68.0 and start it once:HKCU\Software\Classes\zennotes\shell\open\commandreads"…\ZenNotes.exe" "%1". Click the link again: the browser asks to open ZenNotes, and the note opens, with ZenNotes running and with it closed. - Cloud connect. Settings → Cloud → Connect ZenNotes Cloud, sign in on zennotes.org: the browser lands on
127.0.0.1:<port>/auth/complete("ZenNotes is connected") and Settings shows the account. Before: the last page's link did nothing and the app waited forever. - Review an assistant's edits. Build and launch with isolated stores as above, open a note, and in a terminal point an MCP client at the same folder:
zn mcp --vault <folder>(or the desktop'sapps/desktop/out/main/mcp.jswithELECTRON_RUN_AS_NODE=1andZENNOTES_VAULT=<folder>), then have it callreplace_in_noteandappend_to_noteon the note. Before: the note changed and nothing said so. After: the banner names the assistant, Review changes shows both changes inline, Reject puts one back (the file on disk follows), Cmd+Shift+Enter keeps the other, the bar says it is done, and.zennotes/agent-editsis gone. In Vim mode,do,]canddpdo the same. - Tab on a heading (#925, any platform). Build and launch with isolated stores:
npm run build --workspace @zennotes/desktop, thenZENNOTES_USER_DATA_PATH=$(mktemp -d) ZENNOTES_CONFIG_DIR=$(mktemp -d) npx electron apps/desktop/out/main/index.js. In a note, put the cursor on#### Sub oneand press Tab (tab size 4, the default). Before: the line became#### Sub one, a code block in Edit and in Preview. After: the line stays a heading and a notice says "Headings stay at the margin: indented four spaces or more, a heading turns into a code block." Select the heading and the paragraph under it and press Tab: the paragraph indents, the heading stays. In Vim mode,>>on the heading does the same. - Windows on ARM (#928). Without an ARM PC:
npm run build --workspace @zennotes/desktop, thennpx electron-builder --win dir --arm64 --publish neverinapps/desktop(any OS), andfile dist/win-arm64-unpacked/ZenNotes.exereads "PE32+ executable (GUI) Aarch64". On an ARM PC: installZenNotes-2.68.0-win-arm64.exe, and Task Manager's Details tab, with its Architecture column turned on, shows ZenNotes.exe as ARM64 (x64 for 2.67.0). On zennotes.org in Edge on that PC, the hero button links/download/windows-arm64. - Table widths (#920). In a note with a table, drag the right border of a column header in the editor well past the editor's width. Before: the other columns shrank to make room, and Preview squeezed the table into the reading column and wrapped the cells. After: the table grows and scrolls sideways in the editor, the
<!-- zen:cols=… -->line under it records the widths, and Preview and split view draw the same widths with a scrollbar under the table. Export to PDF: the table fits the page. Settings → Typography → Reading width now goes to 1600. - What’s New (desktop). Run
:whatsnew(or What’s New in ZenNotes from the palette): the window shows the 2.68.0 highlights and the ZenNotes Cloud card; Set up ZenNotes Cloud lands on Settings → Cloud; Escape closes. The automatic open needs a build whose version is newer than the one last seen: the first launch of 2.68.0 after 2.67.x opens it once, and the next launch does not. - Anonymous count. Settings → About → Privacy shows Count this copy anonymously, on. A development build never sends (unpackaged); a packaged build sends one
POST https://zennotes.org/api/v1/app/usageon its first launch after installing or updating. With the switch off, nothing is sent. - Demo tour names. Palette → Generate Demo Tour Notes: the notes are named
00 Start Herethrough15 Search Backends and Fuzzy Workflows, and every link in00 Start Hereopens its note. On a vault holding the 2.67.0 tour, the palette offers Regenerate Demo Tour Notes: afterwards there is one tour, under the new names. Remove Demo Tour Notes clears it either way. Before: the names read00, a long dash,Start Here.
Distribution channels
- GitHub release: installers for macOS (Apple silicon and Intel DMG and zip), Windows (x64 and, new, ARM64 installer and zip) and Linux (x64 and arm64 AppImage, deb, rpm, pacman, tar.gz), with five update manifests: Windows on ARM reads the new
latest-arm64.yml. - AUR:
zennotes-bin2.68.0-1 (AUR clone80d9e9a, repocfecf263), pinned toZenNotes-2.68.0-linux-x64.tar.gzSHA-25607b25a34…dce(GitHub's own digest for the asset), after the bump script checked the bundled CLI folder is world-readable; aur-check passed. - Nix:
packaging/nix/release-data.jsonlifted from the update workflow (run 38328629102, bot PR #931 closed) aseedda3ed; itsdesktopHashis the same digest as the AUR pin, and nix-build on main passed. - nixpkgs: NixOS/nixpkgs#572180, which proposed 2.64.0 -> 2.67.0 and had not merged, now proposes 2.64.0 -> 2.68.0; the source hash comes from the tag tarball, and the same prefetch reproduces the 2.64.0 hash in nixpkgs today.
- Homebrew:
zennotes2.68.0 in the tap (fa5bc22), mirrored in78fdf2d4; both DMGs were downloaded and their SHA-256 matches the cask and GitHub's digest;brew styleclean. - Website: ZenNotes/website#73 merged at
b3c38483with the releases entry and its two clips, the docs for every change, the Windows on ARM download (/download/windows-arm64) and the 2.68.0 share viewer, which keeps a table's set column widths in published notes; its main tests and deploy passed, Laravel Cloud's deployment succeeded at 01:22 UTC, zennotes.org/releases serves the entry, one clip matches the local file byte for byte, and the live 2.68.0 and retained 2.65.0 viewer files match their manifests. Earlier this cycle: no long dashes anywhere on the site (#69), one size standard for every box and dark scrollbars (#70), the usage count's endpoint, owner page and privacy section (#71), and a home page screenshot with the renamed demo notes (#72).npm run verify:channels -- 2.68.0: every applicable channel serves v2.68.0, now including/download/windows-arm64(ef4115e8). - zn CLI: 0.7.0 (ZenNotes/tui#7, tag
v0.7.0, Homebrew formula04126ba), bundled here: itszn mcprecords an assistant's edits for review, and its text drops the long dash with the app's. - Server: ZenNotes/znserver v2.68.0 pins the published
web-2.68.0-web.h1c5ca208ca08d773browser artifact and names the demo tour's notes as the apps do (with the same cleanup of the older names); Dockeradibhanna/zennotes:2.68.0,2.68andlatest(linux/amd64 and arm64, one digestcf1b1242…); README #25. - Phones: iPhone 1.24.0 (40) on the published
core-2.68.0-core.h6e3db1e19bc2b662, with the heading and table fixes and the renamed demo tour (its own Regenerate and Remove now clean up the older names too); submitted to App Review on 2026-10-13 (zennotesios#58, main496b571), with the privacy policy URL now on https. Android 1.1.38 (41) on the same core, with the same fixes and the same demo tour cleanup, is merged (zennotesandroid#110, tagv1.1.38, merge251b4cewith the same tree as the uploaded bundle) and was sent to Google Play review on 2026-10-13 for a full rollout.
Release validation
Cycle checks:
-
#509 on a clean Windows 11 Pro VM (build 26200, the beelink
zn-win11container), with the official 2.67.0 installer (SHA-25638c1fa86…2653, GitHub's digest) and a 2.68.0 dev build of238d6504's code: 2.67.0's installer and app left nozennoteskey anywhere in the registry, Windows answered a link with "Get an app to open this 'zennotes' link", and Edge logged "Failed to launch 'zennotes://open?path=…' because the scheme does not have a registered handler". With the fix in the installed app (app.asarswapped in): the key appeared on first launch; links opened the note in the running app (second-instance hand-off) and started the app cold (Windows passeszennotes://open/?path=…, which parses); Edge showed "This site is trying to open ZenNotes" and opened the note; the zip registered its own path; Cloud connect openedhttps://zennotes.org/app/connect?…&callback_url=http://127.0.0.1:58368/auth/callbackand completed on Adib's sign-in (QA account), then Disconnect revoked the VM's device. The PowerShell workaround posted on #509 was run verbatim on 2.67.0: it wrote the same key, and links opened notes cold and warm. Not run end to end: the workaround's Cloud step on 2.67.0 (the website's "Open ZenNotes" button, then the browser prompt). -
Unit tests:
deep-links.test.ts(19, six new),cloud-auth-loopback.test.ts(4) andcloud-auth.test.ts(20) pass; the desktop typecheck is clean. -
#925 in the built app over CDP (isolated stores, MacBook display), with a note holding an ATX
####heading, a setext heading on each underline, a heading inside a list item and a paragraph. Before the fix, at tab size 2: three Tabs left#### Sub oneand Preview drew a code block; two Tabs leftSetext title(code block) and=============(the heading became a paragraph) and pushed the list item's heading out of it. After the fix, at tab size 2 and 4 and with Vim's>>: every heading line was unchanged on disk, Preview still drew H4, H2, H1 and H4, the editor kept its H4 chip and fold arrow, the notice showed, and the paragraph still indented. Unit tests: 12 incm-heading-indent-guard.test.ts(with the guard disabled, the 7 that test it fail and the 5 that pin unchanged behavior pass); all 3,152 app-core tests and the app-core typecheck pass. Website:php artisan view:cacheand the DocsPage tests (9, 197 assertions) pass onwebsite/docs-2.68. -
Agent edit review, end to end in the built app (isolated stores, MacBook display) with a real MCP client named
claude-codedriving the real MCP server process (out/main/mcp.js): the record held the note from before both edits and named Claude Code and both tools; the banner appeared; the review showed two changes with the removed line above the new one; Reject by button restored and saved the old line; Mod+Shift+Enter accepted the other; the notice said every change was decided; the record and its folder were deleted; the editor was editable again (19/19 checks). Vim mode:dthen Escape kept the review,dorejected,]cmoved,dpaccepted (18/18). After Adib's first look, the change bands run the full width of the editor. Demo clips (media/agent-edit-review.mp4,media/agent-edit-review-vim.mp4, recorded bytools/demo-review.mjswith a real MCP client namedclaude-code): the Vim take reopened a review with one change left, and the merge view's "10 unchanged lines" bar was near-white on the dark theme (it takes its grey from CodeMirror's dark-theme flag, which ZenNotes never sets); now themed instyles/index.css(a8c9d8f1), re-recorded after the fix. -
Unit tests:
agent-edits.test.ts(15),agent-edit-recorder.test.ts(9, including concurrent edits and the.zennotesrefusal),cm-agent-review.test.ts(7, including Vim'sdo/]c/dp), the Cloud sync allowlist; Go:internal/vault/agent_edits_test.go(7, including reading a record as the app writes it and following a rename) andinternal/mcp/agentedits_test.go(6). Full suites: shared-domain 1,991, app-core 3,159, desktop 1,086, quicklook 15 (no turbo cache), all 8 typechecks; zngo test ./...andgo vet ./...clean. -
Windows ARM64 (#928), on this Mac: a cross-build (
electron-builder --win dir --arm64) gavewin-arm64-unpacked/ZenNotes.exeand every DLL as AArch64, with no.nodefiles and no other executables in the package; the full target build wroteZenNotes-2.67.0-win-arm64.zipand the NSIS ARM64 payload, then stopped where electron-builder needs Wine on macOS to build the uninstaller (a Windows runner does not).updater.test.ts(45, three new: native ARM64 and emulated x64 readlatest-arm64, x64 Windows, macOS and Linux keep their feeds); desktop suite 1,093 and all 8 typechecks pass.packaged-launch-check.mjspassed against the dev build here (page target in 0.8 s, app drawn, version read) and refused a non-PE file under--expect-arch arm64. Website:DownloadRedirectTestandHomePageTest(46, 196 assertions, including the fallback to the releases page while the latest release has no ARM64 installer),view:cache, and the detection block run in Node for Edge on ARM (ARM64 installer), Edge on x64, Firefox, a refused hint and Safari (all x64 or Mac as before). The release skill'sverify-installers.pyexpectslatest-arm64.ymlfrom 2.68.0 and fails a Windows feed naming the other architecture's installer (still ALL GOOD on 2.67.0). Not run: thewindows-11-armCI job (it runs on the first push), and an x64 install upgraded by the ARM64 installer on real hardware (the path is electron-builder's ordinary NSIS upgrade, the one Obsidian's universal installer takes on ARM PCs). -
#920 in the built app over CDP (isolated stores, MacBook display): the reporter's videos showed the cause (their editor column wider than the 1400 px reading column; Preview squeezed the table and wrapped
hypervisor). Here, with widths that fit (120/520/90), Edit and Preview already matched; with 300/900/300 the editor drew 182/482/182 before the change. After: 300/900/300 in the editor (the widget scrolls, the add-column rail flush with the table's edge), Preview and split view (the wrapper scrolls), 177/501/177 inside the page under print media; tables that fit are unchanged; a table without widths still fills the column; the scroll-sync stamps sit on the wrappers. Dragging the middle column 500 px gave 120/1020/90 on screen and<!-- zen:cols=120,1020,90 -->on disk.markdown.test.ts(74, one new), app-core 3,160 and its typecheck; websiteview:cacheand DocsPage (9, 197 assertions). -
What’s New in the built app over CDP (isolated stores, MacBook display): the palette path on the 2.67.0 dev build (7/7: opens the newest entry, Cloud card for someone not on Cloud, five highlights, focus on Close, Escape closes, Set up ZenNotes Cloud lands on Settings with
data-settings-target="cloud"); the launch path on a build made as 2.68.0 (8/8: a true fresh install, with no config.toml, opens nothing and records 2.68.0; a returning user who last saw 2.67.0 gets the window by itself; j scrolls it in Vim mode; Enter on Close closes it; the next launch shows nothing); a build made as 2.68.1, which has no entry (3/3: the window still opens with the Cloud card and a pointer to the release notes). The temporary versions were put back to 2.67.0. Found on the way: Vim’s global keys did not know the window was open (j moved the sidebar underneath), and a config.toml marks a returning user, as it does for the onboarding. Unit tests:whats-new.test.ts(13),whats-new-launch.test.ts(7, the store: count once, the switch, the Cloud card rules, the phones left out). -
Anonymous count:
usage-count.test.ts(7). The bytes on the wire, from Electron’s ownnet.fetchwith the shipped request options against a local echo server: the five-field JSON body and headers that are the same for every copy; the capture caught Chromium addingAccept-Language: en-US, now overridden with*. Suites: shared-domain 1,991, app-core 3,181, desktop 1,100, all 8 typechecks. -
No long dashes: all 8 typechecks; shared-domain 1,991; desktop 1,102 (two new tests: Regenerate replaces and Remove deletes a tour under the older names, and both fail with the cleanup disabled);
demo-tour.test.ts(6, including a middle-dot name left alone);npm run build --workspace @zennotes/desktopand a scan ofout/in every encoding found no long dash from our code (46 inside Excalidraw, Mermaid, Langium, KaTeX, Prettier and the HTML entity tables). zn:go vet ./...andgo test ./...clean, and no long dash left in the repo.
Release-time gates on 881baa6a, in a clean worktree: typecheck 8/8 and test:run 6/6 with no turbo cache (shared-domain 1,991, app-core 3,187, quicklook 15, desktop 1,102), build:prod green, npm audit --omit=dev --audit-level=high clean (the same 15 low or moderate as 2.67.0). Pack: Developer ID signed, deep strict codesign OK, bundled zn v0.7.0 in a drwxr-xr-x Resources/zn-cli, the Quick Look extension; packaged launch check: page target and drawn app in 1.6 s, version 2.68.0. Smoke suites vim-editor, sidebar-vim and editor-improvements pass after 81f45cee (the first run failed five typing checks: What's New opened over the editor for their returning-user profiles). Release PR #930: all nine checks green, including the first windows-11-arm build, which packaged the ARM64 app and launched it; CodeQL's one alert, in the new launch-check script, was fixed in 881baa6a. Release run 38327391209: all six jobs green in about 45 minutes; verify-installers.py ALL GOOD on the five manifests (latest-arm64.yml new); both DMGs match GitHub's digests, and the Apple silicon app is Notarized Developer ID with a valid stapled ticket.