ZenNotes 2.50.4: asset links that follow renames and moves, live modified dates, and two editor fixes
Released on September 15, 2026 as v2.50.4, at 850cf5f8. The release branch v2.50.4 was fast-forwarded into main through PR #787. All four reports came from @uNyanda; each is closed with a commit-linked comment. Channel and validation details are in the sections at the end.
✨ Features
- Asset links follow renames and moves (#785). Renaming or moving an asset (sidebar menu, drag onto a folder, Assets view, or a rendered image's menu, on local vaults and through the Go server on remote ones) rewrites every note that referenced it, the way renaming a note already updated its wikilinks. References are resolved the way the renderer resolves them (relative to the note, then the vault root, then a unique file name) and re-targeted in the style each was written: a note-relative
../assets/x.pngstays relative, a vault-rootassets/x.pngbecomesmedia/x.png, a barex.pngstays bare while it still names one file and otherwise becomes the full path, and a wikilink never gains... Aliases,|300size hints,#page=2fragments, percent-encoding, angle brackets, and link titles survive; anything inside code stays literal. Only notes that referenced the file are rewritten, and open buffers are flushed first so the rewrite cannot race a pending save. - Live last-modified template variables (#784).
{{modified_date}},{{modified_time}}and{{modified_datetime}}(with the same:FORMATtokens as{{date:FORMAT}}) stay in the note instead of being substituted when it is created. The editor's live preview renders them as the file's last-saved time, revealing the raw token under the caret and leaving code literal; the reading view renders the same values. AnUpdated: {{modified_date}}line in frontmatter keeps itself current. The template editor offers the three after{{. The raw file keeps the token, so other tools show the braces.
🐛 Fixes
- Forwarded tasks showed once per note on Kanban (#786). A task forwarded across notes leaves a
[>]record in each one. The Status board and custom field boards already left those out, but the Priority and Folder boards only skipped done cards, so the same task appeared once per hop, and cancelled[-]tasks leaked onto those boards the same way. Every board now shows open work only. The[>]trail is a closed state by design, like[x]; the copy in the destination note is the one card. The Tasks list keeps its Forwarded group. - Wikilink picker stuck open after
[[inside inline code (#783). With Auto-close Markdown on, typing[[inside backticks and deleting the auto-inserted]]left an open[[in the code span, and every keystroke after the closing backtick re-opened the note picker. The[[,[[Note#and[[Note^pickers now stand down when the brackets sit in inline code or a code block. - Reading view: right-click on an image opens the asset menu. PDF, audio and video embeds and attachment chips had the menu (Open, Rename…, Move…, Duplicate, Copy as Embed, Copy Path, Open as Reference, Reveal in File Manager, Delete Asset…) since 1.1.0; image figures were never tagged for it. They are now.
🧰 For contributors
- #783: ea12d3cb. Closure comment.
- #786: e167302d. Closure comment.
- #785: 99f42040. The renderer's asset resolver moved to
packages/shared-domain/src/asset-path-resolution.ts(re-exported from app-core) and now reports the reading that resolved a reference; the rewriter ispackages/shared-domain/src/asset-link-rename.tswith a Go port inapps/server/internal/vault/asset_link_rename.go. DesktoprenameAsset/moveAssetand GoRenameAsset/MoveAssetsnapshot assets and notes before the file operation; the store gainedrenameAsset/moveAssetactions used by all callers. Closure comment. - #784: 6b565e40.
packages/app-core/src/lib/live-template-tokens.ts(pure) andcm-live-template-tokens.ts(editor plugin, part of the live-preview bundle);Preview.tsxsubstitutes before rendering. Closure comment. - Reading-view image menu: e73e4a4b.
- Version bump: 850cf5f8.
In-app help (help.ts) documents the three live tokens and the rename/move rewriting; the website docs mirror both (docs.blade.php template-variable table and prose, docs-sections.json :template entry).
Demos
All four clips are 1920 × 1080 at 30 fps with burned-in captions and a .vtt sidecar, recorded from the built app over CDP on a clean demo vault.
- 786-kanban-forwarded-once.mp4: 21 s. A task forwarded Monday → Tuesday → Wednesday, then the Status, Priority and Folder boards each showing it once.
- 783-wikilink-picker-code.mp4: 18 s. Typing
[[inside backticks, deleting the auto-closed]], writing on with the picker closed, then a real[[in prose opening it. - 784-live-modified-date.mp4: 20 s. The Evergreen note's
Updated:line and body tokens rendered, the raw token under the caret, an edit moving the time, and the reading view. - 785-asset-rename-move.mp4: 30 s. Rename from the Assets view, the note following, the new image menu in the reading view, a move to
media/lisbon/, and the daily note's relative path still resolving.
Verification
- Unit: shared-domain 15 rewriter tests (rename and move styles, collisions, encoding, code), app-core tests for the pickers, Kanban boards, live tokens (pure and editor plugin) and image-figure tagging, desktop
vault.test.tsend-to-end rename and move, Go pure and end-to-end rename and move tests.apps/desktop && npm run build:prodpassed (typecheck node + web, 768 tests, build, packaged CLI isolation). Govetandinternal/...tests pass. - Live, in the built desktop app over CDP: picker closed after a code-span
[[and open for a prose[[; one card per board for the forwarded chain; live tokens rendered in editor and reading view; rename from the Assets view and from the image menu rewrote both referencing notes on disk, refreshed the open buffer and re-rendered the images; a move from the sidebar menu produced a relative href, a bare wikilink and a re-rooted wikilink, with the daily note's relative path still resolving. - Not exercised live: the Go server path (covered by its tests only) and drag-onto-folder moves (same store action as the sidebar menu).
Distribution channels
- Release workflow: 35036589163. Linux x64 and arm64 finished about 13 and 15 minutes after the tag with all five installer formats each plus both updater manifests; Windows (installer, portable ZIP, blockmap, manifest) at about 40 minutes; macOS (Apple silicon and Intel DMGs, ZIPs, blockmaps, manifest) at about 50 minutes, with Developer ID signing and notarization. 29 assets in total, including the four demo clips.
- Docker:
adibhanna/zennotes:2.50.4published by workflow 35036589166 (amd64 and arm64). - AUR:
64853a3in the separatezennotes-binclone (zennotes-bin 2.50.4-1), mirrored on desktopmainasec8c0c60. Downloaded tarball SHA-256428bdec0f436f4671c34e04a73d38a0f9345fb7119924e7938add50094703527matched GitHub's asset digest. The Arch build check (aur-check.yml) passed on the branch and onmain; the AUR RPC served 2.50.4-1 within minutes. - Nix:
nix-update.ymlrun 35037488414 computed the hashes and opened bot PR #788, lifted exactly ontomainasb28f1eb8and closed.nix-build.yml35037780379 then failed with the intermittentfetchFromGitHubsource-hash disagreement (specifiede6Yww…, gotE3Kpar…); the fetched value was pinned in6c3d95ecandnix-build.yml35037936143 passed. - nixpkgs: PR #561418 restacked onto current master as
zennotes-desktop: 2.45.0 -> 2.50.4(fork commit82bd8c0f, body innixpkgs-pr-body.md). - Homebrew:
ae257ffin the tap (zennotes 2.50.4), mirrored on desktopmainas9a2e5e1a. The cask pins both DMG digests from the GitHub API (arm640a56f41c65aeb1adc06751f277053b88d30e638aca70842493561b7c09921534). - Website: docs mirror and the 2.50.4 releases entry with all four clips pushed as
76a2e20on websitemain; the full suite passed locally (791 tests, 6,236 assertions) and in website CI (tests and linter). The live release page lists 2.50.4 and serves the clips (HTTP 200). npm run verify:channels -- 2.50.4passed: Homebrew tap, AUR, Nix, and all seven website download redirects serve 2.50.4.
Release validation
Release PR #787's CI passed on macOS, Ubuntu, Ubuntu arm64 and Windows, plus CodeQL and the production dependency audit, before the fast-forward. The release workflow passed every platform job. aur-check.yml built the AUR package in a real Arch container on the branch and on main; nix-build.yml 35037936143 rebuilt the desktop and server Nix packages from the pinned hashes on main.
Local gates before the tag: apps/desktop && npm run build:prod (typecheck node + web, 768 tests, build, packaged CLI isolation) and Go vet + internal/... tests. The mac app packaged from a clean npm ci of the release branch (npm run pack) reported 2.50.4 and exposed a CDP page target 0.4 s after launch with both stores isolated and ZEN_PERF=1. That local package was unsigned; release CI signed and notarized both architectures.
The release was cut from a Claude worktree beside the main checkout. Two environment lessons are recorded in the project memory: npm version inside a worktree with symlinked node_modules reifies through the links into the main checkout (repaired by hand this time), and ffmpeg 9 rejects -r together with variable frame timing.