-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat: add SVG and PNG export support for export_diagram tool #566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add support for .svg, .png, .drawio.svg, .drawio.png file extensions - Decode base64-encoded SVG from browser state - Extract SVG data from data:image/svg+xml URLs - Fallback to PNG export if SVG not available - Update tool description to document new formats
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds multi-format export support to the export_diagram tool, enabling users to export diagrams as SVG, PNG, and hybrid formats in addition to the existing XML format.
Changes:
- Added
decodeDataUrl()helper function to decode base64 data URLs from browser state - Added
detectExportFormat()function to auto-detect export format from file extensions - Extended
export_diagramtool to support.svg,.png,.drawio.svg, and.drawio.pngformats - Updated documentation and examples for the new export formats
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| const mimeType = match[1] | ||
| const base64Data = match[2] | ||
|
|
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mimeType variable is extracted but never used. Consider removing it or adding validation to ensure only expected MIME types (image/svg+xml, image/png) are processed.
| // Only process expected image MIME types | |
| if (mimeType !== "image/svg+xml" && mimeType !== "image/png") { | |
| return null | |
| } |
| return "drawio-svg" | ||
| } | ||
| if (lowerPath.endsWith(".drawio.png")) { | ||
| return "drawio-svg" // PNG with embedded XML |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the file path ends with .drawio.png, the function returns 'drawio-svg' instead of a PNG-specific format. However, the code later handles this format by saving SVG data. This creates confusion between the format name and actual behavior. Consider either: 1) Creating a separate 'drawio-png' format type and handling it explicitly, or 2) Adding a comment in the switch statement explaining why PNG embeds use the SVG code path.
| await fs.writeFile(absolutePath, svgBuffer) | ||
| log.info(`Image exported to ${absolutePath} (SVG format)`) |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a user requests .png export, the code saves SVG data to a .png file extension. This creates a file with incorrect extension that won't open properly in image viewers. Either convert the SVG to actual PNG format, or change the file extension to .svg before saving and inform the user of the extension change.
| const xmlComment = `<!-- Draw.io XML:\n${currentSession.xml}\n-->` | ||
|
|
||
| // Insert XML comment after the opening <svg> tag | ||
| const svgInsertIndex = svgContent.indexOf(">") + 1 |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using indexOf('>') to find the SVG tag insertion point is fragile. It will incorrectly match the first > character, which could be in an XML declaration (<?xml version='1.0'?>) or attribute value. Use a more robust approach like matching /<svg[^>]*>/ to find the actual opening SVG tag.
| const svgInsertIndex = svgContent.indexOf(">") + 1 | |
| const svgTagMatch = svgContent.match(/<svg[^>]*>/i) | |
| let svgInsertIndex: number | |
| if (svgTagMatch && typeof svgTagMatch.index === "number") { | |
| svgInsertIndex = svgTagMatch.index + svgTagMatch[0].length | |
| } else { | |
| // Fallback to previous behavior if <svg> tag is not found | |
| svgInsertIndex = svgContent.indexOf(">") + 1 | |
| } |
| const format = detectExportFormat(filePath) | ||
|
|
||
| // Add extension if not present | ||
| const ext = nodePath.extname(filePath) | ||
| if (!ext) { | ||
| switch (format) { | ||
| case "svg": | ||
| case "drawio-svg": | ||
| filePath = `${filePath}.svg` | ||
| break | ||
| case "png": | ||
| filePath = `${filePath}.png` | ||
| break | ||
| default: | ||
| filePath = `${filePath}.drawio` | ||
| } | ||
| } | ||
|
|
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The format is detected before adding the extension, but format detection relies on the file extension. This creates a circular dependency: if no extension exists, detectExportFormat() returns 'drawio' by default, so this switch will only hit the default case. The extension addition logic should be based on explicit format specification or occur before format detection.
| const format = detectExportFormat(filePath) | |
| // Add extension if not present | |
| const ext = nodePath.extname(filePath) | |
| if (!ext) { | |
| switch (format) { | |
| case "svg": | |
| case "drawio-svg": | |
| filePath = `${filePath}.svg` | |
| break | |
| case "png": | |
| filePath = `${filePath}.png` | |
| break | |
| default: | |
| filePath = `${filePath}.drawio` | |
| } | |
| } | |
| // Ensure the file has an extension before detecting the export format | |
| const ext = nodePath.extname(filePath) | |
| if (!ext) { | |
| // Default to .drawio when no extension is provided | |
| filePath = `${filePath}.drawio` | |
| } | |
| const format = detectExportFormat(filePath) |
|
@ardss Thanks for the contribution! I have requested the review of the copilot, could you resolve then first? |
|
Also please fix the failed checkes except Vecel. |
Summary
This PR adds export format support to the
export_diagramtool, allowing users to export diagrams in SVG, PNG, and hybrid formats.Changes
.svg- Standalone SVG image.png- PNG image.drawio.svg- SVG with embedded draw.io XML (editable + viewable).drawio.png- PNG with embedded draw.io XMLdecodeDataUrl()helper to decode base64 data URLs from browser statedetectExportFormat()to auto-detect format from file extensionExample Usage
Technical Details
The browser already caches SVG data (
browserState.svg), this PR simply utilizes that cached data instead of only exporting XML. The hybrid format embeds the full draw.io XML as a comment in the SVG, enabling both viewing as an image and editing in draw.io.