ZenNotes 2.47.0: switching drawings no longer overwrites one with the other, wikilinks at files are files, tasks stay visible during Cloud conflicts, and matching files stop waiting for review
A data-loss fix for Excalidraw drawings from #755, the Connections panel and
gdtreating a wikilink at an image or PDF as a missing note from #757, and two fixes from Unyanda's September 8 report: a note waiting for a Cloud decision no longer takes its tasks out of your day, and copying the same complete file to both devices no longer leaves it stuck in review. Unfinished merge drafts stay protected, including across desktop windows. Release PR: #752.
🐛 Fixes
-
A wikilink at a file in the vault is a file, not a missing note (#757, reported by Unyanda). The Connections panel listed an embedded image such as
![[assets/diagram.png]]under Links From Here as an unresolved wikilink and offered to createassets/diagram.png.mdfor it. The panel only asked the note index whether a target resolved, and an image is not a note. It now checks the vault's files with the same rules that render the embed (relative to the note, relative to the vault root, then a unique file name), lists a match as a file row that opens the file in its own tab, and offers to create a note only for a wikilink that reaches nothing. The same step now runs in every place a wikilink is followed:gdin the editor, a click on a rendered wikilink, and Cmd/Ctrl-click, none of which should proposefile.png.mdeither. PDFs keep their existinggdbehaviour of pinning into the reference pane.How to test locally: launch the isolated desktop as described below, put a PNG in
assets/, and write![[assets/diagram.png]]and[[assets/diagram.png]]in a note. Press Mod+2. Before: aCREATErow forassets/diagram.pngsuggesting/assets/diagram.png.md. After: aFILErow nameddiagram.png;jonto it and Enter opens the image in a tab. Put the cursor inside[[assets/diagram.png]]and pressgdin normal mode. Before: a "Create note for" prompt. After: the image tab opens. -
Switching between drawings no longer overwrites the one you open (#755, reported by gverger). Opening a second Excalidraw drawing could replace its file with the scene of the drawing you just left, and switching back and forth left both files holding the same scene. The drawing editor kept one "latest scene" and wrote it, after its short save delay, to whichever drawing was showing when the delay ran out. On a local vault the next drawing loads within a millisecond, so React folded "clear the canvas" and "show the next drawing" into one update and the previous canvas was never torn down. Excalidraw only reads the scene it is given once, at mount, so that surviving canvas kept reporting the old drawing under the new file's name. Each drawing now saves through its own session, bound to the path it was read from, the canvas is remounted per path, and a canvas being torn down can no longer arm a write against the drawing that replaced it. The viewport memory that restores your pan and zoom per drawing follows the same binding, so a switch no longer copies the previous drawing's viewport onto the next one.
How to test locally: build and launch the isolated desktop as described below. In the sidebar, create a drawing named First and draw a circle, then create Second and draw a rectangle. Click First, then Second, then First, pausing about a second on each. Before: within a second of a switch, the drawing you opened shows and saves the previous drawing's shape, and reopening either file confirms the loss. After: each drawing keeps its own shape through any number of switches, and the files on disk still hold one circle and one rectangle. The same holds when you switch immediately after drawing, before the save delay has run.
-
A conflicted note keeps its tasks in view. List, Calendar, Kanban and the calendar panel continue showing the tasks from your local note, with a small Conflict pending label. Previously those tasks were deliberately withheld until the note was resolved, so deferring a sync decision also hid work from your task views. They now remain visible and refresh when you edit the local note. Resolving the conflict updates them only as the resulting note changes.
How to test locally: launch the isolated desktop as described below. Put a dated task in a note, sync it, then edit the same line differently on two linked test devices. Press Sync now, leave the conflict unresolved, and open Tasks in List, Calendar and Kanban. Before: the note's tasks disappear. After: they remain with Conflict pending. Resolve the note and confirm the tasks match the chosen content and the label clears.
-
Identical files stop waiting for a decision. On the next sync, ZenNotes checks the current local file against the latest tracked Cloud version. When the same file at the same path has identical content and no separate draft to preserve, the conflict clears, the review closes if nothing else is waiting, and later edits sync normally. It does not rewrite either copy or choose one device over the other. Real differences, path/rename/deletion conflicts and unfinished combined-note drafts still need review.
How to test locally: create a conflict and leave Review now open. Copy the complete Cloud version into the local file, then sync. Before: identical versions still block the file. After: the review closes and the next edit uploads. Repeat after typing a distinct combined-note draft: the review and saved draft must remain. Open the same vault in a second desktop window and sync there; the draft stays intact, and only one window can edit that conflict at a time.
🧰 For contributors
- The drawing fix is commit
29c5287onv2.47.0(#755 closed 2026-09-09). Verified in the built desktop app over CDP: with seeded drawings, switching alone corrupted a file in 4 of 6 runs before the fix and 0 of 6 after; with the reporter's flow driven by real mouse drags (draw, switch inside the save delay, draw, switch, then rapid switches), 3 of 3 runs corrupted before and 0 of 3 after. The unit tests inExcalidrawView.test.tsmodel the batched read and the late canvas report; both fail on the previous view. The app's bridge object is frozen in the renderer, so the harness watched the files on disk and hooked the renderer's timers instead of spying onwriteNote. - The wikilink-file fix is commit
bee2284onv2.47.0(#757 closed 2026-09-09). Verified in the built desktop app over CDP against the previous build: the Connections row forassets/diagram.pngwent from a CREATE row suggesting/assets/diagram.png.mdto a FILE row,jthen Enter on it opened the asset tab, andgdinside[[assets/diagram.png]]went from the create prompt to the image tab. Unit tests cover the outgoing-link classifier (connections-outgoing.test.ts) and the follow-link path. Harness note: a real CDP mouse click on a panel row fires no handler, before and after the fix, so the check drives rows through the keyboard. - Build with
npm run build --workspace @zennotes/desktop. Create a scratch root withmktemp -d /tmp/zennotes-247.XXXXXX, then launchZEN_PERF=1 ZENNOTES_USER_DATA_PATH=<scratch>/userdata ZENNOTES_CONFIG_DIR=<scratch>/config ZENNOTES_CLOUD_BASE_URL=http://127.0.0.1:43183 apps/desktop/node_modules/.bin/electron apps/desktop/out/main/index.js --remote-debugging-port=9326. Use a scratch vault and local test account only. The existingnode tooling/scripts/cloud-conflict-demo-fixture.mjssupplies the local Cloud server. - Draft saves are flushed before sync takes the vault lock. Same-vault desktop windows acknowledge preparation and share the result; failed saves or a window opening/closing mid-preparation pause that run safely. Retry after the window loads.
- Verified in the built desktop UI against the local fixture: task visibility, live and persisted conflict convergence, subsequent upload, draft retention, restart and two-window review ownership.
- Release gates run fresh at the cut:
apps/desktopbuild:prod(typecheck, tests, build, packaged CLI isolation), Go vet and tests, the app-core suite (1,980 tests), the signed packaged app launched in an isolated profile and reached a CDP page target in about 3 seconds, and the website suite (691 tests) with the release page entry in place. - The high-severity audit findings for
js-yamlandsvgowere cleared onmainby #753 and merged into this branch before the cut. One moderate finding (hono) remains and predates this release.
Local-first and keyboard-first, as always.