Render Mermaid diagrams inside the Paseo chat timeline
npmjs · Paseo 0.9+
paseo plugin add npm:paseo-plugin-mermaid@0.2.0GitHub · Paseo 0.8 fallback
paseo plugin add dutchakdev/paseo-plugin-mermaid --ref 95cf07db794f6be3e121a41a7e774e3fdde3ccf5Git 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.
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.
| 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.
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.
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.
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 |
MIT
Scanned 05 Oct 2026, 09:53 UTC from dutchakdev/paseo-plugin-mermaid.