Building a native terminal for AI coding agents in Rust + GPUI Arthur Jean built Paneflow, a native terminal workspace for AI coding agents, using Rust and the GPUI framework. The tool is designed to address the limitations of traditional terminal multiplexers by offering git-aware workspaces, live dev-server detection, session restore, and a JSON-RPC control plane for programmatic agent management. The author emphasizes that for a terminal emulator, the UI framework must handle glyph rasterization directly, which led to the rejection of Electron and Tauri in favor of a native Rust approach. Author: Arthur Jean, solo indie maker. More on paneflow.dev and arthurjean.com . Repo: github.com/ArthurDEV44/paneflow . MIT licensed. I spend my days running Claude Code, Codex, and OpenCode in parallel panes. Different agents on different branches, each with their own dev server, all in one terminal. tmux can do this, but every multiplexer I tried treated agents the same as any other shell process: no first-class branch context, no live dev-server detection, no session restore that survives a reboot, no programmatic way for an external tool to drive the editor. I built Paneflow https://paneflow.dev because I needed it. This is a post-mortem, not a launch post. Paneflow is a native terminal workspace, splits, panes, branch-aware workspaces, session restore, built in pure Rust on top of Zed's GPUI framework https://zed.dev and the upstream alacritty terminal crate. It started as a port of cmux https://github.com/manaflow-ai/cmux , a macOS-only Swift/AppKit project, and the Rust rewrite forced a string of decisions I had no good intuition for at the start. I want to walk through the ones that mattered: which UI frameworks I tried and rejected, how the GPUI/alacritty boundary actually looks, how dev-server detection works under the hood, the N-ary layout tree that replaced binary splits, the cross-platform PTY plumbing, the JSON-RPC control plane that makes agents first-class, and four lessons that surprised me. If you take one thing away, take this: for a terminal emulator, the UI framework must own glyph rasterization. Everything else flows from that single constraint. Why a terminal for AI coding agents VSCode and classic IDEs matter less and less when you work with Claude Code or Codex. The work has shifted: instead of typing code in an editor, I'm directing agents from a shell. One pane runs Claude Code working on a feature branch. Another runs Codex doing a refactor on a second branch. A third tails the dev server. A fourth has notes and a markdown viewer open. The terminals we know weren't designed for this. They render a grid, they pipe stdin/stdout, they let you split. They don't know which workspace is on which branch, which pane has a server bound to port 5173, which session restored from yesterday's setup. Every multiplexer treats every pane identically, so the orchestration cognitive load lands entirely on you. Paneflow is built exactly for this mode. Workspaces are git-aware: each one knows its branch and the sidebar surfaces it. Dev-server detection scans for Vite, Next.js, Webpack, and others, then resolves their actual listening ports through the kernel, not by trusting whatever the framework printed. AI agent buttons in the tab bar launch Claude Code, Codex, or OpenCode in the active pane with one keystroke. A local JSON-RPC server exposes the editor so external tools can drive workspaces, send keystrokes, or post agent lifecycle events programmatically. Sessions save and restore so closing the app and reopening it tomorrow lands you exactly where you left off. Side-by-side comparisons vs cmux, WezTerm, iTerm2, and Warp at paneflow.dev/compare https://paneflow.dev/compare . The rest of this post is about how the engine works. Why not Electron, why not Tauri The first option I evaluated was Electron + xterm.js. The architecture review rejected it immediately: "Electron: heavy memory footprint contradicts cmux's 'not Electron' philosophy ." cmux was conceived as a native, low-RAM tool, and the whole point of porting it was to keep that property on Linux and Windows. So Electron was out without debate. The second option was Tauri https://tauri.app , and I genuinely spent time there. I started a real implementation, laid out the architecture, got the first interactions running. On paper it held up: lighter binary than Electron, frontend in whichever stack you want, Rust backend for the sensitive logic, cross-platform distribution with minimal friction. But as I went deeper, two things sharpened up. First, the technical blocker. The webview API surface is too narrow for the kind of low-level keyboard, IME, and input grabbing a multiplexer needs. Everything you take for granted in a native terminal precise key chords without a JS intermediary, clean Asian IME, focus management between panes without a round-trip to the webview becomes a workaround or a hole. For an editor or a typical productivity app, those trade-offs are acceptable. For a terminal that has to absorb every keystroke at sub-frame latency, they are not. Second, and this is what really decided it: I wanted to build something new, not wrap a webview with one more layer on top. When Zed https://zed.dev started, they could have built on Electron, on Tauri, on GTK or Qt. They chose to write their own framework, GPUI https://github.com/zed-industries/zed/tree/main/crates/gpui , because no existing option could deliver what they wanted for code editors: dense GPU-accelerated text rendering at 120 fps with sub-frame keystroke-to-pixel latency. The result speaks for itself. Zed's bet for IDEs applies word for word to terminals. Not wrap, not lean on a portability layer that flattens every feature to the lowest common denominator, but build on a foundation designed specifically for dense, native, low-latency text rendering. The difference with Zed is that I did not have to write the framework: they had already done it. GPUI owns text rendering end-to-end: shaping, atlasing, GPU draw, the lot. There is no "bring your own text path," there is no webview to work around. Adopting GPUI meant choosing the "dedicated native framework" approach over "webview with a Rust shell." Architectural ambition over ecosystem ease. That was the decision. The GPUI mental model A two-paragraph briefing so the rest of this post reads cleanly. Mutable state in GPUI lives in Entity