Skip to content

Render Mermaid diagrams in prose with a kawaii treatment (+ Pretext label metrics) - #9

Open
maceip wants to merge 2 commits into
mainfrom
exp/kawaii-mermaid
Open

Render Mermaid diagrams in prose with a kawaii treatment (+ Pretext label metrics)#9
maceip wants to merge 2 commits into
mainfrom
exp/kawaii-mermaid

Conversation

@maceip

@maceip maceip commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What

Fenced mermaid blocks in agent turns and comments render as inline SVG through a vendored copy of beautiful-mermaid (Craft, MIT) with surgical edits marked AXP::

  • Leaf-shaped arrowheads with a faint midrib — the signature
  • Rounded corners on every rectangle and polygon vertex; rounded edge bends with round caps; 1.5–1.75px strokes
  • Sticker-soft node shadows, dashed rounded subgraphs, pill edge labels
  • No remote font @import (upstream pulls Inter from Google Fonts inside the SVG; the workspace loads nothing third-party)

Second commit: real text metrics via Pretext (MIT). Upstream sized nodes from an Inter character-width table and could not wrap a label; now nodes fit AXP Runde exactly and labels over 190px break into balanced lines ("Checkpoint saved with / the bundle and patch") via two hooks that are no-ops outside a browser.

Wiring: ui/src/Diagram.tsx renders synchronously, refuses script-like output and falls back to the source with the parse error; loaded lazily from the Markdown pre renderer (ELK is its own 1.5 MB chunk). Vendored sources live in ui/vendor behind a small .d.ts boundary so the workspace's strict tsconfig applies to our code only. Notices cover beautiful-mermaid, ELK (EPL-2.0), entities (BSD-2) and Pretext.

Look at

  • docs/design/diagrams.md
  • docs/design/diagrams/*.svg — a flowchart, a state diagram, nested subgraphs (regenerate with npx tsx scripts/design/render-diagrams.mts)
  • npm run demo:ui → "Handle email task retries" has a flowchart in the transcript

Verification

npm run check and npm run test:ui pass.

Landing

Adjacent-line conflicts with siblings in THIRD_PARTY_NOTICES.md / scripts/ui-notices.mjs only.

maceip added 2 commits September 6, 2026 09:36
Fenced ```mermaid blocks in agent turns and comments render as inline SVG
through a vendored copy of beautiful-mermaid (Craft, MIT) with surgical
edits marked AXP: leaf-shaped arrowheads with a faint midrib, rounded
corners on every rectangle and polygon vertex, rounded edge bends with
round caps, sticker-soft node shadows, dashed rounded subgraphs, pill edge
labels, and no remote font @import (the workspace loads nothing third-party).

- ui/src/Diagram.tsx renders synchronously, refuses script-like output and
  falls back to the source with the parse error
- Loaded lazily from the Markdown pre renderer; ELK is its own 1.5 MB chunk
- Vendored sources live in ui/vendor and are typed through a small .d.ts
  boundary so the workspace's strict tsconfig applies to our code only
- Palette in ui/src/diagram-theme.ts; samples in docs/design/diagrams
  regenerated by scripts/design/render-diagrams.mts
- Demo fixture: the email-task session now includes a flowchart
- Notices cover beautiful-mermaid, ELK (EPL-2.0) and entities (BSD-2)
…lines

beautiful-mermaid sized nodes from a character-width table for Inter and
could not wrap a label, so a wordy node became a very wide box. Two hooks
in the vendored renderer, no-ops outside a browser: setTextMeasurer for
exact single-line widths in the page's real font, and setLabelWrapper,
which breaks node labels over 190px (edge labels over 140px) into lines
and then narrows the width by binary search until one more line would be
needed, so lines come out balanced. ui/src/diagram-text.ts implements both
with @chenglou/pretext (MIT, canvas-measured, off the DOM); Diagram.tsx
installs them. Review samples and the demo fixture gain a long label.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-06T22:15:00.711424Z 85ffeb2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85ffeb2d54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/Diagram.tsx
<figure
className="diagram"
role="img"
aria-label="Diagram"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expose the diagram text to assistive technology

For every successfully rendered Mermaid block, assigning role="img" with only the generic label "Diagram" causes assistive technology to treat the entire figure as a single image and ignore its SVG descendants. Screen-reader users therefore lose all node and edge text that was previously available in the fenced source. Include the diagram source or an equivalent textual description in the accessible name/description instead of replacing it with a generic label.

Useful? React with 👍 / 👎.

Comment thread ui/src/diagram-text.ts
Comment on lines +72 to +75
const lines = layoutWithLines(ready, high, fontSize * 1.4).lines.map(
(line) => line.text.trim(),
);
return lines.filter(Boolean).join("\n");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve inline formatting while wrapping labels

When a formatted flowchart label such as A[**A sufficiently long bold label**] exceeds the wrapping width, the parser has already converted the markup to <b>...</b>, and these lines split and rejoin that tagged string verbatim. Because renderLineContent() parses each resulting line with fresh formatting state, only the line containing the opening tag remains bold (and a break inside a tag can expose it as text). Wrap the plain label while carrying or reapplying formatting across generated line boundaries.

Useful? React with 👍 / 👎.

* Returns the type keyword used for routing to the correct pipeline.
*/
function detectDiagramType(text: string): 'flowchart' | 'sequence' | 'class' | 'er' | 'xychart' {
const firstLine = text.trim().split(/[\n;]/)[0]?.trim().toLowerCase() ?? ''

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip leading comments when detecting diagram type

A valid non-flowchart diagram with a leading Mermaid comment, such as %% explanation followed by sequenceDiagram, is classified as a flowchart because detection examines the first raw line. The flowchart parser later removes the comment but rejects sequenceDiagram as its header, so the UI falls back to source instead of rendering. Filter comment lines before selecting the diagram pipeline, as the individual parsers already do.

Useful? React with 👍 / 👎.

Comment on lines +193 to +195
// --- activate / deactivate explicit commands ---
// These are handled implicitly via +/- on messages but can also appear standalone
// For now, we skip explicit activate/deactivate lines (they affect rendering only)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render standalone sequence activation commands

For standard sequence diagrams that use activate B and deactivate B on separate lines, the parser silently discards both commands, so the resulting SVG omits the activation bar and misrepresents the interaction without showing a parse error. The layout and renderer already support activation records through message shorthand; these standalone commands need to update the same activation state rather than being skipped.

Useful? React with 👍 / 👎.

Comment thread ui/src/Diagram.tsx
try {
const svg = renderMermaidSVG(code, {
...AXP_DIAGRAM_COLORS,
font: "AXP Runde",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the self-hosted workspace font for diagrams

The SVG requests AXP Runde, but the application only self-hosts DM Sans Variable and contains no @font-face or package defining AXP Runde. Consequently every browser falls through to its platform system-ui font, rather than inheriting the workspace font as intended, and the Pretext measurements and wrapping vary by operating system. Use the loaded DM Sans Variable family consistently or actually provide the requested font.

Useful? React with 👍 / 👎.

Comment thread scripts/ui-notices.mjs
Comment on lines +15 to +17
"elkjs",
"entities",
"@chenglou/pretext",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ship the vendored renderer's license with the package

The build now collects licenses for the renderer's npm dependencies but never copies ui/vendor/beautiful-mermaid/LICENSE into dist. Because the published package includes dist but excludes ui/vendor, consumers receive the bundled beautiful-mermaid code without its required MIT copyright and permission notice; the short attribution in THIRD_PARTY_NOTICES.md is not the full notice. Append the vendored license to dist/ui/licenses/bundled.txt or otherwise include it in the published files.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant