Repository navigation
Feat/mermaid viewer - #24189
Feat/mermaid viewer#24189blackshot wants to merge 8 commits into
Conversation
Large mermaid diagrams render too small to read inline. A corner button on every rendered diagram now opens it full-window with wheel zoom (anchored at the cursor), drag pan, a floating zoom/fit/close toolbar, and Escape to close. Adds a `fullscreen` variant to DialogContent so the viewer does not restyle the primitive from outside. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fit no longer clamps to the 10% floor, and zoom-out uses a per-diagram floor at or below the fit scale, so very wide diagrams show whole. - The expanded SVG is keyed to the content/theme it was rendered from, so the expand button hides while a newer render waits in the queue instead of opening the previous diagram. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each MermaidBlock kept a second serialized copy of its SVG in state even if it was never expanded. The viewer now reads the sanitized inline diagram's markup when it opens and drops it after the close animation, so idle diagrams hold no extra copy. MermaidBlock only tracks which content/theme it last rendered, which still hides the expand button during a pending re-render. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A ResizeObserver refits the diagram when the viewer's size changes, but only while the user has not zoomed or panned since the last fit, so a window resize never discards a chosen view. - Wheel events in page mode (deltaMode 2) are converted to pixels (800px per page) instead of being read as a tiny pixel delta. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A horizontal wheel event, a zoom click already at the scale limit, or a click without pointer movement marked the view as user-adjusted, so a later resize stopped refitting a diagram that was still fitted. All user inputs now go through one helper that compares the next transform with the current one and only flags an adjustment when the view actually changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The viewer could only be panned by dragging, so keyboard users could not reach clipped parts of a zoomed diagram. The diagram viewport is now a focusable application region that receives focus on open: - Arrow keys pan (Shift for bigger steps), moving the view like scrolling. - + / = zoom in, - zooms out, 0 fits to screen. Modified keys are left to the app, and Escape/Tab keep their dialog behavior. Toolbar tooltips show the matching key chips, and the hint and viewport label are translated in all six locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… toolbar clicks - Adds a "Copy as PNG" toolbar button. The rendered SVG is rasterized on a canvas at 2x over the current theme background (so light-on-dark diagrams stay readable when pasted), scaled down to fit the clipboard pixel budget and Chromium's canvas side limit, and written through the existing writeClipboardImage bridge. Oversized results and failures show a toast instead of silently doing nothing. - The viewer key handler moves from the viewport to the wrapper around the viewport and toolbar, so arrows, +/- and 0 keep working after a toolbar button takes focus. - The toolbar icon button moves to its own module for reuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Hide "Copy as PNG" in the web client, where the clipboard bridge resolves without writing images and the button would report a copy that never happened. - A focused toolbar button's tooltip is the topmost dismissable layer and swallowed the first Escape. Toolbar tooltips now close the whole viewer on Escape through a small close context. - Dragging applies per-move deltas instead of offsets from the drag start, so zooming mid-drag no longer snaps the diagram back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| @@ -0,0 +1,31 @@ | |||
| import { describe, expect, it } from 'vitest' | |||
There was a problem hiding this comment.
PNG export remains untested. These tests check canvas dimensions but never exercise SVG serialization, image loading, rasterization, or the clipboard write. A blank or incorrectly rendered copy could therefore pass the suite. Add a test that exports a representative styled SVG.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (15)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughMermaid diagrams can now open in a fullscreen viewer with fit, zoom, pan, and keyboard controls. The viewer includes a PNG copy control outside the web client. The change also adds viewport and PNG layout helpers, tests, fullscreen dialog styling, and localized viewer text. Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to This adds a fullscreen Mermaid viewer with zoom, pan, and PNG copy. The supplied context shows no concrete failure path, and it is ready to merge after the normal CI checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The viewer reuses sanitized diagrams, and PNG export retains existing clipboard access and size checks. No material security bypass was established. An export can finish after the viewer closes, and incomplete coverage limits confidence in the overall assessment. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 57.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 9 files. (6 skipped: 6 unsupported.)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thank you for this. The writeup and the viewport math were really thorough. Since this PR was opened, Orca got a shared pan/zoom viewer ( |
ELI5
Big Mermaid diagrams in markdown were shrunk to fit the column, so their text was too small to read. Now every rendered diagram has a small "expand" button in its corner. Clicking it opens the diagram over the whole window, where you can zoom with the mouse wheel or keyboard and drag to move around. You can also copy the diagram as a PNG image to paste into chats or documents. Escape or the close button takes you back.
What Changed
Before: a Mermaid diagram inside a markdown file (preview, rich editor, comments, or a
.mmdfile) was always scaled down to the width of its container. Large sequence or flow diagrams became unreadable. There was no way to enlarge them, and no way to get them out as an image.After:
+/=zoom in,-zooms out, and0fits to screen. These keys keep working after a toolbar button has focus.Mechanism:
MermaidBlockalready renders and DOMPurify-sanitizes the SVG inline. When the viewer opens, it reads that sanitized markup from the inline diagram. Nothing is stored beforehand, and the copy is dropped after the close animation. In the copy, the render ID is renamed so the diagram's scoped<style>andurl(#marker)references don't collide with the inline diagram.MermaidBlockalso tracks which content and theme it last rendered, and hides the expand button while a newer render waits in its queue, so it never opens a stale diagram.MermaidDiagramLightboxis built on the existingDialogprimitive, which already handles Escape, the focus trap, and returning focus to the button. The SVG is pinned to its naturalviewBoxsize and moved with a CSStranslate() scale()transform. The wheel listener is non-passive so it can stop Electron's page zoom and scrolling. AResizeObserverdrives the refit. All user inputs go through one helper that ignores inputs that change nothing (a horizontal wheel, zooming past a limit, a click without movement), so those don't count as the user adjusting the view. Dragging applies per-move deltas, so zooming mid-drag doesn't snap the diagram back. Toolbar tooltips hand Escape to the viewer, so a focused button's tooltip doesn't swallow the first Escape.mermaid-diagram-viewport.ts, with unit tests. It covers fitting, zooming around a point, the per-diagram minimum scale, wheel deltas in pixel, line, and page modes, and mapping keys to actions.mermaid-diagram-png.ts) serializes the SVG and draws it on a canvas over the theme's background color. It writes the result through the existingwindow.api.ui.writeClipboardImagebridge. The output size is capped to the clipboard pixel budget and Chromium's maximum canvas side. An image still too large for the clipboard shows a toast instead of failing silently in the main process.DialogContentgets afullscreenvariant. The design-system lint doesn't allow restyling the primitive from outside, so the edge-to-edge layout lives in the primitive.Why
Diagrams are the one piece of markdown content that can't reflow, so fitting them to the column is the only safe inline default. What was missing was a way to read them large, and to get them out as an image. Other approaches I considered:
html-to-imagefor the PNG (already a dependency). It clones every node and inlines computed styles, which is slow for large diagrams. The Mermaid SVG already carries its own styles, so drawing it straight onto a canvas is enough.Putting the button in
MermaidBlockcovers every place a diagram renders (markdown preview, rich markdown editor, sidebar comments, full.mmd/.mermaidfiles) with a single change.Linked Issue
Fixes #22372
Visual Proof
Testing
Windows 11: opened a markdown file with a large Mermaid sequence diagram and checked:
Unit tests:
mermaid-diagram-viewport.test.ts: fit (wide diagram, small-diagram upscale cap, diagrams needing less than 10%, unknown size), zoom anchored at a point, the per-diagram minimum scale, no-op input detection, wheel deltas in pixel, line, and page modes, and the key-to-action mapping.mermaid-diagram-png.test.ts: 2x output, downscaling to the clipboard pixel budget, the canvas side limit, and unmeasured diagrams.Renderer
tsc,oxlint,check:code-quality:changed, and the related editor, sidebar-comment, and i18n test suites pass.I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Implemented with Claude Code (Claude Opus 5.5), reviewed and tested manually by the author.
Review
AI code review summary:
z-index: 9999) and the macOS traffic lights (top-left). Only standard pointer, wheel, and keyboard events are used. Keys pressed with Ctrl, ⌘, or Alt are left to the app (e.g.Ctrl+=app zoom). The clipboard write goes through the existing Electron bridge.writeClipboardImageresolves without writing images, so the Copy as PNG button is hidden there (viaisWebClientLocation()) instead of reporting a copy that never happened.will-changeis deliberately not used, so the SVG re-rasterizes sharply when zoomed. The PNG is rendered only on click.MermaidBlock's existing DOMPurify SVG-profile pass. It is serialized from the inline diagram and re-parsed in the same context (innerHTMLof adiv). The only transformation is renaming the render ID, which is unique per instance. The PNG canvas draws that same sanitized SVG and no external resources.Dialog,Button,Tooltip, andSeparatorprimitives. The floating toolbar uses the samebg-popover/shadow-floatingtokens as other floating surfaces. Shortcut chips useShortcutKeyCombo. The design-system lint gate passes.DialogContent'svariantdefaults to'default', which uses the same classes as before, so existing dialogs are unchanged.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Author
X (Twitter): @HEchiburu
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)