A native macOS terminal that renders LaTeX live over the text — with a shared launcher for Claude Code and Codex sessions.
- Live LaTeX overlays — formulas between
$…$,$$…$$,\(…\),\[…\]render as KaTeX exactly on their source characters. No OCR: a vendored SwiftTerm fork exposes the real cell grid. - Hover, pin & export — hover shows a formula full-size; a click pins it with copy buttons: LaTeX, readable Unicode (
(-b ± √(b²-4ac))/(2a)), PNG, vector PDF, Markdown. - Edit loop — ✎ opens the formula in an inline editor with live preview; Enter types the result into your prompt.
- Auto-tiling panes — ⌘T splits into a balanced grid, ⌘⏎ zooms one pane, layout + working dirs survive a relaunch.
- Claude + Codex launcher — provider-specific resume, quotas, pins and titles; projects, tasks and pins in one Home screen. ⌘K or typing opens one search palette;
/explicitly sends a free AI prompt when the optional backend is installed. - Claude Code cockpit — every pane knows whether its agent is working, done, or waiting for input, notifies you, and picks up the session's
/coloras its accent. Agents drive the terminal themselves via thelatextermCLI. - Plus: ⌘F search, KaTeX errors underlined instead of swallowed, overlays that follow the scroll, a native settings window (⌘,).
The predecessor (LatexTerminalLive) read another terminal's screen with OCR — too flaky for greek glyphs and fractions. LatexTerm is the terminal, so formula positions come straight from the cell grid. Owning the grid paid off twice: the same buffer access now powers the Claude Code session detection.
How the LaTeX overlay works
PTY (login shell) → SwiftTerm VT parser → buffer grid
→ OverlayController scans visible rows
→ LaTeXDetector finds delimited formulas
→ one shared WKWebView + KaTeX renders each as a positioned <div>
- One WebView hosts all formulas; KaTeX loads once, offline (bundled CSS/JS/fonts).
- Overlays are keyed to the absolute scrollback row — scrolling repositions them (one GPU-composited translate) instead of rebuilding.
- Detection handles soft-wrapped inline formulas, Claude Code's own word-wrap (indented continuation lines) and multi-line
$$ … $$blocks; inline hits scale to fit their row and grow into empty neighbour rows. - The source characters under a formula are drawn transparent by the terminal itself (no mask), so selection and coloured backgrounds stay intact.
- Clicks on empty space pass through to normal terminal selection; only formula hitboxes are interactive.
The deep-dive lives in the source — start at LatexTerm/Latex/OverlayController.swift.
Everything here is a plain terminal mechanism — escape sequences, an env var, a Unix socket. Nothing is hardwired to Claude; any agent or script can use the same channels.
Current launcher: choose Claude / Codex per folder. Both offer new sessions, direct
resume, pins (⌘P), rename (⌘E) and shared follow-ups; Codex also keeps its native resume
picker as a fallback. Codex titles are native, while Codex pins belong to the launcher.
Provider badges distinguish actions and quota windows (including Claude's Fable quota).
Claude-specific maintenance stays separately labelled; its status/color heuristics do not
run on Codex panes. Codex has its own dismissible startup curtain.
Home navigation groups Projekte / Aufgaben / Pins; tasks group due follow-ups and show the Inbox count. Open-pane results target the exact pane in the current window, not simply another pane with the same folder. Reliable live session identity is not yet available.
One search bar: ⌘K and typing in Home open the same palette, preserving the first
character. Ordinary text searches locally across projects, sessions and open panes. Start
with / and press Enter to send a free AI prompt through the optional external
projekte assist backend. This can find sessions with source excerpts, propose a session
or Claude/Codex pair, answer questions, or compare answers deliberately pasted into the prompt.
New sessions require a separate preview confirmation. Normal typing does not call a model;
explicit AI requests use the configured backend/account quota and may send selected session
excerpts. Search is bounded to recent candidates, not the full archive. Escape cancels.
Additional controls live in the Home menu: new project ⌘⇧N, reload ⌘R,
pin project/folder ⌘⇧P, key help ⌘/, only projects ⌘⇧B, and expand/collapse all
⌘⇧A / ⌘⇧E. ⌘T remains a plain shell inheriting the focused pane's working directory.
Quota data refreshes via projekte limits --json --agent <provider>; unavailable or stale
data is distinguished from zero usage. Follow-ups use the same external task store for both agents.
The data comes from an external CLI — projekte --json (run through your login shell; change it in Settings → Erweitert). Without it the pane shows a hint and nothing else breaks. The contract (JSON shape) lives with the CLI, not in the app.
Each pane tracks its session: working (titlebar dot pulses, a floating pill in the pane shows the current tool live) → done / needs input (macOS notification if the pane is unwatched; clicking it focuses and zooms the pane).
- Precise: Claude Code hooks write
\e]5522;status=working|input|done|ready[;detail]\ato the pane's tty —detail(e.g. the tool name from aPreToolUsehook) becomes the pill text.ready(from aSessionStarthook) only lifts the home-tile launch curtain. - Zero-config fallback: the pane detects spinner vs. input box straight from the buffer grid; a fresh hook signal silences it for 10 minutes (hooks win, the fallback self-heals crashed sessions).
- Terminal bell (
\a) and OSC 777 (\e]777;notify;Title;Body\a) notify instantly too.
The pane accent (caret, border, titlebar dot) follows the session — passively from Claude Code's /color frame, or explicitly, even through SSH:
printf '\e]5522;accent=#e85e3e\a' # set this pane's accent
printf '\e]5522;accent=reset\a' # back to global/adaptiveLatexTerm renders like Ghostty out of the box: theme Dark+, bundled JetBrains Mono NL at 20 pt, xterm-256 colors, bold stays bold, steady block cursor, 12 px padding. Everything lives in Settings → Darstellung (⌘,). The settings window has six tabs — Allgemein (home tree, Ghostty import), Darstellung, Kacheln (accent, focus), Claude (notifications, prompt text), Formeln, Erweitert (launcher data commands, control socket, reset):
- Themes are Ghostty theme files.
Dark+andEmber(the old warm black) are built in; if Ghostty is installed, all of its ~460 themes appear in the picker. Every surface follows the theme — panes, home pane, launch ring, status pills, formula overlays. - Font: any installed monospace family or the bundled JetBrains Mono NL (no ligatures on purpose — the renderer is cell-exact and formula overlays sit on cell coordinates).
- Import from Ghostty: one button reads
~/.config/ghostty/config(theme, font, size, padding, cursor blink, bold-is-bright) and previews the changes before applying. Color overrides in the config become the theme „Ghostty (Config)“. Lives in Settings → Allgemein.
Agents (or you) can drive the terminal from any shell — the app listens on a per-user socket (0600 + peer check, see SECURITY.md):
latexterm list-panes [--json] # index, UUID, CWD, session state
latexterm new-pane [--cwd DIR] [--exec CMD]
latexterm send [--pane SEL] [--no-enter] TEXT... # type into a pane (Enter by default)
latexterm zoom [--pane SEL]
latexterm focus [--pane SEL]Without --pane, the calling shell's own pane is targeted (via $LATEXTERM_PANE_ID). Put the bundled binary on your PATH once:
ln -s /Applications/LatexTerm.app/Contents/Helpers/latexterm /opt/homebrew/bin/latextermThat closes the loop: a Claude Code session can open panes, start fresh Claudes in them, prompt them, and watch their status — Claude orchestrating Claude.
Grab LatexTerm.app from Releases, unzip, drop into /Applications. The build is unsigned — right-click → Open on first launch, or:
xattr -dr com.apple.quarantine /Applications/LatexTerm.appBuild from source
Needs Xcode 26+ with the Metal Toolchain (xcodebuild -downloadComponent MetalToolchain).
open LatexTerm.xcodeproj # then Cmd+R (App Sandbox is off on purpose: PTY rights)# CLI build + tests
xcodebuild -project LatexTerm.xcodeproj -scheme LatexTerm -configuration Release \
-derivedDataPath .build CODE_SIGNING_ALLOWED=NO build
xcodebuild test -project LatexTerm.xcodeproj -scheme LatexTerm \
-destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO⌘N |
new home pane (project launcher) |
⌘T / ⌘W |
new shell pane (inherits CWD) / close pane |
⌘1…9 |
grow the grid to N panes |
⌘⏎ |
zoom the focused pane (toggle) |
⌘F |
find in the focused pane |
⌘+ ⌘- ⌘0 |
font size (all panes, persisted) |
⌘L |
toggle formula overlays |
⌘, |
settings window (six tabs — theme, font, line spacing, accent, formula scale, notifications …) |
Tip — testing formulas: zsh echo mangles backslashes; use printf '%s\n' '$E=mc^2$' or a quoted here-doc.
- A formula whose opener is scrolled off above the viewport (or that wraps past the bottom) isn't detected; multi-line
$$blocks need each delimiter alone on its line (a leading marker like⏺is fine). - Claude Code's Markdown renderer strips the backslash before ASCII punctuation, so
\(…\),\[…\],\,,\{and\$never reach the terminal intact — use$…$,\thinspace,\lbrace. Matrix row breaks (\\) are repaired heuristically. - A fraction squeezed between two text lines still shrinks to one row height — hover for the full-size view.
$in prose is filtered by Pandoc's rule (no space right of the opener / left of the closer, no digit after the closer) and by colour: opener and closer must share the cell colour, so a$PATHin a code span never pairs with a prose$. Price:$ x $with inner spaces isn't a formula — write$x$or\( x \).- Ligatures are off by design (bundled font is the NL variant); a ligature font can still be chosen, but glyphs then may not align with cells.
MIT — © 2026 Mats Luca Dagott. Bundles SwiftTerm (MIT, vendored fork at SwiftTermLocal/) and KaTeX 0.16.47 (MIT; fonts under SIL OFL 1.1) and JetBrains Mono NL 2.304 (SIL OFL 1.1) — see NOTICE. The demo above is rendered programmatically with Remotion (demo-video/).

