Skip to content

feat: adaptive zoom/pan engine for Mermaid diagrams#1

Open
peak-flow wants to merge 1 commit into
mainfrom
feat/adaptive-zoom-pan
Open

feat: adaptive zoom/pan engine for Mermaid diagrams#1
peak-flow wants to merge 1 commit into
mainfrom
feat/adaptive-zoom-pan

Conversation

@peak-flow

@peak-flow peak-flow commented Mar 6, 2026

Copy link
Copy Markdown
Owner

Replace CSS zoom-based zoom/pan with vector-based SVG resizing engine that stays crisp at any zoom level and handles charts of all sizes.

Key improvements:

  • Vector-based zoom: resizes SVG via viewBox + CSS width/height instead of CSS zoom or transform: scale(). Text stays sharp at 500%+.
  • Smart initial fit: contain mode for small charts, width-priority or height-priority for large charts that would be unreadably tiny. Readability floor (58%) prevents diagrams from rendering too small.
  • Adaptive container height: auto-sizes based on chart aspect ratio, clamped between 360px-960px. No more guessing min-height values.
  • Zoom toward cursor: Ctrl/Cmd+wheel zooms toward mouse position.
  • Pan constraining: diagram stays within visible bounds, can't be dragged offscreen.
  • Regular scroll pans: when diagram overflows, scroll wheel pans without requiring Ctrl/Cmd modifier.
  • Touch support: single-finger pan, two-finger pinch-to-zoom.
  • 1:1 button: view diagram at native rendered size.
  • Double-click to re-fit.
  • Preserves click-to-expand (open full-size in new tab).
  • ResizeObserver re-fits on container resize.

Files changed:

  • templates/mermaid-flowchart.html: full rewrite of zoom/pan system
  • references/css-patterns.md: updated zoom controls documentation

Summary by CodeRabbit

Release Notes

  • New Features
    • Redesigned Mermaid diagram interaction engine with adaptive zoom/pan controls
    • Enhanced zoom controls: Ctrl+Wheel zoom toward cursor, drag-to-pan, and double-click to fit
    • Added touch gesture support for two-finger zoom
    • New diagram buttons: Zoom in/out, Fit, 1:1 zoom, and Open full-size view
    • Improved responsive container sizing that adapts to viewport changes

Replace CSS zoom-based zoom/pan with vector-based SVG resizing engine
that stays crisp at any zoom level and handles charts of all sizes.

Key improvements:
- Vector-based zoom: resizes SVG via viewBox + CSS width/height instead
  of CSS zoom or transform: scale(). Text stays sharp at 500%+.
- Smart initial fit: contain mode for small charts, width-priority or
  height-priority for large charts that would be unreadably tiny.
  Readability floor (58%) prevents diagrams from rendering too small.
- Adaptive container height: auto-sizes based on chart aspect ratio,
  clamped between 360px-960px. No more guessing min-height values.
- Zoom toward cursor: Ctrl/Cmd+wheel zooms toward mouse position.
- Pan constraining: diagram stays within visible bounds, can't be
  dragged offscreen.
- Regular scroll pans: when diagram overflows, scroll wheel pans
  without requiring Ctrl/Cmd modifier.
- Touch support: single-finger pan, two-finger pinch-to-zoom.
- 1:1 button: view diagram at native rendered size.
- Double-click to re-fit.
- Preserves click-to-expand (open full-size in new tab).
- ResizeObserver re-fits on container resize.

Files changed:
- templates/mermaid-flowchart.html: full rewrite of zoom/pan system
- references/css-patterns.md: updated zoom controls documentation
@coderabbitai

coderabbitai Bot commented Mar 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR replaces the CSS zoom/scroll approach for Mermaid diagrams with an adaptive, vector-based zoom/pan engine. It introduces a new container structure, transform-based panning, smart-fit modes, and enhanced user interactions including Ctrl/Cmd+wheel zoom, touch gestures, drag-to-pan, and full-size diagram export.

Changes

Cohort / File(s) Summary
Adaptive Diagram Engine Implementation
templates/mermaid-flowchart.html
Rewrites zoom/pan logic: replaces CSS zoom with vector-based SVG resizing via viewBox and transform-based panning. Implements stateful zoom/fit system, smart-fit modes (contain/width-priority/height-priority), multiple input handlers (wheel with Ctrl/Cmd for zoom toward cursor, drag-to-pan, touch gestures, double-click fit), dynamic height adjustment via ResizeObserver, and a new full-size diagram export mechanism.
Pattern Documentation & Reference
references/css-patterns.md
Documents the adaptive zoom/pan engine approach with updated HTML structure (diagram-shell, mermaid-wrap, diagram-viewport, diagram-canvas), detailed interaction semantics (Ctrl/Cmd+wheel zoom, scroll pan, drag-to-pan, 1:1 zoom), and revised runtime behavior. Includes updated Full Pattern CSS/HTML/JS sections reflecting the new engine and control UI.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Container as Diagram Container
    participant Engine as Zoom/Pan Engine
    participant SVG as SVG Viewport
    participant Handlers as Input Handlers

    User->>Container: Load diagram
    Container->>Engine: Initialize with Mermaid SVG
    Engine->>SVG: Extract natural dimensions
    Engine->>Engine: Compute smart-fit mode
    Engine->>SVG: Set viewBox & apply scaling
    Engine->>Container: Render with initial fit

    User->>Handlers: Ctrl/Cmd + Wheel (zoom)
    Handlers->>Engine: Calculate zoom toward cursor
    Engine->>SVG: Update viewBox & transform
    Engine->>Container: Re-render

    User->>Handlers: Drag mouse
    Handlers->>Engine: Update pan offset
    Engine->>SVG: Apply translate transform
    Engine->>Container: Update position

    User->>Handlers: Touch (two-finger pinch)
    Handlers->>Engine: Compute zoom gesture
    Engine->>SVG: Update viewBox
    Engine->>Container: Re-render

    User->>Handlers: Double-click or Fit button
    Handlers->>Engine: Trigger smart-fit
    Engine->>SVG: Reset to initial fit
    Engine->>Container: Render fitted view
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Poem

🐰 Hops with glee at diagrams now so free!
No more CSS zoom to constrain the view,
With vector transforms and smart-fit so true,
Pan with a drag, zoom with Ctrl and wheel—
Mermaid diagrams have interactive appeal!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: adaptive zoom/pan engine for Mermaid diagrams' clearly and specifically summarizes the main change: replacing the old zoom/pan system with a new adaptive, vector-based engine for Mermaid diagrams.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/adaptive-zoom-pan

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (3)
references/css-patterns.md (2)

794-810: Minor style inconsistency: var vs const/let.

The documented openMermaidInNewTab() function uses var (Lines 796-801) while the template implementation uses const. Consider updating to const/let for consistency with modern JavaScript conventions.

🧹 Suggested alignment with template
 function openMermaidInNewTab() {
-  var svg = canvas.querySelector('svg');
+  const svg = canvas.querySelector('svg');
   if (!svg) return;
-  var clone = svg.cloneNode(true);
+  const clone = svg.cloneNode(true);
   clone.style.width = '';  clone.style.height = '';
-  var styles = getComputedStyle(document.documentElement);
-  var bg = styles.getPropertyValue('--bg').trim() || '#ffffff';
-  var html = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">' +
+  const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg').trim() || '#ffffff';
+  const html = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">' +
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@references/css-patterns.md` around lines 794 - 810, The function
openMermaidInNewTab uses legacy var declarations; update the variable
declarations (svg, clone, styles, bg, html) to appropriate const or let to match
modern JS and the project's template conventions (use const for values that
don't change and let for mutable ones) while preserving the existing logic
inside openMermaidInNewTab.

548-624: Minor inconsistency between documented CSS and template implementation.

The documentation uses generic variable names (e.g., --accent-dim at Line 624) while the template uses palette-specific names (--primary-dim). This is likely intentional for reusability, but the font-size values also differ slightly:

  • Doc Line 550: font-size: 0.74rem (~11.84px)
  • Template Line 155: font-size: 12px

Consider aligning these for easier copy-paste usage, or add a note that templates may adapt variable names to their specific color palette.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@references/css-patterns.md` around lines 548 - 624, The docs show slightly
different sizing and CSS variable names than the template: align
.diagram-shell__hint font-size and the color variable used in .zoom-controls
button; either change .diagram-shell__hint to match the template's 12px (or
change the template to 0.74rem) and standardize the color variable (replace
--accent-dim with the palette-specific --primary-dim or vice versa), or add a
short note in the documentation near the .diagram-shell__hint and .zoom-controls
rules explaining that templates may rename palette variables (e.g., --accent-dim
→ --primary-dim) and may adjust font-size slightly for visual parity to ease
copy-paste usage.
templates/mermaid-flowchart.html (1)

533-547: Consider revoking Blob URL after opening the new tab.

The Blob URL created at Line 546 is not revoked. While the browser typically cleans these up when the document unloads, explicitly revoking prevents accumulation if users open many diagrams.

🧹 Suggested improvement
-    window.open(URL.createObjectURL(new Blob([html], { type: 'text/html' })), '_blank');
+    const url = URL.createObjectURL(new Blob([html], { type: 'text/html' }));
+    window.open(url, '_blank');
+    setTimeout(() => URL.revokeObjectURL(url), 1000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/mermaid-flowchart.html` around lines 533 - 547, The function
openMermaidInNewTab creates a Blob URL but never revokes it; update
openMermaidInNewTab to store the result of URL.createObjectURL(...) in a
variable, pass that variable to window.open, and then revoke it (e.g., via
URL.revokeObjectURL(url) after a short delay or after the new window is created)
so the Blob URL is cleaned up and doesn't accumulate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@references/css-patterns.md`:
- Around line 794-810: The function openMermaidInNewTab uses legacy var
declarations; update the variable declarations (svg, clone, styles, bg, html) to
appropriate const or let to match modern JS and the project's template
conventions (use const for values that don't change and let for mutable ones)
while preserving the existing logic inside openMermaidInNewTab.
- Around line 548-624: The docs show slightly different sizing and CSS variable
names than the template: align .diagram-shell__hint font-size and the color
variable used in .zoom-controls button; either change .diagram-shell__hint to
match the template's 12px (or change the template to 0.74rem) and standardize
the color variable (replace --accent-dim with the palette-specific --primary-dim
or vice versa), or add a short note in the documentation near the
.diagram-shell__hint and .zoom-controls rules explaining that templates may
rename palette variables (e.g., --accent-dim → --primary-dim) and may adjust
font-size slightly for visual parity to ease copy-paste usage.

In `@templates/mermaid-flowchart.html`:
- Around line 533-547: The function openMermaidInNewTab creates a Blob URL but
never revokes it; update openMermaidInNewTab to store the result of
URL.createObjectURL(...) in a variable, pass that variable to window.open, and
then revoke it (e.g., via URL.revokeObjectURL(url) after a short delay or after
the new window is created) so the Blob URL is cleaned up and doesn't accumulate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 530807cc-fa5b-4b35-ac33-58b88067746c

📥 Commits

Reviewing files that changed from the base of the PR and between c6cda6c and a28cfd2.

📒 Files selected for processing (2)
  • references/css-patterns.md
  • templates/mermaid-flowchart.html

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