ZenNotes 2.73.0 lets you move the window by its tab strip when the title bar is turned off, and gives the browser app the same switch. Settings fits a narrow window or a tablet, Vim's o and O keep a quote or callout going, and the bundled zn is 0.9.1.
Released from 10597fe9 through #968 on 2026-10-20. All installers are verified.
Fixes
- With the title bar off, the tab strip moves the window. Turning off Settings → Appearance → Chrome → Window title bar left one place to grab the window by: blank space in the sidebar header. With the sidebar closed there was none, so on a Mac the window could not be moved at all (reported on Discord by SLP, with a video next to Obsidian, which moves by its tab bar). Now blank space in the tab strip works the way a title bar does, as it does in Obsidian: drag it to move the window, and the tabs, the back and forward buttons and every other button still take a click. The sidebar header (including the strip above it) and the note list header work the same way. Only the tab strips along the top of the window do this, so the lower half of a stacked split is not a title bar. Onboarding and the no-vault screen keep a strip at the top for the same purpose. The same applies on Windows and Linux, where the window has no frame with the title bar off.
- The browser app can hide its title bar. The self-hosted browser app had no way to remove the bar with the note's title. Settings → Appearance → Chrome now has a Title bar switch there too; turned off, the workspace starts at the top of the page, and the choice is kept for that browser.
- Settings fits a narrow window or a tablet. In a narrow desktop window, and on an iPad or an Android tablet, many Settings rows squeezed their label into a column of single words while the control ran past the card: every text field (Vault name, the template pickers), the location, font and slider rows, and all of Keymap, whose rows put a key and three buttons on one line. These rows now move the control under the label when there is no room beside it, and Keymap's filter field fits the card.
- Vim's
oandOkeep a quote or callout going (#961). With Vim mode on,oorOon a line inside a quote or callout opened a bare line below it, outside the quote. They now open the line with what Enter would start it with:>in a quote,> -on a quoted list item, the next number on a quoted numbered item, and the quote marker plus indentation on a code line inside a quoted fence. Outside quotes nothing changes. - Callouts and code fences redraw when a slow parse finishes. On a slow machine or in a long note, the editor's parse can run out of its time budget and finish a moment later. When it did, a callout could stay drawn as raw
>lines with its code fences showing until the next keystroke or cursor move. It now redraws as soon as the parse completes. Found by CI on the release PR. - The bundled zn is 0.9.1. On macOS and Linux, the
znthat ZenNotes installs is now 0.9.1: in the TUI's preview, a wikilink of more than one word, such as[[double brackets]], no longer widens its row past the pane and scrolls the whole screen (ZenNotes/tui#11, thanks to @Nebula-Arcanum).
For contributors
- Title bar and window drag (
4df2a6bc):packages/app-core/src/lib/window-chrome.tsholds the two rules.chromeCarriesWindowDrag(showWindowTitleBar, runtime)is true only for a desktop window with the title bar off; a page never gets drag regions.isPaneOnTopEdge(paneLayout, paneId)walks the pane tree: arowsplit keeps every child on the window's top edge, acolumnsplit only its first. EditorPane gives the tab strip headerdrag-regionwhen both hold and marks each tabno-drag(a tab is dragged by HTML5 drag and drop and right-clicked); a pane with tabs off uses its header, and an empty pane with neither gets a 32-pixel strip. The sidebar header's region takes-mt-3 pt-3to reach the aside's top padding, the note list header joins, andTitleBar standalone(onboarding and the no-vault screen) renders a strip where the bar was. App addszn-app-draggingto the root fromdragstarttodragend/drop, and.zn-app-dragging .drag-regionturns the regions off, because a drag region belongs to the window and may not take a drop. The web runtime now honorsshowWindowTitleBarand shows the switch as Title bar;TitleBar.test.tsandSettingsModal.test.tspin the new browser behavior. - Narrow Settings rows (
56937694,c634af68,49fb604a):TextInputRow,TemplateSelectRow,SegmentedRow,FontRow,SliderRowand the Keymap rows use the Vault Location pattern from #871: the row wraps, the label keeps at least 14rem before the control moves under it, and the control may not exceed the row. - Vim open-line (
890ef3c8):registerOpenLineContinuationinlib/cm-vim-open-line.tsnow owns theo/Oaction that was in Editor.tsx. On a quoted line,quotedOpenLinePrefixreads the prefix off Enter's own commands (the quoted-fence command, then lang-markdown's markup continuation) run on a scratch state, sooand Enter cannot disagree; an empty item or a second blank>line is read with placeholder content, because there Enter ends the markup andostill continues it.insertNewlineContinueFencedCodeIndentbecame aStateCommandfor that; Enter's behavior is unchanged. - Perf harness (
dc3cef58):vendor-markdownandvendor-highlightleave the deferred normal-flow list (the editor's live preview renders tables, comments and formulas with them, by design) and get a stricter check underZEN_PERF_ENFORCE=1: neither may be fetched before App's first render, timed from each CDP request'swallTimeagainstrenderer.app.mounted; when that timing is unavailable the enforced run fails instead of passing quietly. - Live-preview block redraw (
10597fe9):wysiwygBlocksPlugin(cm-wysiwyg-blocks.ts) now also rebuilds whensyntaxTree(update.startState) !== syntaxTree(update.state), ascm-live-previewalready did; a parse cut by its time budget finishes in an update that changes nothing else. The test harness finishes the parse after its last change, and a new test swaps the markdown language in through a compartment (a tree-only update) and expects the callout card. Nine other plugins read the tree without a tree-change rebuild (callout and heading folds, code block font and flair, hashtags, template tokens, highlights, list indent, wikilinks); they are a follow-up. - zn pin (
f743c04f):apps/desktop/terminal-release.jsonis ZenNotes/tui v0.9.1's signed manifest, copied byte for byte after it verified againstpackaging/release-signing/zn-release-1.pub(source commitc6e98860, protocol 1). The in-app manual's CLI card says 0.9.1. - What's New
3180c55e; version bump60ddd549. The in-app manual's Window title bar entry and the website's docs say where to drag and cover the browser switch.
Demos
Screenshots of this release on the dev build, attached to this release:
The title bar off, the sidebar closed: the blank tab strip at the top now moves the window.
Settings → Appearance → Chrome, with the Window title bar switch off and where to drag instead.
Keymap in a 900-pixel window: the key and its buttons sit under the label instead of squeezing it.
Vim o in a callout: on > - Pack and launch, o opened > - inside the callout.
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.
- Move the window without a title bar. Settings → Appearance → Chrome, turn off Window title bar. Close the sidebar (
Cmd+1on macOS,Ctrl+1elsewhere) and open a note. Drag the blank part of the tab strip, to the right of the tabs: the window follows. Click a tab and the back button: they still work. Before: nothing at the top of the window could be dragged with the sidebar closed. - Split panes. With the title bar still off, right-click the tab and choose Split Down. The top pane's blank tab strip moves the window; the lower pane's does not.
- The browser app. Run the self-hosted server and open the app in a browser. Settings → Appearance → Chrome → Title bar, turn it off: the bar with the note's title goes away, and stays away after a reload. Before: the switch was not there.
- Settings in a narrow window. Make the window about 900 pixels wide and open Settings → Keymap: each binding's key and its Change, Unbind and Reset buttons sit under the label. Open Settings → Vault: the Vault name label and its description read as sentences. Before: labels one word per line, with buttons past the card's edge.
- Vim
oin a callout. With Vim on, write> [!note] Planand> - oneon the next line, pressEscape, theno: the new line starts with> -inside the callout. PressEscape,Oon the same line: the line above starts with> -too. Before: both opened a bare line outside the callout. - zn 0.9.1. In a terminal,
zn --versionreports 0.9.1 after the managed install updates. Openzn tuion a note holding- jump between notes with [[double brackets]]and look at the preview: the line stays one row and the screen does not scroll.
Distribution channels
- GitHub release: installers for macOS (Apple silicon and Intel DMG and zip), Windows (x64 and ARM64 installer and zip) and Linux (x64 and arm64 AppImage, deb, rpm, pacman, tar.gz), with five update manifests; the four screenshots in Demos are attached too.
- AUR:
zennotes-bin2.73.0-1 (AUR clone3a1e1ee, repo0595d84a), pinned toZenNotes-2.73.0-linux-x64.tar.gzSHA-256dc820eb9…ff97(GitHub's own digest for the asset), after the tarball check confirmed the bundled CLI folder is world-readable. - Nix:
packaging/nix/release-data.jsonlifted from the update workflow (run 39450048272, bot PR #969 closed) ase27a5cfd, and nix-build on main passed. - nixpkgs: NixOS/nixpkgs#575797 now proposes 2.70.0 -> 2.73.0 (
28c291ff), rebased on master; the source hash comes from the tag tarball. - Homebrew:
zennotes2.73.0 in the tap (c8e5264), mirrored in4e742e73; both DMGs' SHA-256 match the cask and GitHub's digests;brew styleclean. - Website: ZenNotes/website#87 merged with the 2.73.0 entry and its four screenshots, the Window title bar docs (drag the tab strip; the browser's Title bar switch) and the zn docs at 0.9.1. Laravel Cloud deployed it at
b6db20c0, and the live check passed: /releases leads with 2.73.0, all four screenshots match the local files byte for byte, /docs carries the title bar wording and /tui/docs names zn 0.9.1. - Server and Docker: ZenNotes/znserver v2.73.0 pins the browser app
web-2.73.0-web.ha47e875be648958c(9bb5a7d); all five binaries matchSHA256SUMS, and the released darwin-arm64 binary served the 2.73.0 app on a scratch vault.adibhanna/zennotes:2.73.0,2.73andlatestshare one digest,sha256:b875d7a1…(linux/amd64 and linux/arm64); the README's install commands follow (ZenNotes/znserver#39). - zn CLI: ZenNotes/tui v0.9.1 (
c6e98860, ZenNotes/tui#11 by @Nebula-Arcanum), its Homebrew formula (tap5ea7388), and the same release bundled in this one. - Boundary packages:
core-2.73.0-core.he1407f30925bb0c4andweb-2.73.0-web.ha47e875be648958c, built by CI from10597fe9; both published as pre-releases once their consumers were built on them. No viewer this time: nothing in 2.73.0 changes how a published note renders. "Latest" still names v2.73.0. - Phones: Android 1.1.43 (versionCode 46, on core 2.73.0, with ZenNotes/zennotesandroid#123's Settings, Vault fix) is in Google Play review at 100% rollout; iPhone 1.29.0 (build 45, with ZenNotes/zennotesios#68's) is uploaded to App Store Connect and goes in for review once Apple has decided on 1.28.0, which is still waiting. On phones the core's new Title bar switch is hidden, since their title bar always is; tablets keep it.
Release validation
Every CI failure since 2.72.0 was read before this release: ci-sweep.py v2.72.0 found none. hazard-scan.py --since v2.72.0 found no hazards, before the bump and again after it.
Release-time gates, in a clean checkout with its own npm ci: typecheck 8/8 with no turbo cache, test:run with no cache (shared-domain 2,048, app-core 3,328, desktop 1,183, quicklook 15 and the rest), and a fresh build:prod. Pack: Developer ID signed (team WYY7PK57DM); packaged launch check: page target and drawn app in 1.6 s, version 2.73.0. Smoke suites vim-editor, sidebar-vim and editor-improvements pass on the first run. The title bar fix was checked in the built app over CDP (with the sidebar closed, 0 of 80 points along the top of the window were draggable before and 67 to 72 after; tabs and buttons stay clickable), with a real mouse drag on the MacBook display (the window moved by exactly the drag, +150 and +80 points), and in the browser app against a local server (the Title bar switch hides the bar and stays off after a reload). A drop onto the blank tab strip could not be proven either way: the scripted mouse never completed a drop, even in a control run with the title bar on.
Release PR #968: the first run's Build (windows-latest) failed in cm-wysiwyg-blocks.test.ts ("hides a code block's closing fence inside a callout too": the whole callout rendered raw). It was a real bug, not a flake: the block plugin did not redraw when only the syntax tree changed, so a parse cut by its time budget on a slow runner left stale decorations. Fixed in 10597fe9 with a regression test that fails without it; pack and the launch check were repeated (1.5 s, 2.73.0), and all 10 checks then passed, the windows-11-arm package and launch check included. CI on main for 10597fe9 (run 39443191868): the macOS job was never picked up ("The job was not acquired by Runner of type hosted even after multiple attempts", GitHub's macOS arm64 capacity, no step ran); its re-run passed, so all six jobs were green before the tag. CI on main for the packaging commits: the AUR commit's run was cancelled when the Nix commit superseded it, e27a5cfd's (run 39452079379) passed, and so did 4e742e73's, the Homebrew commit (run 39454862147). Release run 39447342438: all six jobs green; verify-installers.py ALL GOOD on the five manifests by SHA-512 (14 files); the app in both DMGs is Notarized Developer ID with a valid stapled ticket. npm run verify:channels -- 2.73.0: every channel serves v2.73.0 (Homebrew, AUR, Nix and the website's eight download buttons).



