diago

A diagram toolkit for MoonBit with Dagre, ELK, and Railway layout engines, rendering SVG, ASCII, and Unicode outputs.

graph
moon add Milky2018/diago@0.3.2
Download zip
Author
Version
0.3.2
License
Apache-2.0
Last updated
4 days ago
Downloads
13K
README

#Diago

Diago is a diagram toolkit for MoonBit. It supports a D2-compatible text format and renders diagrams through multiple layout engines.

#Overview

This repository contains:

  • A backend-independent library facade (Milky2018/diago) for parsing, layout, and rendering
  • A native filesystem adapter (Milky2018/diago/fs) for file loading and relative imports
  • A CLI (cmd/diago) with explicit subcommands (render, fmt, validate, layout, themes, version)
  • A WASM-based playground (web/) deployed via GitHub Pages
  • A Node.js Markdown package (markdown/) for markdown-it, remark, and VitePress
  • Multiple layout engines: dagre, elk, and railway

#Installation

moon update moon build

#Quick Start

moon run cmd/diago -- render diagram.txt

#Library API

The root package is supported on all MoonBit targets. It accepts source text and never reads the local filesystem implicitly:

///|
test {
let ascii = @diago.compile(
"a -> b",
options=@diago.CompileOptions::new().with_output_mode(Ascii),
)
inspect(
ascii,
content=(
#|+----+
#|| a |
#|| |
#|+----+
#| |
#|+----+
#|| b |
#|| |
#|+----+
#|
),
)
}

Imports are explicit and backend-independent through ParseOptions::with_import_resolver. Editors can call get_syntax_tokens on every target to obtain error-tolerant Unicode code-point ranges for comments, strings, literals, keywords, identifiers, operators, and punctuation.

#Unsupported D2 features

Diago does not currently support LaTeX labels (|tex or |latex) or sketch rendering (style.sketch, vars.d2-config.sketch, or the sketch render option). These inputs return UnsupportedFeature instead of silently falling back to plain text or ordinary SVG rendering. They can be reconsidered when backend-independent community implementations are available for all MoonBit targets.

Native applications can opt into local file access through the filesystem adapter:

///|
async fn main {
let svg = @diago_fs.compile_file("diagram.d2")
}

compile_file and parse_file use moonbitlang/async/fs, resolve imports relative to the input file, and preserve a custom resolver supplied by the caller.

#Markdown package

The diago npm package renders exact lowercase diago fences to inline SVG at build time. It is Node.js 20+ and ESM-only and bundles the matching Diago Wasm:

import MarkdownIt from 'markdown-it' import diago from 'diago/markdown-it' const md = new MarkdownIt().use(diago) const html = md.render('```diago\na -> b\n```', { path: 'docs/example.md', })

The package also exports diago/remark and diago/vitepress. Failed diagrams remain code fences and produce structured diagnostics. File imports, sketch, and LaTeX are not supported in Markdown fences. Only use the adapters with trusted project-authored Markdown: generated inline SVG is active HTML and is not sanitized by the package. See markdown/README.md for the complete interface and CSP notes.

#CLI

Show help:

moon run cmd/diago -- --help

Common usage:

# Render SVG (default output: input.svg) moon run cmd/diago -- render diagram.txt moon run cmd/diago -- render diagram.txt diagram.svg moon run cmd/diago -- render diagram.txt --output diagram.svg # Choose layout engine moon run cmd/diago -- render --layout elk diagram.txt diagram.svg moon run cmd/diago -- render -l dagre diagram.txt diagram.svg # ASCII / Unicode text moon run cmd/diago -- render --format ascii diagram.txt --output diagram.ascii.txt moon run cmd/diago -- render --format unicode diagram.txt --output diagram.unicode.txt # Format / validate moon run cmd/diago -- fmt diagram.txt moon run cmd/diago -- fmt --check diagram.txt moon run cmd/diago -- validate diagram.txt # Watch mode (rebuilds output on file changes) moon run cmd/diago -- render --watch diagram.txt # Introspection moon run cmd/diago -- layout moon run cmd/diago -- layout elk moon run cmd/diago -- themes moon run cmd/diago -- version

#Example

Create a file example.txt:

server: Web Server database: Database { shape: cylinder } cache: Cache { shape: oval } server -> database: queries server -> cache: reads cache -> database: fallback

Compile it:

moon run cmd/diago -- render example.txt example.svg

#Pipeline

At a high level:

Source → Lexer → Parser → AST → IR → Graph → Layout (dagre/elk/railway) → Render (SVG/ASCII/Unicode)

#Tests

moon check --target all --deny-warn moon test --target all moon build cmd/diago --target native --release moon build cmd/wasm --target wasm --release node scripts/wasm_smoke.mjs _build/wasm/release/build/cmd/wasm/wasm.wasm npm ci --prefix web npm test --prefix web npm ci --prefix markdown npm test --prefix markdown npm run test:corpus --prefix markdown npm run test:package --prefix markdown

#License

Apache-2.0 (see LICENSE).

#
DiagoError

pub(all) suberror DiagoError {
ParseError(String)
IOError(String)
IrError(String)
GraphError(String)
LayoutError(String)
RenderError(String)
ConfigError(String)
TargetError(String)
UnsupportedFeature(String)
} derive(Eq,
Debug
)

#
CompileOptions

pub struct CompileOptions {
output_mode : OutputMode
layout_engine : LayoutEngine
direction :
Direction

target_spec : String?
layout_plugin : LayoutPlugin?
route_plugin : RoutePlugin?
parse_options : ParseOptions
theme_name : String?
dark_theme_id : Int?
sketch : Bool?
pad : Double?
center : Bool?
theme_overrides :
ThemeOverrides
?
dark_theme_overrides :
ThemeOverrides
?
scale : Double?
no_xml_tag : Bool?
salt : String?
omit_version : Bool?
}

#
CompileOptions::new

#
CompileOptions::with_center

fn CompileOptions::with_center(self : CompileOptions, center : Bool?) -> CompileOptions

#
CompileOptions::with_dark_theme_id

fn CompileOptions::with_dark_theme_id(self : CompileOptions, dark_theme_id : Int?) -> CompileOptions

#
CompileOptions::with_dark_theme_overrides

fn CompileOptions::with_dark_theme_overrides(self : CompileOptions, dark_theme_overrides :
ThemeOverrides
?) -> CompileOptions

#
CompileOptions::with_direction

#
CompileOptions::with_layout_engine

fn CompileOptions::with_layout_engine(self : CompileOptions, layout_engine : LayoutEngine) -> CompileOptions

#
CompileOptions::with_layout_plugin

fn CompileOptions::with_layout_plugin(self : CompileOptions, layout_plugin : LayoutPlugin?) -> CompileOptions

#
CompileOptions::with_no_xml_tag

fn CompileOptions::with_no_xml_tag(self : CompileOptions, no_xml_tag : Bool?) -> CompileOptions

#
CompileOptions::with_omit_version

fn CompileOptions::with_omit_version(self : CompileOptions, omit_version : Bool?) -> CompileOptions

#
CompileOptions::with_output_mode

fn CompileOptions::with_output_mode(self : CompileOptions, output_mode : OutputMode) -> CompileOptions

#
CompileOptions::with_pad

fn CompileOptions::with_pad(self : CompileOptions, pad : Double?) -> CompileOptions

#
CompileOptions::with_parse_options

fn CompileOptions::with_parse_options(self : CompileOptions, parse_options : ParseOptions) -> CompileOptions

#
CompileOptions::with_route_plugin

fn CompileOptions::with_route_plugin(self : CompileOptions, route_plugin : RoutePlugin?) -> CompileOptions

#
CompileOptions::with_salt

fn CompileOptions::with_salt(self : CompileOptions, salt : String?) -> CompileOptions

#
CompileOptions::with_scale

fn CompileOptions::with_scale(self : CompileOptions, scale : Double?) -> CompileOptions

#
CompileOptions::with_sketch

fn CompileOptions::with_sketch(self : CompileOptions, sketch : Bool?) -> CompileOptions

#
CompileOptions::with_target_spec

fn CompileOptions::with_target_spec(self : CompileOptions, target_spec : String?) -> CompileOptions

#
CompileOptions::with_theme_name

fn CompileOptions::with_theme_name(self : CompileOptions, theme_name : String?) -> CompileOptions

#
CompileOptions::with_theme_overrides

#
CompletionItem

pub struct CompletionItem {
label : String
kind : CompletionKind
detail : String
insert_text : String
} derive(Eq,
Debug
)

#
CompletionKind

pub(all) enum CompletionKind {
Keyword
Style
Shape
Value
} derive(Eq,
Debug
)

#
LayoutEngine

pub(all) enum LayoutEngine {
Auto
Dagre
Elk
Railway
} derive(Eq,
Debug
)

#
OutputMode

pub(all) enum OutputMode {
Svg
Ascii
Unicode
} derive(Eq,
Debug
)

#
ParseOptions

pub struct ParseOptions {
import_resolver :
ImportResolver
?
}

#
ParseOptions::new

#
ParseOptions::with_import_resolver

fn ParseOptions::with_import_resolver(_self : ParseOptions, import_resolver :
ImportResolver
?) -> ParseOptions

#
SyntaxToken

pub struct SyntaxToken {
start : Int
end : Int
kind : SyntaxTokenKind
} derive(Eq,
Debug
)

A highlighted source range.

start and end are zero-based Unicode code point offsets. The range is half-open: it includes start and excludes end.

#
SyntaxTokenKind

pub(all) enum SyntaxTokenKind {
Comment
String
Number
Boolean
Keyword
Identifier
Operator
Punctuation
} derive(Eq,
Debug
)

A lexical category used by source editors.

#
compile

fn compile(source : String, options? : CompileOptions) -> String raise DiagoError

#
compile_diagram

fn compile_diagram(source : String, options? : CompileOptions) ->
Diagram
raise DiagoError

#
get_board_path_at_position

fn get_board_path_at_position(source : String, line : Int, column : Int) -> Array[String]

#
get_completion_items

fn get_completion_items(source : String, line : Int, column : Int) -> Array[CompletionItem]

#
get_ref_ranges

fn get_ref_ranges(source : String, key : String, board_path? : Array[String], options? : ParseOptions) -> Array[
Range
] raise DiagoError

#
get_syntax_tokens

fn get_syntax_tokens(source : String) -> Array[SyntaxToken]

Tokenize Diago source for syntax highlighting.

The result is available even when the source is incomplete or contains syntax errors.

#
parse

fn parse(source : String, options? : ParseOptions) ->
Map
raise DiagoError