Home / All Skills / Docs & Knowledge / excalidraw-use
Docs & Knowledge · daymade/claude-code-skills

excalidraw-use

Place existing images onto an Excalidraw whiteboard, turn a slide deck into clean per-slide images first, and inspect what is inside a .excalidraw file. Use whenever someone mentions Excalidraw, a whiteboard or canvas, a .excalidraw file, 白板 or 画板 — especially "put these screenshots on my board", "add my old workshop images to the canvas", "space them out so I don't have to adjust spacing by hand", "turn these slides into images I can draw on", or "what's in this scene file". Also use when a talk or workshop is delivered by hand-drawing over screenshots on a shared canvas, or when someone needs a pick tray of many images to choose from. Not for generating a diagram from a text description — that is a different job.

L4 High-Privilege · Graded auth & full auditV1 Commit-PinnedExecute CommandNetwork AccessWrite FileDual-pass Audit
Author daymadeGitHub daymade/claude-code-skills ↗Stars 1385License MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)Commit d5c4678cb5
Control levels describe skill capabilities and suggested control measures; they do not represent quality or safety certifications, nor do they dictate usability. Usage decisions rest entirely with users and administrators. Different agent hosts apply varying constraints; do not assume hosts provide automatic protection. Third-party skill copyrights belong to their original authors. Use at your own risk.

1How It Works & Mechanism

The core trick is packaging a collection of images onto a whiteboard as an independent scene file (a pick tray), rather than directly modifying the board the user is currently working on. Job 1 scales N images proportionally, arranges them centered in a grid, and embeds them into the files map as base64 strings.

excalidraw-use/scripts/build_scene.py
The scene is a *pick tray*: open it in a second Excalidraw tab, select what you want, and copy-paste it onto your real board. It is never written into the board file itself — see references/paste_workflow.md for why that matters.
What is happening here: It physically separates the "deliverable" from the "user's live file" to fundamentally prevent merge-conflict style disasters. SKILL.md dedicates an entire section, "Never write into their live board file", to enforce this discipline.

Image dimensions are parsed directly from file headers without relying on external libraries: reading the 8-byte width and height in IHDR for PNG, and scanning segments up to the SOF marker to extract width and height for JPEG.

excalidraw-use/scripts/build_scene.py
"""(width, height) in pixels for PNG or JPEG, read from the file's own header."""
Implementation details: The PNG branch uses struct.unpack(">II", data[16:24]); the JPEG branch skips 0xFF markers segment by segment until hitting the 0xC0–0xCF series. If parsing fails, it raises a ValueError, which build() converts into a SystemExit to refuse writing the file—the philosophy of "fail early rather than writing corrupted files" runs throughout the script.

Forces an on-disk read-back verification after every write: checks element count, sha1 consistency between fileId and base64 content, aspect ratio match against the original image, and pairwise bounding box non-overlap. Any mismatch results in a SystemExit.

excalidraw-use/scripts/build_scene.py
Deliberately re-reads from disk rather than checking the in-memory objects: the failure this catches is a bad write, and an in-memory assert cannot see one.
The docstring of verify() explains the rationale clearly. SKILL.md uses this output line as an operational usability signal: "Expected output — if you do not see the `verified:` line, the file is not usable".

Deduplication and exclusion are based on content hashes (sha256) rather than filenames, correctly identifying renamed duplicates.

excalidraw-use/scripts/build_scene.py
skip = {hashlib.sha256(p.read_bytes()).hexdigest() for p in exclude if p.exists()}
Within the same function, a seen dictionary performs intra-batch deduplication (digest in seen skips and logs "skipped (identical content)"). Corresponding entry in SKILL.md: "The check is by image content, not filename, so a renamed copy is still caught."

--template-from solves the void where the official JSON schema omits image element fields by deep-copying an authentic image element directly from the user's board to extract the field schema.

excalidraw-use/scripts/build_scene.py
for el in scene.get("elements", []): if el.get("type") == "image" and not el.get("isDeleted"): return copy.deepcopy(el)
references/scene_file_format.md elaborates under the subsection "Image elements — NOT documented": "The published schema describes the `files` map but does not enumerate the image element's own fields.", providing a methodological summary: "copy a working instance from the destination rather than reconstructing one from documentation that does not cover it."

The delivery pipeline deliberately chooses the clipboard over Open or drag-and-drop: the latter two will entirely overwrite the user's active scene.

excalidraw-use/references/paste_workflow.md
The clipboard carries elements *and* their embedded image payloads, so copy-paste merges instead of replacing:
SKILL.md phrases the same point: "at excalidraw.com, both *Open* and drag-and-drop **replace the entire current scene**". paste_workflow.md also provides two independent reasons why disk files do not equate to the live board: browser tabs do not listen for file changes, and exported files are typically older than the current browser state.

Job 2 captures screenshots without missing content by relying on three pillars: extracting slide id and fragmentCount via regex from source code, performing full page.goto per slide (instead of hash navigation), and checking computed opacity rather than innerText to determine if fragments are actually visible.

excalidraw-use/scripts/shoot_deck.mjs
// Fragments that never revealed stay in the DOM at opacity 0, so any check // based on text content reports them as present while the picture is blank. // Measure what actually renders.
The actual empirical test criterion is parseFloat(getComputedStyle(e).opacity) < 0.9. Unrevealed elements are recorded into unrevealed and emit a WARNING (or exit non-zero when --strict is passed). Section 2 of references/deck_screenshot_pitfalls.md highlights this counter-intuitive behavior: "document.body.innerText // shows all four years — looks correct" while the screenshot captures only one year.

The two fragment progression modes are treated as mutually exclusive and strictly enforced at the script level: --advance only accepts pdf or keys.

excalidraw-use/scripts/shoot_deck.mjs
console.error(`--advance must be "pdf" or "keys", got ${ADVANCE}`)
The reason is documented in the file header comments and SKILL.md: "Never both — under `?pdf=1` the fragment index is already at maximum, so extra key presses move to the *next* slide and you silently capture the wrong content."

inspect_scene.py in Job 3 performs a read-only health check: element type counts, embedded payload sizes, sha1 alignment between fileId and content, and the occupied extent (used to tell the user where to scroll before pasting).

excalidraw-use/scripts/inspect_scene.py
# Excalidraw keys files by a content hash; when it holds, a # mismatch means the payload was swapped without rekeying.
The report also checks for reverse issues ("file {fid} is embedded but no live element uses it") and aspect ratio distortions (abs(el['width']/el['height'] - px/py) > 1e-3). The whole file is strictly read-only, and exceptions are caught via except Exception to ensure the report does not crash.

split_scene.py calculates division before naming chunks based on the actual chunk count (avoiding situations like "1-of-4 without 4-of-4"), and bundles only the files referenced by that chunk so the split provides actual payload reduction.

excalidraw-use/scripts/split_scene.py
"files": {e["fileId"]: files[e["fileId"]] for e in part},
Contextual comments state: "Carry only the files this part actually references, or every chunk would be as large as the original and the split would buy nothing." Post-split files also undergo the same read-back verification (element counts, fileId matching, aspect ratio, pairwise non-overlap), using total != len(elements) to catch any chunk-splitting element loss.

2Core Capabilities

01Arrange large batches of existing images into a neatly aligned grid on a whiteboard pick tray (configurable columns, cell size, and spacing)
02Exclude images already present on the board using content hashing (detects renamed copies)
03Extract image element field structures from an existing board file (filling gaps omitted by the official schema)
04Capture clean per-slide PNG screenshots from Vite/React slide decks (hiding speaker decorations and advancing multi-step animations)
05Perform read-only health inspections on .excalidraw files: element breakdown, embedded payload size, broken references, content hash alignment, and bounding extents
06Split large scenes into manageable chunks (preventing clipboard freezes or silent failures when payloads reach tens of megabytes)
07Deliver safe file handoffs with transparent "structurally verified, rendering unverified" warnings alongside copy-paste instructions

3External Dependencies

KindDependency / Endpoint
packagepuppeteer(Job 2 无头浏览器,第三方 npm 包,需可被解析,skill 不负责安装)
clinode(执行 shoot_deck.mjs)
clinpx vite build(把 deck 从源码构建到 /tmp/deck-dist)
clipython3 / python3 -m http.server(本地静态服务器)
networkhttp://127.0.0.1:8080(无头浏览器取页地址;注意 http.server 默认监听 0.0.0.0)
networkhttps://excalidraw.com(不是调用点:只作为生成 scene 的 source 字段字符串,以及文档里指向站点的手工操作说明)
networkhttps://docs.excalidraw.com/docs/codebase/json-schema(参考文献里引用的官方文档链接,非运行时代码)

4Recommended Governance & Evidence

Suggested Controls for Safe Operation
For Agent Runtime
Graded/per-use authorization, sandbox recommended
For Administrators
Approval workflow, rollback support, full audit
Evidence & Triggers (File Paths & Excerpts)
  • scripts/: Executes local scripts or commands: excalidraw-use/scripts/build_scene.py (python3, pure standard library)
  • 网络端点: Declares external network access: http://127.0.0.1:8080 (local static server for headless browser loading of /tmp/deck-dist; python3 -m http.server)
  • 本地工作目录: File I/O and artifact generation: <out>.excalidraw (generated by build_scene.py, containing base64 embedded images; automatically creates parent directory)
Audit Notes: Based on standard evaluation criteria: 1) The three primary scripts (build/split/inspect) are written in pure standard-library Python, only reading local images and .excalidraw files and writing only to user-specified paths with zero network access or credentials (full workspace scans for subprocess/os.environ/API_KEY/TOKEN/SECRET/.env/cookie/keychain yield zero matches); 2) The only external execution is the Puppeteer headless browser in Job 2, which runs strictly against the user's local deck build without falling back to Node runtime execution—posing no arbitrary code execution surface; 3) No credential access, API tokens, tracking, or external mirrors; domain mentions are limited to excalidraw.com (used purely as the source metadata string) and docs.excalidraw.com (documentation links). The two reasons keeping it from a green rating are: Job 2 requires the third-party npm package puppeteer (not bundled with the skill and must exist in the environment), and python3 -m http.server 8080 specified in SKILL.md defaults to binding 0.0.0.0, exposing the /tmp/deck-dist file index and assets across the local network—this is operational guidance in documentation rather than script behavior, but warrants caution.

5Second-Pass Verification

  • [ok] external_deps 每条的调用点是否真实存在 — puppeteer(shoot_deck.mjs 第 1 行 import)、node(shebang)、npx vite build 与 python3 -m http.server(SKILL.md Job 2 命令行)、127.0.0.1:8080(--url 实参)全部实读命中,非推测。
  • [discrepancy] excalidraw.com 是否被当作网络端点误报 — 〔第二遍标注:第一遍易误报,已修正〕build_scene.py 与 split_scene.py 里的 https://excalidraw.com 只是写入 scene 的 "source" 字段值,脚本从不请求该域名;README/文档里的链接同理。已在 external_deps 中显式标注『不是调用点』,避免把字符串常量当成外发。
  • [discrepancy] exec( 命中是否意味着命令执行 — 〔第二遍标注:扫描器误报,已澄清〕shoot_deck.mjs 的 const frag = /fragmentCount:\s*(\d+)/.exec(m[2]) 是 JavaScript RegExp.prototype.exec,属正则匹配,不执行进程。全目录无 child_process / spawn / execSync / subprocess。
  • [ok] 『不写用户现场板子』是否只是口号 — 代码层:build_scene.py 只写 --out 指定路径,从不读入并回写用户的板子文件;文档层:SKILL.md 有独立小节『## Never write into their live board file』并给出两条独立理由(标签页不监听文件、磁盘文件通常旧于浏览器状态)。
  • [ok] 写入后校验是否真做(不是文档承诺) — build_scene.py 的 verify() 在 build() 末尾被调用且从磁盘重读;split_scene.py 对每个分片做同样校验。两道都是可执行代码,失败即 SystemExit。
  • [unlocatable] python3 -m http.server 绑定范围 — 〔第二遍标注:文档未声明,属解释器默认行为〕SKILL.md 只写 python3 -m http.server 8080,未写 --bind 127.0.0.1;http.server 的默认绑定是 0.0.0.0。这是 CPython 的既定行为而非本 skill 的代码,故标注为「文档未声明」并在 security.reason 中提示,不写成 skill 缺陷。
  • [ok] Job 2 的 --advance 互斥是否代码强制 — shoot_deck.mjs 中 if (!['pdf','keys'].includes(ADVANCE)) 报错退出,且 query 只在 ADVANCE === 'pdf' 时拼 ?pdf=1;SKILL.md 有对应警告段,代码与文档一致。
  • [ok] 元数据(license/stars/last_push) — 取自同 repo 同 commit 的池内既有值(MIT、1385★、2026-09-09T12:33:29Z);本次未联网复核 gh。

6Verdict & Scenarios

  • Zero third-party Python dependencies and instant post-write verification, ensuring outputs never fail silently.
  • Treats delivery as a primary operational risk (clipboard vs. Open file), making it one of the few skills that design around live user data safety.
  • Maintains clear awareness of schema omissions and provides concrete workarounds (--template-from) rather than guessing undocumented fields.
  • Screenshot pipeline systematically mitigates deceptive "looks right but captures wrong" failure modes (computed opacity check, full goto navigation, mutually exclusive advance modes, keystroke badge debounce, dual-layer decoration hiding).
  • Honest assessment of capabilities: verifies structure while explicitly cautioning users that visual rendering cannot be pre-validated.
  • Best for: Ideal for scenarios where you have a batch of screenshots or photos that need to be arranged onto an existing whiteboard (sparing you painful manual dragging), or when you need to convert a Vite/React slide deck into clean standalone slide images before using them as canvas assets, or when you need to inspect what is inside a .excalidraw file and check for broken references without opening a browser. It is also well-suited as reference material for "Excalidraw file format implementation notes"—the catalog of undocumented schema quirks in references/scene_file_format.md provides direct value for building custom tools.
    Not for: Not suitable for generating diagrams or flowcharts from natural language prompts—SKILL.md explicitly delegates generative diagramming to other tools; not suitable for fully autonomous end-to-end workflows where the user cannot perform the final paste operation; not suitable for non-Vite/React decks or slide registries with different structures (shoot_deck.mjs strictly relies on regex matching { id: '...', fragmentCount: n } and will exit if missing); not suitable for tasks expecting the agent to open excalidraw.com to verify visuals, as doing so would overwrite the user's active canvas.
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload excalidraw-use.tar.gz
    sha256: 0c4ae44eb4c051d3…
    Option B · Copy Guide for Agent
    install.json
    Agents automatically download and verify mirrors from this JSON guide.
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit d5c4678cb5.