These docs describe the main branch, including unreleased changes. Read the 0.6.0 docs.

Terminal capabilities

This reference is for applications that must select terminal-dependent image, link, clipboard, notification, input, or pixel behavior.

renderer.capabilities is a supported snapshot. The regular-expression helpers that classify response sequences and the native response parser are internal detection machinery. Do not build application policy on their patterns.

Read a changing snapshot#

createCliRenderer() returns after it sends startup queries. It does not wait for the terminal to answer every query. Thus, a false field can mean either not yet detected or unsupported.

import { CliRenderEvents, createCliRenderer, type TerminalCapabilities } from "@opentui/core"

const renderer = await createCliRenderer()

function applyCapabilities(capabilities: TerminalCapabilities | null) {
  if (!capabilities) return
  console.log(capabilities.terminal.name, capabilities.image_protocol)
}

applyCapabilities(renderer.capabilities)
renderer.on(CliRenderEvents.CAPABILITIES, applyCapabilities)

Read the current snapshot first, then handle events. A response can arrive before your listener is attached.

The startup capability handler remains active for five seconds. Each recognized later response updates renderer.capabilities and emits "capabilities". Expect multiple events during that window. Do not wait for one specific count or treat the first event as final.

After the startup window, OpenTUI removes the general capability handler and stops treating private replies as startup responses. Pixel-resolution and theme queries have separate lifecycles.

Public types#

type TerminalCapabilityState = "unknown" | "supported" | "unsupported"
type TerminalMultiplexer = "none" | "tmux" | "zellij" | "screen" | "unknown"
type ImageRenderProtocol = "auto" | "kitty" | "sixel" | "blocks"

interface TerminalInfo {
  name: string
  version: string
  from_xtversion: boolean
}

name and version can come from environment heuristics before an XTVERSION response. from_xtversion is true only when OpenTUI parsed that response. Native storage bounds terminal names to 64 bytes and versions to 32 bytes.

TerminalCapabilities inventory#

Input, output, and width#

Field Type Meaning
kitty_keyboard boolean Kitty keyboard protocol support was detected or inferred
rgb boolean 24-bit RGB output is available
ansi256 boolean ANSI 256-color output is available
unicode WidthMethod Active "unicode" or "wcwidth" method in the public snapshot
explicit_width boolean OSC 66 explicit-width text was detected or forced
scaled_text boolean Scaled-text support was detected
focus_tracking boolean Terminal focus reports are supported
sync boolean Synchronized-output mode is supported
bracketed_paste boolean Bracketed paste is enabled or assumed available
explicit_cursor_positioning boolean OpenTUI should use explicit cursor positioning for this terminal path

bracketed_paste defaults to true in native environment setup, even when a query reports no support. It is not a pure probe result.

Images and pixels#

Field Type Meaning
kitty_graphics boolean Kitty graphics support was detected or inferred
sixel boolean Sixel support was detected or inferred
image_protocol Optional ImageRenderProtocol Active image-protocol override or "auto"
sgr_pixels boolean SGR pixel-coordinate mouse mode was reported

sgr_pixels does not make higher-level mouse events pixel-based. The current higher-level mouse path uses terminal cells.

renderer.resolution is separate from TerminalCapabilities. It is null until a valid terminal window-size response arrives. A PixelResolution has numeric width and height in terminal pixels. OpenTUI requeries it after resize.

Read Image for protocol selection. Do not choose a protocol from one early false value.

Field Type Meaning
hyperlinks boolean OSC 8 hyperlink output is enabled
osc52 boolean OSC 52 clipboard support was detected or inferred
osc52_support TerminalCapabilityState Query result when OpenTUI can distinguish unknown, supported, and unsupported
notifications boolean A notification protocol was selected

The renderer emits OSC 8 sequences only when hyperlinks is true. Text still renders when it is false. Without OSC 8, the terminal detects URLs one row at a time, so a wrapped URL opens only the part on the clicked row.

OpenTUI enables hyperlinks for Ghostty, Kitty, WezTerm, Alacritty, foot, iTerm2, Terminology, xterm.js, VS Code, Zed, mintty, VTE 0.52.2 or newer, and Windows Terminal. TERM_PROGRAM and VTE_VERSION count only outside tmux, Zellij, and GNU Screen. Set FORCE_HYPERLINK to override detection.

Hyperlink URLs use a native slot with a maximum of 512 UTF-8 bytes. A longer URL fails link allocation and the text loses its hyperlink metadata.

osc52 and osc52_support answer different questions. Environment and terminal-family heuristics can set osc52 while osc52_support remains "unknown". Read Clipboard for operation results and remote policy.

notifications does not expose the selected OSC variant. Use renderer.triggerNotification() and its boolean result. Read Notifications for protocol and multiplexer behavior.

Session and terminal identity#

Field Type Meaning
remote boolean The renderer uses remote-session policy
multiplexer TerminalMultiplexer Detected tmux, Zellij, GNU Screen, no multiplexer, or unknown state
terminal TerminalInfo Terminal name, version, and XTVERSION source flag
color_scheme_updates boolean Mode 2031 color-scheme update reports are supported

The snapshot object is replaced after a response. Do not retain one object and expect its fields to mutate.

Remote streams and environment forwarding#

CliRendererConfig.remote has three practical states:

  • true forces remote policy.
  • false forces local policy, even when SSH variables exist.
  • Omitted uses native auto-detection for process output and memory output.

A custom stdout that uses the native span feed defaults to remote: true. OpenTUI cannot assume that the process host terminal is the stream’s real peer.

Auto-detection recognizes SSH_CONNECTION, SSH_CLIENT, SSH_TTY, and MOSH_CONNECTION. In an auto-detected remote session, OpenTUI applies only the color depth keys from the forwarded environment: a TERM that contains 256color enables ansi256, and a COLORTERM of truecolor or 24bit enables rgb and ansi256. These keys describe the remote endpoint: sshd takes TERM from the client’s PTY request, and COLORTERM comes from the client when ssh is configured to send it, or from the server session itself, such as a shell rc file. OpenTUI ignores the other keys, such as TERM_PROGRAM, the multiplexer variables, and the OPENTUI_* overrides. On the process host, they can describe the wrong terminal endpoint.

Without COLORTERM, a 256color TERM gives 256-color output, even in a terminal that supports truecolor. To keep truecolor in an SSH session, export COLORTERM=truecolor on the server, forward it with SendEnv COLORTERM on the client and AcceptEnv COLORTERM on the server, or pass it in environment.

forwardEnvKeys selects process environment names that OpenTUI sends to native detection. Explicit remote mode defaults to an empty list. Other modes default to the documented terminal, SSH, tmux, Zellij, width, graphics, notification, hyperlink, WSL, and Windows Terminal keys.

For a remote terminal, forward only values that describe the remote endpoint:

const renderer = await createCliRenderer({
  stdin: remoteInput,
  stdout: remoteOutput,
  remote: true,
  forwardEnvKeys: ["TERM", "COLORTERM", "OPENTUI_GRAPHICS"],
  width: 80,
  height: 24,
})

forwardEnvKeys reads the process environment, so it describes the remote endpoint only when the process runs inside that endpoint’s session. A server with many clients passes each client’s values in environment instead, for example environment: { TERM: pty.term, COLORTERM: "truecolor" }. These values replace forwarded values with the same name. When auto-detection finds an SSH login, OpenTUI applies only their color depth keys, as it does for forwarded values. Set remote: true for the other values to take effect, as @opentui/ssh does.

Custom streams do not receive SIGWINCH. Call renderer.resize(width, height) when the remote transport reports a window change. Read SSH for the full session setup.

Multiplexers#

OpenTUI detects tmux from XTVERSION, TMUX, TERM_PROGRAM=tmux, or a TERM prefix. It detects Zellij from XTVERSION or Zellij environment keys. It detects GNU Screen from STY or a TERM prefix.

Detection changes feature policy:

  • tmux receives unwrapped probes and answers the ones it implements. OpenTUI does not probe the outer terminal through tmux passthrough because tmux does not route those replies to the pane that asked.
  • tmux skips Kitty and Sixel detection and enables focus tracking.
  • GNU Screen disables graphics queries and selects conservative width behavior.
  • Zellij accepts notification support only from an OSC 99 query or an explicit override.
  • Multiplexer detection can set explicit_cursor_positioning and unicode conservatively.

The terminal fields can describe the multiplexer rather than the outer emulator. Use multiplexer for routing policy.

Overrides#

Terminal detection reads these protocol controls:

Variable Native behavior
OPENTUI_GRAPHICS Exact lowercase false or 0 disables Kitty and Sixel queries. Exact lowercase true or 1 keeps detection enabled.
OPENTUI_IMAGE_PROTOCOL Selects auto, kitty, sixel, or blocks without case sensitivity
OPENTUI_FORCE_EXPLICIT_WIDTH true or 1 forces support. false or 0 disables support and the query.
OPENTUI_FORCE_WCWIDTH Presence selects wcwidth
OPENTUI_FORCE_UNICODE Presence selects Unicode width mode
OPENTUI_FORCE_NOZWJ Presence selects a native no-ZWJ width mode
OPENTUI_NOTIFICATION_PROTOCOL Selects osc9, osc777, osc99, or a disabled value
OPENTUI_NOTIFICATIONS 0, false, or off disables notifications
FORCE_HYPERLINK 0, false, or off disables OSC 8 hyperlinks. Any other value, including an empty value, enables them.

Read Environment variables for the complete value and timing rules.

The native no-ZWJ mode is not represented in the public WidthMethod union or the terminal-capability decoder enum. The OPENTUI_FORCE_NOZWJ snapshot path is therefore unclear. Do not depend on capabilities.unicode under that override until the public type and decoder agree with native state.

Response trust#

Capability replies arrive on the renderer’s input stream. A byte source that can inject terminal response sequences can influence the snapshot and enable later protocol output.

Treat custom and remote streams as a trust boundary. Forward replies only from the terminal endpoint. Do not combine untrusted application data with the control-response channel. OpenTUI validates recognized shapes and bounds parsed numbers, but that validation does not authenticate the sender.

Test fixtures#

Use @opentui/core/testing instead of sending private escape replies in most tests:

import { createTerminalCapabilities, setRendererCapabilities } from "@opentui/core/testing"

const capabilities = createTerminalCapabilities({
  rgb: true,
  hyperlinks: true,
  osc52_support: "supported",
  terminal: { name: "fixture", version: "1.0" },
})

setRendererCapabilities(renderer, capabilities)

createTerminalCapabilities() defaults booleans to false, unicode to "unicode", osc52_support to "unknown", multiplexer to "none", image_protocol to "auto", and terminal strings to empty. Overrides merge the nested terminal object.

setRendererCapabilities() changes a test renderer’s stored snapshot. It does not emulate native protocol setup or terminal output. Read Testing for observable rendering tests.

Next#

  • Renderer owns renderer creation and custom streams.
  • Image selects an image protocol for normal display.
  • Clipboard defines OSC 52 fallback and operation results.
  • Notifications defines notification policy.
  • SSH configures remote sessions.
  • Troubleshooting routes terminal-detection failures.
Changes0.6.0, 0.5.17, 0.5.13
0.6.0
core: The environment option gives terminal environment values to one renderer, for example { TERM: pty.term } from an SSH client. These values replace the forwardEnvKeys values with the same names. In an automatically detected SSH login, only TERM and COLORTERM take effect unless you set remote: true. See Terminal capabilities. (#1603)
native: Terminology gets OSC 8 hyperlinks and OSC 777 notifications when its XTVERSION reply names it. See Terminal capabilities. (#1566)
native: In an SSH or mosh login, automatically detected remote mode reads the color depth from TERM and COLORTERM. A 256-color terminal such as GNU screen 4.x gets 256-color output instead of truecolor that it cannot show. (#1624)
0.5.17
native: Hyperlink detection enables OSC 8 in more terminals: xterm.js (detected with XTVERSION), TERM_PROGRAM values vscode, zed, and mintty, and VTE 0.52.2 or newer. Without OSC 8, the terminal detects URLs one row at a time, so a wrapped link opens only the part on the clicked row. TERM_PROGRAM and VTE_VERSION count only outside a multiplexer, and detection revokes them if OpenTUI detects a multiplexer later. FORCE_HYPERLINK overrides detection, and the values 0, false, and off disable hyperlinks. (#1575)
0.5.13
core: Under tmux, capability queries stay inside tmux and replies go to the pane that asked. OpenTUI no longer sends the Kitty graphics query under tmux and ignores unowned Kitty graphics replies. An explicit image protocol override still works. (#1460)