Repository navigation
Conversation
ZenNotes drew ```mermaid fences with mermaid.js. It worked, but it was the heaviest stack in the renderer (mermaid.core alone is 140 kB gzipped before any diagram chunk, about 800 kB across all of its chunks), it laid diagrams out asynchronously through temporary DOM, and its labels were HTML inside foreignObject, the layer behind every clipped-label bug here (#529, and the getBoundingClientRect patch #911 needed). Theming was a 160-line themeVariables map, rebuilt and re-rendered on every theme change. Pele (pele.run, obsidianmd/pele, MIT) reads the same Mermaid syntax with its own parsers and lays diagrams out itself, returning one SVG string synchronously. Before switching, every example in Mermaid 11.17's docs that mermaid.js accepts (476, across all 32 doc pages) rendered in Pele, as did every fence in the repo and the demo tour; only ZenUML, which ZenNotes never registered, is unsupported. In Chromium those 476 diagrams take 83 ms instead of 4.2 s. What changes: - lib/mermaid-render.ts loads Pele through a new pele-runtime module, so the async chunk has a name the perf harness and the packaged-web browser test can hold off the boot path, and keeps the shared (fonts, source) cache. Once the runtime and a diagram type are loaded, peekMermaidSvg draws a new diagram on the spot, so typing one in live preview no longer shows a pending block. Fonts are the only theme input: labels are measured on a canvas in the note's text font, after any loading font settles. - The cache trims the source first. Preview keeps a fence's trailing newline and the editor trims its body, so split view used to draw every diagram twice under two keys, the double work the shared cache exists to remove. - Colour moved to CSS. A .pele rule in index.css maps --pele-* onto the --z-* tokens with the roles the themeVariables had (nodes on bg-2 with a bg-3 border, subgraphs on bg-softer, edges grey-1, series in the old pie and git order). One drawing is right in every theme, custom CSS themes included, and a theme switch repaints without a relayout. - The #911 getBoundingClientRect patch and its test are gone: labels are SVG text and wrap inside their box. - Images and links in diagrams stay off, as mermaid's strict mode had them, so drawing a diagram never fetches anything. - Preview shows a render error as text in .zen-diagram-error. The old branch wrote the message into innerHTML, and the message quotes the diagram source, so a crafted fence could inject markup. - pele 1.6.0 replaces mermaid in the five workspaces that declared it, pinned exactly while Pele moves fast. mermaid stays installed for Excalidraw's Mermaid-to-Excalidraw dialog, which still lazy-loads it; the chunk comments, preload deferral lists and perf patterns name both. What it deliberately does not do: no maxWidth is passed, so Pele never flips a wide left-to-right flowchart to top-down; the author's direction stands and the existing pan/zoom handles width. Pele's own zoom is not used. What users will notice: layouts are Pele's (node placement differs from mermaid.live, mindmaps draw as an outline tree), $$ math inside a label and raw HTML other than <br> and bold or italic show as written, and %%{init}%% theme settings give way to the app theme. The in-app manual, the website docs and the MCP instructions say so.
On the board grouped by Custom status, a task note with `status: done` vanished, even with `done` listed in kanban_statuses. fieldColumns skipped every task isBoardCard rejects before it read the field, and a task note's checked state comes from that same `status:`: the value that names its column was the value that took it off the board. Grouped by Status, the note sat in Done, so switching boards made it disappear and come back. A task note whose value is one of the configured columns now stays in that column, checked. Only configured values count: a discovered column would turn every finished task note in the vault into a column of its own. A configured `cancelled` column holds cancelled task notes the same way. Checkbox tasks keep the live-work rule on purpose. Their box and their `@status:` are separate, and a status they inherit from the note's frontmatter would pull every checked subtask of an in-progress note into In progress. The manual's Kanban card says so. Four tests, two of which fail without the change. Reported by @mateusmatiazzi.
Closing the last window ended the session on Linux and Windows, and on macOS it destroyed the window, which is where Cloud sync runs. Either way a vault stopped syncing until ZenNotes was opened again, and on a tiling desktop, where minimise has nowhere to go, closing was the only way to get the window off screen. Settings > Appearance > Window > Keep running in the tray (off by default) now hides the last workspace window instead. A tray icon (a template ensō in the macOS menu bar, the app tile elsewhere) brings it back on click; its menu opens Quick capture, shows a linked vault's sync state, and quits for real. Space b and Hide Window to Tray hide the window on purpose; Quit ZenNotes in the palette quits on desktops with no app menu or no tray host, and opening ZenNotes again, or the macOS Dock icon, brings held windows back, including when the relaunch carries a path the opener ignores. Quitting always wins over the hide: before-quit, the tray's Quit, app:quit, Squirrel.Mac's before-quit-for-update and Windows' query-session-end all mark the app as quitting, or an update could never install and a logoff would hang. The setting lives in zennotes.config.json, not config.toml: main needs it before any window exists, and whether a tray exists depends on the machine. The update check, which ran once at launch, now repeats every six hours from a settled state, so a copy kept in the tray for days still hears about a release; notices stay one per version. Not in this change: an icon that changes with the sync state, an Install update item in the tray, and an update restart that stays in the tray. Thanks to @uNyanda for the proposal.
With two windows open, hiding one with Space b and then turning Keep running in the tray off from the other left the first one hidden: applyKeepRunningInTray(false) brought held windows back only when no window at all was visible. The icon was gone by then, so nothing could reach the hidden window, and closing the other one left ZenNotes running with no window and no icon until it was launched again. Turning the setting off now shows every window the tray holds, whatever else is open. Found on Linux by the beelink tray check.
A ```d2 fence now draws as a D2 diagram (d2lang.com), the text-to-diagram language built for architecture: nested containers, labelled connections, sql_table data models, sequence diagrams and sketch mode. Preview gives it zoom and Expand, live preview draws it in the editor until the cursor enters it, and a broken diagram shows D2's message with its line and column, as text. D2 runs as WebAssembly from @terrastruct/d2 0.1.33 (MPL-2.0, pinned exactly in the five workspaces that declare pele). The browser build is one 8 MB module, so d2-runtime.ts is its only import, reached through import() in d2-render.ts: the chunk is named d2-runtime, both Vite configs keep it out of the boot preloads, and the perf harnesses and the packaged-web browser test watch it. A note without a d2 fence never loads it. Three things in d2-render are not optional. The wrapper keeps one pending callback per worker, so a second call made before the first settles strands the first; every call goes through a queue. D2 passes a Markdown label's HTML straight into the SVG (an <img onerror> in a label arrived intact), draws icon: as an <image> that fetches its URL and wraps link: in an <a>; every SVG goes through a DOMPurify instance of its own with images, links, href/src and handlers removed, and with foreignObject as an HTML integration point, without which Chromium empties every label (jsdom keeps them, so only the built app showed it). And Preview and the hidden editor showed the same cached string, so every arrowhead's url(#mk-...) resolved into the hidden copy; withUniqueD2Ids renames the ids per insertion. Colour comes from the theme: a .zen-d2-themed block maps D2's fill-, stroke-, color- and background-color- classes (N1 to N7, B1 to B6, AA, AB) onto the --z-* tokens in the roles Pele uses and makes D2's canvas transparent, so a theme switch repaints without a redraw. A colour set with style is an attribute and stays as written; a diagram that sets theme-id or theme-overrides keeps D2's theme. The link and tooltip badge is hidden, since neither works here. Live preview: cm-mermaid-render now draws mermaid and d2 fences through one widget, so the reveal-on-cursor, last-good-drawing and j/k entry rules are shared. The built-in tour gains 05c D2 Diagrams, linked from 00, 05, 05b and 07; the manual and the MCP instructions describe D2. Not in this change: the self-hosted server's copy of the tour (znserver internal/vault/demo-tour.json) and zn mcp's copy of the instructions in ZenNotes/tui, which had drifted before this.
…953) Preview stamped a source line only on top-level blocks, and a list is one top-level block. In a note that is mostly one long list, double-clicking any item opened the editor on the list's first line, and leaving Preview with the cursor off screen (the Edit button, Mod+4) did the same, often a screen or more above what the reader was looking at. Every list item, nested ones included, now carries its own line. The double-click reader already took the closest stamp, which is now the item. The visible-range reader walks stamps in document order and lets the innermost one still covering the top edge win, so a long list reads as the item at the top of the view instead of as its start. Split scroll sync is untouched on purpose: it selects only the content root's direct children, so it still pairs block for block with the editor. Three tests; the reader's fails without the change. Reported by @Sullti.
The hanging indent and the guide lines are measured in ch, and the source spaces that indent a nested item render in the editor font. With a monospace font the two agree. With a proportional one a space is about half a ch, so a nested bullet sat left of its guide and of its parent's wrapped text, and each level drifted further. Each character of a list line's prefix (the indent, then a bullet or ordered marker and its spaces) is now an inline-block box at least as wide as the columns the hanging indent counts for it, a tab included, and the bullet drawn over a `-` takes one ch with it. A minimum width, so with a monospace font nothing moves: the editor is pixel-identical before and after. One box per character, not one for the run: the list fold arrow is a widget inside the indent, and a mark over the whole run split around it into pieces that each took the full width. The bullet widget is not wrapped by a mark at all, hence the line class that sizes it. Left alone: a list inside a quote or callout, where `>` renders its own way, and a task's marker. Live preview draws a checkbox narrower than the six columns a task's hanging indent counts, a gap that predates this and shows in every font. Five tests. Reported by @Sullti.
Windows asks every window before it signs out, restarts or shuts down, and query-session-end set appQuitting so a close we would turn into a hide could not hold the session up. Nothing ever cleared it: when the user cancelled the sign-out, or another app refused it, closing the window quit ZenNotes instead of hiding it for the rest of that run. The question now holds close-to-tray off for a 10 s grace and then lapses, since Electron sends no event for a cancelled session end. Only session-end, which comes when the session really ends, makes it final. appQuitting is left to the app's own quits (before-quit, the tray's Quit, app:quit, an update's restart), which stay permanent. Checked on Windows 11 with synthetic session messages. After a query with no end, a close 12 s later hides. A close inside the grace, or after session-end, still closes and the app exits. A real restart with the window hidden went straight through.
On Linux and Windows, closing the last window quits ZenNotes. It did not once Quick capture had been opened in that run. The panel hides instead of closing, so the hotkey's next press is instant, and Electron counts a hidden window as open: `window-all-closed` never fired, and every process kept running with nothing on screen, no taskbar entry and no tray icon. Only launching ZenNotes again brought a window back. It dates from Quick capture itself in 1.2.0, and it made turning the tray off (#947) look broken. A hidden panel no longer keeps the app alive. After any window closes, and whenever the panel hides, ZenNotes quits if that panel is all that is left. Unchanged: macOS, which keeps an app running with no window; the tray on, where the window hides and the app and its hotkey keep running; and every other window, hidden or not, which still keeps the app open the way Electron counts it, so a floating note or a standalone file can outlive the last workspace window. A panel pinned on screen holds the app until it hides, so nothing visible closes under the user. A `zennotes://quick-capture` launch, which opens the panel alone as the hotkey's stand-in on Wayland, stays resident between captures until a window it opened is closed. Eight tests for the decision. On Linux (X11 under Xvfb and openbox) with tarballs built before and after from one tree: with Quick capture opened and dismissed, closing the main window left six processes running after 45 s before, and exited within 6 s after. The tray-on, pinned-panel and deep-link launches behave as described.
The tour was written in the spring and had fallen behind the app. It said nothing about the Tasks calendar or boards, templates, drawings, databases, workflows, Cloud, the CLI or MCP, and some of its lines had stopped being true: hint mode on `f` (it is `Space h`), a dropped file landing at the vault root (it goes to `assets/`), archived notes' tasks still showing in Tasks, a broken diagram falling back to its source, "Open Trash" as a palette command. It now has 22 notes. Four are new: 16 Tasks Calendar and Kanban, 17 Templates, Drawings, and Databases, 18 Workflows, and 19 Cloud, CLI, and AI Agents. 00 to 15 were checked against the manual and the code and picked up what shipped since: task states, fields and subtask counts, editable tables, block links and embeds, image sizes, numbered equations, Mermaid with Pele, the leader keys added since spring, themes and overrides, and config.toml. Every palette name the tour quotes is the real title. 17 ships two files to try things on, inside the tour folder: a Tour Sketch drawing and a Reading List database. The database's data.csv and schema.json come from initialDatabaseContents, the code New Database runs, so opening it rewrites nothing. A database is a folder, and removing the tour has to take that folder down before inbox/demo can go: demoTourNestedDirs lists the tour's own folders deepest first, and the desktop's removeDemoTour empties them. The tour test now resolves a link to a shipped file by its name and a link to a database by its title. The server writes the same tour from its own copy, synced in ZenNotes/znserver. The phone apps write it from this file but remove it with their own code, which needs the same nested-folder step when they adopt this core.
What's New opens on the first launch after the update with four highlights. First, Mermaid diagrams drawn by Pele: faster, in the theme's colours, live in the editor, and repainted on a theme switch. Second, D2 diagrams from d2 fences, pointing at the tour note that has examples. Third, Keep running in the tray, with where the setting lives and what keeps working while the window is hidden. Fourth, the refreshed demo tour, with how to regenerate it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ZenNotes 2.71.0: desktop. The website release page and docs follow from the tag; the server, the zn CLI, the boundary packages and the phones are not part of this release.
New
23edf8ee):pele1.6.0 pinned, reached only throughpele-runtime.ts; colours from the--z-*tokens, one cache key per diagram, live in the editor, errors as text. mermaid.js stays only for Excalidraw's Mermaid dialog.c4a38631):@terrastruct/d20.1.33 behindd2-runtime.ts, a serialized queue, DOMPurify with foreignObject labels kept, per-insertion ids, the theme mapped onto D2's classes; Preview, live preview, the manual, the MCP instructions and the tour note 05c.6329c89a,e902b3a1,204ae7b0): an opt-in setting, the tray icon and menu,Space b, Quit ZenNotes, a six-hour update recheck; turning it off brings back every held window; a cancelled Windows sign-out lapses after 10 s.776112f8): 22 notes, four new, a sample drawing and database; removal empties the database folder.Fixes
ab15a36d).0e61c60a).f2512e32).dcaa821e).Also
603fc6dc); version bump (43bd8228).Gates on this head (
603fc6dc), in a clean checkout with its ownnpm ci: typecheck 8/8 uncached; app-core 3,266, desktop 1,175, quicklook 15 and the other suites green; fresh build:prod green;npm run packand the packaged launch check pass (page target, rendered, 1.4 s, 2.71.0); the packaged app draws the tour's Mermaid and D2 notes and shows the 2.71.0 What's New; perf:desktop-runtime on 5,000 notes is within budget with no diagram chunk loaded; vim-editor, sidebar-vim and editor-improvements pass.ci-sweep.py v2.70.0: no failed jobs.hazard-scan.py --since v2.70.0: no errors; the two manualChunks warnings are comment and deferred-preload changes, and the boot chunk imports no diagram stack. dash-lint clean on the notes, commits and diff.