跳到主要内容
知仓学习社ZHICANG

excalidraw-use

>-

读文件联网无严重或高危命中daymade/claude-code-skills

它会碰到什么

扫了多少8 个文本文件,49 KB
它会碰到什么读文件联网
命中总数4 处
命中统计严重 0 · 高 0 · 中 2 · 低 2

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

Excalidraw: images onto a board

Excalidraw is a whiteboard people draw on live. This skill covers the part that

is fiddly to do by hand: getting a lot of existing images onto a board,

laid out so nobody has to drag them apart afterwards.

It does not generate diagrams from prose. If the request is "draw the

architecture" or "make a flowchart", that is a diagram-generation job — several

tools and skills do it, including a diagram skill in the gstack suite if the

user has it installed.

Which job is this?

| The user says | Go to |

|---|---|

| "put these images on my board", "add the old screenshots to the canvas" | Job 1 — build a pick tray |

| "turn the deck into images", "screenshot every slide" | Job 2 — deck to images, then Job 1 |

| "what's in this file", "how big is this board", "is anything broken" | Job 3 — inspect |

| "draw me a flowchart from this description" | not this skill |

Job 1 — build a pick tray of images

python3 scripts/build_scene.py \
  --out tray.excalidraw \
  --cols 6 \
  --template-from ~/path/to/their-board.excalidraw \
  --exclude already-placed.png \
  images/*.png

Expected output — if you do not see the verified: line, the file is not usable:

verified: 30 image element(s), hashes match, no overlap
placed 30 image(s), 6 x 5 grid, min gap 600
wrote tray.excalidraw (3.5 MB)

Hand the user the file plus how to get it onto their board. That second half

is where this goes wrong: at excalidraw.com, both Open and drag-and-drop

replace the entire current scene. Telling someone to "open this file" when

they have a live board is telling them to lose it. The additive route is the

clipboard — full instructions and the reasoning in

[references/paste_workflow.md](references/paste_workflow.md). Read it before you

write the hand-off message.

Three decisions worth making deliberately:

  • --template-from whenever they have an existing board. Excalidraw's

published JSON schema documents the top-level shape and the files map, but

not the image element's own fields (fileId, status, scale, crop), so

copying a live element from their board is the only way to be sure the field

set matches the build they actually run. Without it a reasonable default is

used, which has worked but is one observed field set, not a spec.

[references/scene_file_format.md](references/scene_file_format.md) has the

field-by-field breakdown, keeping what the official docs state separate from

what was read off a real file — open it when you need to hand-build or repair

a scene rather than let the script write one.

  • --exclude anything already on their board. The check is by image content,

not filename, so a renamed copy is still caught. Skipping this is how someone

ends up with the same picture twice and has to delete one by hand.

  • Spacing. --cell is the longest side each image is scaled to; --pitch is

the distance between cell centres. The gap is pitch - cell, and the defaults

(800 / 1400 → 600) are deliberately generous. If a user has ever said "don't

make me fix the spacing again", raise --pitch, don't lower it.

If the scene is more than ~15 MB, split it — the clipboard is where a large

scene stalls:

python3 scripts/split_scene.py --scene tray.excalidraw --out-dir parts/ --chunks 4

Job 2 — turn a slide deck into clean images

For Vite/React decks whose slides are registered in a src/slides/index.ts with

entries shaped { id: 'intro', component: S01, fragmentCount: 3 }.

# 1. Build the deck FROM SOURCE and serve that build (not a dev server).
cd <deck>; npx vite build --outDir /tmp/deck-dist --emptyOutDir
cd /tmp/deck-dist && python3 -m http.server 8080 &

# 2. Shoot it.
node scripts/shoot_deck.mjs \
  --index <deck>/src/slides/index.ts \
  --url http://127.0.0.1:8080/index.html \
  --out shots/ \
  --advance pdf     # or: keys

Expected output:

30 slides (7 with fragments), advance=pdf
30/30 captured, 32 inline chrome element(s) hidden -> shots/
all .fragment elements rendered visible (opacity check)

That last line is the one that matters. If instead you see

WARNING: N slide(s) have fragments that never revealed, those screenshots are

missing content — do not feed them into Job 1. Read

[references/deck_screenshot_pitfalls.md](references/deck_screenshot_pitfalls.md);

the usual cause is that the build being served is older than the source.

puppeteer must be resolvable from wherever you run the script. Decks that

generate their own PDFs usually already have it — run the script from that

project, or symlink its node_modules next to the script. npx puppeteer does

not install a resolvable module for an ESM import.

Picking --advance: grep the deck's Deck component for pdf. If it reads

a ?pdf=1 query, use pdf; otherwise keys. 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.

Add --strict to make unrevealed fragments a non-zero exit when this runs

inside a pipeline. It stays a warning by default so a deck that legitimately

keeps a .fragment hidden does not fail the whole run.

Job 3 — inspect a scene

python3 scripts/inspect_scene.py board.excalidraw --images

Reports element counts by type, embedded payload size, whether every image

element resolves to a file whose content still matches its key, and the occupied

extent. Read-only.

The extent is the practically useful number: pasted content lands wherever

the viewport is, so knowing where existing work sits is how you tell the user

where to scroll before pasting.

Never write into their live board file

Build a separate scene file and let the user paste from it. Two independent

reasons, both observed:

  • A board that is open in a browser tab does not reload when the file on disk

changes, and the next in-app save overwrites whatever you wrote.

  • The file on disk is usually older than the browser's state — someone

exported it days ago and has been working in the tab since. Merging into that

export silently drops everything they have done since.

If a merged file is genuinely wanted, write it to a new path and let the user

choose which to open. Never overwrite the original.

Troubleshooting

| Symptom | Cause | Fix |

|---|---|---|

| Scene opens but a picture is blank | files entry missing or its key does not match the payload | inspect_scene.py reports both; rebuild |

| Images touch or overlap on the board | --pitch not greater than --cell | the script refuses this combination; raise --pitch |

| Paste does nothing / takes forever | scene too large for the clipboard | split_scene.py, then paste each part |

| Pasting replaced the whole board | Open / drag-and-drop was used instead of copy-paste | see references/paste_workflow.md; recover with in-app undo |

| Slides captured missing their later content | fragments never revealed | rebuild the deck from source, re-serve, re-shoot |

| Key-press badges appear in slide screenshots | the wait for them to fade was cut short | the script already waits ~2.9 s after the last press; if a deck lingers longer, raise it |

| Cannot find package 'puppeteer' | not resolvable from the script's location | run from a project that has it |

| Image drawn at the wrong shape | element aspect ratio does not match the source | build_scene.py fails on this rather than writing it |

Next step

After a tray is built, the natural follow-up is the paste itself, which only the

user can do. Give them the file path, the scroll-to-empty-space warning, and the

three-key sequence — then stop. Do not open excalidraw.com to "verify" the file:

loading a scene there replaces whatever is in that browser's current session,

which may be the board you were trying to protect.

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。