Skip to content

feat(claude): excalidraw-diagram スキルを自作し、描画時の通信をなくす - #271

Merged
paveg merged 4 commits into
mainfrom
feat/excalidraw-offline-renderer
Nov 2, 2026
Merged

paveg merged 4 commits into
mainfrom
feat/excalidraw-offline-renderer

Conversation

@paveg

@paveg paveg commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

概要

excalidraw-diagram スキルを自作のものに置き換え、描画時の通信をなくします。これまでは上流(coleam00/excalidraw-diagram-skill)を chezmoi external で取得していましたが、描画のたびに esm.sh から excalidraw、React、node の polyfill、フォントを取得していました。React は ^19.2.0 の範囲指定で取得していたので、描画のたびに版が変わる可能性もありました。

新しい描画スクリプトは、版を固定した npm パッケージを install 時に一度だけ取得し、esbuild で bundle にします。描画時は、ページ、bundle、フォントだけをローカルから返し、それ以外のリクエストはすべて止めます。1 件でも止めた場合は、何も書き出さずに失敗します。

上流にはライセンスがない(gh api repos/coleam00/excalidraw-diagram-skill --jq .license.spdx_id が空)ので、SKILL.md、references、描画スクリプトはすべて新しく書きました。上流のファイルはコピーしていません。

flowchart LR
  subgraph install["chezmoi apply(一度だけ)"]
    lock[pnpm-lock.yaml] --> pnpm["pnpm install --frozen-lockfile"] --> build["build.mjs"] --> dist["dist/ (bundle + fonts)"]
  end
  subgraph render["render.mjs(毎回)"]
    chrome[Chrome] -->|page.route| local["dist/ だけを返す"]
    chrome -->|それ以外| abort["abort → exit 1"]
  end
  dist --> local
Loading
offline-renderer

レビューの観点

  • 中心の変更:renderer/render.mjs、renderer/lib.mjs(リクエストを返すか止めるかの判定と、入力の検証)、renderer/build.mjs(一時ディレクトリに build してから置き換える)、run_onchange_after_build-excalidraw-renderer.sh.tmpl
  • 機械的な変更:pnpm-lock.yaml(pnpm が生成)、external の項目と .chezmoiignore の除外行の削除、旧ファイルの .chezmoiremove への追加
  • 文章:SKILL.md と references/。既定値(roughness: 0、fontFamily: 3)と、描画して画像を見て直す流れは、上流と同じ使い勝手にそろえています
  • business への展開:.chezmoiignore から除外行を消したので、業務端末にも入ります。install 時には npm レジストリへの通信が1回発生します

確認したこと

確認したこと コマンド 結果
旧パイプラインとの出力の一致 homelab の site/diagrams/*.excalidraw 6 本と日本語のサンプル 1 本を、旧パイプライン(esm.sh)と新しい描画スクリプトで描画し、cmp で比較 SVG と PNG の 14 ファイルすべてが一致
描画時のリクエスト 上の 7 図を描画 外へのリクエストは 0 件。旧パイプラインは 15 種類(日本語を含む図では 28 種類)の URL を取得していた
外へのリクエストがあると失敗する node render.mjs tests/skills/excalidraw-diagram/fixtures/outbound-request.excalidraw blocked 1 outbound request(s), nothing written:、exit 1、PNG は作られない
テスト全体 just test ✓ All checks passed!
自作した検査のテストが機能している guards.test.mjs の検査を 1 つずつ壊して実行 壊した 9 か所すべてで失敗した
初回の apply で build を飛ばしても、あとで再実行される chezmoi execute-template(設定は空の一時ファイル) # tools 行に node と pnpm のパスが入る。存在しないツールに対する lookPath は空文字を返す
SKILL.md だけで図を作れる 文脈を持たない agent に、SKILL.md と references だけを渡して図を作らせた 約 60 要素の図を3回の描画で完成した。見つかったテンプレートの誤りと、手順のあいまいな点は修正済み

6 図の描画時間は、旧パイプラインが 6.5 秒、新しい描画スクリプトが 3.9 秒でした(どちらも Chrome で計測)。

補足

  • 描画領域の大きさは、旧パイプラインと同じ固定値(--width × 1200)のままです。図の外枠から計算すると、PNG のピクセルが旧パイプラインとずれたためです
  • 上流は Playwright に同梱の Chromium で描画していましたが、この PR では手元の Chrome を使います。Chromium との PNG の差は確かめていません
  • pnpm-workspace.yaml の allowBuilds: { esbuild: false } は、esbuild の postinstall を承認しないという明示です。pnpm 12.9.0 で確認しました。esbuild はプラットフォーム別のバイナリで動きます
  • 実機での chezmoi apply は、merge したあとに個人用 PC と業務用 PC の両方で確認します
  • この PR を apply したあと、paveg/funailog の writing-funailog-articles/SKILL.md:89 を新しいコマンドに書き換える PR を別に出します

🤖 Generated with Claude Code

paveg and others added 4 commits November 2, 2026 22:29
…nderer

The excalidraw-diagram skill was a third-party archive whose renderer
imported Excalidraw from esm.sh on every render and had no license. Replace
the renderer with a pinned local bundle (esbuild, fonts copied from the npm
package) served from a fake origin to headless Chrome, aborting every other
request and failing the render when any was aborted. SVG and PNG output is
byte-identical to the old pipeline on the reference diagrams.

The chezmoi external and its personal-only ignore are removed, the upstream
files the new skill does not provide are listed in .chezmoiremove, and a
run_onchange script installs and builds the renderer on every machine.
Hermetic tests cover input validation and the asset path guard; an opt-in
recipe renders fixtures through Chrome.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…emplates

Write the skill in original wording: depth decision, layout and visual
patterns, semantic palette, element templates derived from real diagrams,
section-by-section builds, and a mandatory render, read, fix loop against
the offline renderer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…xist, keep dist/ on failed builds

A skipped run exits 0, which chezmoi records as done for that script
content, so a fresh machine never rebuilt after devbox installed pnpm. Embed
the lookPath results in the script so its content changes when the tools
appear. build.mjs now stages into dist.tmp and swaps it in only on success.

render.mjs reports an unknown option with usage, refuses font requests that
resolve to a directory, and reports write failures without leaving a PNG
behind when the SVG cannot be written. Trim comments that restated history.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lidraw-diagram guidance

The two-line bound text in the template used the height and y of a
single line. Use 2 * 16 * 1.25 = 40 and a centered y of 55, confirmed by
rendering. Add short guidance on research without docs, one color per text
element, diamond arrow targets, hand-written JSON versus coordinate-shifting
scripts, and when rendering is optional.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Nov 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: acaf30d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@paveg
paveg merged commit 7f5be19 into main Nov 2, 2026
13 checks passed
@paveg
paveg deleted the feat/excalidraw-offline-renderer branch November 2, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant