Skip to content

Terminals

A terminal element is a real shell, xterm.js 5.5 in the browser, bridged to an actual PTY process. It’s the fastest way to drop into a directory Subspace already knows about, and it’s the entry point for running coding agents like Claude Code against your files.

Any page whose metadata carries fs.path (a page representing a directory on disk) shows an open terminal here · <path> button. Clicking it drops a terminal node rooted at that path, running the shell from $SUBSPACE_SHELL, falling back to $SHELL. The terminal node follows the same node-frame pattern as every other element: it sits after the bullet, and ⌘↵ is still the universal activation key elsewhere on the page.

In the browser, every terminal is server-hosted: the server spawns the PTY (via node-pty) and streams it to the client over the same WebSocket connection everything else uses.

In the desktop app, the terminal runs in the Electron main process instead, with the renderer talking to it over direct IPC, a zero-hop path with no server round trip for every keystroke. Desktop terminals still register themselves the same way server ones do, so the session registry and attach/respawn behavior below work identically in both.

Every terminal, server-hosted or desktop-hosted, registers a row in ops.pty_sessions (node_id, host, status) and mirrors its scrollback, bounded so it can’t grow unbounded, through the server. That’s what makes a terminal shareable state rather than a private connection:

  • opening the same terminal node in a second window attaches to the same session and replays its scrollback, rather than starting a second shell;
  • an agent can attach to a running session the same way, which is part of what makes terminals a practical coding-agent entry point;
  • if the underlying shell dies (a restart, a crash), the session is marked dead and the node shows respawn ↻, which starts a fresh PTY with the exact same {cwd, host} the old one had.

A terminal node isn’t stuck inline. The same promotion action that opens any bullet as its own pane or tab (a zoomed, live, independently routed view of that bullet’s subtree, still backed by the same underlying graph) applies to terminal nodes, so a terminal you’re actively using can move out of the outline flow into a VS Code-style tab or side-by-side pane, and back, without losing its session.