Terminal graph rendering with Unicode box-drawing characters.

Takes a Graph.t() from libgraph and renders it as ASCII/Unicode art in the terminal, with multiline labels inside nodes, edge labels, and automatic layout.

Usage

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:

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:

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:

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)
])

Specialized renderers

Beyond directed graphs, Boxart includes standalone renderers for:

All renderers implement the Boxart.Diagram behaviour.

Color themes

Pass :theme to render with ANSI colors in the terminal:

Boxart.render(graph, theme: :dracula)

Built-in themes: :default, :mono, :neon, :dracula, :nord, :amber, :phosphor.

Custom themes via struct:

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

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

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 by Fabio Souto, which itself was inspired by mermaid-ascii by Alexander Grooff. We also evaluated 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