docs/core/BUFFER_API.md

# Raxol.Core Buffer API Reference

Complete API documentation for the Raxol.Core buffer primitives.

## Overview

The Raxol.Core package provides lightweight, zero-dependency terminal buffer operations designed for standalone use or as a foundation for higher-level abstractions.

## Modules

### Raxol.Core.Buffer

Pure functional buffer operations for terminal rendering.

#### Types

```elixir
@type cell :: %{
  char: String.t(),
  style: map()
}

@type line :: %{cells: list(cell())}

@type t :: %{
  lines: list(line()),
  width: non_neg_integer(),
  height: non_neg_integer()
}
```

#### Functions

##### create_blank_buffer/2

```elixir
@spec create_blank_buffer(non_neg_integer(), non_neg_integer()) :: t()
```

Creates a blank buffer with the specified dimensions.

**Parameters:**
- `width` - Width of the buffer in characters
- `height` - Height of the buffer in lines

**Returns:** A new buffer with all cells initialized to blank spaces

**Example:**
```elixir
buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
# => %{lines: [...], width: 80, height: 24}
```

**Performance:** < 1ms for standard 80x24 buffer

---

##### write_at/5

```elixir
@spec write_at(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) :: t()
```

Writes text at the specified coordinates with optional styling.

**Parameters:**
- `buffer` - The buffer to write to
- `x` - X coordinate (column, 0-indexed)
- `y` - Y coordinate (row, 0-indexed)
- `content` - Text to write (will be split into graphemes)
- `style` - Optional style map (default: %{})

**Returns:** Updated buffer with text written

**Example:**
```elixir
buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 3, "Hello, World!")
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 4, "Styled text", %{bold: true, fg_color: :blue})
```

**Notes:**
- Text wraps character-by-character, no automatic line breaks
- Out-of-bounds writes are silently ignored
- Unicode graphemes are supported

**Performance:** < 1ms for typical strings

---

##### get_cell/3

```elixir
@spec get_cell(t(), non_neg_integer(), non_neg_integer()) :: cell() | nil
```

Retrieves the cell at the specified coordinates.

**Parameters:**
- `buffer` - The buffer to read from
- `x` - X coordinate (column)
- `y` - Y coordinate (row)

**Returns:** Cell at position, or `nil` if out of bounds

**Example:**
```elixir
buffer = Raxol.Core.Buffer.write_at(buffer, 5, 3, "A")
cell = Raxol.Core.Buffer.get_cell(buffer, 5, 3)
# => %{char: "A", style: %{}}

cell = Raxol.Core.Buffer.get_cell(buffer, 1000, 1000)
# => nil
```

**Performance:** O(1) access time

---

##### set_cell/5

```elixir
@spec set_cell(t(), non_neg_integer(), non_neg_integer(), String.t(), map()) :: t()
```

Updates a single cell at the specified coordinates.

**Parameters:**
- `buffer` - The buffer to update
- `x` - X coordinate (column)
- `y` - Y coordinate (row)
- `char` - Character to set (single grapheme)
- `style` - Style to apply

**Returns:** Updated buffer

**Example:**
```elixir
buffer = Raxol.Core.Buffer.set_cell(buffer, 10, 5, "█", %{bg_color: :red})
```

**Notes:**
- Out-of-bounds updates are silently ignored
- More efficient than `write_at` for single characters

**Performance:** < 100μs

---

##### clear/1

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

Clears the buffer, resetting all cells to blank.

**Parameters:**
- `buffer` - The buffer to clear

**Returns:** New buffer with same dimensions, all cells blank

**Example:**
```elixir
buffer = Raxol.Core.Buffer.clear(buffer)
```

**Performance:** Same as `create_blank_buffer/2`

---

##### resize/3

```elixir
@spec resize(t(), non_neg_integer(), non_neg_integer()) :: t()
```

Resizes the buffer to new dimensions.

**Parameters:**
- `buffer` - The buffer to resize
- `width` - New width
- `height` - New height

**Returns:** Resized buffer

**Example:**
```elixir
# Expand buffer
buffer = Raxol.Core.Buffer.resize(buffer, 120, 40)

# Shrink buffer (content is cropped)
buffer = Raxol.Core.Buffer.resize(buffer, 40, 20)
```

**Behavior:**
- Expanding: New cells are filled with blank spaces
- Shrinking: Content is cropped from bottom and right
- Existing content is preserved where it fits

**Performance:** < 2ms for standard sizes

---

##### to_string/1

```elixir
@spec to_string(t()) :: String.t()
```

Converts the buffer to a string representation for debugging.

**Parameters:**
- `buffer` - The buffer to convert

**Returns:** Multi-line string showing buffer contents

**Example:**
```elixir
buffer = Raxol.Core.Buffer.create_blank_buffer(10, 3)
buffer = Raxol.Core.Buffer.write_at(buffer, 0, 0, "Hello")
buffer = Raxol.Core.Buffer.write_at(buffer, 0, 1, "World")

IO.puts(Raxol.Core.Buffer.to_string(buffer))
# Output:
# Hello
# World
#
```

**Notes:**
- Styles are not rendered (use Raxol.Core.Renderer for styled output)
- Useful for testing and debugging
- Each line ends with a newline

**Performance:** < 1ms for standard buffers

---

### Raxol.Core.Renderer

Pure functional rendering and diffing operations.

#### Functions

##### render_to_string/1

```elixir
@spec render_to_string(Buffer.t()) :: String.t()
```

Renders buffer to plain ASCII string (no ANSI codes).

**Parameters:**
- `buffer` - The buffer to render

**Returns:** String representation

**Example:**
```elixir
output = Raxol.Core.Renderer.render_to_string(buffer)
IO.puts(output)
```

**Performance:** < 1ms for 80x24 buffer

---

##### render_diff/2

```elixir
@spec render_diff(Buffer.t(), Buffer.t()) :: list(String.t())
```

Calculates minimal updates between two buffers.

**Parameters:**
- `old_buffer` - Previous buffer state
- `new_buffer` - New buffer state

**Returns:** List of ANSI cursor positioning and update sequences

**Example:**
```elixir
old_buffer = Raxol.Core.Buffer.create_blank_buffer(80, 24)
new_buffer = Raxol.Core.Buffer.write_at(old_buffer, 5, 3, "Changed!")

diff = Raxol.Core.Renderer.render_diff(old_buffer, new_buffer)
# => ["\e[4;6HChanged!"]  # Only updates changed cells
```

**Optimization:**
- Only generates updates for changed lines
- Uses efficient Enum.zip for line comparison
- Minimal ANSI escape sequences

**Performance:** < 2ms for 80x24 buffer (target met in benchmarks)

---

### Raxol.Core.Style

Style management and ANSI escape code generation.

#### Functions

##### new/1

```elixir
@spec new(keyword()) :: map()
```

Creates a new style map with validation.

**Parameters:**
- `opts` - Keyword list of style options

**Returns:** Validated style map

**Options:**
- `:bold` - Boolean
- `:italic` - Boolean
- `:underline` - Boolean
- `:fg_color` - Foreground color (atom, RGB tuple, or integer)
- `:bg_color` - Background color (atom, RGB tuple, or integer)

**Example:**
```elixir
style = Raxol.Core.Style.new(bold: true, fg_color: :blue)
# => %{bold: true, fg_color: :blue}
```

---

##### merge/2

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

Merges two style maps (second overrides first).

**Example:**
```elixir
base = Raxol.Core.Style.new(bold: true, fg_color: :blue)
override = Raxol.Core.Style.new(fg_color: :red)
result = Raxol.Core.Style.merge(base, override)
# => %{bold: true, fg_color: :red}
```

---

##### rgb/3

```elixir
@spec rgb(0..255, 0..255, 0..255) :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}
```

Creates an RGB color tuple.

**Example:**
```elixir
color = Raxol.Core.Style.rgb(255, 100, 50)
style = Raxol.Core.Style.new(fg_color: color)
```

---

##### color_256/1

```elixir
@spec color_256(0..255) :: non_neg_integer()
```

Creates a 256-color palette index.

**Example:**
```elixir
color = Raxol.Core.Style.color_256(196)  # Bright red
style = Raxol.Core.Style.new(bg_color: color)
```

---

##### named_color/1

```elixir
@spec named_color(atom()) :: atom()
```

Validates named colors.

**Supported Colors:**
`:black`, `:red`, `:green`, `:yellow`, `:blue`, `:magenta`, `:cyan`, `:white`

**Example:**
```elixir
color = Raxol.Core.Style.named_color(:blue)
```

---

##### to_ansi/1

```elixir
@spec to_ansi(map()) :: String.t()
```

Converts style to ANSI escape codes.

**Example:**
```elixir
style = Raxol.Core.Style.new(bold: true, fg_color: :blue)
ansi = Raxol.Core.Style.to_ansi(style)
# => "\e[1;34m"
```

---

### Raxol.Core.Box

Box drawing and area fill utilities.

#### Types

```elixir
@type box_style :: :single | :double | :rounded | :heavy | :dashed
```

#### Functions

##### draw_box/6

```elixir
@spec draw_box(Buffer.t(), non_neg_integer(), non_neg_integer(),
               non_neg_integer(), non_neg_integer(), box_style()) :: Buffer.t()
```

Draws a box at the specified coordinates.

**Parameters:**
- `buffer` - The buffer to draw on
- `x` - X coordinate (left edge)
- `y` - Y coordinate (top edge)
- `width` - Width of the box
- `height` - Height of the box
- `style` - Box style (default: `:single`)

**Box Styles:**
- `:single` - Single line (─│┌┐└┘)
- `:double` - Double line (═║╔╗╚╝)
- `:rounded` - Rounded corners (─│╭╮╰╯)
- `:heavy` - Heavy/bold lines (━┃┏┓┗┛)
- `:dashed` - Dashed lines (╌╎┌┐└┘)

**Example:**
```elixir
buffer = Raxol.Core.Box.draw_box(buffer, 5, 3, 30, 10, :double)
```

**Performance:** 38-588μs depending on size and style

---

##### draw_horizontal_line/5

```elixir
@spec draw_horizontal_line(Buffer.t(), non_neg_integer(), non_neg_integer(),
                           non_neg_integer(), String.t()) :: Buffer.t()
```

Draws a horizontal line.

**Parameters:**
- `buffer` - The buffer to draw on
- `x` - Starting X coordinate
- `y` - Y coordinate (row)
- `length` - Length of the line
- `char` - Character to use (default: "-")

**Example:**
```elixir
buffer = Raxol.Core.Box.draw_horizontal_line(buffer, 0, 0, 80, "=")
```

**Performance:** ~10μs for 20 characters

---

##### draw_vertical_line/5

```elixir
@spec draw_vertical_line(Buffer.t(), non_neg_integer(), non_neg_integer(),
                         non_neg_integer(), String.t()) :: Buffer.t()
```

Draws a vertical line.

**Parameters:**
- `buffer` - The buffer to draw on
- `x` - X coordinate (column)
- `y` - Starting Y coordinate
- `length` - Length of the line
- `char` - Character to use (default: "|")

**Example:**
```elixir
buffer = Raxol.Core.Box.draw_vertical_line(buffer, 40, 0, 24, "║")
```

**Performance:** ~8μs for 10 characters

---

##### fill_area/7

```elixir
@spec fill_area(Buffer.t(), non_neg_integer(), non_neg_integer(),
                non_neg_integer(), non_neg_integer(), String.t(), map()) :: Buffer.t()
```

Fills a rectangular area with a character and style.

**Parameters:**
- `buffer` - The buffer to draw on
- `x` - X coordinate (left edge)
- `y` - Y coordinate (top edge)
- `width` - Width of the area
- `height` - Height of the area
- `char` - Character to fill with
- `style` - Style to apply (default: %{})

**Example:**
```elixir
# Fill with background color
buffer = Raxol.Core.Box.fill_area(buffer, 10, 5, 20, 10, " ", %{bg_color: :blue})

# Fill with pattern
buffer = Raxol.Core.Box.fill_area(buffer, 10, 5, 20, 10, "░", %{})
```

**Performance:** ~44μs for 10x10 area, ~1.3ms for full 80x24 buffer

---

## Performance Targets

All operations designed to complete in < 1ms for standard 80x24 buffers:

| Operation | Target | Actual (avg) | Status |
|-----------|--------|--------------|--------|
| create_blank_buffer | < 1ms | ~0.5ms | ✅ |
| write_at (short string) | < 1ms | ~0.1ms | ✅ |
| get_cell | < 1ms | ~0.001ms | ✅ |
| set_cell | < 1ms | ~0.1ms | ✅ |
| clear | < 1ms | ~0.5ms | ✅ |
| resize | < 2ms | ~1ms | ✅ |
| to_string | < 1ms | ~0.5ms | ✅ |
| render_diff | < 2ms | ~2ms | ✅ |
| draw_box | < 1ms | 0.04-0.6ms | ✅ |
| draw_line | < 1ms | 0.01ms | ✅ |
| fill_area (small) | < 1ms | 0.04ms | ✅ |

See `bench/core/` for detailed benchmarks.

## Error Handling

All functions use defensive programming:
- Out-of-bounds coordinates are silently ignored
- Invalid dimensions default to minimum viable values
- No exceptions thrown for normal usage
- Pattern matching validates input types at compile time

## Thread Safety

All modules are pure functional - no shared state:
- Safe for concurrent use
- No GenServers or processes
- Immutable data structures throughout
- Can be used in any context (LiveView, Phoenix, CLI, scripts)

## See Also

- [Getting Started Guide](./GETTING_STARTED.md)
- [Architecture Documentation](./ARCHITECTURE.md)
- [Examples](../../examples/core/README.md)
- [Benchmarks](../../docs/bench/README.md)