Skip to content

feat(frontend): render Mermaid diagrams in messages #2007

Description

@hmans

Context

Chatto stores and exchanges message bodies as plain text while the bundled client renders the deliberately limited Markdown subset documented in FDR-032. Mermaid fences would let people share architecture, sequence, state, and other diagrams while preserving that portable source contract.

The frontend currently routes fenced blocks through apps/frontend/src/lib/markdown.ts, lazy-loads syntax highlighting, and inserts renderer-owned output through MarkdownHtml.svelte. Mermaid is substantially heavier and emits user-influenced SVG, so it needs its own lazy, bounded, reviewed path rather than being treated as another highlighting language.

No protobuf or persisted-message schema change should be necessary. Older/third-party clients can continue to display the fenced source.

Proposed behavior

Use GitHub-compatible fenced syntax:

```mermaid
flowchart LR
  Browser --> Chatto
```
  • Only a fenced block whose normalized language is exactly mermaid becomes a diagram.
  • Preserve the original source through compose, send, edit, copy, reply quoting, realtime delivery, and history hydration.
  • While loading, keep a stable source/code fallback; on parse, render, security, or complexity failure, leave that individual fence readable as code instead of failing the whole message.
  • Do not execute diagram links, click handlers, scripts, raw HTML labels, or other interactive behavior.

Technical direction

Use Mermaid's programmatic render API with startOnLoad: false; do not scan the global document and do not call returned bindFunctions. Dynamically import Mermaid only when an actual Mermaid fence needs rendering and verify it stays out of representative initial-route chunks.

Security is part of the feature, not follow-up work:

  • Pin securityLevel: "strict", disable HTML labels and interactions, and retain site ownership of every security-sensitive option. Mermaid documents that strict mode encodes HTML and disables clicks, but generated SVG is still an untrusted-content boundary: sanitize it with an explicit SVG allowlist or isolate it in an equivalently reviewed boundary before DOM insertion.
  • Do not let message-authored directives or frontmatter relax security/resource configuration or inject CSS/HTML. Reject them initially unless a narrow safe allowlist is deliberately designed.
  • Preserve the existing single audited Markdown HTML sink/Trusted Types model; do not introduce feature-level {@html} usage.
  • Review CSP impact without adding broad unsafe-*, data:, remote-script, or remote-font allowances.
  • Keep Mermaid's dependencies bundled locally and update NOTICE.

See Mermaid's security-level contract and secure configuration keys.

Resource limits and rendering lifecycle

Mermaid parsing/layout is attacker-controlled work. Set explicit limits below Mermaid's generous defaults for source size, edges/nodes, generated SVG size/dimensions, and diagrams per message. Also:

  • render only diagrams near/inside the viewport;
  • bound concurrent renders and cache completed SVGs by source plus theme in a bounded cache;
  • cancel or ignore stale results after unmount/source/theme changes;
  • ensure one expensive or broken diagram cannot block other messages;
  • avoid rerender loops in virtualized timelines.

Mermaid exposes maxTextSize and maxEdges, but those are not a complete DOM/output budget; add Chatto-side limits and adversarial tests too.

Accessibility and presentation

  • Support Mermaid accTitle and accDescr and verify the rendered graphic has an accessible name/description. See Mermaid's accessibility syntax.
  • Provide a readable source/error fallback when an accessible description is absent or rendering is unavailable.
  • Keep diagrams responsive inside the message width, with native overflow/zoom behavior where required; never widen the conversation layout.
  • Render legibly in every supported light/dark theme and update safely when the app theme changes.

Acceptance criteria

  • Valid flowchart, sequence, class/state, ER, and Git graph fences render from the standard mermaid code-fence syntax.
  • Non-Mermaid fences and malformed/unsupported/over-limit Mermaid fences retain readable code-block behavior.
  • Source round-trips unchanged through the rich composer, posting, editing, copy/reply actions, and receiver-side rendering.
  • Mermaid is lazy-loaded only for visible Mermaid content and absent from message-free initial chunks.
  • Site-owned strict security settings cannot be overridden by source; raw HTML, CSS injection, links, scripts, events, and bindFunctions are not active.
  • Generated SVG passes a reviewed sanitization/isolation boundary compatible with Trusted Types and CSP.
  • Source, edge/node, output, diagram-count, and concurrency limits prevent pathological messages from monopolizing the UI.
  • Rendering is stale-safe across virtual-list unmounts, message edits/deletes, and theme changes.
  • Diagrams are accessible, responsive, theme-aware, and do not widen or clip the message layout.
  • Parser/security/limit tests, mounted browser-component tests, realistic Storybook stories, and sender/receiver browser coverage exercise success and fallback paths.
  • FDR-032, the public Message Body Tokens documentation, user-facing formatting docs, dependency metadata, and NOTICE are updated.

Out of scope

  • A graphical diagram editor or composer toolbar.
  • Server-side Mermaid rendering or validation.
  • Diagram click actions, remote resources, custom scripts/styles, or arbitrary per-message Mermaid configuration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or enhancement

    Projects

    Status
    Planned

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions