Repository navigation
Conversation
The reading view renders display math only through typst, and passes the
block to typst verbatim, so real LaTeX such as \frac{a}{b} falls back to
source. This adds the settings the LaTeX engine builds on: math picks
typst (the default) or latex, render_timeout bounds one external render
in seconds (default 10), and render_concurrency caps how many run at
once (default 2).
A bad value never stops zn from starting: an unknown engine reads as
typst and a non-positive number as the default. zn doctor reports it,
and under math = "latex" it also names a missing pdftex or pdftocairo.
Saving a setting stays strict, the way images already is. The starter
config.toml carries the keys with a comment naming the tools, and the
settings screen offers typst or latex.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With [terminal] math = "latex", display math renders through the user's own pdftex instead of typst, so full LaTeX works: amsmath environments, mhchem and TikZ. $$ blocks, \[ \] (one or many lines) and top-level equation, align, gather, multline, alignat, flalign, eqnarray (starred too) and tikzpicture blocks are detected; inside code fences, $$, lists, quotes and callouts they stay text, and under typst nothing changes. Each block is a standalone document over preamble.tex in the config dir. zn writes the default (amsmath, amssymb, mhchem, tikz with arrows.meta and calc) on the first render and never rewrites it. The preamble is dumped once into a pdftex format named by its hash, so a block only pays for itself; a changed preamble builds a new format. pdftex runs with -no-shell-escape and openin_any=p/openout_any=p, so a note cannot run commands or write outside the work dir. pdftocairo turns the PDF into a transparent PNG, cropped to the formula so its size does not shrink with the pane, coloured with the theme's text colour, and cached on disk under zn/tex. Every external render (mmdc, typst, pdftex, pdftocairo) now runs through one runner that kills it after render_timeout, and through one pool of render_concurrency slots. A TeX error shows its first line above the source and is remembered; a timeout is not, so the block renders again when its note is opened, without looping on every redraw. The editor highlights math bodies with chroma's tex or typst lexer to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor already closes $$ and code fences on Enter. Under latex,
\[ now gets \] and \begin{name} gets \end{name} for any environment
name, with the cursor on the empty line between, and no closer is added
when one already follows. Under typst these lines get a plain Enter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The note-writing instructions tell an AI to emit ```tikz fences, which
the latex engine does not render; it renders a top-level tikzpicture
and loads libraries from preamble.tex. Under math = "latex" the server
now serves a copy of the instructions with the TikZ guidance rewritten
for that: a bare \begin{tikzpicture} block, no \usetikzlibrary line.
The copy comes from six exact replacements, and a test fails if any of
them stops matching, so the typst text stays the single source. The
typst text and a user's override file are unchanged, and the setting is
read once when the server starts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README's reading-view section and the in-app help describe the math engine choice, render_timeout and render_concurrency, where preamble.tex lives and what it loads. They also say what latex needs: pdftex from a basic TeX Live plus the packages zn uses, and pdftocairo from poppler. They warn that TeX can read any file by absolute path, so a note can show a readable file in the preview, and advise leaving latex off for untrusted vaults. The Homebrew formula gains caveats with the same install steps and no new dependency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…latex" (by @spirit-of-light) The reading view could only send $$ blocks to typst, verbatim, so the LaTeX that the desktop's KaTeX reads (\frac, align, mhchem) fell back to source in the terminal. This adds [terminal] math = "latex": $$, \[ \] and the top-level amsmath and tikzpicture environments compile through the user's own pdftex over a preamble.tex next to config.toml, precompiled into a format named by its hash, and pdftocairo turns each block into a transparent PNG in the theme's text colour, cropped to the ink and cached under the user cache dir. typst stays the default and nothing changes under it in the preview. Every external render (mmdc, typst, pdftex, pdftocairo) now goes through one runner bounded by render_timeout and one pool of render_concurrency slots, so a runaway TeX loop or a hung mermaid-cli is killed instead of pinning a goroutine. A TeX error shows its first line above the source; a timeout is not remembered, so the block tries again when its note is opened. pdftex runs with -no-shell-escape and openout_any=p, and the README says plainly that TeX can still read files by absolute path. The contributor's suite passes, including the real-TeX tests (CI skips them) against a basic TeX Live with exactly the README's tlmgr list. Review findings land in the commits that follow this merge.
…clipped
standalone's varwidth page is \linewidth wide, about 12 cm, and pdftex
lets a longer display or a wider picture run past its right edge, where
pdftocairo cuts it off. A 26-term sum lost its last half and a 20 cm
TikZ figure stopped at 12 cm, with nothing in the preview to say so.
TeX already reports the overflow ("Overfull \hbox (395pt too wide)"), so
a block whose log says so renders once more with the page widened by
that much through \standaloneconfig. A block that fits renders once, as
before, at the usual width: widening every page instead would stretch
multline and push equation numbers out to the far edge, and the crop
would then keep a wide, mostly empty picture.
The cache key's version token moves on so the clipped PNGs already on
disk are not read again.
…ktop's instructions
Under math = "latex" the MCP server served a rewritten copy of the
note-writing instructions that asked agents for a bare tikzpicture block
instead of a ```tikz fence, because only the bare block rendered in the
zn preview. But the vault is the desktop's too: the desktop, the phones
and the share viewer render ```tikz fences and show a bare tikzpicture
as plain text, so every figure an agent wrote through zn mcp would draw
in one terminal and nowhere else. The instructions are the desktop's
text again, byte for byte, under either engine.
The preview meets them instead: under latex a ```tikz fence now renders
through pdftex, normalised the way the desktop's TikZ renderer does it
(wrapSource in apps/desktop/src/main/tikz.ts): a \documentclass line
goes, and \usepackage or \usetikzlibrary lines above the picture stay in
the preamble, ahead of \begin{document}. The default preamble.tex loads
the TikZ libraries the desktop loads for every fence, so a fence written
there (right=of a, -Stealth) draws here without lines of its own. Until
the picture is ready, and when TeX fails, the fence shows as code, with
the error above it. Under typst a fence stays code, as before.
Bare tikzpicture blocks still render under latex. The real-TeX test now
renders the instructions' own ```tikz example as a fence.
… too The merge taught the editor to colour display math, picking chroma's lexer by engine: tex under latex, typst under typst. typst is the default, and the $$ blocks in a ZenNotes vault hold LaTeX, because the desktop renders them with KaTeX. chroma's typst lexer reads every backslash in \frac or \sqrt as an error token, which the theme paints red, so every existing note's math turned red in the editor for anyone who never touched the setting. $$ bodies are now lexed as tex under both engines; \[ \] and the display environments are still lexed only under latex, where they are math. A ```tikz fence, which chroma has no lexer for and so left plain, is lexed as tex as well.
Everything latex renders stays under <UserCacheDir>/zn/tex: one format per preamble.tex (3.5 MB with the default packages, so every edit of the preamble leaves another behind) and one PNG per block per theme colour, including every intermediate version of a formula typed with the preview open. Nothing was ever deleted, and the README said so. A session now touches the format it renders with and every PNG it reads back, and the first render of a session deletes the formats and PNGs untouched for 30 days, plus the temp files of renders that died midway. Age rather than "not the current preamble" decides, because a second zn with its own config dir, and so its own preamble, shares the cache and keeps its format by using it. A file that cannot be deleted, open in another zn on Windows, waits for the next session.
…ec item it met The latex work arrived with comments and test messages keyed to an acceptance-criteria list (AC7, AC29, "Step 5", "rev 7") that never entered the repository, so a reader met references they could not follow. They now state the constraint itself, and "the AC7 environments" reads "the display environments". One comment was also wrong rather than only unreadable: texEnv claimed pdftex could not read outside its working dir. openin_any=p does not stop it; TeX Live 2026 reads any file by absolute or ../ path anyway, which is what the README warns about and what the real-TeX sandbox test asserts. The comment now says so, and that writes are what stay inside.
Merged
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.
Ships #17 by @spirit-of-light:
[terminal] math = "latex"renders display math through the user's own pdftex, so the LaTeX the desktop's KaTeX reads (\frac,align,mhchem, TikZ) shows as pictures in the terminal preview instead of falling back to source. typst stays the default.The contributor's five commits land unchanged in a merge commit. Review findings follow it as separate commits:
tikzpictureblocks, which the desktop, the phones and the share viewer show as plain text. The instructions are the desktop's text again under either engine, and the preview meets them instead:```tikzfences render under latex, normalised the way the desktop's TikZ renderer does it, and the defaultpreamble.texloads the desktop's TikZ libraries.Verified:
go vet ./...,go test ./..., cross-builds for darwin, linux and windows, the Homebrew formula tests, and the real-TeX suite (CI skips it) against a basic TeX Live 2026 with exactly the README'stlmgrlist. Each fix's real-TeX test fails before it (a long formula and a 20 cm figure both came out 1256 px wide). Driven in tmux on a scratch vault: $$,\[ \], align, mhchem, a bare tikzpicture, a desktop-style```tikzfence withright=of, a long formula and a broken fence (error row above the code), plus the editor's colours under typst before and after. Write escapes (absolute,../,~,$HOME, quoted) are all refused; reads by absolute path are not, as the README warns.