Packages

Terminal graph rendering with Unicode box-drawing

Current section

Files

Jump to
boxart lib boxart.ex
Raw

lib/boxart.ex

defmodule Boxart do
@moduledoc """
Terminal graph rendering with Unicode box-drawing characters.
Accepts a `Graph.t()` from [libgraph](https://hex.pm/packages/libgraph)
and renders it as a text diagram.
## Quick start
graph =
Graph.new()
|> Graph.add_vertex("A", label: "Start")
|> Graph.add_vertex("B", label: "Decision", shape: :diamond)
|> Graph.add_vertex("C", label: "End")
|> Graph.add_edge("A", "B")
|> Graph.add_edge("B", "C", label: "yes")
IO.puts(Boxart.render(graph))
## 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` — raw source code string (renders as a code block with line numbers)
* `:start_line` — first line number for code display (default: `1`)
* `:language` — language atom for syntax highlighting (e.g. `:elixir`)
## Code nodes
When a vertex has a `:source` label, it renders as a code block with line
numbers and optional syntax highlighting:
Graph.add_vertex(g, "block",
source: "x = fetch(url)\ncase decode(x)",
start_line: 5,
language: :elixir
)
## Edge labels
Simple string labels:
Graph.add_edge(g, "A", "B", label: "yes")
Keyword list labels for advanced edge styling:
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)
arrow_type: :circle # :arrow (default), :circle, :cross
])
## Directions
* `:td` / `:tb` — top-down (default)
* `:lr` — left-to-right
* `:bt` — bottom-to-top
* `:rl` — right-to-left
"""
@type option ::
{:charset, :unicode | :ascii}
| {:direction, Boxart.Graph.direction()}
| {:padding_x, non_neg_integer()}
| {:padding_y, non_neg_integer()}
| {:gap, non_neg_integer()}
| {:theme, atom() | Boxart.Theme.t()}
| {:rounded_edges, boolean()}
| {:max_width, pos_integer()}
| {:max_label_width, pos_integer()}
@doc """
Renders a libgraph `Graph.t()` as a Unicode/ASCII text diagram.
## Options
* `:direction` — layout direction (default: `:td`)
* `:charset` — `:unicode` (default) or `:ascii`
* `:padding_x` — horizontal padding inside node boxes (default: `4`)
* `:padding_y` — vertical padding inside node boxes (default: `2`)
* `:gap` — space between nodes (default: `4`)
* `:theme` — color theme: `:default`, `:mono`, `:neon`, `:dracula`, `:nord`,
`:amber`, `:phosphor`, or a `%Boxart.Theme{}` struct
* `:rounded_edges` — use rounded corners on edge turns (default: `true`)
* `:max_width` — clamp output width to this many columns (truncates lines)
* `:max_label_width` — maximum label width before word wrapping (default: `20`)
Returns `""` for empty graphs.
"""
@spec render(Graph.t(), [option()]) :: String.t()
def render(%Graph{} = graph, opts \\ []) do
graph
|> Boxart.Graph.from_libgraph(opts)
|> Boxart.Render.render_graph(opts)
end
end