# `Ghostty.Terminal`
[🔗](https://github.com/dannote/ghostty_ex/blob/v0.5.0/lib/ghostty/terminal.ex#L1)

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)

# `cell`

```elixir
@type cell() :: {binary(), rgb() | nil, rgb() | nil, non_neg_integer()}
```

# `cursor_state`

```elixir
@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
}
```

# `cursor_style`

```elixir
@type cursor_style() :: :bar | :block | :underline | :block_hollow
```

# `format`

```elixir
@type format() :: :plain | :html | :vt
```

# `mouse_modes`

```elixir
@type mouse_modes() :: %{
  tracking: boolean(),
  x10: boolean(),
  normal: boolean(),
  button: boolean(),
  any: boolean(),
  sgr: boolean()
}
```

# `option`

```elixir
@type option() ::
  {:cols, pos_integer()}
  | {:rows, pos_integer()}
  | {:max_scrollback, non_neg_integer()}
  | {:foreground, rgb()}
  | {:background, rgb()}
  | {:cursor_color, rgb()}
  | {:palette, palette()}
  | {:name, GenServer.name()}
```

# `palette`

```elixir
@type palette() :: [rgb()] | %{optional(0..255) =&gt; rgb()}
```

# `render_state`

```elixir
@type render_state() :: %{
  cells: [[cell()]],
  cursor: cursor_state(),
  mouse: mouse_modes(),
  scrollbar: scrollbar(),
  focus_reporting: boolean(),
  foreground: rgb() | nil,
  background: rgb() | nil
}
```

# `rgb`

```elixir
@type rgb() :: {byte(), byte(), byte()}
```

# `scrollbar`

```elixir
@type scrollbar() :: %{
  total: non_neg_integer(),
  offset: non_neg_integer(),
  len: non_neg_integer()
}
```

# `theme`

```elixir
@type theme() :: %{
  foreground: rgb() | nil,
  background: rgb() | nil,
  cursor: rgb() | nil,
  palette: [rgb()]
}
```

# `cells`

```elixir
@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 or `nil`
  * `flags` — bitmask (see `Ghostty.Terminal.Cell` for 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

# `child_spec`

Returns a child spec for use in supervision trees.

# `cursor`

```elixir
@spec cursor(GenServer.server()) :: {non_neg_integer(), non_neg_integer()}
```

Returns the current cursor position as `{col, row}` (0-indexed).

# `cursor_state`

```elixir
@spec cursor_state(GenServer.server()) :: cursor_state()
```

Returns the current render-state cursor metadata for the visible viewport.

# `encode_focus`

```elixir
@spec encode_focus(boolean()) :: {:ok, binary()} | :none
```

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"

# `focus_reporting?`

```elixir
@spec focus_reporting?(GenServer.server()) :: boolean()
```

Returns whether focus reporting (DEC mode 1004) is enabled.

# `input_key`

```elixir
@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>>}

# `input_mouse`

```elixir
@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.

# `mouse_modes`

```elixir
@spec mouse_modes(GenServer.server()) :: mouse_modes()
```

Returns the current terminal mouse reporting mode state.

# `render_state`

```elixir
@spec render_state(GenServer.server()) :: render_state()
```

Returns the current visible render-state cells together with cursor metadata.

# `reset`

```elixir
@spec reset(GenServer.server()) :: :ok
```

Performs a full terminal reset (RIS — Reset to Initial State).

# `resize`

```elixir
@spec resize(GenServer.server(), pos_integer(), pos_integer()) :: :ok
```

Resizes the terminal. Text reflow is handled automatically.

## Examples

    Ghostty.Terminal.resize(term, 120, 40)

# `scroll`

```elixir
@spec scroll(GenServer.server(), integer()) :: :ok
```

Scrolls the terminal viewport.

Positive `delta` scrolls down (towards newer content),
negative scrolls up (towards scrollback history).

# `scrollbar`

```elixir
@spec scrollbar(GenServer.server()) :: scrollbar()
```

Returns the scrollbar state for the terminal viewport.

# `size`

```elixir
@spec size(GenServer.server()) :: {pos_integer(), pos_integer()}
```

Returns the current terminal dimensions as `{cols, rows}`.

# `snapshot`

```elixir
@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)

# `start_link`

```elixir
@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`)
  * `:foreground` - default foreground as `{red, green, blue}`
  * `:background` - default background as `{red, green, blue}`
  * `:cursor_color` - default cursor color as `{red, green, blue}`
  * `:palette` - palette colors as a list starting at index 0, or a map of
    palette indices (`0..255`) to RGB tuples; unspecified entries retain
    libghostty-vt's built-in defaults
  * `:name` - GenServer name registration

# `theme`

```elixir
@spec theme(GenServer.server()) :: theme()
```

Returns the terminal's effective color theme.

Values include defaults configured at startup plus any overrides applied by
terminal programs through OSC color sequences. The palette always contains
256 entries.

# `write`

```elixir
@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"])

---

*Consult [api-reference.md](api-reference.md) for complete listing*
