en un clic
excalidraw
// Use when the user asks for a diagram, sketch, flowchart, architecture diagram, sequence diagram, mind map, or other visual explanation. Renders hand-drawn-style diagrams from Excalidraw JSON via the output_iframe tool.
// Use when the user asks for a diagram, sketch, flowchart, architecture diagram, sequence diagram, mind map, or other visual explanation. Renders hand-drawn-style diagrams from Excalidraw JSON via the output_iframe tool.
Use when the user wants to customize Shelley by injecting behavior at lifecycle events. It documents Shelley's hooks.
Use to discover external services and APIs available on this exe.dev VM via network-edge-injected credentials and to discover VM metadata like owner email, VM tags, and default port.
Use when the user needs Node.js or npm installed, or is running a JS framework dev server (Next.js, Vite, webpack, etc.) on an exe.dev VM — especially if the browser logs 'WebSocket ... failed', or the dev server complains about cross-origin requests, allowedDevOrigins, or allowedHosts.
Use when the user references a previous conversation, asks you to continue earlier work, or you need to look up what was discussed before.
Use when a user requests a task to be done later or on a schedule.
| name | excalidraw |
| description | Use when the user asks for a diagram, sketch, flowchart, architecture diagram, sequence diagram, mind map, or other visual explanation. Renders hand-drawn-style diagrams from Excalidraw JSON via the output_iframe tool. |
Use this skill whenever the user wants something drawn or diagrammed (architecture, flow, sequence, mind map, sketch — anything visual). It renders an Excalidraw canvas inside the chat (view-only — user can pan/zoom but not edit; iteration happens by you editing the JSON) with Download .excalidraw, Download PNG, Copy SVG, Copy PNG, and Copy JSON buttons.
/tmp/excalidraw.html (template below)./tmp/diagram.json as a JSON array of elements (skeleton format — see below).output_iframe with path=/tmp/excalidraw.html, files={"elements.json": "/tmp/diagram.json"}, and libraries=["excalidraw"].output_iframe again.The canvas is view-only because the sandbox blocks edits-out: any change the user made would be silently lost on the next render. Iteration happens by you editing the JSON, or by the user copy/pasting JSON or SVG (with embedded scene) back.
The libraries parameter is essential: it tells the host page to stream the excalidraw runtime into the iframe out-of-band, so the (~2.5 MB) bundle does not get stored in the conversation. Without it, window.__LIBS__ will never resolve.
excalidraw.html)<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
html,body { margin:0; padding:0; height:100vh; font-family:sans-serif; }
#toolbar { display:flex; gap:8px; padding:6px 10px; background:#f5f5f5; border-bottom:1px solid #ddd; align-items:center; font-size:13px; }
#toolbar button { font:inherit; padding:4px 10px; cursor:pointer; }
#status { color:#888; margin-left:6px; }
#editor { width:100%; height:calc(100vh - 40px); }
#err { color:#b00; white-space:pre-wrap; padding:8px; font:12px monospace; }
</style>
</head>
<body>
<div id="toolbar">
<button id="download">Download .excalidraw</button>
<button id="download-png">Download PNG</button>
<button id="copy-svg">Copy SVG</button>
<button id="copy-png">Copy PNG</button>
<button id="copy-json">Copy JSON</button>
<span id="status"></span>
</div>
<div id="editor"></div>
<div id="err"></div>
<script type="module">
try {
const { render } = (await window.__LIBS__).excalidraw;
const raw = window.__FILES__["elements.json"] || "[]";
render({ elements: typeof raw === "string" ? JSON.parse(raw) : raw });
} catch (e) {
document.getElementById("err").textContent = "Excalidraw error: " + (e.stack || e.message);
}
</script>
</body>
</html>
The template is intentionally minimal — toolbar markup plus one render() call. The button ids (download, download-png, copy-svg, copy-png, copy-json) and the editor mount id (editor) are wired up by the excalidraw library. Don't rename them. Any button you omit from the toolbar HTML is simply not bound — e.g. drop copy-png if you don't want it.
Copy SVG embeds the full scene JSON in the SVG (via exportEmbedScene), so pasting that SVG into excalidraw.com restores the diagram exactly. (This skill only ingests the JSON elements array, not SVG — if a user pastes SVG back, extract the embedded <!-- payload-start -->... JSON and use that as the new elements source.)
Elements go through convertToExcalidrawElements, which accepts a "skeleton" form simpler than raw Excalidraw JSON. Required per element: type, x, y, width, height. Give each element a unique id if anything references it (we pass regenerateIds: false so ids survive).
Sensible defaults are applied: strokeColor="#1e1e1e", backgroundColor="transparent", fillStyle="solid", strokeWidth=2, roughness=1, opacity=100. Canvas is white.
{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 80 }.
"roundness": { "type": 3 } for rounded corners."backgroundColor": "#a5d8ff", "fillStyle": "solid" for filled."label": { "text": "Hello", "fontSize": 20 } — auto-centered, no separate text element needed. Works on rectangle, ellipse, diamond, arrow.{ "type": "text", "x": 150, "y": 138, "text": "Title", "fontSize": 24 }. x is the left edge. To center at cx: width ≈ text.length * fontSize * 0.5, so x = cx - text.length * fontSize * 0.25.{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0, "points": [[0,0],[200,0]], "endArrowhead": "arrow" }. points are [dx,dy] offsets from x,y. endArrowhead ∈ null | "arrow" | "bar" | "dot" | "triangle". Use "line" for an unarrowed connector.Attach an arrow to shapes by referencing their ids. convertToExcalidrawElements synthesizes the full bindings (focus, gap) and updates the shapes' boundElements:
{ "type": "arrow", "id": "a1", "x": 100, "y": 50, "width": 200, "height": 0,
"points": [[0,0],[200,0]], "endArrowhead": "arrow",
"start": { "id": "r1" }, "end": { "id": "r2" } }
Referenced ids must exist or the binding is silently dropped and the arrow floats.
Primary strokes: #4a9eed blue, #f59e0b amber, #22c55e green, #ef4444 red, #8b5cf6 purple, #ec4899 pink, #06b6d4 cyan, #84cc16 lime.
Pastel fills (use with fillStyle: "solid"): #a5d8ff (input/source), #b2f2bb (success/output), #ffd8a8 (warning/external), #d0bfff (processing), #ffc9c9 (error), #fff3bf (notes/decisions), #c3fae8 (storage), #eebefa (analytics).
Background zones (use opacity: 30): #dbe4ff UI layer, #e5dbff logic layer, #d3f9d8 data layer.
Diagrams display at ~700px wide. Design the bounding box to be roughly 4:3 so the canvas isn't squished.
Two connected labeled boxes:
[
{ "type": "rectangle", "id": "b1", "x": 100, "y": 100, "width": 200, "height": 100,
"roundness": { "type": 3 }, "backgroundColor": "#a5d8ff", "fillStyle": "solid",
"label": { "text": "Start", "fontSize": 20 } },
{ "type": "rectangle", "id": "b2", "x": 450, "y": 100, "width": 200, "height": 100,
"roundness": { "type": 3 }, "backgroundColor": "#b2f2bb", "fillStyle": "solid",
"label": { "text": "End", "fontSize": 20 } },
{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 150, "height": 0,
"points": [[0,0],[150,0]], "endArrowhead": "arrow",
"start": { "id": "b1" }, "end": { "id": "b2" } }
]
x is the left edge. Use the formula above; don't trust textAlign for positioning (it only affects multi-line wrap).start/end ids must match an existing shape's id.