paseo.cafe
All plugins

Render Mermaid diagrams inside the Paseo chat timeline

v0.2.0 Requires Paseo >=0.8.0

Gallery

Install

npmjs · Paseo 0.9+

paseo plugin add npm:paseo-plugin-mermaid@0.2.0

GitHub · Paseo 0.8 fallback

paseo plugin add dutchakdev/paseo-plugin-mermaid --ref 95cf07db794f6be3e121a41a7e774e3fdde3ccf5

Git security status and fallback installation are pinned to commit 95cf07db794f.

From the plugin's README

paseo plugin install npm:paseo-plugin-mermaid   # Paseo 0.9 and newer
paseo plugin ls          # expect: running

Paseo 0.8 has no npm sources, so install from Git there: paseo plugin add dutchakdev/paseo-plugin-mermaid. Either way, paseo plugin update mermaid pulls later releases. The plugin supports Paseo 0.8.x through 0.11.x, including betas, on both sides: the daemon compiles it, and the app checks its declared version range. It has no server entry, so it starts no daemon process.

The manifest remains >=0.8.0, which already admits 0.9.x, 0.10.x and 0.11.x. Paseo also checks a prerelease's stable version core, so 0.11.0-beta.3 satisfies this range; see version requirements. The development SDK stays pinned to 0.8.0; Paseo supplies the runtime modules.

To work on it locally instead:

git clone https://github.com/dutchakdev/paseo-plugin-mermaid.git
cd paseo-plugin-mermaid
npm install
npm run typecheck && npm test
paseo plugin install "$PWD"

Plugins must be enabled daemon-wide first, under Settings → Plugins → Enable plugins.

README

Built on the plugin timeline API introduced in Paseo 0.8.

Paseo draws Mermaid itself too, in an iframe inside a zoomable box with a fullscreen viewer. This plugin draws it inline instead: readable at the width of the message, from the first streamed line, with the same controls above it for the times the fitted drawing is not enough: zoom, fit to view, pop out, and the source on demand.

Install

paseo plugin install npm:paseo-plugin-mermaid   # Paseo 0.9 and newer
paseo plugin ls          # expect: running

Paseo 0.8 has no npm sources, so install from Git there: paseo plugin add dutchakdev/paseo-plugin-mermaid. Either way, paseo plugin update mermaid pulls later releases. The plugin supports Paseo 0.8.x through 0.11.x, including betas, on both sides: the daemon compiles it, and the app checks its declared version range. It has no server entry, so it starts no daemon process.

The manifest remains >=0.8.0, which already admits 0.9.x, 0.10.x and 0.11.x. Paseo also checks a prerelease's stable version core, so 0.11.0-beta.3 satisfies this range; see version requirements. The development SDK stays pinned to 0.8.0; Paseo supplies the runtime modules.

To work on it locally instead:

git clone https://github.com/dutchakdev/paseo-plugin-mermaid.git
cd paseo-plugin-mermaid
npm install
npm run typecheck && npm test
paseo plugin install "$PWD"

Plugins must be enabled daemon-wide first, under Settings → Plugins → Enable plugins.

What it draws

Diagram Supported
flowchart / graph TD, TB, BT, LR, RL
Node shapes [rect], (round), ([stadium]), [[subroutine]], {diamond}, ((circle)), >asymmetric]
Edges -->, ---, -.->, -.-, ==>, ===
Edge labels `-->
sequenceDiagram participants, actors, notes
Message arrows ->, ->>, -->, -->>, -x, --x, -), --)

Not supported yet: subgraphs, style/classDef directives, and the diagram types beyond these two. Unrecognised lines are counted and reported under the drawing rather than dropped, and a diagram the parser cannot read at all falls back to showing its source.

Controls

Every drawing carries a toolbar: Zoom out · 100% · Zoom in · Fit to view · Pop out on the left and Show code on the right. The zoom model is the one Paseo's own diagram box uses, so a reader who knows one knows the other.

Control What it does
Zoom in / out Steps by ×1.25 and ÷1.25 between 25% and 400%. A button goes quiet at its limit.
100% The zoom relative to the fitted drawing. Fitted means scaled to the width of the row, grown a little when small, and never shrunk past the point where labels stop being readable.
Fit to view In the row: show the whole drawing across the row, even below the readability floor, for the wide ones that would otherwise scroll sideways. It never grows a drawing past 100%. In the pop-out: fit both axes of the window. Quiet once it is there.
Pop out Opens the drawing in a host modal, fitted to the window on both axes, with the same toolbar. Zoomed past the window, it scrolls on both axes.
Show code Swaps the drawing for its Mermaid source and quiets the zoom controls until you switch back.

Zooming in the row makes the row taller and, past its width, scrolls it sideways; the transcript scrolls vertically as it always does, so nothing is clipped. On phones and narrow windows the button labels drop and the icons and the percent stay.

How it works

flowchart TD
    Msg([Message arrives]) --> Check{Has mermaid?}
    Check -->|no| Paseo[Paseo renders it]
    Check -->|yes| Split[[Split into segments]]
    Split --> Text(Prose)
    Split --> Src(Diagram source)
    Text --> Md[Minimal markdown]
    Src --> Parse{Which parser?}
    Parse -->|flowchart| Layout[Layered layout]
    Parse -->|sequence| Columns[Column layout]
    Parse -.->|neither| Raw[Show source]
    Layout --> Draw((Views))
    Columns --> Draw

Three constraints shaped this plugin.

Mermaid itself cannot run here. A Paseo client bundle may import only the modules the app provides: react, react-native, @tanstack/react-query, zod and the @getpaseo/plugin entries. There is no SVG, no canvas and no webview, and Mermaid needs a DOM. So the parser, the layout and the drawing are all written from scratch. Every line you see is a positioned View: node boxes, edge segments 1.5px thick, and arrowheads made from a View with three transparent borders.

A transformer replaces the whole timeline entry. Once a message contains a diagram, the plugin owns the message. It is split into prose and diagram segments, and each becomes its own item, so the text around the diagram keeps its place. That prose goes through a small Markdown renderer covering headings, lists, emphasis and code — less than Paseo's own, which is why messages without a diagram are left alone entirely.

Items store the source, not a parsed graph. Parsing happens at render time. The stored data stays small, and a later fix to the parser improves diagrams that were sent months ago.

The fence is claimed while it is still open. Paseo runs transformers on every streaming update, and it renders mermaid fences itself as soon as one opens. A transformer that waited for the closing ``` would leave the host drawing the diagram for the whole stream and then swap renderers at the last line. An unfinished block is therefore taken over at once and drawn from whatever has parsed so far.

The drawing is fitted to the row. A diagram's natural size is whatever its labels happened to add up to, which is no basis for how big it should appear. The row is measured, the drawing is scaled to fit, small diagrams are grown a little so they do not look lost, and shrinking stops at the point where labels would stop being readable. Only past that does it scroll sideways. The toolbar's zoom multiplies that fit, which is why 100% is the fit itself and not the natural size.

Development

npm run typecheck
npm test                 # 110 tests
paseo plugin reload mermaid

The parsers, the layout, the zoom model and the transform are pure functions with no runtime dependencies, and that is where the tests are: bracket shapes, operator precedence (-.-> must never read as -.- plus a stray >), cycles in a flowchart, node overlap, arrow direction, zoom steps that land back on 100%, which toolbar controls are live, and the JSON round trip Paseo performs on item data before rendering. The Views themselves are checked by npm run typecheck and by reloading the plugin.

File Role
shared/segment.ts splits a message into prose and diagram blocks
shared/flowchart.ts flowchart parser
shared/sequence.ts sequence diagram parser
shared/layout.ts layered layout, orthogonal edge routing, column layout
shared/markdown.ts the Markdown subset used for prose
shared/zoom.ts zoom steps, fit to view, and which controls are live
shared/transform.ts the timeline transform itself
client/diagram.tsx drawing, in Views, at natural size
client/viewer.tsx fitting, zoom and the source toggle, in the row and in the pop-out
client/toolbar.tsx the toolbar
client/markdown.tsx prose rendering
client/item.tsx the two timeline renderers
index.client.tsx registers the transformers and renderers; the only entry

License

MIT

Scanned 05 Oct 2026, 09:53 UTC from dutchakdev/paseo-plugin-mermaid.