-
Notifications
You must be signed in to change notification settings - Fork 1
diagrams
- src/repodocs/diagrams.py
- tests/test_fix_diagrams.py
repodocs.diagrams is an optional, clone-time post-processing step that finds every ```mermaid fenced block in the generated wiki pages, renders it to a pastel PNG using an external Bun + Playwright tool (tools/diagram_poster.ts), and rewrites the page to embed the image instead of the raw fence. GitHub's built-in wiki mermaid renderer is described as intermittently failing to load, so a committed PNG is used as a more reliable fallback. This module is not part of the zero-dependency Python core and requires Bun to be installed.
Sources: src/repodocs/diagrams.py:L1-L20
tool_path() resolves tools/diagram_poster.ts relative to the repodocs source checkout (three parents up from diagrams.py) and returns None if that file doesn't exist — which is the case when running from an installed wheel that didn't ship the dev tool.
Sources: src/repodocs/diagrams.py:L23-L27
cmd_render_diagrams(repo, out) is the CLI entry point. It calls tool_path() and dies via die() if the tool is missing, and separately checks shutil.which("bun") to die with an install hint (https://bun.sh, then cd tools && bun install and bunx playwright install chromium) if Bun itself isn't on PATH.
Sources: src/repodocs/diagrams.py:L95-L102
Mermaid blocks are located with a single compiled regex, _MERMAID_RE = re.compile(r"```mermaid\n(.*?)```", re.DOTALL), matching non-greedily across newlines so multiple blocks in one page are each captured separately.
Sources: src/repodocs/diagrams.py:L20-L20
process_page(md, out, tool) reads the page, finds all mermaid blocks via _MERMAID_RE.finditer, and returns (0, 0) immediately if none are found. Otherwise it resolves a title with md_title(text) (falling back to md.stem), then for each block builds a poster YAML via _poster_yaml and attempts a render via _render_png, collecting either a PNG filename (f"{md.stem}-diagram-{i}.png") or None on failure into the pngs list.
A replacement pass then substitutes each match: if the corresponding pngs[k] entry is None, the original mermaid fence is returned unchanged ("leave the raw mermaid block"); otherwise it emits an image embed , where the caption is "{title} — diagram {k+1}" when there is more than one block, or "{title} diagram" for a single block. The function returns (rendered_count, failed_count).
Sources: src/repodocs/diagrams.py:L65-L92

_poster_yaml(title, slug, mermaid, i, n) builds a minimal YAML document consumed by diagram_poster.ts. It sets a kicker of "REPODOCS · {slug.upper()}", appending " · DIAGRAM {i}/{n}" when there is more than one diagram on the page (n > 1). Text scalars (title, kicker, headline, footer right) are wrapped with json.dumps so titles containing quotes or colons remain valid YAML. The mermaid source itself is emitted as a literal block scalar (mermaid: |) with each line indented by two spaces.
Sources: src/repodocs/diagrams.py:L30-L43
_render_png(tool, out, stem, i, yaml_text) writes the YAML to out/<stem>-diagram-<i>.yaml, then runs bun <tool> <yaml_path> --png via subprocess.run. It considers the render successful only if the process returncode is 0 and the expected PNG (<stem>-diagram-<i>.png) exists and is non-empty. On failure with stderr output, it prints the last line of stderr prefixed with the PNG's basename. The intermediate .yaml and .html files are always removed in a finally block, regardless of success.
Sources: src/repodocs/diagrams.py:L46-L62
cmd_render_diagrams iterates every *.md file in out except index.md (sorted), calling process_page on each. It logs a per-page note ("{r} rendered", plus ", {f} failed" when applicable) via log() whenever a page had any rendered or failed blocks, accumulates totals, prints a final summary line, and returns exit code 1 if any diagram failed across all pages, 0 otherwise.
Sources: src/repodocs/diagrams.py:L95-L114
Failures are handled per-block, not per-page: a page can have some diagrams successfully replaced with image embeds while others remain as raw mermaid fences, since the replacement logic in process_page checks each block's pngs[k] entry independently. This is exercised directly in the test suite — test_process_page_keeps_block_on_render_failure stubs _render_png to always return False and asserts the mermaid fence stays in the output ("unrendered block stays honest").
Sources: tests/test_fix_diagrams.py:L23-L28, src/repodocs/diagrams.py:L83-L89
tests/test_fix_diagrams.py stubs dg._render_png via monkeypatch so the tests exercise process_page and _poster_yaml without needing Bun or Playwright installed:
| Test | Behavior verified |
|---|---|
test_process_page_swaps_rendered_block |
A single successfully rendered block becomes  and the mermaid fence is removed |
test_process_page_keeps_block_on_render_failure |
A failed render leaves the raw ```mermaid fence in place |
test_process_page_numbers_multiple_blocks |
Multiple blocks on one page are captioned "P — diagram 1" / "P — diagram 2" and numbered sequentially in filenames |
test_poster_yaml_indents_mermaid_and_numbers_multi |
_poster_yaml emits "DIAGRAM 2/3" in the kicker and two-space-indents the mermaid literal block |
Sources: tests/test_fix_diagrams.py:L1-L46
- Home
- Installation & Setup
- Architecture
- CLI Reference
- LLM Backend Selection
- Repository Scanning
- Wiki Page Planning
- Page Generation
- Source Citations
- Translation
- HTML Rendering
- Diagram Rendering
- Diagram Poster Tool
- Git Remote Link Resolution
- Publishing
- GitHub Wiki Integration
- Shared Utilities
- Environment Configuration
- Security & Trust Boundaries
- Limitations & Non-goals
- Testing
- Development
- Contributing
- Upgrading
- Changelog