Skip to content

ZenNotes 2.65.0 - #915

Merged
adibhanna merged 8 commits into
mainfrom
v2.65.0
Oct 9, 2026
Merged

adibhanna merged 8 commits into
mainfrom
v2.65.0

Conversation

@adibhanna

@adibhanna adibhanna commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

ZenNotes 2.65.0.

Features

  • Workflows: tag, tagged and not-tagged take a comma-separated tag list (any of them), and a new no-tags step keeps notes with no tag at all. Space-only separation is an error naming the comma form, since old builds ran not-tagged #a #b as not-tagged #a. (Tornado300, Discord)
  • The table menu gains Copy table as Markdown and Duplicate table.

Fixes

  • Vim mode copies a rendered table as Markdown rows (Shift+V then y, p pastes a working table), and ⌘C on a Vim selection in a table copies instead of running c, which emptied the cells and saved the blank table. (Tornado300)
  • Vim keys keep working in the Tags view after a click inside it; pane navigation targets the view in its slot. (Tornado300)
  • New Task outside the Tasks view says "Task added" with Open; ToastHost gains data-toast-host for the phone shells. (zennotesandroid#101)
  • A terminal-launched ZenNotes no longer freezes on Linux: the PATH probe runs detached and a stuck shell's group is killed after five seconds. (Julie, Discord)
  • The sidebar scrollbar thumb is the 30% tint it was meant to be (its opacity never applied).
  • Help → Vim & Ex lists ⌘L for checking off the task under the cursor.

Cycle verification: every fix reproduced in the built app over CDP on an isolated profile, then checked again after; the terminal freeze on Linux (Ubuntu 26.04) with the 2.64.0 tarball. New tests fail against the old code where they pin a behavior change (5 of 7 table, the Tags panel order, 15 of 22 workflow, 3 probe tests).

Local gates on 21ebc827: typecheck 8/8 and test:run 6/6 with no turbo cache (shared-domain 1,921, app-core 3,140, quicklook 15, desktop 1,055), npm audit --omit=dev --audit-level=high clean (15 low or moderate remain in dompurify, fast-uri, ip-address, katex, smol-toml and sprintf-js). npm run pack signed the app on the first try, deep strict codesign OK, the pack log shows electron-builder's "skipped macOS notarization" next to the hook, bundled zn v0.6.3 in Resources/zn-cli (drwxr-xr-x); packaged launch check: page target in 1.4 s reporting 2.65.0. Smoke suites on the built app: vim-editor, sidebar-vim and editor-improvements passed on their first run.

Since #451 (2.16.0) the sidebar's scrollbar thumb is tinted with the
sidebar's own text colour so a long list visibly scrolls. The rule asked for
that colour at `opacity: 0.3` (0.5 on hover), but scrollbar pseudo-elements
ignore opacity, so the thumb has been painted at full text colour: a bright
bar beside every sidebar that overflows, unchanged on hover.

The strength now lives in the colour itself:
`color-mix(in srgb, currentColor 30%, transparent)` at rest and 50% under
the pointer, with the plain currentColor line kept first for a browser
without color-mix. The thumb stays discoverable, which is what #451 wanted,
without competing with the notes list.

Measured in the built app over CDP (isolated stores, an 80-note vault so the
sidebar scrolls), sampling the thumb's pixels. Gruvbox dark: text
212,190,152 over 22,23,24, the thumb went from 209,191,157 to 78,73,64 and
115,106,90 on hover (a 30% and 50% blend). Gruvbox light: text 80,73,69 over
229,226,201, from 79,73,70 to 184,180,161 and 154,150,135 on hover.
scrollbar-style.test.ts fails on the old rule: no scrollbar part may set
opacity, and the sidebar thumb uses the 30% and 50% mixes.
…rsor

Cmd+L (Ctrl+L on Windows and Linux) toggles the checkbox on the cursor
line, and in Vim mode it works from normal, insert, and visual mode
without changing modes. The manual only listed it as "Toggle checkbox" in
the general shortcut table and in a concepts card titled "Any line
becomes a checkbox", so someone looking in the Vim & Ex section for a way
to mark a task done found nothing.

Vim & Ex gains a "Check off the task under the cursor" row, and its key
label follows an editor.toggleCheckbox rebind the way gd and gD do. The
card is renamed "Check off a task with ⌘L" (the shortcut row that points
at it follows) and says how it behaves in each Vim mode. Behavior
verified in the built app over CDP: normal mode stays normal, insert
stays insert, Vjj toggles exactly the selected lines, and one u undoes
them. The website mirror landed in ZenNotes/website 2da9d8d.
…ATH probe

Julie (Discord, 2026-10-09) runs ZenNotes from a terminal on Linux, and
clicking CLI in Settings made it "crash and stop responding".

To answer "is zn on your PATH?", and to find node, npm, rg and fzf, the
main process asks the user's shell for its PATH, interactive login shell
first (`$SHELL -lic`, #634). An interactive shell does job control on its
controlling terminal, and a ZenNotes started from a terminal shares that
terminal with every shell it starts. Reproduced on Linux with the 2.64.0
tarball, two ways:

- zsh, with an rc file that prompts on /dev/tty: each probe took the
  terminal's foreground and waited there for an answer nobody knew was
  being asked for. Interactive shells ignore SIGTERM, so the five-second
  timeout ended none of them, Settings > CLI sat on "Checking install
  status..." for good, and every visit started more of them.
- bash, with ZenNotes started in the background (`zennotes &`): the
  startup probe, finding its process group outside the terminal's
  foreground, sent SIGTTIN to that group, which is ZenNotes' own. The
  main process and the renderer stopped and no window ever came up.

Finder, Dock and app-menu launches have no terminal, which is why this
only shows when ZenNotes is started from one.

The probe now runs in a session of its own (`detached`) with stdin
empty. Without a controlling terminal there is no job control to do:
nothing outside the probe is signalled, and /dev/tty fails at once
instead of blocking. A timeout SIGKILLs the probe's whole process group,
so a stuck rc file costs five seconds per shell instead of the page, and
callers that ask at the same time share one probe instead of starting
three.

What it deliberately does not do: change the interactive-first order of
#634 or the timeout. rc files still run, only without a terminal to talk
to.

Verified with a Linux tarball of this change on the same setup (Ubuntu
26.04, app started under a pty, isolated stores): with the prompting rc
the page loaded in about a second, from one probe with no terminal and
its own process group, and the terminal stayed with ZenNotes; with an rc
that hangs, each stuck zsh was killed with its child at five seconds,
bash answered and the page loaded; `zennotes &` under bash opened its
window and the CLI page. login-shell-path.test.ts fails against the old
probe: a shell that ignores SIGTERM (it and its child must be gone), the
probe's own session, and three callers sharing one shell.
…the table

Tornado300 (Discord, 2026-10-09) selected every row of a rendered table in
Vim mode and could copy only the content, never the table.

Reproduced in the built app (Vim on, isolated stores), two ways:

- V j j y in the table, then p on the line below it, pasted the cells as
  tab-separated text into the middle of that line: the cell cursor yanked
  every selection as TSV, charwise.
- Cmd+C on the same selection reached the cell cursor as a plain `c`,
  Vim's change operator. The clipboard stayed as it was, the selected cells
  were emptied into insert mode, and the Escape that followed wrote the
  blank table back to the note and to disk.

Cmd and Ctrl chords in a cell now go to the app instead of running as Vim
letters, except Ctrl+R (redo), Ctrl+Alt (AltGr) characters, and whatever
the pane moves (Ctrl+h/j/k/l) are bound to. In a visual selection Cmd+C
(Ctrl+C on Windows and Linux) copies it without changing it, and Cmd+X
cuts it. A Shift+V selection yanks as Markdown table rows, linewise (one
that takes in the header brings its divider row too), so p in the note
pastes a working table. A character or block selection still yanks TSV,
which is what a spreadsheet wants. The cell menu (right-click / m) gains
"Copy table as Markdown" and "Duplicate table", and the manual's "Render
tables in live preview" entry says how to copy one.

What it deliberately does not do: change how a yank reaches the system
clipboard (still the "Sync clipboard with Vim registers" setting), or
change table editing with Vim off.

Verified over CDP on the built app: V j j y then p pastes the table below
it as four Markdown lines; Cmd+C puts those rows on the clipboard and the
Escape after it leaves every cell, and the file, intact; the menu copies
the table and duplicates it below (two table widgets). cm-table.test.ts
gains seven tests: the five yank and copy tests fail against the old
code, and the two menu tests drive items that did not exist.
…e it

Tornado300 (Discord, 2026-10-09) found the Vim motions and keybinds dead
in the Tags overview.

The view answers its keys only while the Tags panel holds focus (#412).
The editor pane's capture handlers claim the editor panel for any click or
focus inside the pane, so the first click on a heading or a tag chip
handed the panel to the editor and j/k stopped reaching the view. Tasks
and Atlas already win the panel back on their root element; the Tags view
now does the same, and since inner capture handlers run after the pane's,
its claim is the one that sticks.

Pane navigation had the same gap: with the Tags view as the active tab,
the panel list still named the editor in that slot, so Ctrl+W h/l and
Alt+h/l had no way to land on the view. It names the Tags view now, as it
does Tasks and Atlas.

Verified over CDP on the built app: before, after one click on a heading,
j left the cursor where it was; after, j and k move after heading and
chip clicks. vim-nav.test.ts gains the panel-order case, which fails
against the old code.
…ags step

Tornado300 (Discord, 2026-10-09) wanted every note without tags moved into
a folder to check. Saying "carries none of these tags" took one not-tagged
step per tag, and "carries no tag at all" could not be said.

`tag`, `tagged` and `not-tagged` now take a comma-separated list and match
any tag in it, so `tagged #book, #article` is an allow list and
`not-tagged #someday, #reference` a block list. `no-tags` keeps the notes
that carry no tag at all, frontmatter or inline (a `#` inside inline code
is not a tag, as everywhere else in the app). The canvas labels, the step
reference, completion (the entry under the caret, `#a,#b` included) and
the inspector field (`book, article`) all speak the list, and the file
always saves it as `#a, #b`, a repeated tag dropped whatever its case.
The manual's Workflows card shows the list form and counts thirteen
filter steps.

What it deliberately does not do: read tags separated only by spaces as a
list. Before lists, `not-tagged #a #b` ran as `not-tagged #a` with a
warning, so reading the spaces as a list now would quietly change what an
existing `... | trash` touches. It is an error that names the comma form,
and an errored statement plans no writes. A build without lists rejects
the comma form the same way, so a file shared across versions never runs
wider than written. The step is not called `untagged`: a step kind wins
over a wire of the same name, and `untagged` is a natural wire name.

Verified over CDP on the built app (Vim on, isolated stores):
`all | no-tags | move review` moved exactly the three untagged notes (one
with `#notatag` only in inline code) and left a note whose only tag is in
its frontmatter; `tagged` and `not-tagged #book, #article` wrote the right
sets; typing ", someday" into the inspector autosaved
`#book, #article, #someday`, Backspace took it back to `#book, #article`
without eating into the previous tag, and switching steps re-seeds the
field; the ? reference lists no-tags. 22 new tests: 15 fail against the
old code, the rest guard what must not change (a single tag binds as
before, no completion after a bare space).
New Task from the palette, `:newtask`, and now a phone's Action Button
(zennotesandroid#101) wrote the task file and showed nothing. The task
appears only in the Tasks view, so unless that was on screen, the only way
to know it worked was to go and look.

A task created anywhere but the Tasks view now raises "Task added" with an
Open action that opens the new task note. On the Tasks view the new row is
the confirmation, so it stays quiet there. The toast host gains a
`data-toast-host` hook: the phone shells pin their floating button to the
corner toasts use, and lift toasts above it by that hook.

Verified over CDP on the built app: `:newtask` from a note shows the toast,
and Open opens the task note and dismisses it; the Tasks view's "+ New
task" shows none. store-new-task.test.ts covers both and a cancelled
prompt.
@adibhanna
adibhanna merged commit 21ebc82 into main Oct 9, 2026
9 checks passed
@adibhanna
adibhanna deleted the v2.65.0 branch October 9, 2026 16:20
@adibhanna
adibhanna restored the v2.65.0 branch October 9, 2026 16:20
@adibhanna
adibhanna deployed to boundary-artifacts October 9, 2026 16:24 — with GitHub Actions Active
@adibhanna
adibhanna deployed to boundary-artifacts October 9, 2026 16:25 — with GitHub Actions Active
@adibhanna
adibhanna deployed to boundary-artifacts October 9, 2026 16:28 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
boundary-artifacts — 21ebc827 Deployed Oct 9, 2026 by adibhanna via draft #39
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.

1 participant