# `Ascii.Diagram`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/diagram.ex#L1)

Native, data-driven diagrams rendered as `Ascii.Frame`s.

Flowcharts, sequences and C4 views have separate models and layout
strategies. They share drawing and the existing frame renderers; no Node,
browser or dependency is required. See the [diagram guide](guides/diagrams.md)
for the supported Mermaid syntax and limits.

    iex> {:ok, diagram} = Ascii.Diagram.flowchart([%{id: "a", label: "Hello"}], [])
    iex> {:ok, frame} = Ascii.Diagram.render(diagram)
    iex> frame.text =~ "Hello"
    true

Constructors validate data without truncation. Labels accept single-cell
Unicode and newlines, normalized to NFC. Wide characters, emoji and
residual combining marks require a different display-width contract and
are rejected. The drawing itself can use ASCII or Unicode.

# `error`

```elixir
@type error() ::
  {:error, {:invalid_diagram, term()}}
  | {:error, {:invalid_option, term()}}
  | {:error, {:too_large, {pos_integer(), pos_integer()}}}
  | {:error, {:too_many_colors, pos_integer()}}
```

# `t`

```elixir
@type t() :: %Ascii.Diagram{data: map(), type: :flowchart | :sequence | :c4}
```

# `c4`

```elixir
@spec c4(
  level :: atom(),
  elements :: [map()],
  relationships :: [map()],
  opts :: keyword()
) ::
  {:ok, t()} | error()
```

A C4 `:context`, `:container` or `:component` view.

Elements take `:kind`, `:technology`, `:description`, `:external` and an
optional `:boundary` ID. `boundaries:` is a list of `%{id:, label:}` maps,
with `parent:` for nested boundaries. Elements and boundaries can carry
optional foreground styles (`:color`, `:label_color`, `:border_color`).

# `flowchart`

```elixir
@spec flowchart(nodes :: [map()], edges :: [map()], opts :: keyword()) ::
  {:ok, t()} | error()
```

A directed graph. Node IDs and edge endpoints are strings; direction defaults to `:tb`.

# `layout`

```elixir
@spec layout(diagram :: t(), opts :: keyword()) ::
  {:ok, Ascii.Diagram.Layout.t()} | error()
```

Computes deterministic geometry once, retaining node bounds and edge paths.

Options: `:label_width` (24), `:gap_cols` (8), `:gap_rows` (3),
`:max_cols` (400), `:max_rows` (200). Oversize diagrams return an error.
Rendering options (`:charset`, `:theme`, `:color`) are also accepted.

# `put_style`

```elixir
@spec put_style(
  diagram :: t(),
  target :: {atom(), String.t()},
  style :: map() | keyword()
) ::
  {:ok, t()} | error()
```

Changes the optional foreground styles of one identified element.

Targets are `{:node, id}`, `{:edge, id}`, `{:boundary, id}`, and for
sequences `{:fragment, id}`, `{:note, id}`, `{:activation, id}`.
Styles take `:color`, `:label_color`, `:border_color`, `:line_color`;
a `nil` value clears an override. Colours use `Ascii.Ink.color/1`.
Styles do not affect geometry. Unknown IDs and invalid colours are errors.

# `render`

```elixir
@spec render(diagram :: t(), opts :: keyword()) :: {:ok, Ascii.Frame.t()} | error()
```

Layouts and draws a diagram as a frame, ready for any existing renderer.

# `sequence`

```elixir
@spec sequence(participants :: [map()], messages :: [map()]) :: {:ok, t()} | error()
```

Participants in display order and ordered events: messages, notes, activations and nested fragments.

---

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