# `TTYCast`

Seekable, compressed terminal recordings for BEAM applications.

TTYCast records terminal output, input metadata, resize events, and custom
semantic events into a self-contained `.ttycast` file. Recordings are split
into independently compressed chunks with Ghostty terminal keyframes so readers
can seek by timestamp without replaying the whole file.

## Writing

    TTYCast.write("demo.ttycast", [width: 120, height: 40], fn writer ->
      TTYCast.Writer.write(writer, "hello\r\n")
    end)

## Reading

    cast = TTYCast.open!("demo.ttycast")
    TTYCast.info(cast)
    TTYCast.snapshot!(cast, time_ms: 1_000)
    TTYCast.stream(cast) |> Enum.to_list()

## Recording commands

    TTYCast.record(["sh", "-lc", "echo hello"], path: "demo.ttycast")

See `FORMAT.md` for the binary container layout.

# `event`

```elixir
@type event() :: TTYCast.Writer.event()
```

# `events`

```elixir
@spec events(
  Path.t() | TTYCast.Cast.t(),
  keyword()
) :: [event()]
```

Returns decoded events as a list. Prefer `stream/2` for large recordings.

# `export`

```elixir
@spec export(Path.t() | TTYCast.Cast.t(), :asciinema, Path.t()) ::
  :ok | {:error, term()}
```

Exports a recording to a supported format.

# `export_asciinema`

```elixir
@spec export_asciinema(Path.t() | TTYCast.Cast.t(), Path.t()) ::
  :ok | {:error, term()}
```

Exports terminal input/output streams as asciinema v2 JSON lines.

# `find`

```elixir
@spec find(Path.t() | TTYCast.Cast.t(), binary() | Regex.t(), keyword()) :: [
  %{time_ms: non_neg_integer(), match: binary()}
]
```

Finds times where plain terminal snapshots contain a string or regex match.

# `import`

```elixir
@spec import(Path.t(), :asciinema, Path.t(), keyword()) ::
  {:ok, TTYCast.Cast.t()} | {:error, term()}
```

Imports a recording from a supported format.

# `import_asciinema`

```elixir
@spec import_asciinema(Path.t(), Path.t(), keyword()) ::
  {:ok, TTYCast.Cast.t()} | {:error, term()}
```

Imports asciinema v2 JSON lines into a ttycast file.

# `info`

```elixir
@spec info(Path.t() | TTYCast.Cast.t()) :: map()
```

Returns a compact summary map for a recording.

# `into`

```elixir
@spec into(pid()) :: TTYCast.Sink.t()
```

Returns a collectable sink that writes iodata into the recording.

# `open`

```elixir
@spec open(Path.t() | TTYCast.Cast.t()) :: {:ok, TTYCast.Cast.t()} | {:error, term()}
```

Opens a recording without decoding chunks eagerly.

# `open!`

```elixir
@spec open!(Path.t() | TTYCast.Cast.t()) :: TTYCast.Cast.t()
```

Opens a recording or raises.

# `read_chunk`

```elixir
@spec read_chunk(Path.t() | TTYCast.Cast.t(), map()) ::
  {:ok, map()} | {:error, term()}
```

Reads and decodes one compressed chunk by index entry.

# `read_chunk!`

```elixir
@spec read_chunk!(Path.t() | TTYCast.Cast.t(), map()) :: map()
```

Reads one compressed chunk or raises.

# `record`

```elixir
@spec record(
  [String.t()] | {String.t(), [String.t()]},
  keyword()
) :: {:ok, TTYCast.Recorder.result()} | {:error, term()}
```

Records a command under a real pseudo-terminal.

# `record`

```elixir
@spec record(String.t(), [String.t()], keyword()) ::
  {:ok, TTYCast.Recorder.result()} | {:error, term()}
```

# `record_interactive`

```elixir
@spec record_interactive(
  [String.t()],
  keyword()
) :: {:ok, map()} | {:error, term()}
```

Records a command interactively, forwarding the current terminal to the child PTY.

# `reindex`

```elixir
@spec reindex(Path.t()) :: {:ok, map()} | {:error, term()}
```

Rebuilds the trailer/footer index from intact chunks.

# `snapshot`

```elixir
@spec snapshot(
  Path.t() | TTYCast.Cast.t(),
  keyword()
) :: {:ok, binary() | map()} | {:error, term()}
```

Returns a Ghostty-rendered terminal snapshot at a timestamp.

# `snapshot!`

```elixir
@spec snapshot!(
  Path.t() | TTYCast.Cast.t(),
  keyword()
) :: binary() | map()
```

Returns a terminal snapshot or raises.

# `start_writer`

```elixir
@spec start_writer(keyword()) :: GenServer.on_start()
```

Starts a recording writer.

# `stream`

```elixir
@spec stream(
  Path.t() | TTYCast.Cast.t(),
  keyword()
) :: Enumerable.t()
```

Streams decoded events lazily from chunks matching the optional time range.

# `write`

```elixir
@spec write(Path.t(), keyword(), (pid() -&gt; term())) ::
  {:ok, term()} | {:error, term()}
```

Opens a writer, calls `fun`, and closes the writer afterwards.

---

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