Skip to main content
CODEMUXManual

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.

ShortcutAction
Ctrl+TNew terminal tab
Ctrl+WClose active tab (from outside a terminal — a focused terminal keeps Ctrl+W as delete-word)
Ctrl+Shift+TReopen the last closed agent chat
Ctrl+1 through Ctrl+9Jump to tab by position
Ctrl+PageDown / Ctrl+PageUpNext / 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). Enter or clicking away saves, Escape cancels.
  • 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.json scripts, Makefile targets, justfile recipes, Cargo, Go, Django, and Compose files — plus a field for any other command; one click saves and runs it. Press Ctrl+Shift+G to 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.

NameCommand
Claude Codeclaude --dangerously-skip-permissions
Codexcodex --dangerously-bypass-approvals-and-sandbox
OpenCodeopencode
Geminigemini --yolo
Antigravityagy --dangerously-skip-permissions
Copilotcopilot --allow-all
Cursor Agentcursor-agent --yolo
Ampamp --dangerously-allow-all
Grokgrok --always-approve
Droiddroid (launched with a runtime --settings file pinning autonomyLevel: high, since interactive Droid has no skip-permissions flag)
Mastracodemastracode (YOLO is the interactive TUI default)
Pipi

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)

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 *.localhost open 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's path(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.