A managed terminal emulator backed by libghostty-vt.
Each terminal is a GenServer that owns a libghostty-vt terminal instance. All operations are serialized through the GenServer to ensure thread safety (libghostty-vt terminal instances are not thread-safe).
Examples
{:ok, term} = Ghostty.Terminal.start_link(cols: 120, rows: 40)
Ghostty.Terminal.write(term, File.read!("recording.vt"))
{:ok, html} = Ghostty.Terminal.snapshot(term, :html)Supervision
children = [
{Ghostty.Terminal, name: :main, cols: 80, rows: 24, max_scrollback: 50_000}
]Effects
Terminal programs can trigger side effects via VT sequences (query
responses, bell, title changes). These are forwarded as messages to
the process that called start_link/1:
{:pty_write, binary}— query responses to write back to the PTY:bell— BEL character:title_changed— title change via OSC 2
Snapshot formats
:plain— plain text with all styling stripped:html— HTML with inline styles preserving all colors and attributes:vt— raw VT escape sequences (round-trippable)
Summary
Functions
Returns the terminal screen as a grid of cells.
Returns a child spec for use in supervision trees.
Returns the current cursor position as {col, row} (0-indexed).
Returns the current render-state cursor metadata for the visible viewport.
Encodes a focus event into a terminal escape sequence.
Returns whether focus reporting (DEC mode 1004) is enabled.
Encodes a key event into a terminal escape sequence.
Encodes a mouse event into a terminal escape sequence.
Returns the current terminal mouse reporting mode state.
Returns the current visible render-state cells together with cursor metadata.
Performs a full terminal reset (RIS — Reset to Initial State).
Resizes the terminal. Text reflow is handled automatically.
Scrolls the terminal viewport.
Returns the scrollbar state for the terminal viewport.
Returns the current terminal dimensions as {cols, rows}.
Returns a snapshot of the terminal screen content.
Starts a terminal process linked to the caller.
Writes VT-encoded data to the terminal.
Types
@type cell() :: {binary(), rgb() | nil, rgb() | nil, non_neg_integer()}
@type cursor_state() :: %{ x: non_neg_integer() | nil, y: non_neg_integer() | nil, visible: boolean(), blinking: boolean(), style: cursor_style(), wide_tail: boolean(), color: rgb() | nil }
@type cursor_style() :: :bar | :block | :underline | :block_hollow
@type format() :: :plain | :html | :vt
@type option() :: {:cols, pos_integer()} | {:rows, pos_integer()} | {:max_scrollback, non_neg_integer()} | {:name, GenServer.name()}
@type render_state() :: %{ cells: [[cell()]], cursor: cursor_state(), mouse: mouse_modes(), scrollbar: scrollbar(), focus_reporting: boolean() }
@type scrollbar() :: %{ total: non_neg_integer(), offset: non_neg_integer(), len: non_neg_integer() }
Functions
@spec cells(GenServer.server()) :: [[cell()]]
Returns the terminal screen as a grid of cells.
Each cell is {grapheme, fg, bg, flags} where:
grapheme— UTF-8 binary (empty for blank cells)fg/bg—{r, g, b}tuples ornilflags— bitmask (seeGhostty.Terminal.Cellfor helpers)
Examples
rows = Ghostty.Terminal.cells(term)
for row <- rows, {char, fg, _bg, flags} <- row, char != "" do
if Ghostty.Terminal.Cell.bold?({char, fg, nil, flags}), do: IO.write("*")
IO.write(char)
end
Returns a child spec for use in supervision trees.
@spec cursor(GenServer.server()) :: {non_neg_integer(), non_neg_integer()}
Returns the current cursor position as {col, row} (0-indexed).
@spec cursor_state(GenServer.server()) :: cursor_state()
Returns the current render-state cursor metadata for the visible viewport.
Encodes a focus event into a terminal escape sequence.
Examples
{:ok, seq} = Ghostty.Terminal.encode_focus(true) # => "\e[I"
{:ok, seq} = Ghostty.Terminal.encode_focus(false) # => "\e[O"
@spec focus_reporting?(GenServer.server()) :: boolean()
Returns whether focus reporting (DEC mode 1004) is enabled.
@spec input_key(GenServer.server(), Ghostty.KeyEvent.t()) :: {:ok, binary()} | :none
Encodes a key event into a terminal escape sequence.
Returns {:ok, sequence} with the bytes to send to the PTY,
or :none if the key event produces no output.
Examples
Ghostty.Terminal.input_key(term, %Ghostty.KeyEvent{key: :enter})
# => {:ok, "\r"}
Ghostty.Terminal.input_key(term, %Ghostty.KeyEvent{key: :c, mods: [:ctrl]})
# => {:ok, <<3>>}
@spec input_mouse(GenServer.server(), Ghostty.MouseEvent.t()) :: {:ok, binary()} | :none
Encodes a mouse event into a terminal escape sequence.
Returns {:ok, sequence} or :none if mouse tracking is not
enabled by the running program.
@spec mouse_modes(GenServer.server()) :: mouse_modes()
Returns the current terminal mouse reporting mode state.
@spec render_state(GenServer.server()) :: render_state()
Returns the current visible render-state cells together with cursor metadata.
@spec reset(GenServer.server()) :: :ok
Performs a full terminal reset (RIS — Reset to Initial State).
@spec resize(GenServer.server(), pos_integer(), pos_integer()) :: :ok
Resizes the terminal. Text reflow is handled automatically.
Examples
Ghostty.Terminal.resize(term, 120, 40)
@spec scroll(GenServer.server(), integer()) :: :ok
Scrolls the terminal viewport.
Positive delta scrolls down (towards newer content),
negative scrolls up (towards scrollback history).
@spec scrollbar(GenServer.server()) :: scrollbar()
Returns the scrollbar state for the terminal viewport.
@spec size(GenServer.server()) :: {pos_integer(), pos_integer()}
Returns the current terminal dimensions as {cols, rows}.
@spec snapshot(GenServer.server(), format()) :: {:ok, binary()}
Returns a snapshot of the terminal screen content.
Formats
:plain— plain text, stripped of all styling (default):html— HTML with inline styles preserving colors and attributes:vt— raw VT escape sequences
Examples
{:ok, text} = Ghostty.Terminal.snapshot(term)
{:ok, html} = Ghostty.Terminal.snapshot(term, :html)
{:ok, vt} = Ghostty.Terminal.snapshot(term, :vt)
@spec start_link([option()]) :: GenServer.on_start()
Starts a terminal process linked to the caller.
Effect messages (:bell, :title_changed, {:pty_write, data})
are sent to the calling process.
Options
:cols- number of columns (default:80):rows- number of rows (default:24):max_scrollback- maximum scrollback lines (default:10_000):name- GenServer name registration
@spec write(GenServer.server(), iodata()) :: :ok
Writes VT-encoded data to the terminal.
The terminal's VT parser processes escape sequences and updates the internal screen, cursor, and style state. Accepts iodata.
Examples
Ghostty.Terminal.write(term, "hello world")
Ghostty.Terminal.write(term, "\e[31mred\e[0m")
Ghostty.Terminal.write(term, ["line 1\r\n", "line 2\r\n"])