Back to all features
ZenTerm with tiled panes and both drawers open on this site's codebase
Developer tools

ZenTerm

Built inside Praxis Labs2026 — Present

A dev-first terminal for macOS. Panes that tile, a pair of drawers per tab, and any tool as a float over the work, built on a libghostty core.

Context

I spend eight to twelve hours a day in a pty. Ghostty, gh-dash, tuicr, and linear-tui are the reason I enjoy it, and each one does a single job well. I wanted them to feel like part of the same system, so I built one. ZenTerm is the terminal at the center of it.

Challenge

A terminal emulator is years of work before it is even correct: escape sequences, shell semantics, a GPU text renderer. None of that was the part I wanted to change, and writing one first would have consumed the project before it started.

Decisions

  • Built on libghostty instead of writing an emulator. TerminalSurface is the whole contract, and anything only one engine can do stays below it. libghostty fills it today; SwiftTerm could fill it tomorrow without the rest of the app changing.
  • The engine keeps its own key handling underneath ZenTerm's, so the two collide invisibly. The seam asks it what it would do with a keystroke nobody pressed, and a sweep test fails when anything unexpected survives underneath.
  • libghostty reads config from files and has no setter API, so every change costs a write and a re-read. Fine for a theme swap, wrong for a font size held down. Size became its own call.
  • An off-main Carbon text-input call kills the process with no crash report and no stderr, and config parsing reached it. Tests never caught it, so the compiler does: main-actor isolation, with the warning promoted to a build error.
  • Focus belongs to ZenTerm, not AppKit. The responder chain doesn't propagate reliably while a fast split reparents many pane views at once, so one panel per tab holds it explicitly.
  • New surfaces compose the existing primitives rather than re-implementing the feel. One button with variants, one card shell, one keyboard model across every card. A bespoke control reads as a different app.

Result

ZenTerm is what I work in every day, including this site: the first shot is this repo open in it. It ships signed, notarized, and self-updating at v1.0, and went public in August 2026 with no announcement, so there is nothing to say about adoption yet. It does the one thing I built it for: a full day, ticket to merge, without leaving the terminal.

Stack
SwiftSwiftUIAppKitlibghosttyZigMetalSparkleSwiftPM