Skip to content

qest-cz/flowchart-studio

Repository files navigation

flowchart-studio

flowchart-studio is a mermaid-flowchart-compatible diagram editor with no dependency on mermaid, dagre, or elkjs. It accepts mermaid flowchart syntax 1:1, but parses, lays out, and renders every diagram with its own hand-written engine end to end. On top of that engine sits a mermaid.live-style live editor.

Quickstart

This project uses pnpm (never npm — no package-lock.json in the repo).

pnpm install
pnpm dev     # start the editor at http://localhost:3000
pnpm test    # run the vitest suite
pnpm build   # production build (next build)

Other useful scripts: pnpm exec tsc --noEmit (typecheck), pnpm start (serve a production build).

Architecture

A diagram flows through three independent, framework-free stages — parse → layout → render — glued together by the public renderMermaid(source) entry point in src/lib/flowchart/index.ts. Parsing turns source text into a FlowchartModel (nodes, edges, subgraphs, classes, styles, click bindings); layout runs a custom Sugiyama-style layered algorithm over that model to produce absolute coordinates and routed edge paths; render turns the positioned graph into an SVG string (or DOM, for the live preview). Each stage is pure and independently testable, with no dependency on the others' internals beyond the shared FlowchartModel / PositionedGraph types. The Next.js app (src/app/, src/components/) is a thin UI layer over this library — it never touches parsing/layout/rendering logic directly.

src/lib/flowchart/
  types.ts        shared model types (FlowNode, FlowEdge, Subgraph, ...)
  index.ts         public API: renderMermaid(source) -> { ok, svg } | { ok, error }
  parse/           text -> FlowchartModel
    parser.ts        top-level recursive-descent parser (statements, config, directives)
    node-parser.ts    node id/label/shape/@{...} metadata parsing
    edge-parser.ts    edge stroke/head/length/label parsing
    shapes.ts         bracket-shape table + @{shape:...} alias table
    preprocess.ts     frontmatter, comments, entity-code decoding
    fixtures.ts       flowchart-docs example corpus, used as parser+render smoke tests
  layout/          FlowchartModel -> PositionedGraph
    rank.ts           cycle removal + longest-path layer assignment
    order.ts          in-layer ordering (barycenter heuristic)
    position.ts        coordinate assignment, clustering, edge routing
  render/          PositionedGraph -> SVG
    shapes-svg.ts     per-shape SVG element generators
    measure.ts        text measurement (canvas in-browser, approximate in tests/SSR)
    curves.ts         edge path interpolation (basis/linear/step*/monotone*/...)
    svg.ts            top-level SVG document assembly (styles, markers, click/animation)

src/app/           Next.js App Router pages
src/components/    editor UI (CodeMirror pane, SVG preview, toolbar, error panel)
src/lib/editor/    editor-only concerns: sample diagrams, share-link encode/decode, SVG/PNG export

Syntax coverage

Feature group Status Notes
Directions (TB/TD/BT/LR/RL) Full All four rendered directions, implemented as coordinate transforms over a canonical TB layout.
Bracket node shapes ([ ], ( ), ([ ]), [[ ]], [( )], (( )), >], { }, {{ }}, [/ /], [\ \], [/ \], [\ /], ((( )))) Full All 15 bracket forms.
Expanded @{ shape: ... } named shapes Full ~50 canonical names + all documented aliases (rect/proc/process, diamond/decision, etc.).
Icon shape (@{ shape: icon, ... } and the @{ icon: "..." } shorthand) Partial Parses and renders (with or without an explicit shape: key); the icon glyph itself is a text placeholder, not a real FontAwesome/iconify glyph (no icon-font/kit dependency is bundled).
Image shape (@{ img: "...", ... }) Full Renders an <image> element with the given pos/w/h.
Links: solid/dotted/thick/invisible, arrow/circle/cross/none heads (incl. bidirectional), length modifiers, labels (inline and piped) Full
Link chaining (A --> B --> C) and fan-out/fan-in (A & B --> C & D) Full
Edge IDs and animation (e1@--> B, e1@{ animate: true }, animation: fast|slow) Full
Subgraphs, incl. nested subgraphs, per-subgraph direction, and titled subgraphs Full Laid out recursively as clusters, then treated as super-nodes in the parent layout.
classDef (incl. default, comma-separated multi-def), class, ::: shorthand, style, linkStyle (incl. multi-index) Full
Click interactions (click id callback/call/href ..., tooltips, _self/_blank/_parent/_top) Full
Comments (%%), entity codes (#35;, #quot;, ...) Full
Markdown-string labels (["`**bold** text`"]) Partial Bold/italic runs render, including _/__ markers and nested combinations (**_bold italic_**); multi-line markdown labels and markdownAutoWrap auto-wrapping are unsupported.
Basic FontAwesome tokens (fa:/fab:/fas:/far:/fal:/fad:/fak:) inside plain labels Partial Parsed and preserved in the label text; rendered as plain text rather than an actual icon glyph.
Frontmatter ---config--- block Full (parse) All keys parse and are attached to the model; only flowchart.curve currently affects layout/rendering.
defaultRenderer: elk Deferred Parses and is stored on the model's config but is always ignored — layout is always flowchart-studio's own Sugiyama engine (see Known limitations).
Diagram types other than flowchart (sequence, class, ER, ...) Deferred Out of scope for v1 — architecture keeps parser/layout/render behind one interface so more types could be added later.

Editor features

  • Live preview: source is parsed/laid out/rendered on every edit (~300ms debounce); on a parse error the last valid diagram stays on screen and an error panel shows the message plus line/column.
  • CodeMirror 6 editor pane with mermaid-flowchart syntax highlighting.
  • Pan (drag) and zoom (wheel) on the preview, plus a "fit to view" button.
  • Sample diagram picker covering shapes, subgraphs, styling, edge labels/arrow types, and edge animation.
  • Export the current diagram as SVG or PNG (rasterized client-side via <canvas>).
  • Share links: the diagram source is deflate-compressed (pako) and base64url-encoded into a #pako:... URL hash, mermaid.live-style — no server-side storage.
  • Light/dark theme toggle; the diagram theme and the app chrome derive from one token system (src/lib/flowchart/theme.ts).

Manual editing (direct manipulation)

The preview is a live editor: every gesture writes a targeted edit into the Mermaid source (never a regeneration), so comments and formatting survive and Ctrl+Z undoes gestures like any other edit. Before committing, each edit is re-parsed — an edit that would break the document is discarded and the error panel explains why.

Gesture Effect on the code
Drag a node's body moves the node — writes a position pin A@{ x: ..., y: ... }
Drag from a connection port (dots on hover) appends A --> B (inside the shared subgraph when both nodes live in one)
Double-click empty canvas inserts n1["New node"] pinned at that spot and opens inline label editing
Double-click a node inline label edit → rewrites just the label token
Click a node/edge, then Delete removes the statement; chains and A & B --> C fan-outs are rebuilt, neighbors survive
Right-click a node context menu: change shape (@{ shape: ... }) or assign a class
Drag empty canvas / wheel pan / zoom

The canvas toolbar (bottom left) offers add-node, delete-selection, undo/redo, and zoom controls.

Position pins are a flowchart-studio extension of the @{ ... } metadata syntax: pinned nodes override the automatic layout (their edges reroute as straight border-to-border segments); unpinned nodes keep the layered auto-layout. Delete the @{ x, y } line to return a node to automatic placement.

Portability of studio extensions

Syntax Mermaid-compatible?
shapes, labels, subgraphs, edge labels/strokes yes — standard Mermaid
e1@{ animate: true } yes — Mermaid v11.3+
A@{ x: 10, y: 20 } position pins studio-only — other renderers ignore them
e1@{ anchorStart: ..., anchorEnd: ... } studio-only

A pinned design pasted into a Notion/GitHub mermaid block will be re-laid-out by that renderer and look different. For a 1:1 look in documents, embed the exported SVG/PNG (or a share link); the validate_diagram MCP tool flags studio-only syntax via the allowStudioExtensions rule so docs-bound diagrams stay portable.

MCP server (diagram guardrails)

mcp/ contains a stdio MCP server, flowchart-rules, that lets any MCP client (Claude Code, documentation pipelines, ...) generate diagrams that conform to this repo's rules:

Tool Behavior
get_rules Returns rules/diagram-rules.md + rules/constraints.json — load before generating
validate_diagram Parses with the studio parser; returns parse errors (line/column) and rule violations
render_svg Self-contained SVG string for embedding in docs
render_png PNG image (rasterized via @resvg/resvg-js)
create_share_link #pako: URL that opens the diagram in the live editor

Setup: pnpm --dir mcp install once, then the repo-root .mcp.json registers the server automatically for Claude Code sessions in this repo. From another project, register it with the absolute path:

{
  "mcpServers": {
    "flowchart-rules": {
      "command": "pnpm",
      "args": ["--dir", "/path/to/flowchart-studio/mcp", "exec", "tsx", "src/server.ts"]
    }
  }
}

Smoke test: pnpm --dir mcp smoke. The rules files are read from disk on every call, so editing rules/ needs no restart. Edit rules via the diagram-rules skill (.claude/skills/diagram-rules/).

Known limitations

  • Only flowcharts. Other mermaid diagram types (sequence, class, state, ER, ...) are not implemented; this is a deliberate v1 non-goal.
  • No ELK renderer. defaultRenderer: elk in frontmatter is accepted but has no effect — layout is always flowchart-studio's own Sugiyama-style layered algorithm.
  • FontAwesome icons are placeholders. Both the icon shape (@{ shape: icon, icon: "..." }) and inline fa:-style tokens parse and render, but as text placeholders rather than real icon glyphs — no FontAwesome/iconify asset is bundled.
  • No multi-line markdown labels. Single-line markdown-string labels (bold/italic) render; a label spanning multiple lines inside "`...`" is not supported, nor is markdownAutoWrap.
  • No server-side persistence. Sharing a diagram works only via the URL hash (#pako:...); there is no save/load against a backend.
  • linkStyle default is explicitly rejected by the parser (only numeric indices are supported).

Testing

pnpm test runs the vitest suite (parser, layout invariants, renderer snapshots, and end-to-end renderMermaid checks). src/lib/flowchart/parse/fixtures.ts holds a corpus of examples lifted directly from the mermaid flowchart docs; every fixture is asserted to both parse successfully and render to well-formed SVG, so the corpus doubles as a docs-parity regression test.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages