From 42a4cc8adf5ee0e874a309a2cfb5165cd6214a90 Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 29 Sep 2026 10:56:27 +0200 Subject: [PATCH] commit wireframe collab doc to repo as README --- plans/sketches/README.md | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 plans/sketches/README.md diff --git a/plans/sketches/README.md b/plans/sketches/README.md new file mode 100644 index 0000000000..e8fde08611 --- /dev/null +++ b/plans/sketches/README.md @@ -0,0 +1,98 @@ +# Wireframe collaboration without a shared agent + +Two developers work the same UX flow. Each has an LLM agent, and the agents share nothing: +no session, no memory, no canvas. The thing under review is a picture. The fix is to make +the diagram a source file. It lives in the repo, both humans edit it in their IDE, both +agents read and write it directly, and every change is inspected as a rendered image. + +## The loop + +**1. Generate.** Alice prompts her agent to write the flow as an `.excalidraw` file, asking +for the wireframe and for a file that stays comfortable to edit by hand. The second half is +what agents get wrong by default, so it is spelled out below. + +**2. Edit.** Alice opens it in her IDE with the +[Excalidraw Editor plugin](https://plugins.jetbrains.com/plugin/32029-excalidraw-editor) +(id 32029, by Agustin). Same window as the code, same worktree, no export step. + +**3. Diff.** She renders both revisions with +[excalirender](https://github.com/JonRC/excalirender) and flips between them as an animated +GIF at the same pixel alignment, so anything that moved flickers. This is also how she +checks what her agent did when she asked for an edit. + +**4. Review.** She commits to `plans/sketches/` and asks Bob, who edits the diagram in his +own IDE and commits back, or reviews in text on GitHub. His agent reads the same file and +needs nothing from Alice's. + +**5. Iterate.** Alice renders Bob's commit against its parent and goes back to step 1 or 2. + +The repo is the channel, the file is the message, the render is the review surface. + +example PR: https://github.com/simplex-chat/simplex-chat/pull/7525 +example diff GIF: https://claude.ai/artifact/GhFaatzznQ2GthwG2UQKUd + +## Conventions + +For the agent. Follow these when generating or editing an `.excalidraw` file here. + +### Keep it editable by hand + +- **Group every panel.** Each screen, alert or card is one group, so dragging moves the box, + its labels and its buttons together. Nest a second level for a row or a band. +- **Bind both ends of every arrow**, through `startBinding` and `endBinding` plus the + arrow's id in each target's `boundElements`. An unbound arrow detaches on the first drag. + Lines are not bindable, so where arrows fan out from a rail, make that rail a very thin + rectangle rather than a line. +- **Elbow arrows** store the attachment as `fixedPoint` on the binding. Dropping an endpoint + onto a target a fraction of a pixel wide resets it to the side midpoint, so set the value + instead of dragging. +- **Labels in containers.** A text with a `containerId` moves and wraps with its box. +- **One multiline text per paragraph.** Lines centred inside a card stay separate elements. +- One flow per file, named `YYYY-MM-DD-topic.excalidraw`, in `plans/sketches/`. + +### Draw primitives, not cosmetics + +Boxes, arrows and text in black, plus one accent colour for whatever is tappable. No fills, +rounded corners, phone bezels, avatars or status bars. Group sections with a filled, +borderless rectangle behind them in a desaturated tint, one per section. Rows share a single +y and a fixed column pitch, and a caption sits 7.4px under its card, left edges aligned. + +### Use fontFamily 9 + +Liberation Sans. Never 2, Helvetica: excalirender does not bundle it, falls back to +monospace, and renders every string at 140% of its stored width, so every card overflows. +Both tools bundle 9, and it is metric-compatible with Helvetica. + +### Normalize before every commit + +The plugin writes the full element schema, pretty-printed, on every save: 14k lines and +~90k tokens for a 354-element sketch. `plans/sketches/normalize-excalidraw.py` drops the +fields Excalidraw re-derives on load and writes one element per line, and +`.git/hooks/pre-commit` runs it over staged files. A changed element then costs one changed +line: a real commit went from 219 insertions and 826 deletions to 28 and 43. + +Never add a field to its strip list without a plugin round trip to prove it. `strokeWidth` +defaults to 2, so dropping a 1 doubles every stroke in the drawing, and `lineHeight` is +recomputed from font metrics when absent. + +### Diff visually, not textually + +``` +git show : > /tmp/old.excalidraw +excalirender /tmp/old.excalidraw -o old.png -s 2 +excalirender -o new.png -s 2 +excalirender diff /tmp/old.excalidraw # element counts and tags +``` + +`excalirender diff` classifies correctly but renders only the changed elements, which on a +sketch where 42 of 354 changed is a near-blank canvas. Build the flip from two full renders +and align them by content bounding box: the tool crops with a 20px margin, so a revision +whose box shifted renders at a different offset and the flip jitters. + +Arrow labels are drawn where they are stored rather than recomputed, so keep them about +16px clear of the line or excalirender strikes through them. + +### Report in the reviewer's terms + +Name panels by the labels a human reads on them. "Panel 4e removed, the price in 2c went +from $20 to $100" is reviewable. "15 elements removed, 27 modified" is only the headline.