Skip to content

ZenNotes 2.70.0 - #945

Merged
adibhanna merged 13 commits into
mainfrom
v2.70.0
Oct 14, 2026
Merged

adibhanna merged 13 commits into
mainfrom
v2.70.0

Conversation

@adibhanna

@adibhanna adibhanna commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

ZenNotes 2.70.0: desktop, with the bundled zn raised to 0.8.0. The phones, the server and the boundary packages follow from the tag.

New

Fixes

Tests

Also

  • What's New: the 2.70.0 highlights (9e50b245); version bump (35d4df8f).

Gates on this head (9e50b245), in a clean checkout of it with its own npm ci: npm audit --omit=dev --audit-level=high clean; typecheck 8/8 and test:run with no turbo cache (shared-domain 1,994, app-core 3,235, quicklook 15, desktop 1,148); terminal 13/13; web-dist-lock; test:shared-packages, test:app-core-package and test:app-core-browser PASS. Pack: Developer ID signed, deep strict codesign OK, bundled zn v0.8.0 in a drwxr-xr-x Resources/zn-cli, the Quick Look extension; packaged launch check: page target and drawn app in 2.1 s, version 2.70.0. Smoke suites vim-editor (12), sidebar-vim (12) and editor-improvements (19) pass on the first run. ci-sweep.py v2.69.1: no failed jobs; hazard-scan.py --since v2.69.1: clean.

A first pack in the .claude release worktree failed the launch check (no page target; a main-process error dialog): that worktree resolves packages from the main checkout, and electron-builder left every hoisted dependency out of the asar. Not a product bug; the runbook now packs in a checkout with its own node_modules.

uNyanda and others added 13 commits October 13, 2026 23:58
global.searchNotes is bound to Mod+P, which resolves to Ctrl+P on Linux
and Windows. That is also the Emacs previous-item chord every palette
handles through isPalettePreviousKey().

focusPaneHandler runs capture-phase on window, so its toggle called
preventDefault() and stopImmediatePropagation() before React's delegated
root listener ever saw the key. The Ctrl+P branch was unreachable while
the palette was open. Ctrl+N, Ctrl+J and Ctrl+K were unaffected because
none of them is a global binding, which is the whole of the asymmetry.

Removes the toggle rather than guarding it. Escape, opening a note and
clicking outside all still dismiss the palette, and the in-app manual
already describes Mod+P as opening it rather than toggling. #510's
capture-phase registration and the open path below are untouched, so
Ctrl+P still opens the palette from the editor and still beats Vim's
cursor-up.
… again

After 2.69.1, the note grammar's habit of reading text again looked like
waste worth removing. noteMarkdown's outer tree always spans the whole note,
so when a state update's 20 ms parse slice runs out, CodeMirror drops the
older tree past the point the parse reached, and the parse that resumes
reads that text again instead of reusing its blocks.

Reusing those blocks was tried, and it is not waste. Lezer's markdown reuse
can keep a block whose meaning changed with text outside it: an indented
code block whose indent was deleted along with the frontmatter, a paragraph
whose end moved when a table row under it broke. The grammar happens to read
again exactly where that bites. Checked against a fresh parse 50,000 times
over 100,000 random edits, the grammar as it is was stale 3 times, with that
reuse 29 times, and plain markdown() 205 times. The time it would save is
small: a typed character parses in about 4 ms in an 800 KB note, inside its
slice, so a cut needs a slow machine or a pause first, and reading the rest
after one costs what opening the note costs, in background slices.

So the parser does not change. The header of cm-markdown-language.ts says
why both full reads (after a cut, and when the body start moves) stay, and
that syntaxTree(state).length is always the whole note, so code that needs
to know how far the parse got asks ensureSyntaxTree or syntaxTreeAvailable.
A new test pins the read after a cut, next to the one that pins the full
parse when the closing fence is typed; both fail on the reusing version.
Quick capture renders as its own window root, so App.tsx and its
capture-phase handler are never loaded there. Key handling was a plain
window listener in the bubble phase, and codemirror-vim consumes Ctrl+P,
Ctrl+N and Ctrl+W as normal-mode chords and stops propagation. Three of
the four shortcuts the footer advertises were dead in exactly the mode
they matter in. The main window already fixed this twice (#510, #579);
neither fix reached this window.

A capture-phase listener now claims those chords, and only while no
overlay is open. Inside an overlay its input has focus and the palette's
own isPalettePreviousKey/isPaletteNextKey branch navigates, so standing
aside there is what keeps Ctrl+P navigating rather than closing. That is
the main window's behaviour, and Ctrl+N always worked there because the
overlay guard returns early before newNoteHere.

Mod+W needed different treatment, because useCloseWindowShortcut is
shared with the main window. It takes a capture flag that only this call
site sets; the main window leaves it off, since it already resolves that
chord itself and doing both would double-handle it.

Three further defects in the same window, all found while testing the
first:

- The picker rows put the filename last behind the path, so the filename
  truncated to a stub while the path stayed readable. This is #635, whose
  fix landed in Open Buffers and never reached here. The row layout now
  mirrors BufferPalette, and the path column shows only the subpath, since
  the folder is already labelled on the left and the filename sits beside
  it.
- Neither overlay scrolled the selection into view, so moving past the
  bottom of the list left the highlight off screen. Both now do, keyed off
  data-qc-idx to stay clear of SearchPalette's data-search-idx.
- The footer is now state-aware, and the command overlay no longer claims
  Mod+N/Mod+P on rows whose chords move the selection instead.

The navigation hint reads Ctrl, not Mod, and that is load-bearing rather
than stylistic. isPaletteNextKey and isPalettePreviousKey match on
ctrlKey and reject metaKey, so Cmd+N/Cmd+P navigate in no palette on any
platform. Every other palette hardcodes the literal for that reason. An
earlier draft used modKey here, which would have rendered a Cmd hint that
does nothing on macOS.

11 tests added. They dispatch on document for the chords that open or
create, so the capture path runs while the editor never sees the key, and
on the focused input for navigation inside an overlay. Each puts Vim into
normal mode first, since insert mode is where the bug does not reproduce.
… mcp does

An assistant reading tasks through the MCP server saw a task's due date,
priority and tags, but not the column it sits in on a board grouped by
status: a task note's `status:`, a checkbox line's `@status:`, or the
status a line takes from its note's frontmatter. zn mcp reports it with
ZenNotes/tui#10 (#941), so the two servers would have
answered the same question differently.

list_tasks now carries `status` on every task note (`open` when it has
none) and on every line that has one, read the way the app reads it: the
first `@status:` on a line wins and the value is lower-cased. The token
stays in `content`, as every field does in this grammar, so ids and
content do not change. The app's own field reader, inlineTaskField, is
now exported from shared-domain and imported here rather than copied
again. The legacy TypeScript CLI shares this scan, so its `task list
--json` gains the field too.

The tool's description names the field, and its `status` argument says
it filters by state (open, done, waiting), not by that column, in the
same words as zn mcp.

A new test reads a task note with and without a status, a line that
inherits its note's status, and one with two `@status:` tokens, and
checks every line against shared-domain's parser. With the built server
over stdio, list_tasks on a scratch vault returned the same ids, content
and statuses as zn mcp, and tools/list showed the new descriptions.
…mes your key

Review follow-up to #944, kept apart so @uNyanda's commit stands as theirs.

The picker's path column is right to left so its ellipsis lands on the
left, and #944's comment says an LRE/PDF pair keeps the path itself left
to right, as Open Buffers does (#635). The pair was missing from the code,
so a subpath that starts with digits or punctuation came out reordered: a
note in inbox/2026/Planning showed "Planning/2026". The path is now wrapped
in U+202A and U+202C, the same string BufferPalette renders, and the test
pins it.

The footer's new close hint was written as Mod+W, but the key behind it is
Close active tab, which follows a rebind and can be unbound (#893). A
rebound key was advertised as Mod+W, and an unbound one as a key that does
nothing. useCloseShortcutDisplay reads the binding the way
useCloseWindowShortcut does, kept current through the same storage event,
and the footer shows it, or leaves the hint out once it is unbound.

The in-app help's Quick capture section gains Ctrl+N / Ctrl+P: inside the
note picker and the command palette they move the selection, which the
footer now says and the help did not.

Checked: the app-core typecheck and its full suite (3,235 tests, 2 new),
and the built app over CDP. With Vim on and the editor in normal mode,
Ctrl+P opens the picker, Ctrl+N and Ctrl+P move within it, Ctrl+N outside
it saves the draft to Quick Notes and starts a new one, the footer reads
"⌘W close", and the 2026/Planning row reads in order.
Review follow-up to #939, kept apart so @uNyanda's commit stands as theirs.
The comment that explains why Search Notes is no longer a toggle cited
#933, the New note form footer PR, for the Ctrl+P report. The report is
#938.

Behavior is unchanged. Checked in the built app on macOS: Cmd+P opens
Search Notes, Ctrl+N and Ctrl+P move within it, Cmd+P inside it no longer
closes it, and Escape does.
The bundled CLI is the floor a desktop build guarantees, and 0.8.0 is the
newest release. It carries two zn releases into a desktop-managed install
from the first launch instead of the first daily update check: 0.7.2,
where `zn task` and `zn mcp` read tasks in numbered lists and quotes the
way the app does, and 0.8.0, which adds `zn task watch` (one line per
task status change, for scripts that react to a card moving,
#941) and gives each task's status in
`zn task list --json` and in `list_tasks`. The pin is the release's
terminal-release.json copied byte for byte after it verified against
packaging/release-signing/zn-release-1.pub in ZenNotes/tui (source commit
58b0e56, integration protocol 1, all four archives matching
checksums.txt). Staged through `npm run terminal:stage`; the native
darwin-arm64 binary reports zn v0.8.0. The in-app manual's CLI card says
0.8.0 and what it changes, and the Tasks and folders card describes
`zn task watch`.
What's New opens on the first launch after the update with four highlights.
First, zn task watch in the bundled zn 0.8.0 (macOS and Linux, where ZenNotes
bundles the CLI). Second, the task status that list_tasks now returns through
MCP. Third, Quick capture's shortcuts with Vim in normal mode, naming where each
key was broken: Ctrl+P and Ctrl+N everywhere, Ctrl+W only on Linux and Windows,
where it is the close key. Fourth, Ctrl+P in Search Notes on Linux and Windows,
the only platforms where it used to close the palette.
@adibhanna
adibhanna merged commit 9e50b24 into main Oct 14, 2026
10 checks passed
@adibhanna
adibhanna deleted the v2.70.0 branch October 14, 2026 19:13
@adibhanna
adibhanna deployed to boundary-artifacts October 14, 2026 19:36 — with GitHub Actions Active
@adibhanna
adibhanna deployed to boundary-artifacts October 14, 2026 19:39 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
boundary-artifacts — 9e50b245 Deployed Oct 14, 2026 by adibhanna via draft #56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Quick Capture Shortcuts Are Dead In Normal And Visual Mode Ctrl+P Closes Search Palette Instead of Navigating Up

2 participants