// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT // Package diagram renders Mermaid diagrams to SVG with an engine of its // own, built on the standard library alone. The flowchart grammar, // including the historical "graph" spelling, and the sequenceDiagram // grammar are covered; every other diagram type is refused with an error // naming it. // // The output is deterministic: the same source always renders // byte-identical SVG, because every layout decision falls back to the // order of appearance and never to map iteration. package diagram import ( "fmt" "strings" ) // Render renders the Mermaid diagram source to SVG. Statements are lines; // blank lines and lines opening with %% are comments. A diagram type the // engine does not carry is refused with an error naming the type, and a // statement the grammar does not know is refused with an error naming the // line. func Render(source []byte) ([]byte, error) { lines := statementLines(source) if len(lines) == 0 { return nil, fmt.Errorf("diagram: empty source") } header := strings.Fields(lines[0]) switch strings.ToLower(header[0]) { case "flowchart", "graph": dir := "TB" if len(header) > 1 { dir = strings.ToUpper(header[1]) if !flowDirections[dir] { return nil, fmt.Errorf("diagram: unknown flow direction %q", header[1]) } } return renderFlowchart(lines[1:], dir) case "sequencediagram": auto := len(header) > 1 && strings.ToLower(header[1]) == "autonumber" return renderSequence(lines[1:], auto) default: return nil, fmt.Errorf("diagram: unsupported diagram type %q", header[0]) } } var flowDirections = map[string]bool{ "TB": true, "TD": true, "BT": true, "LR": true, "RL": true, } // statementLines splits the source into statements: one statement per // line, comments and blank lines dropped. func statementLines(source []byte) []string { var lines []string for raw := range strings.SplitSeq(string(source), "\n") { line := strings.TrimSpace(raw) if line == "" || strings.HasPrefix(line, "%%") { continue } lines = append(lines, line) } return lines }