Files
claude-engineering-plugin/plugins/compound-engineering/skills/excalidraw-png-export/SKILL.md
John Lamb f524c1b9d8 feat(excalidraw): improve diagram quality with canvas measurement, validation, and conventions
Replace the charCount * fontSize * 0.55 text sizing heuristic with canvas-based
measurement (graceful fallback when native deps unavailable). Add validate.mjs for
automated spatial checks (text overflow, arrow-text collisions, element overlap).
Update element format reference with sizing rules, label guidelines, and arrow routing
conventions. Add verification step to SKILL.md workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:19:14 -06:00

6.2 KiB

name, description
name description
excalidraw-png-export This skill should be used when creating diagrams, architecture visuals, or flowcharts and exporting them as PNG files. It uses the Excalidraw MCP to render hand-drawn style diagrams locally and Playwright to export them to PNG without sending data to any remote server. Triggers on requests like 'create a diagram', 'make an architecture diagram', 'draw a flowchart and export as PNG', or any request that needs a visual diagram delivered as an image file.

Excalidraw PNG Export

Create hand-drawn style diagrams with the Excalidraw MCP and export them locally to PNG files. All rendering happens on the local machine. Diagram data never leaves the user's computer.

Prerequisites

First-Time Setup

Run the setup script once per machine to install Playwright and Chromium headless:

bash <skill-path>/scripts/setup.sh

This creates a .export-runtime directory inside scripts/ with the Node.js dependencies. The setup is idempotent and skips installation if already present.

Required MCP

The Excalidraw MCP server must be configured. Verify availability by checking for mcp__excalidraw__create_view and mcp__excalidraw__read_checkpoint tools.

Workflow

Step 1: Design the Diagram Elements

Translate the user's request into Excalidraw element JSON. Load excalidraw-element-format.md for the full element specification, color palette, and sizing guidelines.

Key design decisions:

  • Choose appropriate colors from the palette to distinguish different components
  • Use label on shapes instead of separate text elements
  • Use roundness: { type: 3 } for rounded corners on rectangles
  • Include cameraUpdate as the first element to frame the view (MCP rendering only)
  • Use arrow bindings (startBinding/endBinding) to connect shapes

Step 2: Render with Excalidraw MCP

Call mcp__excalidraw__create_view with the element JSON array. This renders an interactive preview in the Claude Code UI.

mcp__excalidraw__create_view({ elements: "<JSON array string>" })

The response includes a checkpointId for retrieving the rendered state.

Step 3: Extract the Checkpoint Data

Call mcp__excalidraw__read_checkpoint with the checkpoint ID to get the full element JSON back.

mcp__excalidraw__read_checkpoint({ id: "<checkpointId>" })

Step 4: Convert Checkpoint to .excalidraw File

Use the convert.mjs script to transform raw MCP checkpoint JSON into a valid .excalidraw file. This handles all the tedious parts automatically:

  • Filters out pseudo-elements (cameraUpdate, delete, restoreCheckpoint)
  • Adds required Excalidraw defaults (seed, version, fontFamily, etc.)
  • Expands label properties on shapes/arrows into proper bound text elements
# Save checkpoint JSON to a file first, then convert:
node <skill-path>/scripts/convert.mjs <input.json> <output.excalidraw>

The input JSON should be the raw checkpoint data from mcp__excalidraw__read_checkpoint (the {"elements": [...]} object).

For batch exports: Write each checkpoint to a separate raw JSON file, then convert each one:

node <skill-path>/scripts/convert.mjs raw1.json diagram1.excalidraw
node <skill-path>/scripts/convert.mjs raw2.json diagram2.excalidraw

Manual alternative: If you need to write the .excalidraw file by hand (e.g., without the convert script), each element needs these defaults:

angle: 0, roughness: 1, opacity: 100, groupIds: [], seed: <unique int>,
version: 1, versionNonce: <unique int>, isDeleted: false,
boundElements: null, link: null, locked: false

Text elements also need: fontFamily: 1, textAlign: "left", verticalAlign: "top", baseline: 14, containerId: null, originalText: "<same as text>"

Bound text (labels on shapes/arrows) needs: containerId: "<parent-id>", textAlign: "center", verticalAlign: "middle", and the parent needs boundElements: [{"id": "<text-id>", "type": "text"}].

Step 5: Export to PNG

Run the export script. Determine the runtime path relative to this skill's scripts directory:

cd <skill-path>/scripts/.export-runtime && node <skill-path>/scripts/export_png.mjs /tmp/excalidraw-export/diagram.excalidraw /tmp/excalidraw-export/output.png

The script:

  1. Starts a local HTTP server serving the .excalidraw file and an HTML page
  2. Launches headless Chromium via Playwright
  3. The HTML page loads the Excalidraw library from esm.sh (library code only, not user data)
  4. Calls exportToBlob on the local diagram data
  5. Extracts the base64 PNG and writes it to disk
  6. Cleans up temp files and exits

The script prints the output path on success. Verify the result with file <output.png>.

Step 5.5: Validate and Iterate

Run the validation script on the .excalidraw file to catch spatial issues:

node <skill-path>/scripts/validate.mjs /tmp/excalidraw-export/diagram.excalidraw

Then read the exported PNG back using the Read tool to visually inspect:

  1. All label text fits within its container (no overflow/clipping)
  2. No arrows cross over text labels
  3. Spacing between elements is consistent
  4. Legend and titles are properly positioned

If the validation script or visual inspection reveals issues:

  1. Identify the specific elements that need adjustment
  2. Edit the .excalidraw file (adjust coordinates, box sizes, or arrow waypoints)
  3. Re-run the export script (Step 5)
  4. Re-validate

Step 6: Deliver the Result

Read the PNG file to display it to the user. Provide the file path so the user can access it directly.

Troubleshooting

Setup fails: Verify Node.js v18+ is installed (node --version). Ensure npm has network access for the initial Playwright/Chromium download.

Export times out: The HTML page has a 30-second timeout. If it fails, check browser console output in the script's error messages. Common cause: esm.sh CDN is temporarily slow on first load.

Blank PNG: Ensure elements include all required properties (see Step 4 defaults). Missing seed, version, or fontFamily on text elements can cause silent render failures.

"READY" never fires: The exportToBlob call requires valid elements. Filter out cameraUpdate and other pseudo-elements before writing the .excalidraw file.