Skip to content

feat(design): Mermaid diagrams open as whiteboards - #156

Merged
filipeforattini merged 1 commit into
mainfrom
feat/design-whiteboard
Sep 6, 2026
Merged

feat(design): Mermaid diagrams open as whiteboards#156
filipeforattini merged 1 commit into
mainfrom
feat/design-whiteboard

Conversation

@filipeforattini

@filipeforattini filipeforattini commented Sep 6, 2026

Copy link
Copy Markdown

Tranche D of the lavish-axi parity plan, the last one: the whiteboard.

In the prototype — the SDK joins every rendered diagram in a .mermaid (or data-redcode-mermaid / data-lavish-mermaid) container with a sibling frame (sandbox="allow-scripts allow-popups", data-redcode-ui) hosting the Excalidraw whiteboard; the container is hidden, not replaced, so a theme re-render stays harmless and the file keeps its Mermaid. Diagram identity is the container's index in document order. The prototype's policy gains frame-src <origin>/design/:id/whiteboard only while the bundle is on the machine.

The frame — lavish's whiteboard-frame.js + css, vendored with the redcode prefix on its messages, token global and data attributes: boots locked in view mode behind a click-catcher, converts the source (label line-break repair, duplicate-id fallback, fonts loaded before the second materialisation), restores a saved scene when the source hash matches, offers Re-convert / Keep editing when the hash moved and the scene has real edits (style-only and sub-2px jitter do not count), autosaves 800 ms after a change, confirms external links in a dialog (http/https/mailto only), and on Queue feedback exports a PNG and posts the summary. Fullscreen asks the shell to reopen the diagram in an overlay; the inline frame parks on about:blank meanwhile so two editors never autosave one scene.

The shell — hosts the overlay frame (same sandbox), and is the only party with server access: it authenticates each frame's channel token, requires descent (source.parent === frame.contentWindow) for inline frames, fetches the Mermaid sources and the saved scene, mediates saves (chained per diagram), flushes open whiteboards for up to 1.5 s before a reload, tells an open overlay when its source changed underneath, and queues the note as tag: "whiteboard" with the summary, note, and the scene/PNG paths, replacing an unsent note for the same diagram (queueKey).

The server (design/whiteboard.ts) — HMAC channel tokens bound to one prototype, 5-minute TTL, timingSafeEqual; Mermaid sources extracted from index.html with htmlparser2 in document order (comments, raw text, template and noscript ignored, <br> kept as a label break), hashed; scenes and the agent's .excalidraw/.png under <root>/.review/whiteboards/, written atomically through a per-diagram queue; the frame page with its own CSP; bundle assets resolved by real path and served with access-control-allow-origin: *, an ETag and no-cache. Routes: GET /whiteboard (frame), GET /mermaid-sources, GET|PUT /whiteboard/:index (20 MB), POST /whiteboard-channel, POST /whiteboard/:index/feedback-files, GET /design/vendor/whiteboard/*. feedback.ts accepts the excalidraw-scene target (lavish's normaliser, bounded), rendered as whiteboard: diagram N.

The bundlebun run build:whiteboard (Bun.build: Excalidraw 0.18.1, mermaid-to-excalidraw 2.2.2 with mermaid pinned at 11.12.1, React) produces dist/whiteboard/ (7 MB script, css with fonts inlined, Excalidraw's font families). Not in the binary: script/build.ts adds redcode-whiteboard-<version>.tar.gz to the release assets, and the server fetches it into Global.Path.data/designs/whiteboard/<version>/ the first time a review asks (REDCODE_DISABLE_WHITEBOARD_DOWNLOAD=1 never fetches; REDCODE_WHITEBOARD_DIR points at a build; a checkout uses its own dist/whiteboard). Until it is there, the shell and SDK are told whiteboard: false and diagrams stay plain.

Prompt — the mode explains the whiteboard: diagram N note, the summary and the files, and that the answer is a Mermaid edit, never the scene written back.

Tests: lavish's 31 whiteboard-core tests vendored; whiteboard.test.ts (channel, sources, scenes, frame, assets, download refusal for a source build); route scenarios (sources and scene round trip, files, foreign origin refused, frame token bound to one prototype, bundle CORS/304/traversal, CSP and SDK config flip when the bundle is present); shell, SDK and feedback tests. 427 design tests and 1514 across session/tool/server/cli pass; core, app and tui typecheck.

Dependencies: none added to the workspace. script/whiteboard-bundle.ts installs its exact pins (Excalidraw 0.18.1, mermaid-to-excalidraw 2.2.2, mermaid 11.12.1, React 18.2.0) in its own project under node_modules/.cache/redcode-whiteboard-build/ and builds there, so the terminal UI's React and the lockfile are untouched.

https://claude.ai/code/session_013JrKXL4pdgTu2EtCjgiYyq


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Every rendered diagram gets an Excalidraw whiteboard beside it, and a Fullscreen action that opens the same one over the page: converted from the Mermaid source, drawn on and rearranged, autosaved beside the review's own state, and queued as one ordinary note carrying a summary of what changed plus the edited scene and a PNG on disk. The agent edits the Mermaid source in response; nothing is converted back. A scene saved for an older diagram is never merged silently. The frames run sandboxed with no origin and no server access; the review page does every read and write, only for a frame that proved a channel token minted for this prototype and its descent from the prototype frame. The bundle is not in the binary: a release ships it as a tarball the server fetches on first use; a checkout builds it with bun run build:whiteboard.

lavish-axi's whiteboard core and frame are vendored (MIT); the sources, store, channel and routes are ours.

Claude-Session: https://claude.ai/code/session_013JrKXL4pdgTu2EtCjgiYyq
@filipeforattini
filipeforattini merged commit 3a6f023 into main Sep 6, 2026
12 checks passed
@filipeforattini
filipeforattini deleted the feat/design-whiteboard branch September 6, 2026 07:07
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