全部技能 / 文档与知识 / excalidraw-use
文档与知识 · 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.

风险提醒:蓝色 · 知晓即可AI 侦查报告
作者 daymadeGitHub daymade/claude-code-skills ↗Stars 1385许可 MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)commit d5c4678cb5
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

核心把戏是把「一堆图摆上白板」做成一个独立 scene 文件(pick tray 挑选托盘),而不是去改用户那块正在用的板子。整个 Job 1 就是把 N 张图等比缩放、居中排进网格、base64 内嵌进 files 映射。

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.
注:这里在做什么:把「交付物」和「用户的现场文件」在物理上分开,从根上避免合并冲突式的事故;SKILL.md 用一整节『Never write into their live board file』来强化这条纪律。

图片尺寸不靠外部库,直接从文件头解析:PNG 读 IHDR 的 8 字节宽高,JPEG 走段扫描到 SOF 标记再取宽高。

excalidraw-use/scripts/build_scene.py
"""(width, height) in pixels for PNG or JPEG, read from the file's own header."""
注:实现细节:PNG 分支用 struct.unpack(">II", data[16:24]);JPEG 分支逐段跳过 0xFF 标记直到 0xC0-0xCF 系列。读不出就 raise ValueError,由 build() 转成 SystemExit 拒绝写文件——「宁可失败也不写坏文件」的取向贯穿全脚本。

每次写入后强制从磁盘 read-back 自证:元素数、fileId 与 base64 内容的 sha1 是否一致、绘制长宽比与原图是否一致、两两包围盒是否重叠,任一不符即 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.
注:verify() 的 docstring 把理由讲得很清楚。SKILL.md 把这个输出行当作可用性信号:『Expected output — if you do not see the `verified:` line, the file is not usable』。

去重与排除都按内容哈希(sha256)而不是文件名,改过名的副本同样被识别。

excalidraw-use/scripts/build_scene.py
skip = {hashlib.sha256(p.read_bytes()).hexdigest() for p in exclude if p.exists()}
注:同一函数里还有 seen 字典做批内去重(digest in seen 即跳过并记录『skipped (identical content)』),SKILL.md 对应条目:『The check is by image content, not filename, so a renamed copy is still caught.』

--template-from 解决的是「官方 JSON schema 没写 image element 自己的字段」这个真空:从用户自己的板子里深拷贝一个真实 image element 来拿字段集。

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 用一个『Image elements — NOT documented』小节展开:『The published schema describes the `files` map but does not enumerate the image element's own fields.』,并给出方法论总结:『copy a working instance from the destination rather than reconstructing one from documentation that does not cover it.』

交付路线刻意选剪贴板而非 Open / 拖拽:后两者会整块替换用户当前场景。

excalidraw-use/references/paste_workflow.md
The clipboard carries elements *and* their embedded image payloads, so copy-paste merges instead of replacing:
注:SKILL.md 同一件事的措辞:『at excalidraw.com, both *Open* and drag-and-drop **replace the entire current scene**』。paste_workflow.md 还给出为什么磁盘文件不等于现场板子的两条独立理由:浏览器标签页不监听文件变更、以及导出的文件通常比浏览器状态旧。

Job 2 的截图之所以不丢内容,靠三件事:从源码正则抽 slide id 与 fragmentCount、每页全量 page.goto(而不是改 hash)、以及用 computed opacity 而不是 innerText 判断 fragment 是否真的显示出来。

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.
注:对应的实测判据是 parseFloat(getComputedStyle(e).opacity) < 0.9,未揭示的会被记进 unrevealed 并打印 WARNING(--strict 时改为非零退出)。references/deck_screenshot_pitfalls.md 第 2 节把这个反直觉点单列:『document.body.innerText // shows all four years — looks correct』而截图只有一年。

两种 fragment 推进模式被判定为互斥,脚本层直接禁止混用:--advance 只接受 pdf 或 keys。

excalidraw-use/scripts/shoot_deck.mjs
console.error(`--advance must be "pdf" or "keys", got ${ADVANCE}`)
注:原因写在文件头注释与 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.』

Job 3 的 inspect_scene.py 是只读体检:类型计数、内嵌 payload 体积、fileId 与内容 sha1 是否仍然一致、以及已占用 extent。extent 是给用户「往哪滚再粘贴」用的。

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.
注:report 里同时检测反向问题(『file {fid} is embedded but no live element uses it』)与长宽比失真(abs(el['width']/el['height'] - px/py) > 1e-3)。whole file 只读不写,异常被 except Exception 兜住只为报告不崩。

split_scene.py 先整除再按实际分片数命名(避免出现「1-of-4 却没有 4-of-4」),并且每个分片只携带自己引用到的 files,否则分片体积毫无收益。

excalidraw-use/scripts/split_scene.py
"files": {e["fileId"]: files[e["fileId"]] for e in part},
注:上下文注释写着『Carry only the files this part actually references, or every chunk would be as large as the original and the split would buy nothing.』;分片后同样跑一遍 read-back 校验(元素数、fileId 匹配、长宽比、两两不重叠),并用 total != len(elements) 兜住「分片丢元素」。

2核心能力

01把大批已有图片摆成规整网格的白板托盘(可指定列数/格边长/间距)
02按内容哈希排除已在板上的图(改名副本也认得出)
03从真实板子抽取 image element 字段集(补齐官方 schema 没写的部分)
04把 Vite/React 幻灯片逐页截成干净 PNG(隐藏讲者专用装饰、展开分步动画)
05只读体检 .excalidraw:元素构成、内嵌体积、坏引用、内容哈希是否还对得上、占用范围
06大场景分片(解决剪贴板在几十 MB 时卡死或静默失败)
07给出「文件安全、渲染未验证」的诚实交付说明与粘贴操作序列

3外部依赖

类型依赖
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(参考文献里引用的官方文档链接,非运行时代码)

4风险提醒 风险提醒:蓝色 · 知晓即可

风险提醒:蓝色 · 知晓即可
  • Job 2 依赖外部 npm 包与构建工具,且 skill 不负责安装。 — puppeteer 必须从脚本所在位置可解析(SKILL.md 明说 npx puppeteer 不提供可 import 的模块);npx vite build 在缺 vite 时可能走 npm registry,属第三方供应链面。
  • 文档建议的本地静态服务器默认对外网卡开放。 — python3 -m http.server 8080 未指定 --bind,默认 0.0.0.0;/tmp/deck-dist 的内容与目录列表在同网段可达。改为 --bind 127.0.0.1 即可消除。
  • Job 2 会在无头浏览器中执行被截页面的 JavaScript。 — deck 页面可能是第三方模板;页面脚本运行在 Chromium 沙箱内,脚本不回落 Node,但仍应只对可信 deck 运行。
  • PNG/JPEG 之外的格式会被直接拒绝。 — image_size 只处理 PNG 与 JPEG,其他格式 raise ValueError(『not a PNG or JPEG (only these two carry readable dimensions here)』),build_scene.py 随即 SystemExit——是硬失败而非降级,批量任务里可能出现部分图片不可用。
  • 剪贴板路线需要用户手工操作,且验证边界只能靠人。 — 最终一步(选中→复制→滚到空白→粘贴)无法自动化;skill 明确要求把这一步交给用户,agent 不得自行打开 excalidraw.com 去验证。
风险提醒:蓝色,知晓即可。按统一分档:① 三个主脚本(build/split/inspect)是纯标准库 Python,只读本地图片与 .excalidraw、只写用户指定的输出文件,零网络、零凭证(全目录 token 扫描 subprocess/os.environ/API_KEY/TOKEN/SECRET/.env/cookie/keychain 均零命中);② 唯一的「执行外部代码」是 Job 2 的 puppeteer 无头浏览器,跑的是用户自己的 deck 页面且不回落 Node——不构成任意代码执行面;③ 无凭证读取、无密钥、无埋点、无第三方镜像,域名级只出现 excalidraw.com(还只是写进 scene 的 source 字符串)与 docs.excalidraw.com(文档链接)。使其不是绿色的两点:Job 2 依赖第三方 npm 包 puppeteer(skill 不自带、要求环境里已可解析),以及 SKILL.md 指定的 python3 -m http.server 8080 默认绑定 0.0.0.0,会把 /tmp/deck-dist 目录列表与内容暴露给同网段——这是文档层面的操作建议,不是脚本行为,但值得留意。

5第二遍独立确认

  • [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。

6结论

  • 零 Python 第三方依赖 + 写入即自证,产物不会「悄悄坏掉」。
  • 把「交付」这一步当作主要风险来设计(剪贴板而非 Open),是为数不多把用户现场数据安全写进流程的 skill。
  • 对官方 schema 的空白有清醒认知,并给出可执行的替代方案(--template-from)而非猜测字段。
  • 截图路径把「看起来对其实错」的失败模式逐条堵死(opacity 判据、全量 goto、advance 互斥、按键徽标等待、双路隐藏装饰)。
  • 诚实标注能力边界:结构可验、渲染不可验,并要求把这句话讲给用户。
  • 适合:适合这样的场景:手上有一批截图/照片要成批摆到现有白板上(手工拖到崩溃),或者要把一套 Vite/React 幻灯片先变成干净的单页图再当素材用,或者需要在不打开浏览器的情况下先摸清某个 .excalidraw 里到底有什么、有没有坏引用。也适合把它当作「Excalidraw 文件格式的实测笔记」来读——references/scene_file_format.md 里那份「官方文档没写的部分」清单对自研工具有直接价值。
    不适合:不适合「从一句话画一张架构图/流程图」——SKILL.md 明确把这个任务推给别的工具,本 skill 不做生成;不适合需要 agent 独立完成到底、无法让用户手工粘贴的场景;不适合非 Vite/React 或 slide 注册表形状不同的 deck(shoot_deck.mjs 的正则只认 { id: '...', fragmentCount: n } 这种条目,找不到就直接退出);不适合期望 agent 打开 excalidraw.com 帮你看一眼效果的场景——那恰好会顶掉用户当前的板子。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-15
    方式 A · 人下载镜像包下载 excalidraw-use.tar.gz
    sha256: 0c4ae44eb4c051d3…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit d5c4678cb5;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库daymade / daymade/claude-code-skills
    Stars1385
    最近推送2026-09-09
    本 skill commitd5c4678cb5
    许可MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)
    本站信息
    收录日期2026-09-06
    分类文档与知识
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近