npm.io
0.19.5 • Published 3d agoCLI

@grethel-labs/excaliplant

Licence
MIT
Version
0.19.5
Deps
3
Size
3.3 MB
Vulns
0
Weekly
0
Stars
1

excaliplant

npm downloads ci node license

PlantUML → ELK layout → Excalidraw renderer with a plugin-based parser.  ·  v0.19.5  ·  265 tests  ·  MIT

@grethel-labs/excaliplant takes PlantUML source, runs it through a plugin-based parser, lays it out with ELK, and emits a .excalidraw JSON document — opening cleanly in any Excalidraw front-end. Optional helpers convert the result to SVG or PNG so the same pipeline can also produce static documentation artefacts.

Module structure
Module structure — rendered by excaliplant itself
renderPlantUml flow
renderPlantUml flow — rendered by excaliplant itself

This README is generated. Edit docs/README.template.md.njk and run npm run build:docs.

AI-generated project notice: This repository, including source code, documentation, comments, and generated artefacts, was created almost entirely with AI assistance. The maintainer does not adopt every generated sentence, implementation detail, or artefact as a personal statement, endorsement, or guarantee. Review everything carefully and use the code at your own risk; the MIT License warranty disclaimer applies.


How to use

Install

npm install @grethel-labs/excaliplant

@resvg/resvg-js is pulled in as a runtime dependency so the SVG and PNG export paths work out of the box.

Render PlantUML to an Excalidraw document
import { renderPlantUml } from "@grethel-labs/excaliplant";

const excalidraw = await renderPlantUml(plantumlText, { sourceLabel: "demo" });
// → write `excalidraw` to disk as <name>.excalidraw, or hand it to an
//   Excalidraw embed.
Render to SVG / PNG

The result of renderPlantUml(...) is a thenable — you can await it to get the Excalidraw JSON, or chain .toSvg() / .toPng() on it to get the rasterised diagram in a single line. Both outputs keep the hand-drawn Excalidraw look (strokes are produced via roughjs, the same library Excalidraw uses internally).

import { renderPlantUml } from "@grethel-labs/excaliplant";

const svg = await renderPlantUml(plantumlText).toSvg();
const png = await renderPlantUml(plantumlText).toPng({ width: 4800 });

The lower-level helpers are still exported if you need them:

import {
  renderPlantUml,
  excalidrawJsonToCanvasSvg,
  svgToPng,
} from "@grethel-labs/excaliplant";

const doc = await renderPlantUml(plantumlText);
const svg = excalidrawJsonToCanvasSvg(doc, { width: 1200 });
const png = svgToPng(svg, { width: 4800 });   // 4× SVG width

Lower-level entry points are also exported:

Export Purpose
parsePlantUml(text) PlantUML → Diagram model
layoutDiagram(diagram) Sizing + ELK layout + edge routing
exportDiagram(diagram) Diagram → Excalidraw JSON
excalidrawToSvg(doc) Excalidraw JSON → tightly-cropped SVG
excalidrawJsonToCanvasSvg(…) …same, letter-boxed onto a fixed-aspect canvas
svgToPng(svg) Rasterise SVG to PNG (@resvg/resvg-js)

The complete list of exported symbols, with parameter tables and return types, lives in docs/API.md. It is regenerated from JSDoc on every npm run build:docs run.

Sequence diagram coverage

Sequence diagrams support participants (participant, actor, boundary, control, entity, database, collections, queue), quoted aliases in both Name as Alias and Alias as "Name" forms, message arrows including async/reply/reverse/bidirectional variants, notes, participant box ... end box groups, ref over references, dividers (== label ==), delays (... label ...), spacers (||| / ||45||), autonumber start/stop/resume, lifecycle controls (create, activate, deactivate, destroy) and inline message lifecycle suffixes (++, --, **, !!). Combined fragments render for opt, loop, alt/else, par/and, break, critical/option, and group/option blocks. A small sequence skinparam subset maps directly to output colours in block or compact form: ArrowColor, ParticipantBackgroundColor, ParticipantBorderColor, and LifeLineBorderColor; supported CSS-like <style> blocks safely map participant, arrow, note, group, lifeline, divider, and activation-bar colour properties. Graph diagrams also support a sanitized colour subset for common skinparam keys and CSS-like <style> selectors covering backgrounds, component-style boxes, arrows, edge labels, notes, and containers. Shared PlantUML presentation commands such as title, caption, header, footer, legend, and mainframe are preserved in parsed models and rendered where supported; PlantUML links in graph labels and sequence message labels are kept as safe Excalidraw link metadata. Creole and legacy HTML-like label markup degrades to readable plain text across supported diagram families, including emphasis, headings, lists, tables, Unicode notation, and icon placeholders.

See the full Sequence Diagram Component Coverage for detailed examples and support matrix.

See the generated Diagram Module Coverage Gallery for rendered small and complex examples across diagram modules. The gallery includes SVG artefacts for edge cases, overlap-prone layouts, functional decisions, deliberate design choices, wild feature combinations, and repo-derived examples where the diagram type naturally maps to the source tree.

UML graph diagram coverage

Class diagrams support the common PlantUML class-family declarations (class, abstract, interface, enum, annotation, entity, protocol, struct, record, dataclass, and related aliases), generic headers, extends / implements, class body members and separators, colon member rows, implicit relationship endpoints, inheritance, realization, composition, aggregation, dependency and association arrows, multiplicities, member-qualified arrows, association classes, qualified associations, packages/namespaces, notes on classes, members and links, JSON/object display blocks, hide/show/ remove/restore filters, and sanitized graph colours/styles.

Object diagrams support object declarations, quoted aliases, field rows, body fields, class-style relationships with implicit object endpoints, association-object diamonds, map/associative-array tables, row anchors via Map::key, row-to-object links, PERT-style map nodes, JSON display blocks, shared graph notes/styles/presentation, and underlined object titles in generated Excalidraw output.

Activity diagrams support the practical PlantUML activity core: beta :action; flows with multiline text, start/stop/end/kill/detach, if/else/switch/while/repeat/fork/split controls, swimlanes, partitions, notes, connectors, goto/label, simple -/* action lists, SDL-style stereotypes, and legacy (*) --> "Activity" arrows with labels. Activity output uses the shared graph layout/rendering pipeline for deterministic Excalidraw, SVG, and PNG artefacts.

Component diagrams support bracket and keyword component declarations, aliases, multiline labels, optional interface endpoints, lollipop interfaces, packages/nodes/folders/frames/clouds/databases, direction hints, styled arrows, notes including note-on-link, presentation metadata, hidden edges, port / portin / portout declarations, Component::port anchors, JSON display blocks, and shared hide/remove/restore and style handling.

Deployment diagrams support official deployment elements such as node, artifact, folder, frame, cloud, database, queue, card, package, stack, storage, agent, file, process, and action; nested containers; aliases; bracketed long descriptions; JSON display blocks; styled arrows; implicit node endpoints; and shared graph notes, presentation, filters, and style handling.

State diagrams support simple and composite states, aliases, start/end, choice, fork, join, history and deep-history pseudostates, state descriptions, nested state blocks, orthogonal region separators, labelled transitions, direction hints, inline colours, notes, JSON display blocks, scale/layout controls, hide-empty-description commands, and shared graph presentation/style handling.

Timing diagrams support PlantUML robust, concise, binary, clock, analog, and rectangle participant declarations, aliases, participant state lists, absolute and relative time markers, anchored times, state changes, binary and clock waveforms, analog polylines, cross-participant messages, duration constraints, highlights, top/bottom notes, scale and hidden-axis controls, footbox/resource hiding directives, separators, and safe text/colour handling on a deterministic shared time axis.

Data, Grammar, and Math Diagram Coverage

Regex diagrams support @startregex sources with literal text, shorthand and range character classes, escaped and unicode categories, groups, lookaround-like assertion groups, anchors, alternatives, and repetition operators. They render as bounded railroad-style graph diagrams.

EBNF diagrams support @startebnf rules with terminals, non-terminals, sequences, alternatives, optional blocks, repetition blocks, grouping, special sequences, comments/notes, and documented style/pragma lines tolerated by the parser. They share the railroad-style graph output used by regex diagrams.

JSON diagrams support @startjson objects, arrays, strings, numbers, booleans, null, empty objects/arrays, unicode and escape sequences through the platform JSON parser, plus PlantUML #highlight paths. YAML diagrams support the documented safe subset for mappings, sequences, scalars, symbol/unicode keys, and #highlight paths. Both render nested data as deterministic graph trees.

Math support covers standalone @startmath / @startlatex formula diagrams and inline <math> / <latex> tags in existing labels. The renderer intentionally uses a safe readable text fallback instead of pulling in external formula rendering dependencies.

Network, UI, and Architecture Coverage

Network diagrams support @startnwdiag with nwdiag network lanes, address metadata, groups, devices, multi-homed device declarations, and peer connections. Devices render through the shared graph pipeline so labels are measured and routed deterministically.

Salt diagrams support @startsalt wireframes with low-fidelity labels, buttons, radio buttons, checkboxes, text fields, dropdowns, tabs, grid/table rows, and textarea-style blocks. Unsupported Salt decoration degrades to safe readable labels instead of raw markup.

Archimate diagrams support archimate element declarations with categories, aliases, stereotypes, junction declarations, simple rectangle states, and directed relationships. External sprites and stdlib-specific icons use safe text/type fallbacks.

Planning, Hierarchy, and Files Coverage

Gantt diagrams support @startgantt task declarations with durations, explicit starts and ends, milestones, project starts, separators, and simple task dependencies. The parser records scheduling details on deterministic task boxes and routes dependencies through the shared graph pipeline.

Mindmap and WBS diagrams support PlantUML OrgMode-style markers, Markdown-style heading/list input, side/direction directives, mixed * / + hierarchy markers, and safe text handling. Files diagrams support @startfiles path trees with shared folder merging and file/folder stereotypes.

Chronology diagrams support dated milestones, labelled timestamp entries, simple ranges, start/end directives, and dependencies between events. Unsupported calendar-specific presentation lines are tolerated so useful chronology content still renders as safe readable timeline graphs.

Entity Relationship Coverage

Information Engineering diagrams support PlantUML entity declarations with attribute rows plus crow-foot relationship operators such as }|..||, }o..o|, ||--o{, and |o--||. Cardinality and optionality markers are kept as endpoint labels so exported diagrams remain deterministic and reviewable.

Chen ER diagrams support @startchen entity, relationship, attribute, derived-attribute, multi-valued-attribute, and key declarations, including empty or member-bearing blocks, simple orientation directives, aliases, and cardinality-labelled connections such as Person -N- Birthplace.

Special Rendering Coverage

Ditaa diagrams support @startditaa blocks and inline ditaa(...) sources. The parser preserves bounded ASCII canvas lines, consumes common rendering options such as no-separator, no-shadow, and scale hints, and emits a safe deterministic Excalidraw graph representation instead of executing an external Ditaa renderer.

Chart diagrams support @startchart sources with horizontal and vertical axes, bar, line, area, and scatter series, labels, colours, legends, orientation, and stacking options. Series and option rows render as reviewable graph-backed chart metadata with the same SVG escaping and layout bounds as other diagram families.

Use-case diagrams support actor and usecase declarations in keyword, colon, parenthesized, quoted-alias, reverse-description, stereotype, and business / forms; nested package / rectangle system boundaries; include, extend, generalization, directional, labelled, and inline-styled relationships; notes attached to aliases or usecases; and shared graph presentation, link, text-markup, skinparam, and <style> handling.

Run the tests
npm test

Ships with 265 tests across functional, edge-case, security (XSS / ReDoS / prototype-pollution), and self-introspection suites.


Self-rendered architecture diagrams

The diagrams below are produced by excaliplant itself at build time from PlantUML sources that describe this very repository. The text under each image is extracted from the source via @diagram JSDoc tags.

Module structure

Module structure

Sources: PlantUML · SVG

The module graph reflects how the source is laid out under src/. Diagram-type behavior is collected in first-class module folders under src/diagrams/, orchestration lives under src/main/, and host capabilities live under src/general/platform/.

renderPlantUml flow

renderPlantUml flow

Sources: PlantUML · SVG

The call graph for renderPlantUml(text) walks three subsystems:

  1. parser turns PlantUML text into a model (Diagram / SequenceDiagram). The parser is plugin-driven; see the next diagram for the plugin breakdown.
  2. layout decides positions. Component diagrams go through ELK (layered + orthogonal routing); sequence diagrams use a small deterministic tabular layout.
  3. renderer walks the laid-out model and emits Excalidraw JSON. The same model can also be exported to SVG via src/general/render/svg.mjs — used by the documentation pipeline.
Parser plugins

Parser plugins

Sources: PlantUML · SVG

Each parser plugin is a tiny self-contained file that handles ONE PlantUML construct. The engine offers each input line to plugins in registration order; the first plugin that returns true wins.

To add support for a new PlantUML keyword, drop a new file in the owning diagram module folder and append it to that module's parser contract. No engine change required.

Model classes

Model classes

Sources: PlantUML · SVG

The model diagram is generated dynamically from exported classes in src/general/model/diagram.mjs. It shows how the reusable arrow classes sit underneath both component connections and sequence messages, so future model classes appear in the README without hand-maintained PlantUML.

Pipeline

PlantUML text
     │ parsePlantUml()
     ▼
  Diagram (planes, subplanes, boxes, connections)
     │ layoutDiagram()  (sizing → ELK layered + orthogonal routing → chamfer)
     ▼
  Diagram with absolute positions and edge paths
     │ exportDiagram()
     ▼
  Excalidraw JSON
     │ excalidrawJsonToCanvasSvg()  (optional)
     ▼
  SVG  ── svgToPng() ──▶  PNG  (both optional, no headless browser)

Repository layout

excaliplant
├── assets
│   ├── arrowheads
│   │   ├── arrowheads.svg
│   │   └── manifest.json
│   └── fonts
│       ├── Excalifont-Regular.ttf
│       ├── Excalifont-Regular.woff2
│       └── LICENSE.txt
├── bin
│   └── excaliplant.mjs
├── docs
│   ├── ressources
│   │   ├── module-coverage
│   │   │   ├── archimate
│   │   │   ├── chart
│   │   │   ├── chen
│   │   │   ├── chronology
│   │   │   ├── class
│   │   │   ├── component
│   │   │   ├── deployment
│   │   │   ├── ditaa
│   │   │   ├── ebnf
│   │   │   ├── files
│   │   │   ├── gantt
│   │   │   ├── ie
│   │   │   ├── json
│   │   │   ├── math
│   │   │   ├── mindmap
│   │   │   ├── nwdiag
│   │   │   ├── object
│   │   │   ├── regex
│   │   │   ├── salt
│   │   │   ├── sequence
│   │   │   ├── state
│   │   │   ├── timing
│   │   │   ├── wbs
│   │   │   └── yaml
│   │   └── sequence
│   │       ├── puml
│   │       └── svg
│   ├── scripts
│   │   ├── build-docs.mjs
│   │   ├── build-module-coverage.mjs
│   │   ├── build-sequence-coverage.mjs
│   │   ├── check-build-manifest.mjs
│   │   ├── config.mjs
│   │   ├── extract-api.mjs
│   │   ├── extract-docs.mjs
│   │   ├── file-tree.mjs
│   │   ├── module-coverage-sources.mjs
│   │   └── self-diagrams.mjs
│   ├── API.md
│   ├── API.template.md.njk
│   ├── README.template.md.njk
│   ├── module-coverage.md
│   ├── module-coverage.template.md.njk
│   ├── sequence-components.md
│   ├── sequence-components.template.md.njk
│   └── src-structure-refactor-plan.md
├── scripts
│   ├── auto-patch-deps.mjs
│   ├── bump-release-version.mjs
│   ├── clean-test-output.mjs
│   ├── merge-driver-prefer-higher-version.mjs
│   ├── prepublish-guard.mjs
│   ├── setup-merge-drivers.mjs
│   └── smoke.mjs
├── src
│   ├── diagrams
│   │   ├── activity
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── archimate
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── base
│   │   │   ├── artifacts.mjs
│   │   │   ├── assets.mjs
│   │   │   ├── dependencies.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── index.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── renderer.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── chart
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── chen
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── chronology
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── class
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   ├── style.mjs
│   │   │   └── tests.mjs
│   │   ├── component
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── deployment
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── ditaa
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── ebnf
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── files
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── gantt
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── ie
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── json
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── math
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── mindmap
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── nwdiag
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── object
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── regex
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── salt
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── sequence
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── context.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── layout_engine.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── render_excalidraw.mjs
│   │   │   ├── security.mjs
│   │   │   ├── spacing.mjs
│   │   │   └── tests.mjs
│   │   ├── shared
│   │   │   ├── common_plugins
│   │   │   ├── graph_plugins
│   │   │   ├── data_runtime.mjs
│   │   │   ├── er_runtime.mjs
│   │   │   ├── graph_context.mjs
│   │   │   ├── graph_parser.mjs
│   │   │   ├── graph_runtime.mjs
│   │   │   ├── planning_runtime.mjs
│   │   │   ├── railroad_runtime.mjs
│   │   │   ├── special_runtime.mjs
│   │   │   └── tree_runtime.mjs
│   │   ├── state
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── timing
│   │   │   ├── docs
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── render_excalidraw.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── use-case
│   │   │   ├── plugins
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── wbs
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   ├── yaml
│   │   │   ├── docs
│   │   │   ├── tests
│   │   │   ├── assets.mjs
│   │   │   ├── docs.mjs
│   │   │   ├── layout.mjs
│   │   │   ├── module.mjs
│   │   │   ├── parser.mjs
│   │   │   ├── render.mjs
│   │   │   ├── security.mjs
│   │   │   └── tests.mjs
│   │   └── index.mjs
│   ├── general
│   │   ├── layout
│   │   │   ├── elk_layout.mjs
│   │   │   └── sizing.mjs
│   │   ├── model
│   │   │   └── diagram.mjs
│   │   ├── platform
│   │   │   ├── asset_base.mjs
│   │   │   ├── diagnostics.mjs
│   │   │   ├── security_base.mjs
│   │   │   └── services.mjs
│   │   ├── render
│   │   │   ├── canvas_svg.mjs
│   │   │   ├── excalidraw.mjs
│   │   │   ├── png.mjs
│   │   │   ├── rng.mjs
│   │   │   ├── schema.mjs
│   │   │   └── svg.mjs
│   │   └── style
│   │       ├── colors.mjs
│   │       ├── font.mjs
│   │       ├── style.mjs
│   │       └── text.mjs
│   ├── main
│   │   ├── builtin.mjs
│   │   ├── dependencies.mjs
│   │   ├── introspection.mjs
│   │   ├── metadata.mjs
│   │   ├── parser.mjs
│   │   ├── pipeline.mjs
│   │   └── registry.mjs
│   └── util
│       ├── parser_engine.mjs
│       └── plantuml_utils.mjs
├── tests
│   ├── helpers
│   │   └── output.mjs
│   ├── activity_components.test.mjs
│   ├── class_components.test.mjs
│   ├── component_components.test.mjs
│   ├── deployment_components.test.mjs
│   ├── edge_cases.test.mjs
│   ├── functional_more.test.mjs
│   ├── merge_driver.test.mjs
│   ├── modular_architecture.test.mjs
│   ├── module_coverage.test.mjs
│   ├── object_components.test.mjs
│   ├── plantuml.test.mjs
│   ├── security.test.mjs
│   ├── self_introspection.test.mjs
│   ├── sequence_components.test.mjs
│   ├── state_components.test.mjs
│   ├── style.test.mjs
│   └── usecase_components.test.mjs
├── AGENTS.md
├── CHANGELOG.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── GEMINI.md
├── LICENSE
├── README.md
├── SECURITY.md
├── THIRD_PARTY_NOTICES.md
├── index.mjs
├── lifecycle-test.svg
├── package.json
├── style.example.json
├── style.example.yaml
└── tsconfig.json

Generated artefacts (docs/ressources/generated/) live in .gitignore — they are rebuilt by npm run build:docs. The single-page API reference at docs/API.md is generated by the same command from JSDoc.

Module documentation

diagrams
diagrams/activity/assets
diagrams/activity/docs
diagrams/activity/layout
diagrams/activity/module
diagrams/activity/parser
diagrams/activity/plugins/syntax
diagrams/activity/render
diagrams/activity/security
diagrams/activity/tests
diagrams/activity/tests/activity_components
diagrams/base
diagrams/base/artifacts
diagrams/base/assets
diagrams/base/dependencies
diagrams/base/docs
diagrams/base/layout
diagrams/base/module
diagrams/base/parser
diagrams/base/renderer
diagrams/base/security
diagrams/base/tests
diagrams/class/assets
diagrams/class/docs
diagrams/class/docs/coverage_examples
diagrams/class/layout
diagrams/class/module
diagrams/class/parser
diagrams/class/plugins/syntax
diagrams/class/render
diagrams/class/security
diagrams/class/style
diagrams/class/tests
diagrams/component/assets
diagrams/component/docs
diagrams/component/docs/coverage_examples
diagrams/component/layout
diagrams/component/module
diagrams/component/parser
diagrams/component/plugins/syntax
diagrams/component/render
diagrams/component/security
diagrams/component/tests
diagrams/deployment/assets
diagrams/deployment/docs
diagrams/deployment/layout
diagrams/deployment/module
diagrams/deployment/parser
diagrams/deployment/plugins/syntax
diagrams/deployment/render
diagrams/deployment/security
diagrams/deployment/tests
diagrams/ebnf/parser
diagrams/object/assets
diagrams/object/docs
diagrams/object/docs/coverage_examples
diagrams/object/layout
diagrams/object/module
diagrams/object/parser
diagrams/object/plugins/syntax
diagrams/object/render
diagrams/object/security
diagrams/object/tests
diagrams/regex/layout
diagrams/regex/module
diagrams/regex/parser
diagrams/regex/render
diagrams/sequence/assets
diagrams/sequence/docs
diagrams/sequence/layout
diagrams/sequence/module
diagrams/sequence/parser
diagrams/sequence/render
diagrams/sequence/security
diagrams/sequence/tests
diagrams/shared/data_runtime
diagrams/shared/graph_parser
diagrams/shared/graph_runtime
diagrams/shared/railroad_runtime
diagrams/state/assets
diagrams/state/docs
diagrams/state/docs/coverage_examples
diagrams/state/layout
diagrams/state/module
diagrams/state/parser
diagrams/state/plugins/syntax
diagrams/state/render
diagrams/state/security
diagrams/state/tests
diagrams/state/tests/output
diagrams/state/tests/state_components
diagrams/timing/assets
diagrams/timing/docs
diagrams/timing/docs/coverage_examples
diagrams/timing/layout
diagrams/timing/module
diagrams/timing/parser
diagrams/timing/plugins/syntax
diagrams/timing/render
diagrams/timing/render_excalidraw
diagrams/timing/security
diagrams/timing/tests
diagrams/timing/tests/timing_components
diagrams/use-case/assets
diagrams/use-case/docs
diagrams/use-case/layout
diagrams/use-case/module
diagrams/use-case/parser
diagrams/use-case/plugins/actors
diagrams/use-case/plugins/containers
diagrams/use-case/plugins/notes
diagrams/use-case/plugins/relationships
diagrams/use-case/plugins/usecases
diagrams/use-case/render
diagrams/use-case/security
diagrams/use-case/tests
layout

Layout chooses positions for every shape and routes every edge. Component / use-case / deployment diagrams flow through ELK (elkjs) using the layered algorithm with orthogonal edge routing. After ELK returns we chamfer 90° corners so the result matches Excalidraw's diagonal-corner aesthetic.

Sequence diagrams skip ELK entirely — their layout is strictly tabular (lifelines on the X axis, time on the Y axis), so a deterministic ~90-line algorithm produces better, more compact results than a force-directed solver could.

main/builtin
model

Input-agnostic diagram model. Two top-level kinds:

  • Diagram — component / deployment / use-case style (planes, subplanes, boxes, connections).
  • SequenceDiagram — lifelines + messages + notes.

Layout and renderer dispatch on the model class. Anything that can be expressed as one of these two shapes flows through the pipeline; the parser is just one possible source. Callers can also build a Diagram programmatically and feed it to renderDiagram().

modules/dependencies
modules/introspection
modules/metadata
modules/pipeline
modules/registry
parser/engine

A ~50-line line-walker. The engine itself knows nothing about PlantUML syntax; that lives entirely in plugins. Block plugins (multi-line notes, class bodies) take exclusive ownership of subsequent lines until they release.

platform/asset-base
platform/diagnostics
platform/security-base
platform/services
render

Emits Excalidraw JSON. Each model shape is dispatched to a dedicated renderXxx() function that produces one or more Excalidraw primitive elements (rectangle, ellipse, line, arrow, text). The output document is a stand-alone .excalidraw file that any Excalidraw front-end can open. The companion module src/general/render/svg.mjs converts the same JSON to SVG for the build-time documentation pipeline.

sequence-spacing

Central spacing contract for sequence diagrams.

Sequence layout has many visual item types (messages, notes, refs, dividers, fragments, lifecycle bars). They all reserve vertical space through this module so adding a new timeline item does not introduce a one-off top/bottom rhythm.

License

MIT grethel-labs