# `Alaja.Cell`
[🔗](https://github.com/Lorenzo-SF/alaja/blob/2.1.0/lib/alaja/cell.ex#L1)

Atomic visual unit for the terminal buffer.

A cell holds a single character, optional foreground/background RGB
colours, and a list of text effects (bold, italic, underline, etc.).

All operations return new cells — no mutation. Colours can be RGB
tuples, Pote theme atoms (resolved at ANSI-build time), or `nil`.

# `color`

```elixir
@type color() :: {0..255, 0..255, 0..255} | atom() | nil
```

# `effect`

```elixir
@type effect() ::
  :bold
  | :italic
  | :underline
  | :strikethrough
  | :reverse
  | :blink
  | :hidden
  | :dim
```

# `effects`

```elixir
@type effects() :: [effect()]
```

# `t`

```elixir
@type t() :: %Alaja.Cell{
  bg: color(),
  char: String.t(),
  effects: effects(),
  fg: color()
}
```

# `apply_effect`

```elixir
@spec apply_effect(t(), effect()) :: t()
```

Applies an additional effect to a cell, returning a new cell.

## Examples

    iex> cell = Cell.new("A", {255, 0, 0})
    iex> Cell.apply_effect(cell, :bold)
    %Cell{char: "A", fg: {255, 0, 0}, bg: nil, effects: [:bold]}

# `empty`

```elixir
@spec empty() :: t()
```

Creates an empty cell (space, no color, no effects).

## Examples

    iex> Cell.empty()
    %Cell{char: " ", fg: nil, bg: nil, effects: []}

# `equal?`

```elixir
@spec equal?(t(), t()) :: boolean()
```

Compares two cells for visual equality.

Two cells are equal if they would produce identical terminal output.
Effects are compared as sets (order-independent).

# `merge`

```elixir
@spec merge(t(), t()) :: t()
```

Merges two cells, with the overlay cell taking precedence over the base.

- If overlay has a non-space char, it replaces base.char
- If overlay has a non-nil color, it replaces base color
- Effects are merged (overlay first, unique)

## Examples

    iex> base = Cell.new("A", {255, 0, 0})
    iex> overlay = Cell.new("B", {0, 255, 0})
    iex> Cell.merge(base, overlay)
    %Cell{char: "B", fg: {0, 255, 0}, bg: nil, effects: []}

# `new`

```elixir
@spec new(String.t(), color() | keyword(), color(), keyword()) :: t()
```

Creates a new cell with the given character and optional colors.

## Parameters

- `char` - The character to display (default: `" "`)
- `opts` - Optional keyword list for foreground, background and effects.
- `fg` - Foreground color as `{r, g, b}` tuple or atom (default: `nil`)
- `bg` - Background color as `{r, g, b}` tuple or atom (default: `nil`)

## Examples

    iex> Cell.new("A", {255, 0, 0}, {0, 255, 0})
    %Cell{char: "A", fg: {255, 0, 0}, bg: {0, 255, 0}, effects: []}

    iex> Cell.new("★", {255, 215, 0}, nil, effects: [:bold])
    %Cell{char: "★", fg: {255, 215, 0}, bg: nil, effects: [:bold]}

# `to_ansi`

```elixir
@spec to_ansi(t()) :: iodata()
```

Converts a cell to its ANSI escape sequence representation.

Produces true-color (24-bit) ANSI sequences for RGB colors.
Always resets formatting after the character.

## Examples

    iex> Cell.to_ansi(Cell.new("X", {255, 0, 0}))
    "[38;2;255;0;0mX[0m"

    iex> Cell.to_ansi(Cell.empty())
    " "

# `to_ansi_prefix`

```elixir
@spec to_ansi_prefix(t()) :: iodata()
```

Returns the ANSI prefix for a cell without the character or reset.

Enables run-length encoding in the renderer: consecutive cells with
identical styling share a single prefix and reset.

# `visual_width`

```elixir
@spec visual_width(t()) :: 0 | 1 | 2
```

Returns the visual display width of the cell's character.

ASCII characters have width 1. Wide characters (CJK, emoji) have width 2.
Control characters have width 0.

## Examples

    iex> Cell.visual_width(Cell.new("A"))
    1

    iex> Cell.visual_width(Cell.new("中"))
    2

---

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