Files
Matt Van Horn 899579b459 feat: import Excalidraw diagrams for editorial redraw (#192)
* feat(import): add Excalidraw extractor and scene fixtures

excalidraw_extract.py parses .excalidraw/.excalidraw.json scenes into the
same digest shape as the draw.io and Mermaid extractors: nodes, edges,
frames, groups, budget flags, and a discard ledger. Source content is
treated as untrusted data throughout; links, embeds, images, freedraw,
and unknown element types are inventoried, never rendered or fetched.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

* docs(import): Excalidraw redraw reference, command, prompt, worked example

Mirrors the Mermaid import surfaces: references/import-excalidraw.md
carries the four dials and fidelity-ledger contract, the plugin command
and Pi prompt route to it, and the gallery ships a worked example tab.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

* ci: verify Excalidraw imports

verify-excalidraw-import.py drives the real extractor against both
fixtures (clean whiteboard + adversarial scene), checks trust-boundary
behavior, resource caps, named exit-2 failures, encoding safety, and
reference/command wiring. test-verify-excalidraw-import.py proves the
verifier rejects intentional breakage.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

* docs: route Excalidraw import across README, SKILL.md, doctor, and manifests

Adds .excalidraw routing to SKILL.md §11, README import section and file
tree, CONTRIBUTING gates, cookbook R6, doctor script/routing inventories,
docs-sync routing surfaces, and the four manifest descriptions. No
plugin version bumps.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

* fix(excalidraw_extract): reject non-finite geometry with the promised exit 2

A scene is untrusted input, and three shapes reached the digest's integer
formatting and crashed there instead of being diagnosed: the JSON tokens
`Infinity` and `NaN` (OverflowError / ValueError) and an out-of-range integer
coordinate ("int too large to convert to float"). All three exited 1 with a
traceback rather than the documented exit 2.

`_num` now rejects non-finite values and out-of-range ints, `json.loads`
refuses the non-finite constant tokens via parse_constant, and scene_bounds
guards the derived width/height arithmetic. Absent or non-numeric fields keep
reading as 0, so no valid scene changes: both fixtures produce byte-identical
output to before this commit.

* Run mobile render checks in the Playwright gate

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Cathryn Lavery <cathryn@bestself.co>
2026-09-09 19:03:15 -05:00
..
2026-08-14 14:28:44 -07:00