Terminal
Terminal panes with tabs, splits, GPU-accelerated rendering, pane persistence across workspace switches, and PTY integration.
The terminal is the primary interface in Codemux. It uses xterm.js with GPU-accelerated rendering where it's a win (and a DOM-renderer fallback elsewhere — see Renderer), Unicode 11 character widths, and the kitty keyboard protocol — backed by real PTY sessions managed in Rust.
Panes and Splits
Each tab contains a pane tree. Panes can be split horizontally or vertically, resized by dragging the divider, and swapped by dragging pane headers.
- Split right:
Ctrl+Shift+D— creates a new pane to the right - Split down:
Ctrl+Shift+E— creates a new pane below - Close pane:
Ctrl+Shift+W— removes the active pane - Move focus:
Alt+Shift+Arrow keys— focus the pane to the left, right, above, or below. Landing on a chat pane puts the caret in its composer - Zoom pane:
Ctrl+Shift+Enter— the active pane fills the tab and a Zoomed chip appears; press again (or click the chip) to bring the split back. The other panes keep running while hidden
In a split, the pane that receives your keystrokes has an accent border and the others a faint one. Cycling panes in order has no default keybind and cannot be bound — run it from the command palette (Ctrl+K → "Focus next pane" / "Focus previous pane").
Resizing from the keyboard: Tab to a divider, then use the arrow keys to move it 16px (64px with Shift), and Home / End to jump to either limit. Double-click a divider to even out the split.
Tabs
Each workspace can have multiple tabs. Each tab has its own independent pane tree.
| Shortcut | Action |
|---|---|
| Ctrl+T | New terminal tab |
| Ctrl+W | Close active tab (from outside a terminal — a focused terminal keeps Ctrl+W as delete-word) |
| Ctrl+Shift+T | Reopen the last closed agent chat |
| Ctrl+1 through Ctrl+9 | Jump to tab by position |
| Ctrl+PageDown / Ctrl+PageUp | Next / previous tab |
Open a browser tab from the tab bar's + dropdown (or the command palette → Open Browser). Tabs can be reordered by dragging. A new terminal tab takes the lowest free "Terminal N" name, so the first one is always "Terminal 1".
The title-bar tabs (the default tab strip in the GUI) and the classic tab bar work the same way:
- Right-click a tab for Close tab, Close other tabs, Close tabs to the right, Split right, Split down, and Rename tab.
- Middle-click a tab to close it.
- Rename inline by double-clicking the label (or Rename tab).
Enteror clicking away saves,Escapecancels. - An editor tab with unsaved changes shows a dot.
- When the tabs don't all fit, the strip scrolls sideways with a fade on the clipped edge, and the active tab is scrolled into view.
Closing tabs and panes
Closing a tab or pane ends every process and stops every agent in it, so Codemux asks first when something would be lost — from the tab's ×, middle-click, the context menu, Ctrl+W, Ctrl+Shift+W, or the pane header. The dialog names what's at stake — "The agent is still working. Closing stops it." or "node and cargo are still running. Closing ends them." — and Cancel has focus, so Enter keeps the work alive.
It asks when an agent in the tab is working, waiting on your answer, or monitoring, or when a terminal has a foreground job (a dev server, a build, an agent CLI). A shell sitting at its prompt, or a job started in the background with &, closes straight away. Foreground-job detection reads /proc, so it covers local terminals on Linux only; agent status is checked everywhere. If the check fails, the close isn't blocked. "Close other tabs" and "Close tabs to the right" ask tab by tab.
Closing an agent chat shows a "Closed …" toast with Undo for 6 seconds. Ctrl+Shift+T (or Reopen closed chat in the command palette) brings back the most recently closed chat with its transcript, at its old tab position. Each closed chat reopens only once.
Preset Bar
The preset bar sits above the pane area and provides one-click agent launching:
- Pinned presets — Pin your most-used agent configurations for instant access. Fourteen builtins ship by default — Claude Code, Codex, OpenCode, Gemini, Antigravity, Copilot, Cursor Agent, Amp, Grok, Droid, Mastracode, Pi, Shell, and Chat Agent — of which all but Shell start pinned.
- Run button — Shows "Run" when a dev command is configured, or "Set Run" to configure one. Set Run (or the caret beside Run) opens a popover of run commands detected from the project —
package.jsonscripts, Makefile targets,justfilerecipes, Cargo, Go, Django, and Compose files — plus a field for any other command; one click saves and runs it. PressCtrl+Shift+Gto run from anywhere. - Settings — Gear icon opens preset management. Toggle bar visibility from the dropdown.
Custom presets support multiple commands, working directory overrides, and launch modes (new tab or split pane). When the bar has more pinned presets than fit on screen it becomes a horizontally-scrollable row — a plain vertical mouse wheel pans it.
Builtin Agent Presets
Every builtin agent preset launches in its CLI's skip-permissions / auto-approve ("YOLO") mode so the agent runs without per-action confirmation prompts. The exact flag was verified against each tool's own --help.
| Name | Command |
|---|---|
| Claude Code | claude --dangerously-skip-permissions |
| Codex | codex --dangerously-bypass-approvals-and-sandbox |
| OpenCode | opencode |
| Gemini | gemini --yolo |
| Antigravity | agy --dangerously-skip-permissions |
| Copilot | copilot --allow-all |
| Cursor Agent | cursor-agent --yolo |
| Amp | amp --dangerously-allow-all |
| Grok | grok --always-approve |
| Droid | droid (launched with a runtime --settings file pinning autonomyLevel: high, since interactive Droid has no skip-permissions flag) |
| Mastracode | mastracode (YOLO is the interactive TUI default) |
| Pi | pi |
Antigravity ships alongside Gemini rather than replacing it — Google is transitioning Gemini CLI to Antigravity CLI, and Gemini CLI stops serving free/Pro/Ultra tiers on 2026-06-18.
Agent Launch and Prompt Handoff
When you create a workspace with a task description and an agent selected, Codemux automatically launches the agent and hands off your prompt:
How Prompts Are Delivered
- Claude Code and Codex — The prompt is embedded directly in the CLI command using ANSI-C quoting so newlines survive (e.g.
claude --dangerously-skip-permissions $'fix the login bug') - Every other preset (Gemini, OpenCode, Pi, Antigravity, Copilot, Cursor Agent, Amp, Grok, Droid, and all custom presets) — the prompt is typed into the PTY once the pane settles. The trigger is a quiet-after-output heuristic (~1.5s of silence), not a fixed timer, since these agents use TUI interfaces that need time to initialize
Agent Context (--system-prompt)
For Claude Code, Codemux appends --system-prompt "$CODEMUX_AGENT_CONTEXT" to the launch command. This environment variable contains instructions telling Claude Code to use Codemux's built-in browser commands instead of system browsers, and lists the available codemux browser subcommands.
The CODEMUX_AGENT_CONTEXT env var is set on all terminal sessions, so the $CODEMUX_AGENT_CONTEXT reference in the flag expands at shell execution time.
Autonomous Mode
The default Claude Code preset uses --dangerously-skip-permissions to run in autonomous mode. This skips Claude Code's interactive permission prompts, which is appropriate for agent work in Codemux where you monitor progress through the agent status indicators instead.
Custom Agent Presets
You can edit the built-in presets or create your own with the New preset button in Settings > Presets (or the preset-bar gear menu). A custom preset can be a raw command (literal shell lines) or a structured agent launcher — pick an agent, a model + reasoning, and a prompt, and Codemux assembles the launch command for you. See Settings → Presets for the full editor. When editing a Claude Code preset, keep the --system-prompt "$CODEMUX_AGENT_CONTEXT" flag for best results — this ensures the agent knows how to use Codemux's browser and tools.
Smart Headers
Terminal pane headers display contextual information:
- Agent name — A pane launched from a recognized agent preset (Claude, Codex, OpenCode, Gemini, Antigravity, Copilot, Cursor Agent, Amp, Grok, Droid, Mastracode, Pi) carries that preset's name and icon instead of a generic label
- Terminal — A plain shell pane is labeled
Terminal - Working directory — When the terminal is somewhere other than the workspace root, the header appends the current directory beside the title in mono text. At the root — where every session starts — nothing is appended, so the hint only shows up when it tells you something
The directory is shortened to stay readable, with the full untrimmed path on hover:
- Inside the workspace, the relative path keeps its last two segments with the head elided —
…/src/pty_daemon - Outside the workspace but under your home, it contracts to
~—~/dotfiles - A short path fully outside keeps its leading slash —
/var/log— so it can't be misread as workspace-relative
Shells with shell integration (OSC 7) update the hint instantly and exactly, including on remote and SSH panes. Without it, Codemux falls back to reading the shell process's directory, which works on Linux only — it follows cd but not a long-running child process. On Windows and macOS, a shell without OSC 7 integration simply shows no hint.
Renderer
Codemux renders terminals with xterm.js, using the WebGL renderer only where GPU acceleration is verifiably a win and the DOM renderer everywhere else. A one-time probe decides at startup:
- Software-rendered WebGL (VMs without GPU passthrough, SwiftShader/llvmpipe-class drivers) → DOM renderer. Loading WebGL on a CPU rasterizer actually adds per-keystroke latency, so Codemux declines it.
- Linux (where the app's webview is WebKitGTK) → DOM renderer by policy. WebKitGTK masks its renderer string, so hardware backing can't be verified from inside the app, and this engine has a documented history of WebGL input lag.
- Windows / macOS with a real GPU → WebGL renderer, for GPU-accelerated glyph rasterization on long, fast agent output.
You can override the decision for testing by setting localStorage["codemux:terminal-renderer"] to "webgl" or "dom". Either renderer also falls back to the DOM one if the GPU drops the canvas context or WebGL2 is missing. Unicode 11 character widths are enabled so wide characters and emoji render at the right cell width — important for TUIs like lazygit and btop.
Pane Persistence Across Workspace Switches
Terminals survive workspace switches — including long-running alt-screen TUIs like Claude Code, lazygit, vim, and btop. The PTY session keeps running in the Rust backend whether or not its pane is on screen, and the backend retains the session's output history. When you switch back, the pane restores its scrollback and replays that history, so a TUI agent comes back fully redrawn and in sync.
The restore and replay are throttled: bytes drain into the terminal over a few frames on a fixed byte budget, so switching to a workspace holding megabytes of buffered agent output stays smooth instead of freezing the UI while it pours in.
Sessions end on the real lifecycle events: close-pane, close-tab, close-workspace, and PTY exit.
Background Workspaces
Only the active workspace's panes are rendered at all — an off-screen workspace costs no rendering work, so a busy agent in another workspace can't introduce typing lag in the one you're using. Its output keeps flowing into the backend's buffer and is replayed when you return.
Terminal Features
- Kitty keyboard protocol — Enhanced key reporting for tools like OpenCode
- Ctrl+Backspace — Backward kill word (sends Ctrl+W to PTY)
- Ctrl+W — Reaches the shell as delete-word while the terminal has focus
- Ctrl+Shift+C / Ctrl+Shift+V — Copy/paste
- Auto-resize — PTY dimensions sync when panes resize, including when a hidden pane comes back into view
- Status overlay — Shows terminal state while the shell starts, and an action card when it exits or fails (see below)
Links
URLs and file references in the output are underlined on hover; Ctrl+click (Cmd+click on macOS) opens them.
- Local URLs —
localhost,127.x.x.x,0.0.0.0,::1, and*.localhostopen in a browser pane beside the terminal. Other URLs open through the app's usual link handling. - File paths —
path,path:line,path:line:col, and tsc'spath(line,col)open in the right panel's editor at that line, resolved against the pane's working directory. Windows paths with\separators and a drive letter (C:\src\main.rs:12) work too; fragments inside a UNC path (\\server\share\…) are not linked. - OSC 8 hyperlinks — links that programs emit explicitly open with the same Ctrl+click.
Find
Ctrl+F opens a find bar over the terminal that searches the scrollback, with a match count, previous / next (Shift+Enter / Enter), and Match case and regular expression toggles. Escape closes it. Full-screen apps such as vim and less keep Ctrl+F for themselves. The binding can be changed as Find in terminal in Settings → Shortcuts.
Exited shells
When the shell or an agent CLI exits, the pane shows Process exited with an exit-code chip — green for 0, red otherwise — and two buttons: Restart shell and Close pane. Restart has focus after you type exit, so Enter restarts. Restarting starts a fresh shell in the same pane, keeps the scrollback, and draws a dim ── shell restarted ── line. A shell that fails to start offers Retry and Copy error instead.
Theming
Terminal colors sync with the system theme (Omarchy integration). The 16 ANSI colors, foreground, cursor, and selection colors are all theme-reactive. When no Omarchy theme is detected, a Tokyonight-inspired fallback is used.
Persistence
Pane layout and tab structure persist across restarts. Terminal scrollback is saved on close and restored on reopen, and supported agents like Claude Code automatically resume their sessions. See Session Persistence for details on the adapter system and configuration.