Skip to content

Commit 657b0ed

Browse files
committed
feat(ui): add click-to-enlarge Mermaid diagrams with zoom overlay and export
Click any Mermaid diagram in chat to open a full-size overlay with: - Pinch-to-zoom and drag-to-pan (200% default zoom) - Zoom controls with percentage display - Export menu: PNG, SVG, or copy raw Mermaid source - High-resolution re-render at 2x for crisp detail - Escape or click backdrop to dismiss Also adds MermaidWebRenderer.renderToSVG() for extracting SVG markup directly from the mermaid.js render pipeline. Bumps version to 20260411.1.
1 parent 5f10d0d commit 657b0ed

6 files changed

Lines changed: 512 additions & 14 deletions

File tree

Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
<key>CFBundlePackageType</key>
2020
<string>APPL</string>
2121
<key>CFBundleShortVersionString</key>
22-
<string>20260409.1</string>
22+
<string>20260411.1</string>
2323
<key>CFBundleVersion</key>
24-
<string>20260409.1</string>
24+
<string>20260411.1</string>
2525
<key>LSApplicationCategoryType</key>
2626
<string>public.app-category.productivity</string>
2727
<key>LSMinimumSystemVersion</key>

Resources/whats-new.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
{
22
"releases": [
3+
{
4+
"version": "20260411.1",
5+
"release_date": "April 11, 2026",
6+
"introduction": "This release adds interactive Mermaid diagrams - click any diagram to open a zoomable, pannable full-size viewer with high-resolution rendering and PNG export.",
7+
"improvements": [
8+
{
9+
"id": "diagram-zoom-overlay",
10+
"icon": "arrow.up.left.and.arrow.down.right",
11+
"title": "Click-to-Enlarge Mermaid Diagrams",
12+
"description": "Mermaid diagrams in conversations are now interactive. Click any diagram to open a full-size overlay with pinch-to-zoom, click-and-drag panning, and zoom controls. Diagrams re-render at double resolution for crisp detail when zoomed in. A small expand icon in the corner hints at the interaction."
13+
},
14+
{
15+
"id": "diagram-png-export",
16+
"icon": "square.and.arrow.down",
17+
"title": "Export Diagrams as PNG, SVG, or Mermaid Source",
18+
"description": "The diagram zoom overlay includes an export menu with three options: save as PNG for raster images, save as SVG for scalable vector graphics editable in tools like Inkscape or Draw.io, and copy the raw Mermaid source to your clipboard for pasting into other tools."
19+
}
20+
],
21+
"bugfixes": [
22+
{
23+
"id": "streaming-completion-vanish",
24+
"icon": "arrow.down.circle.fill",
25+
"title": "Fixed Content Vanishing on Streaming Completion",
26+
"description": "Fixed a regression where conversation content could disappear when a streaming response finished. This was a follow-up fix to the scroll system rewrite."
27+
}
28+
]
29+
},
330
{
431
"version": "20260409.1",
532
"release_date": "April 9, 2026",

Sources/UserInterface/Chat/CachedDiagramView.swift

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,48 @@ struct CachedDiagramView: View {
1717

1818
@State private var isRendering = false
1919
@State private var renderError: String?
20+
@State private var showingZoomOverlay = false
2021

2122
var body: some View {
2223
Group {
2324
if let cachedImage = cache[mermaidCode] {
2425
// Use cached image - instant display
25-
Image(nsImage: cachedImage)
26-
.resizable()
27-
.interpolation(.high)
28-
.aspectRatio(contentMode: .fit)
29-
.frame(maxWidth: min(cachedImage.size.width, 560))
30-
.padding(.vertical, 4)
26+
ZStack(alignment: .topTrailing) {
27+
Image(nsImage: cachedImage)
28+
.resizable()
29+
.interpolation(.high)
30+
.aspectRatio(contentMode: .fit)
31+
.frame(maxWidth: min(cachedImage.size.width, 560))
32+
.padding(.vertical, 4)
33+
34+
// Zoom hint icon
35+
Image(systemName: "arrow.up.left.and.arrow.down.right")
36+
.font(.system(size: 10, weight: .semibold))
37+
.foregroundColor(.secondary)
38+
.padding(5)
39+
.background(.ultraThinMaterial)
40+
.cornerRadius(4)
41+
.padding(8)
42+
.opacity(0.7)
43+
}
44+
.onTapGesture {
45+
showingZoomOverlay = true
46+
}
47+
.onHover { hovering in
48+
if hovering {
49+
NSCursor.pointingHand.push()
50+
} else {
51+
NSCursor.pop()
52+
}
53+
}
54+
.sheet(isPresented: $showingZoomOverlay) {
55+
DiagramZoomOverlay(
56+
mermaidCode: mermaidCode,
57+
initialImage: cachedImage,
58+
isPresented: $showingZoomOverlay
59+
)
60+
.frame(minWidth: 800, minHeight: 600)
61+
}
3162
} else if let error = renderError {
3263
errorView(error)
3364
} else {

0 commit comments

Comments
 (0)