Why Mermaid to ASCII?

echo 'graph TD
    A --> B --> C' | mermaid-ascii

┌───┐
│ A │
└─┬─┘
  │
  ▼
┌───┐
│ B │
└─┬─┘
  │
  ▼
┌───┐
│ C │
└───┘

Why Rust?

Prototype in Python, ship in Rust

Compiler Pipeline

Mermaid DSL text
       │
       ▼
  Tokenizer + Parser (recursive descent)
       │
       ├─── Flowchart AST (current)
       ├─── Sequence AST  (future)
       └─── Architecture AST (future)
              │
              ▼
    Layout engine (Sugiyama / timeline / force)
              │
              ▼
       Layout IR (LayoutResult)
       LayoutNode[] + RoutedEdge[]
              │
              ├─── ASCII Renderer (current)
              └─── SVG Renderer  (future)

AST Graph (Parser Output)

Graph {
    nodes:     Node[]      # id, label, shape
    edges:     Edge[]      # from, to, edge_type, label
    subgraphs: Subgraph[]  # nested groups
    direction: Direction   # TD | BT | LR | RL
}

Sugiyama Layout Algorithm

Phase Algorithm Purpose
1. collapse_subgraphs() Replace subgraph members with compound node
2. remove_cycles() Greedy-FAS Reverse back-edges to make a DAG
3. assign_layers() Longest-path Assign each node a rank (layer)
4. insert_dummy_nodes() Break multi-layer edges into unit segments
5. minimise_crossings() Barycenter (24-pass) Reorder nodes within layers to reduce edge crossings
6. assign_coordinates() Layer centering Compute x,y positions with refinement
7. expand_compound_nodes() Position subgraph members inside compounds
8. route_edges() A* pathfinding Compute waypoints avoiding obstacles

A* Pathfinding for Edge Routing

How it works:

OccupancyGrid (. = free, # = blocked by node)

. . . . . . . . .
. # # # . # # # .
. # A # . # B # .     A* finds path: down, across, up
. # # # . # # # .     Simplified to 3 waypoints
. . . * * * . . .
. . . . . . . . .

Every edge in the final output was routed by A* — no overlapping, no crossing through nodes

Layout IR (The Boundary)

LayoutResult {
    nodes:  LayoutNode[]    # x, y, width, height, label, shape
    edges:  RoutedEdge[]    # waypoints (from A*), edge_type, label
    direction: Direction
}

ASCII Renderer (7 Phases)

Phase What it does
1. Direction transform Transpose x/y for LR/RL
2. Paint subgraph borders Compound node boxes
3. Paint node boxes Shape-aware: ┌┐└┘ rect, ╭╮╰╯ rounded, /\ diamond, () circle
4. Paint edges Solid ─│, dotted ╌╎, thick ═║ with smart junction merging
5. Paint arrowheads ▼ ▲ ► ◄ outside boxes + edge labels at midpoint
6. Paint exit stubs ┬ ┴ ├ ┤ on source node borders
7. Direction flip Reverse rows for BT, reverse cols for RL

Comparison with Other Tools

┌──────────────┬──────────────────┬──────────────────┬──────────────────┬──────────────────┐
│              │ Ours             │ Go (mermaid-     │ TS (ascii-       │ D2               │
│              │                  │ ascii)           │ mermaid)         │                  │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Parser       │ Recursive        │ Regex            │ Regex            │ Custom DSL       │
│              │ descent          │ line-by-line     │ line-by-line     │ parser           │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Layout       │ Sugiyama (full)  │ Grid BFS + A*    │ Grid BFS + A*    │ Dagre / ELK      │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Crossing Min │ Barycenter       │ None             │ None             │ Barycenter       │
│              │ 24-pass          │                  │                  │ (via Dagre)      │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Edge Routing │ A* pathfinding   │ A* pathfinding   │ A* pathfinding   │ Spline curves    │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Node Shapes  │ 4                │ 1 (rect only)    │ 13               │ Many             │
├──────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤
│ Target       │ ASCII/Unicode    │ ASCII/Unicode    │ ASCII/Unicode    │ SVG              │
└──────────────┴──────────────────┴──────────────────┴──────────────────┴──────────────────┘

Dual-Language Architecture

Python (src/mermaid_ascii/)     Rust (src/rust/)
──────────────────────────      ─────────────────
parsers/flowchart.py            parsers/flowchart.rs
layout/sugiyama.py              layout/sugiyama.rs
layout/pathfinder.py            layout/pathfinder.rs
renderers/ascii.py              renderers/ascii.rs
api.py                          lib.rs
(no CLI)                        main.rs + wasm.rs

Iterate fast in Python, ship in Rust, play in WASM

Demo

cat <<'EOF' | mermaid-ascii
graph TD
    Start[Start] --> Decision{Decision}
    Decision -->|yes| ProcessA[Process A]
    Decision -->|no| ProcessB[Process B]
    ProcessA --> End[End]
    ProcessB --> End
EOF

          ┌───────┐
          │ Start │
          └───┬───┘
              │
              ▼
        /──────────\
        │ Decision │
        \─────┬────/
      yes     │        no
      ┌───────┴────────┐
      ▼                ▼
┌───────────┐    ┌───────────┐
│ Process A │    │ Process B │
└─────┬─────┘    └─────┬─────┘
      │                │
      └───────┬────────┘
              ▼
           ┌─────┐
           │ End │
           └─────┘

WASM playground: https://homun.posetmage.com/mermaid-ascii/