Repository navigation
🤖 feat: render Mermaid diagrams with Pele in the selected theme - #7820
Conversation
Adds pele@1.9.0 (newest release past minimumReleaseAge) and the Coder product content-link and highlight-* tokens that the Pele diagram theme uses. Dark values sit in theme-dark.css; light and flexoki-light share one light rule. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Replaces the mermaid package with Pele's synchronous render(). Diagrams wait for web fonts, then measure labels with the same --font-primary and --font-monospace that .mermaid-container draws with. links and images are off because chart text is untrusted model output. .mermaid-container maps Pele's CSS variables to Coder tokens, so diagrams follow every theme without a re-render. sanitizeMermaidSvg now accepts only strict SVG and removes foreignObject, since Pele never emits it. Tests render with real Pele instead of a mermaid module mock. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Keeps Mermaid's cyclomatic complexity from growing (crap-check floor-cc): renderMermaidSvg owns the font wait, the Pele call and sanitization. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
The vite alias pointed at src/mocks/mermaidStub.ts, which does not exist, so e2e runs already used the real renderer. With mermaid gone the stub, the alias and the env var have nothing to do. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Pele SVGs keep their measured width, so a diagram wider than its frame overflows. justify-content: center pushed the left part out of the scroll area at phone widths; safe center keeps it reachable. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
The link and highlight tokens that feed Pele's accent and series colors held Coder values in every theme. Light and dark are Coder-derived and keep them. flexoki-light now uses Flexoki 600 and flexoki-dark Flexoki 400, matching each block's other accents. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `medium`_
The 70% cap only shrank wide diagrams, and Pele never upscales small ones, so it wasted about 100 px of a phone screen. Without the cap the auto margins center nothing, so they go too. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
On phones and narrow panes a diagram kept its desktop layout and scrolled sideways. The diagram now measures its container and passes the width to Pele as maxWidth, so Pele wraps labels, tightens spacing and, below 640 px, turns layouts like flowchart LR top-to-bottom. A width change re-renders at once, without the 350 ms chart debounce. An unmeasured (zero) width keeps the natural layout. Below 640 px the 300 px height clamp is lifted too: a top-to-bottom diagram would otherwise shrink until its labels are unreadable, and the transcript scrolls vertically instead. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
The narrow-width rules raised the Mermaid component's complexity above the CRAP gate's floor. diagramLayout now turns the measured container size into Pele's maxWidth and the height-clamp style, so the zero-width rule lives in one place. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Dropping the 70% centered box left small diagrams at the left edge of the artifact viewer, which renders Mermaid outside .markdown-content. The flex centering and overflow rules now apply to every .mermaid-container. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
--- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efe7ed7f26
ℹ️ 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".
🟢 e2e stories (Haiku): 14 passed22 agent steps · 7 replayed from cache · 15 model calls · 46.4k tokens (24% cached) All 14 tests in 9 files
e2e 0.20.0 · 1m 13s · web · run artifacts 🟢 e2e Electron stories (Haiku): 3 passed3 agent steps · 3 model calls · 8.0k tokens (22% cached) All 3 tests in 3 files
e2e 0.20.0 · 17.6s · electron · run artifacts 🟢 e2e agent repros (Haiku): no tests selectedSpend: $0.00 of the $1.00 cap (proxy ledger) e2e 0.20.0 · 5.3s · 2 targets (web, phone) · run artifacts |
The artifact viewer renders Mermaid without a .markdown-content ancestor, so the svg rules scoped under it never matched there: the zoom buttons changed --diagram-zoom but the diagram kept its size, and the expanded view lost its 80vh cap. The container rule was already global; the svg and modal svg rules now are too. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=xhigh -->
Mermaid rendered $$...$$ labels as KaTeX MathML. Pele shows the raw TeX unless render() gets a math resolver, so the switch to Pele regressed these labels. renderMathLabel typesets each label with KaTeX (MathML output, default trust: false) and measures it in an offscreen probe. It returns null for invalid TeX, and Pele then shows the source. KaTeX is now a direct dependency. Mermaid.tsx imports it statically, and the Diagram lazy boundary keeps it off the first load. sanitizeMermaidSvg still removes every foreignObject except the exact shape Pele wraps around a math result. The predicate also checks each MathML element name, because the HTML parser turns a MathML-namespace <img> inside <mtext> into a real image. The layout-width tests now restore their getBoundingClientRect spy. happy-dom shares element classes across windows, so the spy leaked into later tests. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=xhigh -->
Architecture diagrams nest icon svgs. The descendant selector zoomed them a second time and applied the height cap to each icon. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
|
@codex review Both round-1 findings are fixed (see the thread replies). Please take another look. |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1203c3bef7
ℹ️ 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".
document.fonts.ready does not start loading an @font-face that nothing on the page uses yet, such as Geist Mono on Linux. Pele then sized label boxes for the fallback font, and the boxes were wrong once the real font swapped in. renderMermaidSvg now requests the primary and monospace families with document.fonts.load before it awaits ready. Promise.allSettled keeps a failed font load from blocking the render. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
The expand dialog copied the inline SVG, so in a narrow chat column or the artifact sidebar it showed the narrow, often vertical layout at full screen size. Measurement and rendering move into a useDiagramSvg hook that both frames call. The inline frame keeps its debounce, last-valid render, error text, zoom and copy source. The dialog renders renderedDiagram.chart (a chart that already rendered) at its own width, with its own id prefix. The modal innerHTML effect and its ref are gone. A render error now records the chart that failed, so a newer chart hides it without an effect that clears it. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
AGENTS.md asks breakpoint-dependent UI to pin a Pixel viewport variant, mirror it with the story viewport global, and back it with a play contract. The 640 px Mermaid breakpoint had none, so Pixel snapshots only showed the laptop layout. MermaidPhone renders a six-node flowchart LR in a 358 px column (390 px Pixel phone viewport). Its play checks the pinned variant, then asserts the diagram is taller than wide, does not overflow sideways, and has no max-height cap. MermaidLaptop renders the same chart in a 768 px column and asserts it stays wider than tall. Both columns have fixed widths because the test runner plays at desktop width. They add 2 Pixel snapshots (685 of 690) and no new story file. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_ <!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
|
@codex review All three round-2 findings are fixed (see the thread replies). Please take another look. |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Readiness record for head
Generated with |
|
Verifier verdict: PASS a. Theme colors: Generated with |
Summary
Xum now draws Mermaid diagrams with Pele instead of the
mermaidpackage. Diagrams use the colors of the selected Xum theme, and they change their layout to fit narrow screens. Pele reads the same Mermaid syntax, has no dependencies, and is much smaller and faster.Background
Xum always drew diagrams in Mermaid's fixed dark theme, also in the light themes. On a phone, a wide diagram kept its desktop layout and scrolled sideways. Pele is an MIT-licensed renderer from Obsidian. It supports every diagram type built into Mermaid 12.1, takes its colors from CSS variables, and can reflow a diagram to a given width.
Measured in Chromium against Mermaid 11.17.2: a flowchart took 3.2 ms instead of 34 ms, and a sequence diagram took 0.18 ms instead of 32 ms. The lazy diagram chunk is 260 kB gzipped instead of 976 kB. The first diagram also loads the shared 77 kB KaTeX chunk if markdown math has not loaded it yet.
Implementation
--pele-*variables to Xum theme tokens. The neutral roles use tokens that already exist. Xum's dark and light themes already use Coder's product surface tokens. This PR adds Coder's link color and its six highlight colors (--color-content-link,--color-highlight-*) for accents and chart series. The two Flexoki themes set these tokens to Flexoki's 600 shades (light) and 400 shades (dark). A theme switch recolors a diagram without a new render.useResizeObservermeasures the diagram box, and Xum passes the width to Pele asmaxWidth. Below 640 px, Pele can switch a wideLRflowchart to top-to-bottom, and Xum removes the 300 px height cap so the labels stay readable. I removed the old 70% width cap. Pele never makes a diagram larger, so the cap only made wide diagrams smaller. The expand dialog renders its own copy at the dialog's width, so a diagram from a narrow pane opens in its wide layout. Xum loads the label fonts before Pele measures, so the boxes fit the real font.javascript:links. Xum also turns off links and images (an image fetches from the network as soon as it renders). The existing sanitizer stays as a second layer. Pele's output is well-formed XML, so the sanitizer now uses a strict parse only, and it removesforeignObject.$$...$$labels are typeset with KaTeX as MathML, as Mermaid did. The sanitizer keeps aforeignObjectonly in Pele's exact math wrapper. Every element in it must be a MathML element that KaTeX emits. Names are checked as well as namespaces, because the HTML parser turns a MathML-namespace<img>inside<mtext>into a real image.mermaiddependency, an unused stub, a vite alias that pointed to a missing file, andVITE_DISABLE_MERMAIDin the e2e setup.Validation
foreignObjectkept, nomaxWidth, width not in the effect keys, no height-cap lift), and each break failed its matching test.MermaidPhoneandMermaidLaptopstories pin the Pixel viewports. Their plays check the top-to-bottom layout below 640 px and theLRlayout above it.docs/img/plan-mermaid.webpfrom its story.Risks
Low severity. The change affects only how diagrams look in chat, plans and Mermaid artifacts.
%%{init: {theme}}%%andthemeVariablesno longer apply.LRchain that is wider than the chat column still scrolls sideways. The 640 px breakpoint keeps itLR, because a top-to-bottom chain under the 300 px height cap would be unreadable.A problem that predates this PR, left as a follow-up: the zoom buttons are nearly invisible in the light theme (#7821). Two hardening items for the sanitizer's second layer are tracked in #7828. This PR fixes the other old zoom problem: the buttons now work in the artifact viewer.
Screenshots
Dark, light and Flexoki dark at 1280 px:
A 10-node
LRflowchart at 390 px (it changes to top-to-bottom) and at 1280 px (it staysLR):Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$8.15