Skip to content

Fix: scrolling a note in Edit mode no longer snaps back to the caret (zennotes#891) - #44

Merged
adibhanna merged 2 commits into
mainfrom
fix/891-edit-scroll
Oct 5, 2026
Merged

adibhanna merged 2 commits into
mainfrom
fix/891-edit-scroll

Conversation

@adibhanna

Copy link
Copy Markdown
Contributor

The iOS side of ZenNotes/zennotes#891 (reported on Android, fixed there in ZenNotes/zennotesandroid#96). This shell has the same src/ui-mobile/editor-keyboard-scroll.ts, and it had the same bug: with the keyboard up, every swipe was pulled back to the caret.

Cause. The helper revealed the caret on every DOM change (a page-wide MutationObserver re-observed the toolbars, which re-reported their size, and called revealEditorCaret()). CodeMirror draws lines as a note scrolls, so each swipe asked for a reveal.

Change. The caret is revealed only when something can have covered it: the keyboard landing, the formatting toolbar or selection bubble appearing or growing, or the viewport getting smaller. Touch-driven scrolling of the note and its fling belong to the reader: no reveal runs during them, and they cancel the keyboard's pending settle steps. Both files are byte-identical to the Android shell's (cmp clean).

Tests. New src/ui-mobile/editor-keyboard-scroll.test.ts (6 tests) over the real module; 4 fail against the old file (20 page changes gave 20 reveals). npm test 186/186, typecheck clean.

Simulator (iPhone 17, iOS 27.0, real XCUITest swipes; caret on line 3 of a 250-line note, keyboard up).

Swipe Before (top line) After (top line)
1 027, snapped to the caret twice mid-fling 046
2 013, behind where it started 093
3 016 139
4 018 185
5 019 232

A tap on a line the keyboard will cover still lifts the caret above the toolbar, and typing near the bottom keeps it there, in both builds. With the keyboard dismissed there was no snap on iPhone in either build, because dismissing blurs the editor; an iPad with a hardware keyboard (editor focused, no on-screen keyboard) was not tested.

Also carries the sass override that keeps braces (GHSA-vfj7-8cjw-p6xm) out of the production tree, the same commit as on #42 and #43, so the audit gate passes.

…e caret

With the keyboard up and the caret in a note, every swipe was pulled back
to the caret mid-fling, so a long note could not be scrolled past it
(ZenNotes/zennotes#891, reported on Android; this shell has the same file).

The keyboard-scroll helper watched the whole page with a MutationObserver
and, on every DOM change, re-observed the toolbars and revealed the caret.
CodeMirror draws lines as a note scrolls, so each swipe asked for a reveal.

The caret is now revealed only when something can have covered it: the
keyboard landing, the formatting toolbar or the selection bubble appearing
or growing, or the viewport getting smaller. Touch-driven scrolling of the
note, and the fling that follows it, belongs to the reader: no reveal runs
during it, and it cancels the keyboard's pending settle steps. Both files
are byte-identical to the Android shell's (ZenNotes/zennotesandroid#96).

Verified on an iPhone 17 simulator (iOS 27.0) with real XCUITest swipes,
caret on line 3 of a 250-line note, keyboard up: before, five swipes ended
on line 19 with two to five snaps back per swipe; after, the same swipes
reached line 232 with none. A tap on a line the keyboard will cover still
lifts the caret above the toolbar, and typing near the bottom keeps it
there.
)

A high-severity advisory for braces (stack exhaustion on deeply nested
patterns, no fixed release) failed the production npm audit gate on every
pull request. It reached production through @excalidraw/excalidraw, which
lists sass 1.51.0 as a dependency; that sass pulls in chokidar 3, which
pulls in braces. Excalidraw never loads sass at runtime: nothing in its
build output imports it.

Excalidraw's sass is overridden to 1.103.1, the version the desktop repo
already carries. It watches files with a chokidar that has no braces, so
braces stays only under build tools. No existing package changes version.
@adibhanna
adibhanna merged commit 53843b8 into main Oct 5, 2026
1 check passed
renovate Bot added a commit to scottames/copr that referenced this pull request Oct 6, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ZenNotes/zennotes](https://redirect.github.com/ZenNotes/zennotes) |
minor | `2.60.0` → `2.62.0` |

---

### Release Notes

<details>
<summary>ZenNotes/zennotes (ZenNotes/zennotes)</summary>

###
[`v2.62.0`](https://redirect.github.com/ZenNotes/zennotes/releases/tag/v2.62.0):
ZenNotes v2.62.0

[Compare
Source](https://redirect.github.com/ZenNotes/zennotes/compare/v2.61.0...v2.62.0)

ZenNotes 2.62.0 brings ZenNotes to Finder: press Space on any Markdown
file and Quick Look shows it the way ZenNotes renders it, with an Open
in ZenNotes button. It also makes Cloud restores and backups behave the
way they read, and tidies the rough edges ZenNotes Cloud's launch
testing turned up. A restored note no longer leaves a copy behind, a
refused backup names the limit it hit, Settings → Cloud opens on what
needs you, and an embed whose file is not on this device says so instead
of drawing an empty player. It also answers this week's reports: a
Kanban board can order its cards by due date, link hints reach the links
in the Edit view, ⌘W closes Settings and floating windows, Blinking
cursor off holds in every window, and a dated wikilink like
`[[2024.01.15]]` opens its note instead of the browser.

Released from
[`fb3d13f8`](https://redirect.github.com/ZenNotes/zennotes/commit/fb3d13f81c77d4fd3e69e5dcd24658ad0ad3b3d1)
through [PR
#&#8203;895](https://redirect.github.com/ZenNotes/zennotes/pull/895) on
October 5, 2026. All installers are verified.

#### Features

- **Quick Look for Markdown files (macOS).** Select a `.md` file in
Finder and press Space: Quick Look shows it rendered by ZenNotes' own
Preview, in your theme and fonts, with callouts, tasks, tables,
highlighted code, math, Mermaid diagrams and the images the note embeds.
Open in ZenNotes opens it in the app (a note inside a vault you use
opens there, any other file in its own window); once you click into the
preview, Enter does the same, and with Vim mode on `o` opens it while
`j` / `k`, Ctrl+D / Ctrl+U, `gg` and `G` move through the note. The
preview is read-only and never goes online: website links open in your
browser, and pictures hosted on the web are not loaded. It turns on
after you open ZenNotes once, and System Settings → General → Login
Items & Extensions → Quick Look turns it off.
- **Order Kanban cards by due date.** The board's toolbar has Order next
to Group by: Manual, as before, or Due date. By due date every column on
every board, custom status boards included, sorts earliest first with
cards without a date last, and a card moved to another column (a drag,
`Shift+H` / `Shift+L`) lands in its date slot. Before, the first drag
saved a hand-made order for the columns and that order won from then on,
so a task whose date moved up stayed where it had been dragged. The
dragged arrangement is kept while you order by date, and Manual brings
it back exactly. The same switch is `s` on the board in Vim mode,
`:order due | manual` on the Tasks ex line, the palette's "Kanban: Order
Cards by Due Date", Settings → Tasks → Card order, and
`kanban_card_sort` under `[view]` in config.toml
([#&#8203;889](https://redirect.github.com/ZenNotes/zennotes/issues/889)).
- **Settings → Cloud shows a skipped automatic backup.** When the daily
backup could not run, the Automatic daily backups row now says why: a
plan limit is worded from its numbers ("The automatic backup on
10/5/2026 was skipped. This vault holds 54 MB, and backups on your plan
hold up to 52 MB…"), anything else says the backup failed and will be
tried again. Before, a skipped backup was silent.

#### Fixes

- **A restored note leaves no copy behind.** Restoring a Cloud backup
that moved a note back (out of Trash, for example) wrote the note in its
old place and left the copy where it had been; the next scan uploaded
that copy as a new note to every device. The old copy now goes, but only
when this device still holds exactly what it synced there; an edit made
here since is kept and reported as a conflict. On the phones, a restore
that only changed a note's capitalization could delete the only copy on
storage that ignores case (Android's default); it now renames in place,
and a phone stopped in the middle of that rename, or a save landing in
the middle of it, no longer leaves a hidden copy behind or loses the
save.
- **A refused backup names the limit it hit.** "This backup would exceed
your plan limits" now reads, for example, "This vault holds 54 MB, and
backups on your plan hold up to 52 MB. Remove large files, or contact
support to raise the limit.", and likewise for the file count and the
number of backups kept. The restore prompt quotes the backup's label
("Restore “Weekly”?") and the result counts in the singular when it
should.
- **Settings → Cloud opens on what needs you.** The status bar's Review
and Set up, and `Space r` or the palette after a Cloud vault was
removed, open Settings on the Cloud page scrolled to This vault, with
the keyboard on the first thing waiting (a removed-vault notice, a
settings difference, a sync failure, files needing attention). Before,
the page opened at its top with the keyboard in the settings search.
- **The Cloud usage card stays current.** It read usage when Settings
opened; it now reads it again a few seconds after a sync run moves
files, and waits while the window is hidden.
- **The restore result reads plainly.** "This vault is synced to cursor
18." is now "This vault now matches the backup."
- **An embed whose file is not on this device says so.** A file too
large for Cloud stays on the device that has it, while its note syncs
everywhere; other devices drew `![[clip.mp4]]` as an empty player and
images or PDFs as broken frames. Edit mode and Preview now show
"clip.mp4 isn't on this device." in its place, and the embed turns into
the player as soon as the file arrives. A file reached through a symlink
is listed as an attachment and plays as before.
- **Link hints label the links in the Edit view.** `Space h` labelled
every control on screen and the links in Preview, but none of the links
in the Edit view, which draws them as styled text. Hint mode now also
labels the visible links of every open editor (bare URLs,
`[[wikilinks]]` with an alias or a heading, Markdown links and images),
down the note in reading order and never inside code, and following a
label does what clicking the link does
([#&#8203;894](https://redirect.github.com/ZenNotes/zennotes/issues/894)).
- **⌘W closes Settings and floating windows.** With Settings open, ⌘W
closed the tab behind it (or the whole window once no tab was left), and
in a floating note it did nothing. ⌘W now closes the frontmost window,
the macOS rule, and Settings counts as one. Floating notes, the
external-file window and Quick Capture close on it like their close
button: a pending save is written first, and Quick Capture hides and
keeps its draft. A rebind or unbind of Close active tab applies there
too. While a dialog, palette or menu is open ⌘W does nothing, so it can
never close the tab behind it
([#&#8203;893](https://redirect.github.com/ZenNotes/zennotes/issues/893)).
- **Blinking cursor off holds in every window.** Floating notes, Quick
Capture and the external-file window (and the reference pane and the
template editor) kept blinking with Blinking cursor turned off. Every
editor now follows the setting, and an open window follows a change made
in Settings at once
([#&#8203;892](https://redirect.github.com/ZenNotes/zennotes/issues/892)).
- **`[[2024.01.15]]` opens its note.** `gd`, `gD`, ⌘-click on a link's
source, a link in a table cell, a database relation chip and a missing
dated note clicked in Preview sent a wikilink with dots in its name to
the browser as `https://2024.01.15`, even with the note right there, and
`gy` offered to copy that address. A wikilink now reaches the browser
only when it is written with a scheme (`[[https://…]]`); Markdown links
keep reading `[site](example.com)` as a web address.
- **A card's menu keeps what you type into it.** A card's right-click
menu on the Kanban board filters as you type, and the manual suggests
typing "prio"; the board's own single keys saw the letters first, so
that `i` marked the card in progress (an `x` or a `c` would check it off
or cancel it). Behind Settings the Tasks view's keys fired too: `1`, `2`
and `3` switched the view and `a` opened the new-task prompt. Both now
wait while a menu, a prompt or a dialog is open.
- **Setting a value back in config.toml applies.** Change a setting in
the app, then set it back by hand in `config.toml`, and the file is byte
for byte what the app had written before; the app took that for a late
echo of its own write and ignored it, so it stayed on the newer value
while the file said otherwise. A put-back now applies like any other
edit: at once, or about a second and a half later when the app has only
just written the file.
- **A right-click on a wikilink stays where you are.** In the Edit view
a right-click on a rendered wikilink opened the linked note and then
drew the menu over it, and on a link to a missing note it asked to
create the note. Only the primary button follows a link now.

#### For contributors

- Quick Look lives in the new `apps/quicklook` workspace: the page
(`src/`, app-core's Preview with a bridge shim, Typst and plot renderers
left out, 11 MB), the sandboxed `ZenNotesQuickLook.appex` (Swift, serves
the page and embedded files over `zenql://` behind a no-network CSP;
WebKit still needs `network.client` to start), and
`ZenNotesQuickLookOpener.xpc` inside it (unsandboxed, because Quick
Look's sandbox lets a preview launch nothing).
`apps/desktop/build/after-pack.js` builds and signs it per architecture
with the app's identity and timestamp server, since electron-builder
never signs `Contents/PlugIns`. `ZENNOTES_QUICKLOOK_SELFTEST=1` compiles
a self-test for local validation (never in CI).
`apps/quicklook/README.md` covers testing. The typography CSS variables
moved from `App.tsx` to `lib/typography-variables.ts`.
- Cloud sync: an upsert with a path change retires the previous path
only when this device vouches for it (`retirePreviousPath`,
inode-checked `sameFile`); the coordinator's matching-bytes shortcut no
longer skips a move. The portable repository renames case-only through a
temporary name, writes a record under
`.zennotes/zennotes-cloud-sync/respellings` first, and skips the second
step when a save took the name;
`CloudSyncRepository.recoverInterruptedWork` (optional) settles leftover
records at the start of every run and never fails it.
- `cloudBackupLimitMessage` and `cloudBackupCreateError` in
shared-domain word BACKUP_QUOTA_EXCEEDED details; `formatCloudBytes`
moved to shared-domain (app-core re-exports it).
`CloudBackupSchedule.last_failure` is declared in bridge-contract.
- Browse (`packages/app-core/src/browse.ts`) now returns `files` rows
for the phones' folder view, placed as the desktop sidebar places them;
additive, unused by desktop and web.
- The store records `assetFilesListed`, and `resolveLocalAsset()`
reports `missing`; `indexImportedAssets` indexes an import before its
embed is inserted. Desktop `listAssets` includes symlinked files;
znserver needs
[ZenNotes/znserver#11](https://redirect.github.com/ZenNotes/znserver/pull/11)
in the release that pins this web artifact.
- Phones:
[ZenNotes/zennotesios#42](https://redirect.github.com/ZenNotes/zennotesios/pull/42)
and
[ZenNotes/zennotesandroid#95](https://redirect.github.com/ZenNotes/zennotesandroid/pull/95)
carry the shell side (note dates follow renames, folder file rows, iOS
folder vault list, Cloud UI tests); the file rows, media notice and
Settings fixes reach the phones with a core built from this release.
Shell-only fixes from the same issue round:
[ZenNotes/zennotesandroid#96](https://redirect.github.com/ZenNotes/zennotesandroid/pull/96)
and
[ZenNotes/zennotesios#44](https://redirect.github.com/ZenNotes/zennotesios/pull/44)
(Edit mode scrolling no longer snaps back to the caret,
[#&#8203;891](https://redirect.github.com/ZenNotes/zennotes/issues/891))
and
[ZenNotes/zennotesios#43](https://redirect.github.com/ZenNotes/zennotesios/pull/43)
(Ubuntu Sans and Ubuntu Sans Mono bundled on iPhone and iPad,
[#&#8203;890](https://redirect.github.com/ZenNotes/zennotes/issues/890)).
- Links: `linkRangeAtCursor` reports the link's kind from the shared
shape table (`linkRangesInLine` reads the same table), and
`externalUrlForLink(target, kind)` decides what opens in the browser;
`followLinkTarget` takes `kind`, and `extractLinkAtCursor`, which
dropped it, is gone. Hint mode collects editor links in
`lib/editor-link-hints.ts` and follows a wikilink through
`openWikilink`. The rendered-wikilink mousedown handler ignores every
button but the primary one.
- Every editor builds its cursor layer through `lib/cm-cursor-blink.ts`
(`cursorDrawSelection`, `storedCursorDrawSelection` for windows that
read the prefs blob and follow its `storage` event); a test fails if
another file calls `drawSelection()`.
- Kanban
([#&#8203;889](https://redirect.github.com/ZenNotes/zennotes/issues/889)):
`kanbanCardSort` (`'manual' | 'due'`) is a portable pref (`[view]
kanban_card_sort`), global only and deliberately not a vault view
override, which the Go server's vault.json mirror would have to learn.
The board orders cards through `orderCards`; `compareCardsByDue` is the
one comparator the column builders share (three copies before);
`placeTaskInColumnOrder` writes nothing while ordered by date.
`isOverlayOrDialogOpen` in `lib/overlay-open.ts` adds aria-modal dialogs
and Settings to `isAppOverlayOpen`; the board and the Tasks view use it,
the other list views still use the narrower check.
- `apps/desktop/src/main/app-config.ts`: an earlier own-write text
counts as a stale watcher read only while the module's own writes are
landing (in flight, or within `OWN_WRITE_SETTLE_MS`, 1.5 s, of the last
one), and a read skipped for that reason is re-checked once they settle.
Remembering own-write texts with no time bound swallowed any hand edit
that put an earlier version back. A read that outlives its watcher is
dropped.
- `lib/close-shortcut.ts`: `resolveCloseShortcut` decides what Mod+W
does in the main window (close Settings, keep, Vim prefix, close tab,
close window); `useCloseWindowShortcut` gives the floating,
external-file and Quick Capture windows the same binding, rebinds
included. Settings marks its panel `data-settings-dialog`.

#### Verification

How to test locally:

1. Build and launch with isolated stores: `npm run build --workspace
@zennotes/desktop`, then `ZENNOTES_USER_DATA_PATH=$(mktemp -d)
ZENNOTES_CONFIG_DIR=$(mktemp -d) npx electron
apps/desktop/out/main/index.js`.
2. Missing media: in a note write `![[assets/nope.mp4]]`,
`![[assets/nope.png]]` and `![[assets/nope.pdf]]`. Before: an empty
player and broken frames. After: "nope.mp4 isn't on this device." (and
so on) in Edit and Preview; copy a real `nope.png` into `assets/` and
the card turns into the picture.
3. Restore (needs Cloud): move a note to Trash, sync, restore the backup
from before. Before: the note in Inbox and a copy in Trash, uploaded to
every device. After: only the note in Inbox.
4. Backup limits (needs Cloud on a small plan): Create backup on a vault
over the limit. Before: "This backup would exceed your plan limits."
After: the sentence names the size and the limit.
5. Settings → Cloud: with a kept-both conflict copy waiting, press
Review on the status bar. Before: Settings at the top of the Cloud page.
After: This vault in view, the file list focused.
6. Link hints
([#&#8203;894](https://redirect.github.com/ZenNotes/zennotes/issues/894)):
in the Edit view open a note holding `https://example.net`,
`[[ZenNotes]]`, `[GitHub](https://github.com)` and ``
`https://code.test` ``, then press `Space h`. Before: labels on the
toolbar and sidebar only. After: labels on the three links too, none on
the code span; typing a label opens that link.
7. ⌘W
([#&#8203;893](https://redirect.github.com/ZenNotes/zennotes/issues/893)):
with three tabs open press `⌘,` then `⌘W`. Before: Settings stays and a
tab closes. After: Settings closes and the tabs stay. Run Open in
Floating Window from the palette, then `⌘W` in it. Before: nothing.
After: the window closes. In Quick Capture type a line and press `⌘W`:
it hides, and the line is still there when it opens again.
8. Blinking cursor
([#&#8203;892](https://redirect.github.com/ZenNotes/zennotes/issues/892)):
Settings → Editor, turn Blinking cursor off, then open a note in a
floating window and open Quick Capture. Before: both blink. After: both
hold still, and turning the setting back on makes both blink without
reopening them.
9. Dotted wikilinks: make a note named `2024.01.15` and, in another
note, write `[[2024.01.15]]`. With Vim on, put the caret in the link and
press `gd` (or ⌘-click the link while its brackets show). Before: the
browser opens <https://2024.01.15>. After: the note opens. Then
right-click the rendered link. Before: the linked note opens under the
menu. After: the menu opens where you are.
10. Quick Look: `cd apps/desktop && npx electron-builder --dir`, open
the packed `dist/mac-arm64/ZenNotes.app` once (or `pluginkit -a
dist/mac-arm64/ZenNotes.app/Contents/PlugIns/ZenNotesQuickLook.appex`),
then select a note in Finder and press Space. Before: macOS showed the
raw Markdown text. After: the note as ZenNotes renders it, in your
theme, with Open in ZenNotes; pressing it opens the note in the app.
(Opening a packed build also makes it the handler for `zennotes://`; see
`apps/quicklook/README.md` for testing on a separate profile.)
11. Kanban card order
([#&#8203;889](https://redirect.github.com/ZenNotes/zennotes/issues/889)):
in a scratch vault write `- [ ] Write spec @status:backlog
due:2026-10-20`, `- [ ] Fix login @status:backlog due:2026-10-08`, `- [
] Someday idea @status:backlog` and `- [ ] Ship v1 @status:backlog
due:2026-10-12`, put `kanban_statuses = ["backlog", "doing", "review"]`
under `[view]` in config.toml, and open Tasks → Kanban with Group by:
Custom status. Drag Someday idea to the top of Backlog, then change
Write spec's date to `2026-10-06`. Before: the column keeps the dragged
order and Write spec stays at the bottom, with no way to sort by date.
After: set Order to Due date (or press `s` with Vim on): Write spec, Fix
login, Ship v1, Someday idea; `Shift+L` on a card lands it by date in
the next column; Order: Manual brings back Someday idea on top.
12. Menu keys: with Vim on, right-click a card on the board and type
`prio`. Before: the card turns `[/]` (in progress) and the menu filters
on "pro". After: the menu narrows to the priority entries and the card
is unchanged. Also press `⌘,` on the Tasks view and then `1`: before,
the view behind Settings switched to List; after, nothing happens behind
Settings.
13. config.toml put-back: launch as in step 1 but with a fixed config
directory (`ZENNOTES_CONFIG_DIR=/tmp/zn-cfg`), open Tasks → Kanban, and
set Order to Due date, then Manual, then Due date again. Wait two
seconds, change `kanban_card_sort = "due"` back to `"manual"` in
`/tmp/zn-cfg/config.toml` and save. Before: the board stays on Due date.
After: it switches to Manual within half a second.

#### Distribution channels

- AUR 2.62.0-1: `beeb7d7`, mirrored in `372a44ba`; tarball SHA-256
`903baa6e...ca69cf4` matches GitHub's digest, the bundled CLI folder
check passed, and the AUR package checks passed on `main` and the
release branch.
- Homebrew: tap `b28fc38`, mirrored in `8983e4ae`; the arm64 DMG was
downloaded and its SHA-256 matches the cask and GitHub's digest, the x64
hash is GitHub's digest; `brew style` clean.
- Nix: the first hash-generation run failed. The packaged app now
carries `@parcel/watcher` and its prebuilt musl binary (sass 1.103.1,
which this release's braces override gives Excalidraw, depends on it,
and nothing loads it), and autoPatchelf could not satisfy
`libc.musl-x86_64.so.1`. `93934229` drops the musl variants inside the
Nix package; the rerun
[37382323699](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37382323699)
passed, `f16bbe0d` carries its values, and the main rebuild
[37382901964](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37382901964)
passed. `desktopHash` is the same tarball digest the AUR pins. [nixpkgs
PR
#&#8203;570652](https://redirect.github.com/NixOS/nixpkgs/pull/570652)
carries 2.61.0 -> 2.62.0 as one commit on current master and awaits
upstream review. Its source hash is the tag tarball's, computed with
`nix store prefetch-file --unpack`; the same command reproduces master's
current 2.61.0 hash. nixpkgs' derivation builds from source and has no
autoPatchelf step.
- `verify:channels -- 2.62.0` passed for Homebrew, AUR, Nix, and all
seven website download routes. GitHub latest is v2.62.0. [Website PR
#&#8203;62](https://redirect.github.com/ZenNotes/website/pull/62) merged
at `ff57488f`. Its main tests and deploy passed (run
[37384433044](https://redirect.github.com/ZenNotes/website/actions/runs/37384433044)),
and zennotes.org/releases serves the 2.62.0 entry; the docs carry the
Quick Look section and `:order`.
- Self-hosted server: [znserver
2.62.0](https://redirect.github.com/ZenNotes/znserver/releases/tag/v2.62.0),
released the same day, embeds this release's client code
(`web-2.62.0-web.h53c3b6622d59ff2e`, published as a pre-release) and
lists files reached through a symlink as assets
([znserver#11](https://redirect.github.com/ZenNotes/znserver/pull/11));
Docker `adibhanna/zennotes:2.62.0`, `2.62` and `latest` (linux/amd64 and
linux/arm64, reporting v2.62.0).
- Phones: iPhone 1.18.0 (34) and Android 1.1.32 (35) carry this
release's app code as core `core-2.62.0-core.hcf81934394c63533`,
published as a pre-release. Android 1.1.32 is live on Google Play and on
GitHub with Play's universal APK; iPhone 1.18.0 is in App Review.

#### Release validation

- Fresh typechecks passed (no cache); 6,056 unit tests passed, 5 skipped
(shared-domain 1,896, app-core 3,096, quicklook 15, desktop 1,049).
- The signed local package (`npm run pack`) built and signed the Quick
Look extension in afterPack, launched isolated with a CDP page in 1.5
seconds and reported 2.62.0; `codesign --verify --deep --strict` passed.
Vim editor (12 checks), sidebar navigation (12) and editor improvements
(19) smoke suites passed on the first run.
- Release PR
[#&#8203;895](https://redirect.github.com/ZenNotes/zennotes/issues/895):
all 9 checks passed on `fb3d13f8`.
- Release run
[37379286092](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37379286092)
passed on every platform on the first attempt, with 25 assets.
- All four update manifests and their 13 referenced files passed size
and downloaded SHA-512 verification. The app in the arm64 DMG passed
notarization (`Notarized Developer ID`), staple and strict deep
signature checks, reports 2.62.0, bundles `zn` v0.6.2, and carries
`ZenNotesQuickLook.appex` with its opener service: the first CI build of
the extension, signed by team WYY7PK57DM with the hardened runtime, a
secure timestamp and the sandbox entitlements as designed. Quick Look
itself was exercised end to end on a locally packed build during the
cycle (Finder Space, Open in ZenNotes, Vim keys); the CI build's
extension was checked for presence and signatures, not registered with
Quick Look.
- Issues
[#&#8203;889](https://redirect.github.com/ZenNotes/zennotes/issues/889),
[#&#8203;892](https://redirect.github.com/ZenNotes/zennotes/issues/892),
[#&#8203;893](https://redirect.github.com/ZenNotes/zennotes/issues/893)
and
[#&#8203;894](https://redirect.github.com/ZenNotes/zennotes/issues/894)
and the dotted-wikilink, menu-key and config.toml fixes were each driven
in the built app over CDP on an isolated profile before they were
committed.
- Cloud: the restore fix was verified against production in the built
app during the October 5 acceptance run (no duplicate, sequence
unchanged). Billing was accepted separately in Stripe's sandbox, and
publishing earlier the same day.

###
[`v2.61.0`](https://redirect.github.com/ZenNotes/zennotes/releases/tag/v2.61.0):
ZenNotes v2.61.0

[Compare
Source](https://redirect.github.com/ZenNotes/zennotes/compare/v2.60.0...v2.61.0)

ZenNotes 2.61.0 makes ZenNotes Cloud say what it is waiting on. A note
paused by a sync conflict says so on the note, Cloud's limits name the
file and the limit, and a deleted Cloud vault no longer stops syncing in
silence. Videos and audio play in the editor, the backup browser reaches
every file, and heading links follow in the reading view.

Released from
[`80c284b8`](https://redirect.github.com/ZenNotes/zennotes/commit/80c284b8a6c125109f74b58d51600a2a5300f40f)
through [PR
#&#8203;887](https://redirect.github.com/ZenNotes/zennotes/pull/887) on
October 2, 2026. All installers are verified.

#### Features

- **A note paused by a sync conflict says so on the note.** When the
same note changes on two devices, a one-row banner across its top reads
"Sync is paused for this note. It changed on this device and on another
device.", in Edit, Preview and split alike, with a Review button that
opens the conflict queue on that note (with Vim on, `Space r` does the
same and the banner names it). A notification with Review appears once
for each new conflict, stays quiet while the queue is open, and comes
back if a resolved conflict returns. Before, the only sign was "1 file
needs review" in the status bar, which zen mode hides.
- **Videos and audio play in the editor.** Attaching or dropping an
image, PDF, audio or video file now embeds it (`![[path]]`) instead of
writing a link, and Edit mode draws a standalone audio or video line as
the same player the reading view uses, with Open and `</>` (edit the
line) in its header.
- **The backup browser reaches every file.** Settings → Cloud → Backups
→ Browse notes now reads "Showing 50 of N notes" with **Load more**, and
its search asks Cloud, matching any part of a path across the whole
backup. Before, it listed the first 50 files and searched only those, so
a note that sorted later could not be restored on its own.
- **Deleting a Cloud vault asks for its name.** The dialog reads "Delete
“Name” for every device?", says what is lost and what stays on your
devices, points to Unlink this device for stopping on one device only,
and keeps Delete disabled until the vault's exact name is typed.

#### Fixes

- **Cloud names what stopped a sync.** The status bar and Settings say
"1 file too large for Cloud", "Cloud storage full" or "Cloud item limit
reached" instead of "Sync incomplete", the message names the file
("“clip.mov” is larger than the 10 MB Cloud file-size limit, so it stays
on this device."), and the note itself shows "Not synced to Cloud:
larger than the 10 MB file-size limit, so it stays on this device."
under that file. A 10 GB plan now reads 10 GB rather than 9.3 GB.
- **A deleted Cloud vault no longer stops syncing in silence.** When a
Cloud vault is deleted on another device or on the website, the devices
still linked to it used to unlink quietly and show the success color.
Now the status bar says "Cloud vault deleted" (or "Cloud vault
unavailable" when Cloud refuses it to this account), and Settings →
Cloud → This vault explains what happened until you link or create a
vault, or dismiss the notice; Review, `Space r` and the new **Review
Cloud Vault** palette entry all open it, and the notice survives a
restart. Settings also stops offering a vault that is gone.
- **Settings → Cloud after signing in.** Signing in while the page was
still loading reported "Cloud account changed while loading
credentials."; the newest load now wins and a cancelled one retries.
Failures of This vault's actions appear inside This vault instead of at
the top of the page, and messages no longer start with an internal class
name such as "CloudServiceRequestError:".
- **Large files sync in bounded steps.** Cloud now hands over large
revisions as references fetched in bounded pages, downloads go to a
staging file checked for length and SHA-256 before they replace a note
or attachment, an interrupted catch-up keeps its place, sync honors
Cloud's retry delays across windows, and it stops cleanly on sign-out
and quit
([#&#8203;884](https://redirect.github.com/ZenNotes/zennotes/pull/884)).
- **Heading links follow in the reading view.** A Markdown
`[text](#heading)` had nothing to scroll to because rendered headings
carried no id; it now lands on the heading, as do `#Heading%20Text` and
`#heading-slug`, and a click on a `[[#Heading]]` link no longer opens
the hover card first.
- **Folder pickers on touch screens open without the keyboard.** On a
touch screen, Move to… for a note or a folder shows the whole folder
list; tap the field to type a path. Prompts with nothing to tap still
open with the keyboard.
- **Cloud never syncs another device's bookkeeping.** A vault that is
also open on a phone could carry the phone's `zennotes-cloud-sync`
folder, which then synced back and forth on every run; sync now skips
it.
- The **Review Cloud Sync Conflicts** palette entry names `Space r` only
with Vim on, since the leader does nothing without it.

#### For contributors

- New optional bridge method `listCloudBackupItemsPage(backupId, { page,
search })` with the desktop IPC handler `cloud-backup-items:page` (the
vault comes from main-process state; page and a 200-character search are
validated there). Hosts without it keep the old single page with local
filtering. ZenNotes Cloud's backup items endpoint accepts `search`
([ZenNotes/website#57](https://redirect.github.com/ZenNotes/website/pull/57)).
- The status row carries `data-cloud-sync-phase` and
`data-cloud-sync-review`, and Settings marks its dialog with
`data-settings-target`, so the phone shells can style and route without
matching Tailwind classes.
- The phones already carry this release's app code: core `2.60.4`
([core-2.60.4-core.hae0e49f9e5397fc2](https://redirect.github.com/ZenNotes/zennotes/releases/tag/core-2.60.4-core.hae0e49f9e5397fc2))
is in iPhone 1.17.1 and Android 1.1.31, and the self-hosted web client
`web-2.60.4-web.ha0258ecc7b3c0133` is in [znserver
2.60.1](https://redirect.github.com/ZenNotes/znserver/releases/tag/v2.60.1).
No new boundary artifacts are built for 2.61.0: the code is the same.

#### Verification

How to test locally:

1. Build and launch with isolated stores: `npm run build --workspace
@zennotes/desktop`, then `ZENNOTES_USER_DATA_PATH=$(mktemp -d)
ZENNOTES_CONFIG_DIR=$(mktemp -d) npx electron
apps/desktop/out/main/index.js`.
2. Media: drop a short `.mp4` into a note. Before: `[clip.mp4](…)`.
After: `![[…/clip.mp4]]` and a player in Edit mode and in Preview.
3. Heading links: put `[jump](#section-three)` at the top of a note and
`## Section Three` far below, open Preview and click the link. Before:
nothing. After: the note scrolls to the heading.
4. Cloud (needs an account): link a vault on two devices, take one
offline, edit the same line on both, reconnect. Before: only the status
bar knew. After: the note shows the banner and a notification offers
Review, which opens the queue on that note.
5. Cloud backups: Settings → Cloud → Backups → Create backup, wait for
Ready, Browse notes on a vault with more than 50 files. Before: 50 rows.
After: "Showing 50 of N notes", Load more, and search finds files on
later pages.
6. Cloud delete: Settings → Cloud → Delete Cloud vault. After: the
dialog names the vault and keeps Delete disabled until its exact name is
typed. Cancel.

#### Distribution channels

- AUR 2.61.0-1: `96731c2`, mirrored in `c9e2427f`; tarball SHA-256
`c0d8d21a...aa69b36` matches GitHub's digest, the bundled CLI folder
check passed, and the AUR package checks passed on `main` and the
release branch.
- Homebrew: tap `1c0b680`, mirrored in `9a74e705`; both DMGs were
downloaded and their SHA-256 matches the cask and GitHub's digests;
`brew style` clean.
- Nix: `fcd44f09`; hash-generation run
[37078241465](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37078241465)
and the main rebuild
[37078403704](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37078403704)
passed. `desktopHash` is the same tarball digest the AUR pins. [nixpkgs
PR
#&#8203;569616](https://redirect.github.com/NixOS/nixpkgs/pull/569616)
carries 2.60.0 -> 2.61.0 as one commit on current master and awaits
upstream review. Its source hash is the tag tarball's, which nixpkgs
fetches differently from this repo's package, computed with `nix store
prefetch-file --unpack`; the same command reproduces master's current
2.60.0 hash.
- `verify:channels -- 2.61.0` passed for Homebrew, AUR, Nix, and all
seven website download routes. GitHub latest is v2.61.0. [Website PR
#&#8203;59](https://redirect.github.com/ZenNotes/website/pull/59) merged
at `7beab52`; its main tests and deploy passed (run
[37081053002](https://redirect.github.com/ZenNotes/website/actions/runs/37081053002)),
and zennotes.org/releases serves the 2.61.0 entry.
- Self-hosted server: [znserver
2.60.1](https://redirect.github.com/ZenNotes/znserver/releases/tag/v2.60.1),
released the same day, embeds the same client code
(`web-2.60.4-web.ha0258ecc7b3c0133`); Docker
`adibhanna/zennotes:2.60.1`, `2.60` and `latest`. No server release
follows 2.61.0, because the code is identical.
- Phones: iPhone 1.17.1 (33) and Android 1.1.31 (34) carry this
release's app code as core 2.60.4 and are in store review.

#### Release validation

- Fresh typechecks passed (no cache); 5,831 unit tests passed, 5 skipped
(shared-domain 1,861, app-core 2,945, desktop 1,025).
- The signed local package (`npm run pack`) launched isolated with a CDP
page in 1.4 seconds, reporting 2.61.0 and bundling CLI 0.6.2; `codesign
--verify --deep --strict` passed. Vim editor (12 checks), sidebar
navigation (12) and editor improvements (19) smoke suites passed.
- Release PR
[#&#8203;887](https://redirect.github.com/ZenNotes/zennotes/issues/887):
all 9 checks passed on `80c284b8`.
- Release run
[37077212178](https://redirect.github.com/ZenNotes/zennotes/actions/runs/37077212178)
passed on every platform on the first attempt, with 25 assets.
- All four update manifests and their 13 referenced files passed size
and downloaded SHA-512 verification. The app in the arm64 DMG passed
notarization (`Notarized Developer ID`), staple and strict deep
signature checks, reports 2.61.0, and its bundled `zn` reports v0.6.2.
- Cloud: the changes were exercised against production during the
October 2 device acceptance run on a real iPhone and this desktop app,
and the backup browser was checked against production with a 91-file
backup. Publishing, full-vault restore and billing were not part of this
release's checks.

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/Los_Angeles)

- Branch creation
  - Every minute (`* * * * *`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/scottames/copr).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjEzNC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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