Terminals
How Daintree's terminal panels work: the PTY, the WebGL and DOM renderers, scrollback, alt-screen mode, search, links, clipboard, drag-and-drop, and the type-anywhere rescue.
A terminal is the only panel kind with a real process behind it, and it is the kind agents run in. Everything on this page applies equally to a plain shell and to an agent panel: an agent is a terminal running an agent CLI, not a separate kind. For the panel model itself, see Terminals & Panels.
The PTY and the Renderer
Daintree runs node-pty for native pseudo-terminal processes and xterm.js for rendering. You get full terminal emulation: colors, cursor positioning, the alternate screen buffer, and the sixel and iTerm2 image protocols. PTYs live in a separate utility process, and output crosses to the renderer over an IPC channel with explicit flow control, which is what the Paused badge reports on.
WebGL and the DOM fallback
Each visible terminal wants a WebGL context, because the DOM renderer falls back to the configured font and mangles block, box-drawing, and Powerline glyphs, exactly the characters agent TUIs are built out of. Chromium caps how many WebGL contexts one renderer can hold, and silently evicts the oldest when you go over, so Daintree manages the pool itself.
The manager runs in one of two modes and flips between them on a count, with hysteresis so opening one panel at the boundary doesn't flap the whole fleet. On a typical machine the upper threshold is around 18 open terminals wanting WebGL and the lower is around 15; both scale with your RAM and the active resource profile. Above the upper threshold every terminal drops to the DOM renderer; once the count falls back to the lower threshold they all re-attach.
A circuit breaker sits behind that: three genuine context-loss events inside 60 seconds disables WebGL for the rest of the session. A single GPU eviction that drops every context at once counts as one event, not many, so one bad frame can't trip it, but a machine with a persistently faulty GPU driver stops strobing between renderers and settles on DOM.
xterm's glyph atlas is shared across every terminal using the same font and theme, and it has no per-entry eviction, so truecolor agent output would otherwise only ever add to it. Daintree caps the atlas texture size at 1024 px, which is what makes eviction reachable at all: once every page has grown to the cap, the next demand clears the atlas instead of doubling it. The practical effect is that a long-running fleet of streaming agents plateaus in memory instead of climbing.
Redraw
Occasionally a TUI and the renderer disagree about what is on screen: a resize landed mid-repaint, or a program wrote cursor-relative output over a stale grid. Redraw Terminal (Command-K, then Command-D Control-K, then Control-D Control-K, then Control-D , or the panel's right-click menu) repairs it. When you trigger it yourself it forces a full geometry resync immediately, rather than waiting for a gap in the output, so it works on a busy agent, which is exactly when you need it. The same repair dispatched automatically by a plugin or an agent keeps the write-quiescence delay, because an unprompted repaint under a live cursor is the thing that delay exists to prevent.
Adaptive refresh rates
Rendering adapts its refresh rate to visibility and focus. With a dozen panels open, that saves real work:
| Tier | Interval | Roughly | When |
|---|---|---|---|
| Burst | 16 ms | 60 fps | Active output streaming |
| Focused | 100 ms | 10 fps | The focused panel |
| Visible | 200 ms | 5 fps | On screen, not focused |
| Background | 1000 ms | 1 fps | Off screen or in an inactive worktree |
Backgrounded Terminals Stay Live
Daintree does not sleep individual terminals. There is no "Sleep Terminal" action, no sweep over idle panels, and no hibernated state a terminal can be in. Scroll a panel out of view, switch worktrees, or leave an agent alone for an hour, and it keeps rendering at the background tier with its process and its buffer fully intact.
Earlier versions did suspend a background terminal's renderer and rebuild it on reveal, keeping the PTY alive underneath. It reclaimed memory, and it was the root cause of a recurring class of agent-TUI corruption (zebra striping, double-spaced output, dropped lines) reproduced across OpenCode, Codex, Gemini CLI and Claude Code. The resync on reveal could never be made reliable for a program that had redrawn itself while the view was gone. Removing per-terminal hibernation removed the whole failure mode.
Scrollback
Pick a base scrollback size in Settings > Panel Grid > Scrollback: 500, 1,000 (default), 2,500, or 5,000 lines. The settings panel shows an estimated memory cost for the size you pick.
That number is a base, not a limit. Daintree scales it by what the terminal is actually doing, because an agent's transcript is the thing you scroll back through and a shell's is usually not:
| Terminal | Multiplier | Floor | Ceiling |
|---|---|---|---|
| Agent | 10× | 500 lines | 10,000 lines |
| Plain shell | 0.3× | 200 lines | 2,000 lines |
At the default 1,000-line base, an agent panel holds 10,000 lines and a shell holds 300. The efficiency resource profile lowers the agent ceiling to 4,000 lines on constrained hardware; Performance Mode drops every terminal to a flat 100 lines.
Scrollback restore
When a session is restored, Daintree replays its saved buffer into the fresh terminal. If that replay can't finish, an inline banner appears at the top of the panel with a Reset terminal button that clears the buffer and starts fresh. You can also dismiss the banner and keep working. The message names what went wrong:
- Scrollback restore timed out: the replay didn't finish in time.
- Scrollback contents couldn't be replayed: the saved buffer couldn't be parsed.
- Scrollback restore failed: a general fallback carrying the underlying error.
In every case the terminal itself is fine and the live process is untouched. The only thing missing is earlier output.
Alt-Screen Mode
Most agent CLIs can render either inline (writing into the normal buffer, the way a shell command does) or on the alternate screen, the full-screen buffer that programs like vim and htop use. The two behave differently in ways you will notice:
- Inline is smoother in Daintree. Output joins the WebGL-rendered scrollback, so you can scroll back through it and search it, and a resize reflows cleanly.
- Alt screen matches what the CLI looks like in a bare terminal, which is what you want if you rely on its own full-screen layout.
Inline is the default. Turn Use alt-screen mode by default on in Settings > CLI agents to flip it globally, and override it per agent or per preset with the tri-state Alt-screen mode control (Default / Inline / Alt screen). See Agents and Agent Presets.
Runtime State Badges
The panel header carries small badges for states Daintree enters on your behalf. They are automatic and each recovers on its own:
| Badge | What it means |
|---|---|
| Paused | Output is arriving faster than it can be drained. Streaming pauses to prevent data loss, then resumes once the buffer clears. The tooltip shows how long it has been held. |
| Paused (memory) | The app is under memory pressure and this terminal was paused to ease it. Recovers automatically. |
| N queued | Commands are queued for this terminal and will be sent in order. |
| [exit N] | The process has exited with that code. |
If you'd rather not wait out a pause, Force resume (paused) is in the right-click menu and bound to Command-K, then Command-U Control-K, then Control-U Control-K, then Control-U . A watchdog also fires it for you if a pause outlasts what the flow-control cycle should need.
An agent panel's header also carries its state chip, and a waiting agent says why it is waiting: Approval, Question, or Error. See Agent States.
Process Icons
A terminal shows an icon for whatever is running inside it. Start node and the generic terminal icon becomes a Node.js icon; run pytest and it becomes pytest. This works everywhere a terminal appears: the panel header, the tab bar, the dock, the worktree sidebar, and the quick switcher.
The detector walks each terminal's child process tree and matches running executables against one registry of about 60 tools: package managers, language runtimes, compiled-language toolchains, Python and JavaScript tooling, build systems, containers, cloud and tunnel CLIs, databases, and terminal tools. Agents are merged on top from the agent registry, and plugins can contribute their own detections through contributes.processTools.
When several processes match at once, precedence runs tool > runtime > package manager. That ordering is deliberate and it is the opposite of what you might expect: a package manager or a language runtime is usually a launcher for the thing you actually care about. node /path/to/vite.js reports Vite rather than Node, and npm run dev reports whatever the script started, with npm as the fallback only when nothing identifiable runs beneath it.
Dedicated agent panels are the exception: they always show their own brand icon whatever is running inside them, so an agent that shells out to npm install doesn't briefly become an npm panel.
Search
Command-F Control-F Control-F opens the search bar at the top of the focused panel with a "Find in terminal" input. Matches highlight as you type, debounced so a fast typist doesn't fire a search per keystroke. The counter reads "3 of 12" once you're stepping through matches, "12 matches" before you are, or "No matches".
Toggles. Three buttons, freely combined: Aa for case sensitivity, .* to treat the query as a regular expression, and an underlined ab for whole-word matching. Case-sensitive and regex persist across searches; whole word resets each time you reopen the bar.
Overview ruler. A thin strip beside the scrollbar marks every match across the whole buffer, with a brighter tick for the active one, so you can see how matches are distributed without scrolling.
Navigation. Enter or the down chevron goes to the next match, Shift+Enter or the up chevron to the previous. Navigation wraps. Escape closes the bar.
History. With the input focused, ArrowUp and ArrowDown cycle through recent searches. Anything you've typed but not yet recalled is held as a draft, so stepping back to it loses nothing.
Scroll Navigation
When you scroll up to read earlier output, Daintree tracks whether new content has arrived since you left the bottom.
"New output below." A floating pill appears at the bottom of the panel when you're scrolled back and new output has actually arrived: scrolling up on its own doesn't trigger it. Click it to jump to the bottom and take keyboard focus. It keeps clear of drag-to-select and right-click menus, and respects reduced-motion preferences.
Scroll to last activity (Option-Command-L Control-Alt-L Control-Alt-L ) jumps to the line where output was most recently written, which is not the same as the bottom. If an agent produced output 500 lines up and a prompt sits at the end of the buffer, this takes you to the output. Press it twice in a row to land at the absolute bottom.
Links in Output
Daintree scans terminal output for things worth clicking, and validates candidates against the filesystem before turning them into links, so a slash-command like /help never becomes one.
| What | Click does |
|---|---|
File paths, with optional :line:col | Opens the file in a File Viewer at that position. |
file:// URLs | Same as a file path. Agent CLIs print generated images this way. |
| Directory paths | Opens the File Browser, revealing that folder. |
[image #N] references | Jumps to that figure; a modified click opens it in a lightbox. |
http / https URLs | Opens in your default browser. |
| A localhost URL, Cmd/Ctrl-clicked | Opens it in an in-app Browser panel in the same worktree, reusing an existing one if there is one. |
A file link pointing outside your project roots won't open in place: the containment rules exist for a reason. Instead the failure toast offers Reveal in File Manager, since you clicked it deliberately and the file is real, just elsewhere. Every other failure offers Copy path. A burst of bad links from a scrolled stack trace collapses into one toast rather than twenty.
Clipboard
Terminal panels support VS Code-style copy and paste from the keyboard, the right-click menu, and the clipboard image path.
| Action | Shortcut |
|---|---|
| Copy selection | Cmd+C |
| Paste | Cmd+V |
Cmd+C copies the current terminal selection even when the hybrid input bar has focus, so you don't have to click into the output first.
| Action | Shortcut |
|---|---|
| Copy selection | Right-click > Copy |
| Paste | Ctrl+Shift+V |
Ctrl+C sends an interrupt to the running process, so copying goes through the context menu. Paste is Ctrl+Shift+V, matching the VS Code terminal convention.
Daintree picks the best available shell: pwsh.exe, then powershell.exe, then cmd.exe. Each launches in UTF-8 (PowerShell with -NoLogo and a UTF-8 bootstrap, cmd.exe with a silenced chcp 65001), so box-drawing characters and emoji render correctly with no startup banner.
| Action | Shortcut |
|---|---|
| Copy selection | Right-click > Copy |
| Paste | Ctrl+Shift+V |
| Copy on select | Select text (writes to PRIMARY) |
| Paste from selection | Middle-click |
Ctrl+C sends an interrupt to the running process, so copying goes through the context menu. Paste is Ctrl+Shift+V.
Daintree follows the X11 and Wayland selection conventions: selecting text writes to PRIMARY on mouse release, and middle-click pastes from PRIMARY into the PTY, bracketed-paste wrapped where the shell supports it. Middle-click paste is disabled while a panel's input is locked. Copy-on-select fails silently on minimal Wayland compositors that don't implement the primary-selection protocol; the keyboard and menu paths still work there.
Pasted text is wrapped for shells that support bracketed paste, so multi-line content doesn't execute before you press Enter.
Clipboard images. Paste a screenshot into a terminal and Daintree writes it to a PNG in a temporary directory, then inserts the shell-escaped absolute path at the cursor. Pasting into the hybrid input bar does the same and adds a thumbnail preview chip inline. Clipboard images are cleaned up after 24 hours.
Right-click menu. Right-clicking a terminal opens a menu with Copy, Paste, and Send to Agent at the top; Copy and Send to Agent are disabled with no selection. Right-clicking a detected link adds Open Link and Copy Link Address. Shift-F10 Shift-F10 Shift-F10 opens the menu from the keyboard. The full reference is on Palettes & Menus.
Drag and Drop
Drag files from Finder, Explorer, the File Browser, or the Review Hub onto a terminal, and what gets inserted depends on what is running in it:
- An agent gets an
@filetoken, spelled relative to the terminal's live working directory, falling back to an absolute path for anything out of tree. That is the same reference the@autocomplete and the input bar produce, so a file means the same thing however you attach it. - A plain shell gets a shell-escaped absolute path, which is what a shell can actually consume.
Delivery follows the terminal's live bracketed-paste mode. Agent CLIs read raw stdin and can't tell an injected byte from a typed one, so an unwrapped @ would drive their own interactive file picker character by character; wrapping the batch marks it as a paste instead. A drop never appends a carriage return: it inserts text, it doesn't submit.
Dragging over a terminal shows drop feedback, and that feedback is suppressed while the panel's input is locked rather than advertising a target that would discard the drop. Paths containing a carriage return, newline, or escape character are skipped: all three are legal in a filename and none of them can be delivered safely. See Unified Input for the rest of the attach surfaces.
Type-Anywhere Rescue
Typing a prompt into a window that has quietly lost focus is a small, constant annoyance. Daintree handles it in two ways, from one listener, and the two are deliberately asymmetric.
Locate. If a terminal does have focus but has scrolled out of view, the keystroke goes where it always would (the event is never touched) and Daintree scrolls that panel back into view and pulses it, so you can see where your typing is landing.
Rescue. If nothing useful has focus at all (the document body, the chrome of a read-only panel), the character is routed into the input draft of the last agent you typed to, and focus moves there. With no typing history yet, it falls back to the sole open agent; with several open and no history, it refuses rather than guessing.
Nothing is ever submitted. The character lands in a draft you can see and edit. Rescue is gated hard: it only fires on a plain printable keystroke, never during IME composition, never while a modal is open, never into a locked or restarting panel, and never into a plain shell: routing a draft into a shell would execute it on Enter.
Line Editing
Word-by-word cursor movement. On macOS, Option+← and Option+→ jump the cursor a word at a time in the shell line editor. Daintree emits the meta sequences ESC b and ESC f rather than xterm's default modifier arrows, because neither zsh's ZLE nor bash's readline binds the modifier arrows to word motion: out of the box, the keys would do nothing. This applies to the normal buffer only: a full-screen TUI decodes the modifier arrows itself and would only be confused by the substitution.
Hybrid input bar. An optional text field above each terminal, giving you ordinary editing (selection, cursor movement, clipboard, multi-line) before the input reaches the PTY. Toggle it in Settings > Panel Grid, with an optional auto-focus. It is also the surface the type-anywhere rescue routes into, so turning it off disables the rescue.
Terminal Info
Every terminal has a diagnostic dialog covering how it was spawned, its PTY internals, and its runtime statistics. Use it to check which flags an agent launched with, confirm a TTY device path, or grab a dump for a bug report. Open it from the panel header's right-click menu (View Terminal Info) or the command palette; there is no dedicated shortcut. It is offered only for terminals: browser, dev preview, review, and the file panels don't expose it.
| Section | Contains |
|---|---|
| Session Metadata | Terminal ID, kind, title and title mode, project and worktree IDs, current directory, location, spawn source, whether it was started via MCP, spawn status, and creation time. |
| Spawn Command | The shell path, the command, and every argument as its own selectable chip. The easiest place to confirm an agent launched with the flags you expected. |
| Agent Launch Context | Agent panels only: launch agent, launch flags, model, preset, preset color, and the original preset if it was changed. |
| Agent Live State | Agent panels only: the currently detected agent and its session ID. |
| Terminal Classification | Whether it carries an agent launch hint, whether a PTY is active, whether activity analysis is on, and the resize strategy. |
| PTY Diagnostics | Dimensions, shell PID, TTY device, foreground process, and exit code once it has exited. |
| Runtime Statistics | Running time, spawn timestamp, and restart count, useful when auto-restart or a crash loop is in play. |
| Activity Metrics | Last input and last output, the agent state, the last state change, and the current refresh tier. |
| Performance & Diagnostics | Output and semantic buffer sizes in lines, plus whether synchronized output (DEC 2026) is active. |
The footer's Copy to Clipboard button produces a structured plain-text dump of every section, ready to paste into a bug report.
Closing and Resuming
Closing a terminal ends its process. Closing an agent session records it in the session journal, so Command-K, then Command-R Control-K, then Control-R Control-K, then Control-R can bring it back with its conversation intact where the agent supports resume. Shift-Command-T Control-Shift-T Control-Shift-T reopens the last closed panel as a fresh session in the same configuration. What survives a close, a restart, and a project switch is covered on Session Management.