Skip to content

Conversation

@ardss
Copy link

@ardss ardss commented Jan 10, 2026

Summary

This PR adds export format support to the export_diagram tool, allowing users to export diagrams in SVG, PNG, and hybrid formats.

Changes

  • Add support for .svg - Standalone SVG image
  • Add support for .png - PNG image
  • Add support for .drawio.svg - SVG with embedded draw.io XML (editable + viewable)
  • Add support for .drawio.png - PNG with embedded draw.io XML
  • Add decodeDataUrl() helper to decode base64 data URLs from browser state
  • Add detectExportFormat() to auto-detect format from file extension

Example Usage

export_diagram({ path: "./diagram.svg" })       // SVG image
export_diagram({ path: "./diagram.png" })       // PNG image
export_diagram({ path: "./diagram.drawio.svg" }) // Hybrid (viewable + editable)

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.

- 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
@DayuanJiang DayuanJiang requested a review from Copilot January 10, 2026 14:14
Copy link
Contributor

Copilot AI left a 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_diagram tool to support .svg, .png, .drawio.svg, and .drawio.png formats
  • 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]

Copy link

Copilot AI Jan 10, 2026

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.

Suggested change
// Only process expected image MIME types
if (mimeType !== "image/svg+xml" && mimeType !== "image/png") {
return null
}

Copilot uses AI. Check for mistakes.
return "drawio-svg"
}
if (lowerPath.endsWith(".drawio.png")) {
return "drawio-svg" // PNG with embedded XML
Copy link

Copilot AI Jan 10, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +723 to +724
await fs.writeFile(absolutePath, svgBuffer)
log.info(`Image exported to ${absolutePath} (SVG format)`)
Copy link

Copilot AI Jan 10, 2026

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.

Copilot uses AI. Check for mistakes.
const xmlComment = `<!-- Draw.io XML:\n${currentSession.xml}\n-->`

// Insert XML comment after the opening <svg> tag
const svgInsertIndex = svgContent.indexOf(">") + 1
Copy link

Copilot AI Jan 10, 2026

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.

Suggested change
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
}

Copilot uses AI. Check for mistakes.
Comment on lines +652 to 669
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`
}
}

Copy link

Copilot AI Jan 10, 2026

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
@DayuanJiang
Copy link
Owner

@ardss Thanks for the contribution! I have requested the review of the copilot, could you resolve then first?

@DayuanJiang
Copy link
Owner

Also please fix the failed checkes except Vecel.

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.

2 participants