--- name: diagram preamble-tier: 1 version: 1.0.0 description: | Turn an English description (or mermaid source) into a diagram triplet: the source, an editable .excalidraw file you can open on excalidraw.com, and rendered SVG + PNG. The SVG/PNG use clean mermaid style; the .excalidraw carries the hand-drawn aesthetic. Fully offline. Use when asked to "make a diagram", "draw the architecture", "create a flowchart", "diagram this", or "visualize this flow". (gstack) allowed-tools: - Bash - Read - Write - AskUserQuestion triggers: - make a diagram - draw a diagram - create a flowchart - diagram this - visualize this flow - architecture diagram --- {{PREAMBLE}} # /diagram — English in, editable diagram out Every run emits a **triplet**, never a dead pixel dump: | Artifact | What it's for | |---|---| | `.mmd` | the mermaid source — the LLM-friendly interchange format | | `.excalidraw` | editable scene — open it at excalidraw.com, move a box, keep working | | `.svg` + `.png` | crisp vector for docs + raster for chat/issues/READMEs | Rendering is fully offline: the diagram-render bundle (`lib/diagram-render/dist/diagram-render.html`) is one self-contained page, and `gstack-render` opens it from a loopback server on this machine — in the Aside browser when Aside is running, otherwise in gstack's own headless browser. Its first output line says which (`ENGINE=aside` or `ENGINE=browse`); the triplet is identical either way. No CDN, no network. ## Step 1 — Author the diagram Write mermaid for the user's request. Rules: - **Flowcharts (`graph LR`/`graph TD`) and sequence diagrams** convert to a fully editable excalidraw scene (real boxes, arrows, and text). Prefer `graph LR` for pipelines/flows, `graph TD` for hierarchies. - State, class, gantt, and the other mermaid types render to SVG/PNG fine and still get an `.excalidraw`, but the converter exports them as ONE image element: it opens at excalidraw.com and can be moved and annotated, not edited box by box. Tell the user that when you deliver one. - Keep node labels short; put detail in edge labels. 5-15 nodes is the readable range. If the user's ask needs more, split into multiple diagrams and say why. Decide the output directory: `./diagrams/` when the cwd is a git repo (artifacts the user can commit), else `/tmp/gstack-diagrams/`. Derive `` from the diagram's subject (kebab-case, ≤40 chars). ## Step 2 — Stage the render bundle (once per session) `gstack-render` serves the bundle's directory on 127.0.0.1 for each render (Aside refuses `file://`, and both engines get the same origin). Stage the bundle under gstack's own render staging directory, `${TMPDIR:-/tmp}/gstack-render` (yours alone: if that name is a symlink or another user's directory, a private `mktemp -d` is used instead), content-addressed by bundle sha: the served directory holds nothing but gstack bundles, and concurrent sessions or mixed gstack versions never clobber each other. ```bash BUNDLE="" for c in "$HOME/.claude/skills/gstack/lib/diagram-render/dist/diagram-render.html" \ "$(git rev-parse --show-toplevel 2>/dev/null)/lib/diagram-render/dist/diagram-render.html"; do [ -f "$c" ] && BUNDLE="$c" && break done [ -z "$BUNDLE" ] && echo "BUNDLE_MISSING — run: cd ~/.claude/skills/gstack && bun run build:diagram-render" && exit 1 RD="${TMPDIR:-/tmp}/gstack-render" if [ -e "$RD" ] && { [ -L "$RD" ] || [ ! -O "$RD" ]; }; then RD=$(mktemp -d "${TMPDIR:-/tmp}/gstack-render.XXXXXX"); else mkdir -p -m 700 "$RD"; fi SHA=$(shasum -a 256 "$BUNDLE" | cut -c1-16) STAGED="$RD/gstack-diagram-render-$SHA.html" [ -f "$STAGED" ] && shasum -a 256 "$STAGED" | grep -q "^$SHA" || { cp "$BUNDLE" "$STAGED.$$" && mv "$STAGED.$$" "$STAGED"; } echo "STAGED: $STAGED" ``` Remember the `STAGED:` path — every render below opens it (it stands in for ``). If `BUNDLE_MISSING`: stop and show the user the build command. Do not improvise a CDN fallback — offline is the contract. ## Step 3 — Render the triplet Write the mermaid source to `/.mmd` first (Write tool). ONE `gstack-render` call renders the whole triplet: it opens the staged bundle in the browser, waits for the page to finish loading (`#done`), runs the `--eval` expressions in order inside that page, and writes each result to the `--out` path that follows it. The page cannot read files itself, so ship the source in via **base64** — never splice file contents into a JS template literal (backticks, `${`, and backslashes in the source would be interpreted and corrupt it): ```bash SRC=$(base64 < /.mmd | tr -d '\n') bun run ~/.claude/skills/gstack/bin/gstack-render.ts "" --wait-selector '#done' \ --eval "window.__renderMermaid('diagram-1', atob('$SRC')).then(s => (window.__svg = s))" --out /.svg \ --eval "window.__rasterize(window.__svg, 1950)" --out /.png \ --eval "window.__mermaidToExcalidraw(atob('$SRC')).then(j => (window.__scene = j))" --out /.excalidraw ``` Always run all three `--eval`/`--out` pairs, whatever the diagram type. The PNG is 1950px wide (300dpi of a 6.5in placement). Success prints one `OK ` line per artifact. Read the output for two other lines: - A hard `ERROR:` line (e.g. `ERROR: render script did not finish: Error: Parse error on line 4: ...`) is a mermaid parse error. Nothing was copied out. Show the error to the user, fix the `.mmd`, and retry — do not hand the user a broken source file. - A `PAGE_ERRORS=[...]` entry containing `Error processing Mermaid diagram` means the excalidraw converter fell back to a single image element (Step 1's state/class/gantt case). The triplet is complete and correct; deliver the `.excalidraw` anyway with the note that it is not element-editable. Any OTHER `PAGE_ERRORS` text: read it before trusting the output. Note: `atob()` yields Latin-1; for sources with non-ASCII labels use `decodeURIComponent(escape(atob('…')))` to recover UTF-8 exactly. `gstack-render` picks the browser itself: Aside when it is running, otherwise gstack's own headless browser. Only when it prints `NEEDS_ASIDE` or `ASIDE_NOT_RUNNING` followed by `ERROR: no browser available` is there nothing to render with — Aside (macOS 15+, aside.com) is not open and gstack's browser is not built. Tell the user to open Aside, or to run `./setup` in the gstack repo to build the fallback, and stop. Never install Aside for them, and never substitute a CDN or another renderer. ## Step 4 — Show and deliver 1. Read the PNG with the Read tool so the user sees the diagram inline. 2. List the triplet paths. 3. One-line editability note: "The `.excalidraw` file opens at excalidraw.com (File → Open) — edit it there and I can re-render from the edited scene." 4. If the user wants changes, edit the `.mmd` source and re-run Step 3 — the source is the single source of truth. Re-rendering an EDITED `.excalidraw` (user round-trip): load the scene file and export without touching the mermaid — base64 transport again, since scene JSON is full of quotes and backslashes: ```bash SCENE=$(base64 < /.excalidraw | tr -d '\n') bun run ~/.claude/skills/gstack/bin/gstack-render.ts "" --wait-selector '#done' \ --eval "window.__excalidrawToSvg(atob('$SCENE')).then(s => (window.__svg = s))" --out /.svg \ --eval "window.__rasterize(window.__svg, 1950)" --out /.png ``` This path prints one benign `PAGE_ERRORS` entry — excalidraw's font subsetter falls back from a worker to the main thread inside the single-file bundle (`WorkerInTheMainChunkError`). The SVG/PNG are correct; ignore that one. ## Rules - **Never ship the triplet without rendering it.** A `.mmd` file alone is not a diagram. If rendering is impossible (bundle missing, no browser available), say so and stop. - For diagrams destined for a PDF: remind the user that `make-pdf` renders ` ```mermaid ` fences natively — embedding the `.mmd` in their markdown is better than embedding the PNG. ## Completion status - DONE — triplet delivered and shown (with the image-only note for non-flowchart, non-sequence types). - BLOCKED — bundle missing or no browser available; the build command, "open Aside", or "run ./setup" surfaced.