Mermaid Formatter

Streaming formatter that renders Mermaid diagrams inline in the terminal using the best available graphics protocol, with a Unicode half-block fallback that works everywhere.

Name mermaid_formatter
Type Formatter Pipeline Plugin
Priority 28 (structural formatting range)
Dependencies Pillow (required), mmdc (optional, recommended)

Features

  • Auto-detection: Picks the best graphics protocol for your terminal
  • 4 rendering backends: Kitty, iTerm2, Sixel, Unicode half-blocks
  • Streaming: Detects mermaid blocks incrementally across chunks
  • Artifact saving: PNG files saved for vision capture feedback
  • Graceful degradation: Falls back to syntax-highlighted code when no renderer is installed
How it works
Model output containing a mermaid block:

  "Here's the architecture:
   ```mermaid
   graph TD
       A[Client] -->|HTTP| B[Server]
       B --> C[Database]
       B --> D[Cache]
   ```
   As shown above..."

The formatter:
1. Yields "Here's the architecture:" immediately
2. Buffers the mermaid source
3. Renders to PNG via mmdc
4. Displays inline using terminal protocol
5. Saves artifact to /tmp/jaato_vision/
6. Yields "As shown above..."
Install mermaid-cli (recommended)
# Gold standard renderer (requires Node.js)
npm install -g @mermaid-js/mermaid-cli

# Verify installation
mmdc --version

Terminal Graphics Backends

The plugin auto-selects the best rendering backend based on your terminal. Detection uses shared/terminal_caps.py, which reads TERM_PROGRAM and TERM environment variables and caches the result process-wide.

Backend Protocol Supported Terminals
kitty Kitty graphics protocol Kitty, Ghostty
iterm iTerm2 inline images iTerm2, WezTerm, Mintty
sixel Sixel bitmap foot, mlterm
rich_pixels Unicode half-blocks (▀) All terminals
Multiplexer handling
tmux and screen strip graphics escape sequences, so the plugin automatically falls back to rich_pixels when a multiplexer is detected. Override with JAATO_MERMAID_BACKEND=kitty if you have tmux passthrough enabled.
Backend selection priority
# 1. Explicit override (always wins)
os.environ["JAATO_MERMAID_BACKEND"] = "kitty"

# 2. Global graphics override
os.environ["JAATO_GRAPHICS_PROTOCOL"] = "sixel"

# 3. Auto-detection from TERM_PROGRAM
#    kitty/ghostty    → Kitty protocol
#    iTerm.app/WezTerm → iTerm2 protocol
#    foot/mlterm       → Sixel protocol

# 4. Universal fallback
#    Everything else   → Unicode half-blocks
Backend output examples
# Kitty: sends PNG via escape sequences
ESC_Gf=100,a=T,m=1;base64data...ESC\
ESC_Gm=0;base64data...ESC\

# iTerm2: single OSC escape sequence
ESC]1337;File=inline=1;size=N:base64...BEL

# Sixel: DCS with color palette + bitmap
ESC P0;1;0q"1;1;W;H #0;2;R;G;B ...data... ESC\

# rich_pixels: Unicode half-block characters
▀▀▀▀▀▀▀▀▀  (with ANSI fg/bg colors)
▀▀▀▀▀▀▀▀▀  (2 pixels per cell height)

Mermaid Rendering

Diagrams are converted from Mermaid source text to PNG using the first available renderer. The rendering step is separate from the terminal display step, allowing different combinations.

mmdc (mermaid-cli)

The official Mermaid CLI tool. Uses Puppeteer to render diagrams in a headless browser. Supports all Mermaid diagram types and themes.

Installnpm install -g @mermaid-js/mermaid-cli
RequiresNode.js
Detectionshutil.which("mmdc")
Timeout30 seconds

mermaid-py (Python fallback)

Python package that produces SVG, converted to PNG via cairosvg. Used when mmdc is not available.

Installpip install mermaid cairosvg
Optional extrapip install jaato[mermaid]

Passthrough (no renderer)

When neither renderer is available, the source is emitted as a ```mermaid code block with a dim install hint. The downstream code_block_formatter applies syntax highlighting.

Rendering pipeline
```mermaid source
    │
    ├── mmdc available?
    │   └── YES → subprocess mmdc → PNG bytes
    │
    ├── mermaid-py available?
    │   └── YES → mermaid.Graph → SVG → cairosvg → PNG bytes
    │
    └── Neither available
        └── Pass through as ```mermaid code block
            + hint: "npm install -g @mermaid-js/mermaid-cli"
Supported diagram types (via mmdc)
graph TD/LR      Flowcharts
sequenceDiagram  Sequence diagrams
classDiagram     Class diagrams
stateDiagram     State diagrams
erDiagram        Entity relationship
gantt             Gantt charts
pie               Pie charts
gitgraph         Git branch diagrams
mindmap          Mind maps
timeline         Timelines
quadrantChart    Quadrant charts
sankey            Sankey diagrams

Configuration

JAATO_MERMAID_BACKEND

Force a specific graphics backend, overriding auto-detection.

Typestring
Valueskitty, iterm, sixel, ascii, off
DefaultAuto-detect

Setting off disables diagram rendering entirely.

JAATO_MERMAID_THEME

Mermaid rendering theme.

Typestring
Valuesdefault, dark, forest, neutral
Defaultdefault

JAATO_MERMAID_SCALE

Rasterization scale factor. Higher values produce sharper images on retina/HiDPI displays but use more memory.

Typeint
Default2

JAATO_GRAPHICS_PROTOCOL

Global terminal graphics protocol override. Affects all plugins that use shared/terminal_caps.py, not just the mermaid formatter.

Typestring
Valueskitty, iterm, sixel, none
DefaultAuto-detect
Environment variable examples
# Force Kitty protocol (e.g., inside tmux with passthrough)
export JAATO_MERMAID_BACKEND=kitty

# Use dark theme for diagrams
export JAATO_MERMAID_THEME=dark

# Higher resolution for retina displays
export JAATO_MERMAID_SCALE=3

# Disable diagram rendering entirely
export JAATO_MERMAID_BACKEND=off

# Save artifacts to custom directory
export JAATO_VISION_DIR=~/diagrams
Pipeline configuration (.jaato/formatters.json)
{
  "formatters": [
    {"name": "hidden_content_filter", "enabled": true},
    {"name": "diff_formatter", "enabled": true},
    {"name": "table_formatter", "enabled": true},
    {"name": "mermaid_formatter", "enabled": true, "config": {
      "theme": "dark",
      "scale": 2,
      "background": "transparent"
    }},
    {"name": "code_block_formatter", "enabled": true,
     "config": {"line_numbers": true}}
  ]
}
Programmatic configuration
from shared.plugins.mermaid_formatter import create_plugin

formatter = create_plugin()
formatter.initialize({
    "theme": "dark",
    "scale": 2,
    "background": "white",
    "console_width": 120,
})
formatter.set_console_width(terminal_width)

Artifact Output

Every rendered diagram is saved as a PNG file in the artifact directory (JAATO_VISION_DIR, default /tmp/jaato_vision). This integrates with the vision capture system, enabling:

  • Model feedback loop: The model can "see" its own diagrams and iterate
  • Export: Users can open/share the high-fidelity PNG files
  • Documentation: Artifacts persist across turns for reference

Files are named sequentially: mermaid_001.png, mermaid_002.png, etc. The counter resets when the plugin is re-initialized.

Artifact output
# After rendering a diagram, the output includes:

[rendered diagram inline]
    [saved: /tmp/jaato_vision/mermaid_001.png]

# Multiple diagrams in one turn:
[first diagram]
    [saved: /tmp/jaato_vision/mermaid_001.png]

[second diagram]
    [saved: /tmp/jaato_vision/mermaid_002.png]
Vision capture integration
Pipeline flow:

  mermaid_formatter (priority 28)
      │
      ├── Renders diagram inline
      ├── Saves PNG to JAATO_VISION_DIR
      │
      ▼
  vision_capture_formatter (priority 95)
      │
      └── Observes rendered output
          for model feedback loop

Dependencies

Required

PackagePurpose
Pillow>=10.0.0 Image processing for all graphics backends

Optional

PackagePurposeInstall
@mermaid-js/mermaid-cli High-fidelity Mermaid rendering npm install -g @mermaid-js/mermaid-cli
cairosvg>=2.7.0 SVG to PNG conversion pip install jaato[mermaid]
Quick setup
# Minimal (Unicode half-block rendering only)
pip install jaato

# With mermaid-cli (recommended)
pip install jaato
npm install -g @mermaid-js/mermaid-cli

# Full setup (all optional deps)
pip install jaato[mermaid]
npm install -g @mermaid-js/mermaid-cli
Check availability
from shared.plugins.mermaid_formatter.renderer import (
    is_renderer_available
)
from shared.terminal_caps import detect

# Check if rendering will work
print(is_renderer_available())  # True/False

# Check which graphics backend will be used
caps = detect()
print(caps["graphics"])  # "kitty"/"iterm"/"sixel"/None