github ZenNotes/zennotes v2.78.0
ZenNotes v2.78.0

latest releases: core-2.78.0-core.h0bc32cbd73a78fac, viewer-2.78.0-viewer.haeee4ee85b34a271, web-2.78.0-web.h769e1b278b8e65b3...
3 hours ago

ZenNotes 2.78.0 gives notes a face and diagrams their math. A note can now carry a cover, a wide image across its top the way Notion draws one, picked from the keyboard and stored in the same frontmatter Obsidian's banner plugins read. Mermaid diagrams typeset $$...$$ math in their labels and keep labels readable on any fill, in every theme. The phones get a Calendar widget for the Home Screen and PDFs that open in the phone's own viewer.

Released from 52ab86fd through #1000 on 2026-10-27. All installers are verified.

Features

  • Note covers. A note can carry a cover: a wide image across its top that scrolls away with the text. Space l c in Vim mode, Add Cover in the command palette, or :cover opens the cover picker: one search field over eight gradients, six public-domain photographs (Hokusai, Van Gogh, Bierstadt and Cole from The Met's open collection, Earthrise and the Blue Marble from NASA), the images already in your vault, and any https image link you paste. Upload image takes a file from disk, and an image pasted or dropped on the picker becomes the cover. Hover the cover for Change cover, Reposition and Remove; Reposition follows a drag or the arrow keys (j/k in Vim mode), Enter keeps the new position and Escape puts the old one back. Every edit is a normal edit, so u and Cmd+Z take it back.
  • Covers are plain frontmatter. A cover is saved as banner: "![[assets/lisbon.jpg]]" with an optional banner_y focal point, the keys Obsidian's banner plugins read, so a vault shared with Obsidian shows the same banner; a note that uses cover: shows its image too. Everything a pick brings lands in your vault's assets folder as an ordinary file, so covers work in a local vault, on a self-hosted server, in the web app and over Cloud sync, end-to-end encrypted vaults included, and the Assets view counts a cover image as used. Settings, Typography, Note covers chooses Scroll with note (the default), Pinned above the editor and Read view, or Hidden (note_cover_placement under [appearance] in config.toml).
  • Covers on published notes and in Quick Look. A note you publish shows its cover on its public page, and the macOS Quick Look preview (Space in Finder) draws it too, at the note's focal point.
  • Math in Mermaid labels. A label like A["$$\frac{a}{b}$$"] is now typeset with KaTeX, as on mermaid.live, GitHub and Obsidian, instead of showing its TeX. It works in Preview, the editor's live preview, the expanded diagram view, Quick Look, published notes and the phones, in flowcharts, sequence, state, class and ER diagrams and mindmaps. Each formula is measured before the label is laid out, so it sits inside its box.
  • Mermaid labels readable on any fill. Diagrams now draw with Pele 1.12.0. A label on a fill you chose (classDef, style, a sequence rect or box) picks near-black or white text, whichever reads on that fill, where a dark node used to be unreadable in a light theme and a light one in a dark theme. Labels format <u>, <sub>, <sup>, <code> and <kbd>; text that only looks like a tag stays (List<T> used to come out as List); Chinese and Japanese labels wrap instead of stretching the diagram; and C4, sequence self-messages, timelines, sankey, treemap and block diagrams lay out better.
  • A Calendar widget for the phones (asked for by Dave). The Calendar sheet on your Home Screen: the month with its writing dots, open-task marks, week numbers and today ring, and the selected day's tasks below with a box to tick on each. On Android it resizes up to the whole screen and the task list scrolls; on iPhone the large size lists the day and iOS 27's extra-large size lists the week, paging with arrows. A tick shows at once; on Android it is written into the note even with the app closed, and on iPhone it lands the next time the app opens. "Add a task for ..." opens the Calendar on that day with the keyboard up. Ships in the next phone releases, built on this core.
  • The bundled zn is 0.12.2. With [terminal] math = "latex", zn draws display math and TikZ in the terminal preview through the TeX installed on your computer, says plainly when TeX is missing (zn doctor prints the install commands), and fits preview pictures to the pane as it resizes.

Fixes

  • PDFs on the phones (thanks to Dave). On Android, "Open as reference" and Preview on an embedded PDF opened an empty sheet: Android's WebView has no PDF viewer. A PDF now opens in the phone's own viewer, the PDF card no longer offers a Preview that cannot draw, and the card keeps its file name instead of squeezing it to nothing and spilling its badge under the Preview button at phone width. Done and Back close the reference sheet again and leave you on your note. iPhone keeps drawing PDFs in the app, as before.
  • Hide Reference Pane for a PDF pinned to one note. With a PDF pinned to the note it sits in, the command palette offered neither Hide Reference Pane nor Focus Reference Pane. Both now work for it, on the desktop as on the phones.
  • Ticking tasks in notes saved on Windows. In a note with Windows line endings, ticking, moving or editing a task from the task list did nothing, and in a note mixing line endings it acted on the wrong task. It now acts on the right task and keeps the note's line endings.
  • The Themes picker keeps your theme while you type. Typing a filter in the command palette's Themes picker could switch the app to a theme you never highlighted (from Gruvbox Hard dark, typing l jumped to Nord Light) and keep it. A theme now previews only when you move to it, and Escape still puts yours back. The picker also finds a family named with its mode or flavor, such as "apple light" or "gruvbox dark hard", which used to list nothing.
  • The phones' drawer lists periodic folders once (thanks to uNyanda). The Daily, Weekly and Monthly Notes folders showed twice in the phone drawer, as shortcut rows and again under Notes; the Notes list now leaves out the folders those rows already open, as the desktop sidebar always has. Ships in the next phone releases.

For contributors

  • Covers (ea74766b, fec49657): shared-domain/note-cover.ts reads and writes banner / banner_y (and cover:) through updateFrontmatterFields, so every other frontmatter line stays byte-identical; the value is an embed (![[...]]) so every host's asset index (desktop extractAssetEmbeds, the Go server's port, the phones' vault-core) counts it with no scanner changed. Picks import through importPastedImage. The editor draws the cover as a CodeMirror block widget (cm-note-cover.ts) that the pane portals the same NoteCover into as the Read view; ReadOnlyNoteCover serves the share viewer and Quick Look without the picker. Gradient specs live in shared-domain/note-cover-gradients.ts, so the share viewer redraws a built-in gradient instead of uploading its SVG; Quick Look resolves other cover forms over a new zenql://cover route. COMMAND_OVERLAY_FIELDS now names the picker and the reposition mode. New portable pref noteCoverPlacement.
  • Diagrams (7ae3afcf, fa668530): Pele 1.6.0 to 1.12.0 with no API change; lib/mermaid-label-math.ts is the math resolver passed through renderOptions in mermaid-render: KaTeX to MathML, measured in an off-screen probe and cached by size and source, reached only through pele-runtime, so nothing new loads before a diagram does.
  • Phones (16722caf, 4bacd562, b1fd339d, 3fc40107): canShowPdfInline decides between the PDF frame and PdfOpenElsewhere; openVaultAssetExternally takes { confirm: false }; activeReferencePath is the one reading of the pin the pane shows. lib/calendar-data.ts holds the Calendar panel's grid and marks; getCalendarWidgetData and setTaskChecked (@zennotes/app-core/calendar, /tasks) feed the widget and take its ticks back, each task carrying its line before and after a toggle, so no shell parses a task line.
  • Tasks (57513b7e): the index walks in shared-domain/tasklists.ts match a line without its \r and put the ending back on what they write. The desktop MCP and the Go copies already count CRLF notes correctly (the MCP normalizes first; Go's . matches \r).
  • Themes picker (52ab86fd): the preview effect remembers the mode and list it last saw and skips the commit in which either changed, so it never reads an active index meant for the previous list; rankThemes (lib/themes.ts) adds the row read as one phrase. Read-only covers (e6bf83f2): readOnlyCoverSource keeps http(s), zenql:// and data:image/ sources at the element, after CodeQL flagged the share page's cover img.
  • zn 0.12.2 (4bda75e8): apps/desktop/terminal-release.json is the release's signed manifest, copied byte for byte after verifying against zn-release-1.

Demos

Clips and screenshots of this release on the release build, attached to this release:

Note covers: Space l c picks the Dusk gradient, then NASA's Earthrise; j and k reposition it, u and Ctrl+R take the change back and forth. Watch the clip (40 seconds, captioned).

A note with the Earthrise cover and its banner frontmatter

Math in Mermaid labels: $$ labels typeset inside their boxes, and text on a dark and a yellow fill stays readable from Gruvbox dark to light. Watch the clip (22 seconds, captioned).

A flowchart with typeset math labels in a light theme

The Calendar widget on an Android Home Screen: a tick written into the note with the app closed. Watch the Android clip (24 seconds) or the iPhone clip, with iOS 27's extra-large week size (22 seconds). Both are in the phone releases built on this core.

The ZenNotes Calendar widget on an Android Home Screen

PDFs on Android open in the phone's own viewer, and Back returns to the note: watch the clip (20 seconds). The drawer lists Daily, Weekly and Monthly Notes once: watch the clip (25 seconds).

Verification

How to test locally. Build once with npm run build --workspace @zennotes/desktop and launch with isolated stores: ZENNOTES_USER_DATA_PATH=$(mktemp -d) ZENNOTES_CONFIG_DIR=$(mktemp -d) npx electron apps/desktop/out/main/index.js. Put these notes in the vault:

Trip.md            # Lisbon trip
                   Three days in October.

Diagram.md         ```mermaid
                   flowchart LR
                     A["$$\frac{a}{b}$$"] --> B["$$x^2$$"]
                     B --> C["List<T> with <sub>sub</sub>"]
                     classDef dark fill:#1f2937
                     class C dark
                   ```

Windows tasks.md   - [ ] first
(saved with CRLF)  - [ ] second
  1. Covers. Open Trip and press Space l c (Vim mode) or pick Add Cover from the command palette. Type dusk and press Enter: a gradient spans the top of the note and the frontmatter reads banner: "![[assets/cover-gradient-dusk.svg]]". Press u: the cover and the frontmatter are gone. Run :cover earthrise, hover the cover, pick Reposition, press k a few times and Enter: banner_y changes. Run :cover! to remove it. In Settings, Typography, Note covers, pick Hidden: the cover stops drawing and the frontmatter stays.
  2. Label math. Open Diagram in Preview: both labels are typeset fractions and powers, inside their boxes. Before: they showed $$\frac{a}{b}$$ as written.
  3. Labels on fills. In the same diagram, node C sits on a dark fill: in a light theme its text is white and reads; List<T> shows with its <T>, and sub is subscripted. Before: dark text on the dark node, and List alone.
  4. Windows line endings. Open the Tasks view and tick "second": the note now reads - [x] second and still has CRLF endings (file "Windows tasks.md" says so). Before: the tick did nothing.
  5. Reference pane. Put ![[assets/some.pdf]] in a note, use Open as reference on its card, then open the palette: Hide Reference Pane is offered and hides it. Before: neither Hide nor Focus was offered.
  6. zn. In a terminal, the zn that ships with this build reports zn v0.12.2 for zn --version.

On the phones (in the next phone releases): on Android, tap an embedded PDF's card and it opens in the phone's PDF viewer; Back returns to the note. Add the ZenNotes Calendar widget to the Home Screen, tick a task, and open the note: it is ticked.

Distribution channels

  • GitHub release: installers for macOS (Apple silicon and Intel DMG and zip), Windows (x64 and ARM64 installer) and Linux (x64 and arm64 AppImage, deb, rpm, pacman, tar.gz), with five update manifests; the clips and screenshots in Demos, desktop and phone, are attached too.
  • AUR: zennotes-bin 2.78.0-1 (AUR clone 5bd7333, repo 8b178691), pinned to ZenNotes-2.78.0-linux-x64.tar.gz SHA-256 efad197f…0afc, after the tarball check confirmed the bundled CLI folder is world-readable.
  • Nix: packaging/nix/release-data.json lifted from the update workflow (run 40582526633, bot PR #1002 closed) as 2161e6f1, and nix-build on main passed.
  • nixpkgs: NixOS/nixpkgs#579420 now proposes 2.74.0 -> 2.78.0, changing only the version and two hashes; the source hash comes from the tag tarball, and the same command reproduces master's 2.74.0 hash.
  • Homebrew: zennotes 2.78.0 in the tap (08a0f4a), mirrored in e8833509; both DMGs' SHA-256 match the cask and GitHub's digests; brew style clean.
  • zn CLI: the bundled zn is 0.12.2, the latest ZenNotes/tui release (4bda75e8).
  • Website: ZenNotes/website#94 merged with the 2.78.0 entry, two captioned clips (note covers, label math) and the Calendar widget screenshot, the docs for covers, label math, Pele 1.12 and zn 0.12.2, and the 2.78.0 share viewer. Laravel Cloud deployed it at b0ce3eaf, and the live check passed: /releases leads with 2.78.0, every clip matches the local file byte for byte, seeking works, /docs has the new sections, and the live viewer files match their manifest.
  • Server and Docker: ZenNotes/znserver v2.78.0 pins the browser app web-2.78.0-web.h769e1b278b8e65b3 (f4ed35e1, ZenNotes/znserver#47); all five binaries match SHA256SUMS, and the released darwin-arm64 binary served the 2.78.0 app on a scratch vault and answered 401 without its token. adibhanna/zennotes:2.78.0, 2.78 and latest are published from the tag (one digest, amd64 and arm64).
  • Boundary packages: core-2.78.0-core.h0bc32cbd73a78fac, web-2.78.0-web.h769e1b278b8e65b3 and viewer-2.78.0-viewer.haeee4ee85b34a271, built by CI from 52ab86fd and published as pre-releases (core once both phones were submitted on it). The viewer is new this time, since published notes now draw covers; the website keeps the 2.72.0 viewer for pages already open. "Latest" still names v2.78.0.
  • Phones: iPhone 1.34.0 (build 51, ZenNotes/zennotesios#79, on core 2.78.0) is Waiting for Review in App Store Connect, set to release automatically. Android 1.1.48 (versionCode 51, ZenNotes/zennotesandroid#136) is in Google Play review at 100% rollout with no devices lost. Both carry the Calendar widget, the PDF fixes and the drawer fix, plus everything in core 2.78.0.

Release validation

Every CI failure since 2.77.0 was read before this release: ci-sweep.py v2.77.0 found none (three bot-branch runs aside). hazard-scan.py --since v2.77.0 found no hazards and one warning: the Windows line-endings task fix changed the task walks in tasklists.ts. The other task-line copies were checked and need no change: the desktop MCP normalizes line endings before counting, the server does too and has no toggle, and zn's Go walks match a CRLF line because Go's . matches \r (checked with a probe on its main).

Release-time gates in the v2.78.0 worktree with its own node_modules: the production dependency audit (no high advisories), typecheck 8/8 and test:run with no turbo cache (shared-domain 2,164, app-core 3,593, desktop 1,198, quicklook 18), a fresh build, a Developer ID signed pack, and the packaged launch check (page target and drawn app in 1.6 seconds, version 2.78.0). The vim-editor, sidebar-vim and editor-improvements smoke suites passed on the first run.

Four problems were caught and fixed before this release shipped. While recording the diagram clip, typing a filter in the Themes picker applied a theme nobody had highlighted (52ab86fd, a bug older than this release; six runs of a CDP check pass, and one earlier run read the theme before it repainted on a machine at a load average of about 38). CodeQL flagged the read-only cover image as cross-site scripting through the DOM: an image source runs no script and the sources were already limited, so the alert was dismissed as a false positive after e6bf83f2 put an explicit allowlist at the element, with tests. On the phones, the iPhone CI build failed because Xcode 26 cannot name iOS 27's extra-large widget size (gated by compiler version in build 51, ZenNotes/zennotesios 5d14fe4), and Android lint failed on raw byte-order marks in the widget's tick writer (written as escapes in ZenNotes/zennotesandroid 9404133; the bundle rebuilt from that commit matched the uploaded one entry for entry, signature aside).

Release PR #1000: all 10 checks passed on 52ab86fd, CodeQL after the dismissal and a re-run. CI on main for 52ab86fd (run 40577585174): all six jobs green before the tag, and CodeQL too. Release run 40580480061: all six jobs green; verify-installers.py ALL GOOD by SHA-512 on the five manifests; the app in both DMGs is Notarized Developer ID with a valid stapled ticket. npm run verify:channels -- 2.78.0: every channel serves v2.78.0 (Homebrew, AUR, Nix and the website's eight download buttons).

The first core build failed its browser test on one unexpected 404: a background read of a note the test had just moved to the Trash, landing after the move on a slow runner (the same test passes locally on this commit and passed on 2.77.0's core build); the rebuild from the same commit passed. The first Nix update run failed on an npm registry 502 and passed on a re-run.

On the phones: Android typecheck, 350 tests, build and sync, and CI's build and emulator launch passed on core 2.78.0; iPhone boundaries, typecheck, 318 tests and sync, and CI's build passed. The Calendar widget was walked on an Android emulator and an iOS 27 simulator when it was built, and the PDF fix on an Android emulator.

Don't miss a new zennotes release

NewReleases is sending notifications on new releases.