# Boxart
Terminal graph rendering with Unicode box-drawing characters.
Takes a `Graph.t()` from [libgraph](https://hex.pm/packages/libgraph)
and renders it as ASCII/Unicode art in the terminal, with multiline labels
inside nodes, edge labels, and automatic layout.
## Usage
```elixir
graph =
Graph.new()
|> Graph.add_vertex("A", label: "Start")
|> Graph.add_vertex("B", label: "Process")
|> Graph.add_vertex("C", label: "End")
|> Graph.add_edge("A", "B")
|> Graph.add_edge("B", "C")
IO.puts(Boxart.render(graph, direction: :lr))
```
```
┌─────────┐ ┌───────────┐ ┌───────┐
│ │ │ │ │ │
│ Start ├───►│ Process ├───►│ End │
│ │ │ │ │ │
└─────────┘ └───────────┘ └───────┘
```
Branching with edge labels and node shapes:
```elixir
graph =
Graph.new()
|> Graph.add_vertex("A", label: "Start")
|> Graph.add_vertex("B", label: "Decision", shape: :diamond)
|> Graph.add_vertex("C", label: "Process")
|> Graph.add_vertex("D", label: "End")
|> Graph.add_edge("A", "B")
|> Graph.add_edge("B", "C", label: "yes")
|> Graph.add_edge("B", "D", label: "no")
IO.puts(Boxart.render(graph, direction: :td))
```
```
┌────────────┐
│ │
│ Start │
│ │
└──────┬─────┘
│
│
▼
┌──────◇─────┐
│ │
│ Decision │
│ │
└──────◇─────┘
│
│
├─────────────────╮no
yes│ │
▼ ▼
┌────────────┐ ┌────────────┐
│ │ │ │
│ Process │ │ End │
│ │ │ │
└────────────┘ └────────────┘
```
## Code nodes
Render source code with line numbers inside nodes — useful for control flow
graphs, program dependence graphs, and code analysis tools:
```elixir
graph =
Graph.new()
|> Graph.add_vertex("entry",
source: "def fetch(url) do\n case HTTP.get(url) do",
start_line: 1,
language: :elixir
)
|> Graph.add_vertex("ok",
source: "{:ok, body} ->\n body",
start_line: 3
)
|> Graph.add_vertex("err",
source: "{:error, reason} ->\n raise reason",
start_line: 5
)
|> Graph.add_edge("entry", "ok", label: ":ok")
|> Graph.add_edge("entry", "err", label: ":error")
IO.puts(Boxart.render(graph, direction: :td))
```
```
┌───┬─────────────────────────────┐
│ 1 │ def fetch(url) do │
│ 2 │ case HTTP.get(url) do │
└───┴────────────┬────────────────┘
│
│
├─────────────────────────────────────╮:ok
:error│ │
▼ ▼
┌───┬─────────────────────────────┐ ┌───┬─────────────────────┐
│ 5 │ {:error, reason} -> │ │ 3 │ {:ok, body} -> │
│ 6 │ raise reason │ │ 4 │ body │
└───┴─────────────────────────────┘ └───┴─────────────────────┘
```
When `makeup` and `makeup_elixir` are installed, code is syntax-highlighted
with ANSI colors in the terminal.
## Vertex labels
Vertex labels are keyword lists (libgraph convention). Boxart recognizes:
- `:label` — display text inside the node (defaults to `inspect(vertex)`)
- `:shape` — node shape atom (`:rectangle`, `:diamond`, `:rounded`, `:hexagon`,
`:stadium`, `:circle`, `:cylinder`, etc.)
- `:source` — source code string (renders as code node with line numbers)
- `:start_line` — first line number for code display (default: `1`)
- `:language` — language atom for syntax highlighting (e.g. `:elixir`)
## Edge labels
Simple string labels:
```elixir
Graph.add_edge(g, "A", "B", label: "yes")
```
Keyword list labels for advanced edge styling:
```elixir
Graph.add_edge(g, "A", "B", label: [
label: "yes", # display text
style: :dotted, # :solid (default), :dotted, :thick
bidirectional: true, # arrows on both ends
arrow: false # no arrow (T-junction instead)
])
```
## Specialized renderers
Beyond directed graphs, Boxart includes standalone renderers for:
- `Boxart.Render.StateDiagram` — state machine diagrams with start/end markers
- `Boxart.Render.Sequence` — sequence diagrams with lifelines, messages,
activation boxes, notes, and interaction blocks
- `Boxart.Render.GitGraph` — git branch/commit visualization
- `Boxart.Render.Gantt` — Gantt charts with task bars and time axis
- `Boxart.Render.Mindmap` — tree layout with left/right branching (accepts `Graph.t()`)
- `Boxart.Render.PieChart` — horizontal bar charts
All renderers implement the `Boxart.Diagram` behaviour.
## Color themes
Pass `:theme` to render with ANSI colors in the terminal:
```elixir
Boxart.render(graph, theme: :dracula)
```
Built-in themes: `:default`, `:mono`, `:neon`, `:dracula`, `:nord`, `:amber`, `:phosphor`.
Custom themes via struct:
```elixir
theme = %Boxart.Theme{
node: ~w[blue]a,
edge: ~w[faint]a,
arrow: ~w[red bright]a,
label: ~w[bright]a,
edge_label: ~w[italic faint]a
}
Boxart.render(graph, theme: theme)
```
## Options
```elixir
Boxart.render(graph,
direction: :td, # :td, :lr, :bt, :rl
charset: :unicode, # :unicode (default) or :ascii
padding_x: 4, # horizontal padding inside nodes
padding_y: 2, # vertical padding inside nodes
gap: 4, # gap between nodes
theme: :default, # color theme (:mono, :neon, :dracula, :nord, :amber, :phosphor)
rounded_edges: true # rounded (╭╮╰╯) or sharp (┌┐└┘) edge corners
)
```
## Installation
```elixir
def deps do
[
{:boxart, "~> 0.1.0"},
# optional, for syntax highlighting in code nodes
{:makeup, "~> 1.0"},
{:makeup_elixir, "~> 1.0"}
]
end
```
## Prior art
Boxart's layout engine is an Elixir port of [termaid](https://github.com/fasouto/termaid) by Fabio Souto,
which itself was inspired by [mermaid-ascii](https://github.com/AlexanderGrooff/mermaid-ascii) by Alexander Grooff.
We also evaluated [beautiful-mermaid](https://github.com/lukilabs/beautiful-mermaid) by Craft
and chose termaid for its cleaner layout pipeline (Sugiyama-style with barycenter crossing
minimization, A* routing with soft obstacles, and direction-aware canvas junction merging).
## License
MIT