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.
Summary
Functions
Applies an additional effect to a cell, returning a new cell.
Creates an empty cell (space, no color, no effects).
Compares two cells for visual equality.
Merges two cells, with the overlay cell taking precedence over the base.
Creates a new cell with the given character and optional colors.
Converts a cell to its ANSI escape sequence representation.
Returns the ANSI prefix for a cell without the character or reset.
Returns the visual display width of the cell's character.
Types
Functions
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]}
@spec empty() :: t()
Creates an empty cell (space, no color, no effects).
Examples
iex> Cell.empty()
%Cell{char: " ", fg: nil, bg: nil, effects: []}
Compares two cells for visual equality.
Two cells are equal if they would produce identical terminal output. Effects are compared as sets (order-independent).
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: []}
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]}
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())
" "
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.
@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