Generated banner for Drawio
Generated logo for Drawio

Drawio

GitHubOfficialFirst committed by parveen-dotnetDistributed by GitHub

  • Skill
  • Tool integration
  • Automation and integrations
  • MIT
  • Low risk
  • Has scripts

From the author

Generate draw.io diagrams as .drawio files and export to PNG/SVG/PDF with embedded XML
— the description by parveen-dotnet

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The drawio skill provides a structured approach to programmatically generating diagrams in the mxGraphModel XML format and converting them into visual assets. It includes a Node.js export script that leverages either the draw.io CLI or a headless browser to render diagrams into PNG, SVG, or PDF formats, ensuring that the original XML data remains embedded for future editing.

What it does

  • Generates draw.io compatible XML for diagrams.
  • Exports diagrams to PNG, SVG, and PDF formats.
  • Embeds source XML into exported files for portability and editability.
  • Provides a Node.js script for automated rendering.
  • Supports multiple rendering backends including draw.io CLI and headless browsers.
  • Defines standardized XML style conventions for professional diagram consistency.

When to use it

  • When you need to generate diagrams programmatically as part of a documentation workflow.
  • When you require diagrams that are both visually presentable and editable in draw.io.
  • When automating the conversion of diagram files into image formats for reports or presentations.
  • When maintaining consistent visual styling across multiple technical diagrams.

Advantages

  • Ensures diagrams remain editable even after export.
  • Supports automated batch processing of diagram files.
  • Offers flexible rendering options to suit different environments.
  • Promotes visual consistency through predefined style templates.

Usage examples

“Create a system architecture diagram for a web service and export it as a PNG.”
The agent generates the corresponding mxGraphModel XML, saves it as a .drawio file, and runs the export script to produce a PNG image.
“Convert all .drawio files in the current directory to SVG format.”
The agent executes the bundled Node.js script with the directory flag to process all diagrams and output SVG files.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/github/drawio/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip drawio.zip -d .agents/skills

Documentation by parveen-dotnet

Draw.io Diagram Skill

Generate draw.io diagrams as native .drawio files and export them to PNG images that can be embedded in Word documents.

How to Create a Diagram

  1. Generate draw.io XML in mxGraphModel format for the requested diagram

  2. Write the XML to a .drawio file using the create/edit file tool

  3. Export to PNG using the bundled export script

Bundled Export Script

This skill includes drawio-to-png.mjs, a Node.js export script with two rendering backends:

  1. draw.io CLI (pixel-perfect, fastest) — used automatically if draw.io desktop is installed

  2. Official draw.io viewer in headless browser (pixel-perfect, needs Chromium/Edge) — fallback when CLI is unavailable

Usage
# Install dependencies (one-time, from the scripts folder)
cd skills/drawio/scripts && npm install

# Export a single diagram
node skills/drawio/scripts/drawio-to-png.mjs <input.drawio> [output.png]

# Export all .drawio files in a directory
node skills/drawio/scripts/drawio-to-png.mjs --dir <directory>

# Force a specific renderer
node skills/drawio/scripts/drawio-to-png.mjs --renderer=cli|viewer|auto <input.drawio>
Skill Folder Contents
FilePurpose
SKILL.mdThis instruction file
scripts/drawio-to-png.mjsNode.js export script (CLI + browser fallback)
scripts/package.jsonDependencies (puppeteer-core)

Supported Export Formats

FormatEmbed XMLNotes
pngYesViewable everywhere, editable in draw.io
svgYesScalable, editable in draw.io
pdfYesPrintable, editable in draw.io

Draw.io XML Style Conventions

Use these styles for consistent, professional diagrams:

<!-- Primary service (highlighted) -->
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=2;arcSize=12;shadow=1;" />

<!-- External system -->
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;" />

<!-- Success/processing stage -->
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" />

<!-- Warning/quality gate -->
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" />

<!-- Error/failure path -->
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" />

<!-- Data store (cylinder) -->
<mxCell style="shape=cylinder3;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" />

<!-- Arrow -->
<mxCell style="edgeStyle=orthogonalEdgeStyle;rounded=1;strokeColor=#6c8ebf;strokeWidth=2;" />

Locating the draw.io CLI

Try drawio first (works if on PATH), then fall back:

  • Windows: "C:\Program Files\draw.io\draw.io.exe"

  • macOS: /Applications/draw.io.app/Contents/MacOS/draw.io

  • Linux: drawio (via snap/apt/flatpak)

CLI Export Command
drawio -x -f png -e -b 10 -o <output.png> <input.drawio>

Flags: -x (export), -f (format), -e (embed diagram XML), -b (border), -o (output path).

Files

scripts/drawio-to-png.mjs

/**
 * drawio-to-png.mjs - Convert .drawio files to PNG with accurate rendering.
 *
 * Rendering priority:
 *   1. draw.io CLI (if installed) — pixel-perfect, fastest
 *   2. Official draw.io viewer JS in headless browser — pixel-perfect, needs network
 *
 * Usage: node drawio-to-png.mjs <input.drawio> [output.png]
 *        node drawio-to-png.mjs --dir <directory>   (converts all .drawio files in directory)
 *        node drawio-to-png.mjs --renderer=cli|viewer|auto <input.drawio> [output.png]
 */

import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
import { join, basename, dirname, resolve } from "path";
import { spawnSync } from "child_process";
import { inflateRawSync } from "zlib";
import puppeteer from "puppeteer-core";

// --- Build HTML that uses the official draw.io viewer for rendering ---
function buildViewerHtml(rawFileContent) {
  // Escape for embedding in a JS template literal
  const escaped = rawFileContent
    .replace(/\\/g, "\\\\")
    .replace(/`/g, "\\`")
    .replace(/\$/g, "\\$");

  // The official draw.io viewer (viewer-static.min.js) contains the full mxGraph
  // rendering engine — it handles orthogonal edge routing, all shape types,
  // container layouts, and compressed/uncompressed diagram formats.
  return `<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    * { margin: 0; padding: 0; }
    body { background: white; }
  </style>
</head>
<body>
  <div id="diagram-host"></div>
  <script>
    // Prepare diagram XML and set up the viewer target div
    (function() {
      var raw = \`${escaped}\`;

      // Wrap raw mxGraphModel in mxfile if needed (viewer expects mxfile format)
      var xmlStr = raw.trim();
      if (xmlStr.startsWith('<mxGraphModel')) {
        xmlStr = '<mxfile><diagram name="Page-1">' + xmlStr + '</diagram></mxfile>';
      } else if (!xmlStr.startsWith('<mxfile')) {
        // Assume it's already an mxfile or a diagram element
        if (xmlStr.startsWith('<diagram')) {
          xmlStr = '<mxfile>' + xmlStr + '</mxfile>';
        }
      }

      var config = {
        xml: xmlStr,
        highlight: "none",
        nav: false,
        resize: true,
        toolbar: null,
        "toolbar-nohide": true,
        edit: null,
        lightbox: false,
        "auto-fit": true,
        "check-visible-state": false
      };

      var div = document.createElement('div');
      div.className = 'mxgraph';
      div.setAttribute('data-mxgraph', JSON.stringify(config));
      document.getElementById('diagram-host').appendChild(div);
    })();

    // Poll until the viewer renders the diagram (viewer script loaded separately)
    window.__pollStarted = false;
    window.__startPoll = function() {
      if (window.__pollStarted) return;
      window.__pollStarted = true;
      // Explicitly trigger viewer processing
      if (typeof GraphViewer !== 'undefined' && GraphViewer.processElements) {
        GraphViewer.processElements();
      }
      (function poll() {
        // Viewer places SVG directly inside .mxgraph div
        var mxDiv = document.querySelector('.mxgraph');
        if (mxDiv) {
          var svg = mxDiv.querySelector('svg');
          if (svg) {
            var rect = mxDiv.getBoundingClientRect();
            if (rect.width > 10 && rect.height > 10) {
              window.__renderComplete = true;
              window.__renderWidth = rect.width;
              window.__renderHeight = rect.height;
              return;
            }
          }
        }
        setTimeout(poll, 150);
      })();
    };
  </script>
</body>
</html>`;
}

// --- Extract mxGraph XML from .drawio input (supports mxGraphModel and mxfile) ---
function extractMxGraphModelXml(inputXml) {
  const trimmed = inputXml.trim();

  if (trimmed.startsWith("<mxGraphModel")) {
    return trimmed;
  }

  const diagramMatch = trimmed.match(/<diagram\b[^>]*>([\s\S]*?)<\/diagram>/i);
  if (!diagramMatch) {
    throw new Error("Unsupported .drawio format: missing <mxGraphModel> or <diagram> content");
  }

  const diagramContent = diagramMatch[1].trim();

  if (diagramContent.startsWith("<mxGraphModel")) {
    return diagramContent;
  }

  // draw.io compressed diagrams are base64(deflateRaw(encodeURIComponent(xml)))
  try {
    const inflated = inflateRawSync(Buffer.from(diagramContent, "base64")).toString("utf-8");
    const decoded = decodeURIComponent(inflated);
    if (!decoded.trim().startsWith("<mxGraphModel")) {
      throw new Error("decoded content is not mxGraphModel XML");
    }
    return decoded;
  } catch (err) {
    throw new Error(`Failed to decode compressed <diagram> content: ${err.message}`);
  }
}

function resolveRenderer(rawArgs) {
  let renderer = "auto";
  const args = [];

  for (const arg of rawArgs) {
    if (arg.startsWith("--renderer=")) {
      renderer = arg.substring("--renderer=".length).trim().toLowerCase();
      continue;
    }
    args.push(arg);
  }

  if (!["auto", "cli", "viewer"].includes(renderer)) {
    throw new Error(`Invalid renderer '${renderer}'. Use auto, cli, or viewer.`);
  }

  return { renderer, args };
}

function findDrawioCliPath() {
  const envPath = process.env.DRAWIO_PATH;
  if (envPath) {
    try {
      if (statSync(envPath).isFile()) return envPath;
    } catch { /* ignore */ }
  }

  const candidates = [
    "C:\\Program Files\\draw.io\\draw.io.exe",
    "C:\\Program Files (x86)\\draw.io\\draw.io.exe",
    "/Applications/draw.io.app/Contents/MacOS/draw.io",
    "/usr/bin/drawio",
    "/usr/local/bin/drawio",
  ];

  for (const p of candidates) {
    try {
      if (statSync(p).isFile()) return p;
    } catch { /* ignore */ }
  }

  const locator = process.platform === "win32" ? "where" : "which";
  const names = process.platform === "win32" ? ["drawio", "draw.io"] : ["drawio"];

  for (const name of names) {
    const probe = spawnSync(locator, [name], { encoding: "utf-8" });
    if (probe.status === 0 && probe.stdout) {
      const first = probe.stdout.split(/\r?\n/).map(line => line.trim()).find(Boolean);
      if (first) return first;
    }
  }

  return null;
}

function exportWithDrawioCli(drawioPath, input, output) {
  const args = ["-x", "-f", "png", "-e", "-b", "10", "-o", output, input];
  const result = spawnSync(drawioPath, args, { encoding: "utf-8" });
  if (result.status !== 0) {
    const stderr = (result.stderr || "").trim();
    const stdout = (result.stdout || "").trim();
    throw new Error(stderr || stdout || `draw.io CLI failed with exit code ${result.status}`);
  }
}

// --- Main ---
async function main() {
  const parsed = resolveRenderer(process.argv.slice(2));
  const renderer = parsed.renderer;
  const args = parsed.args;

  let files = [];
  if (args[0] === "--dir") {
    const dir = resolve(args[1] || ".");
    files = readdirSync(dir)
      .filter(f => f.endsWith(".drawio"))
      .map(f => ({
        input: join(dir, f),
        output: join(dir, f.replace(/\.drawio$/, ".drawio.png"))
      }));
  } else if (args[0]) {
    const input = resolve(args[0]);
    const output = args[1] || input.replace(/\.drawio$/, ".drawio.png");
    files = [{ input, output }];
  } else {
    console.error("Usage: node drawio-to-png.mjs <input.drawio> [output.png]");
    console.error("       node drawio-to-png.mjs --dir <directory>");
    console.error("       node drawio-to-png.mjs --renderer=cli|auto|custom <input.drawio> [output.png]");
    process.exit(1);
  }

  if (files.length === 0) {
    console.log("No .drawio files found.");
    return;
  }

  const drawioCliPath = findDrawioCliPath();

  // --- Path 1: draw.io CLI (best fidelity, no network needed) ---
  if (renderer === "cli" || (renderer === "auto" && drawioCliPath)) {
    if (!drawioCliPath) {
      console.error("draw.io CLI not found. Install draw.io desktop or set DRAWIO_PATH.");
      process.exit(1);
    }
    console.log(`Using renderer: draw.io CLI (${basename(drawioCliPath)})`);
    for (const { input, output } of files) {
      console.log(`Rendering: ${basename(input)}`);
      try {
        exportWithDrawioCli(drawioCliPath, input, output);
        let kb = "?";
        try {
          kb = (statSync(output).size / 1024).toFixed(0);
        } catch { /* ignore size read errors */ }
        console.log(`  -> ${basename(output)} (${kb} KB)`);
      } catch (err) {
        console.error(`  Error rendering ${basename(input)}: ${err.message}`);
      }
    }
    console.log("Done.");
    return;
  }

  // --- Path 2: Official draw.io viewer in headless browser ---
  // Find browser
  const browserPaths = [
    process.env.CHROME_PATH,
    process.env.EDGE_PATH,
    "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
    "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
    "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
    "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
    "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
    "/usr/bin/google-chrome",
    "/usr/bin/chromium-browser",
    "/usr/bin/microsoft-edge",
  ].filter(Boolean);

  let execPath;
  for (const p of browserPaths) {
    try {
      if (statSync(p).isFile()) { execPath = p; break; }
    } catch { /* not found */ }
  }

  if (!execPath) {
    console.error("No browser found. Set CHROME_PATH or EDGE_PATH environment variable.");
    process.exit(1);
  }

  console.log(`Using renderer: draw.io viewer (${basename(execPath)})`);

  const browser = await puppeteer.launch({
    executablePath: execPath,
    headless: true,
    args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu"],
  });

  for (const { input, output } of files) {
    console.log(`Rendering: ${basename(input)}`);
    try {
      const rawContent = readFileSync(input, "utf-8");
      const html = buildViewerHtml(rawContent);

      const page = await browser.newPage();
      await page.setViewport({ width: 2400, height: 1600, deviceScaleFactor: 2 });

      // Set HTML content first (sets up the .mxgraph div with diagram XML)
      await page.setContent(html, { waitUntil: "domcontentloaded" });

      // Load the official draw.io viewer JS via addScriptTag (more reliable than inline src)
      const VIEWER_URL = "https://viewer.diagrams.net/js/viewer-static.min.js";
      try {
        await page.addScriptTag({ url: VIEWER_URL });
      } catch (scriptErr) {
        throw new Error(`Failed to load draw.io viewer JS: ${scriptErr.message}`);
      }

      // Start polling for the rendered diagram
      await page.evaluate(() => window.__startPoll());

      // Wait for the viewer to finish rendering
      await page.waitForFunction(() => window.__renderComplete === true, { timeout: 30000 });

      // Check rendering succeeded
      const viewerOk = await page.evaluate(() => window.__renderWidth > 0);
      if (!viewerOk) {
        throw new Error("draw.io viewer failed to load or render (check network access)");
      }

      // Take element screenshot of just the diagram div for exact bounds
      const containerHandle = await page.$('.mxgraph');
      let pngBuffer;

      if (containerHandle) {
        pngBuffer = await containerHandle.screenshot({ type: "png" });
      } else {
        // Fallback: full-page screenshot
        const dims = await page.evaluate(() => ({
          w: Math.ceil(window.__renderWidth),
          h: Math.ceil(window.__renderHeight)
        }));
        pngBuffer = await page.screenshot({
          type: "png",
          clip: { x: 0, y: 0, width: dims.w + 20, height: dims.h + 20 },
        });
      }

      writeFileSync(output, pngBuffer);
      console.log(`  -> ${basename(output)} (${(pngBuffer.length / 1024).toFixed(0)} KB)`);

      await page.close();
    } catch (err) {
      console.error(`  Error rendering ${basename(input)}: ${err.message}`);
    }
  }

  await browser.close();
  console.log("Done.");
}

main().catch(err => { console.error(err); process.exit(1); });

Requirements and compatibility

Written for: GitHub Copilot

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

First committed by
GitHub avatar of parveen-dotnetparveen-dotnet
Publisher
GitHub (Official)
Distributed by
GitHub
Source
github/awesome-copilot · skills/drawio · commit 797337f
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Contributors
parveen-dotnet

AutoGineer is not affiliated with or endorsed by parveen-dotnet or GitHub.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download drawio.zip