Docs: build-time mermaid + tmux-layout diagram rendering#1071
Merged
Conversation
why: The intro framed the feature as a set of config "keys" to set, which is the implementation surface, not what a workspace builder means to the reader. Users meet the YAML knobs last, not first. what: - Open by defining what a workspace builder is and that it works out of the box, so most readers can stop after the first paragraph - Use progressive disclosure: default -> builder options (pane_readiness) -> swap builders -> write your own in Python - Name the cost/benefit of the prompt wait explicitly - Fold the narrative cross-reference onto "write your own" and drop the redundant standalone pointer below the table
why: The intro now points advanced readers at libtmux as the layer a custom builder drives, but left it as bare text with no way to get there. what: - Link the first libtmux mention to https://libtmux.git-pull.com/, matching the plain-link style used in topics/library-vs-cli.md
why: After the intro was rewritten to address the reader directly and
lead with the concept, the per-setting sections still read as terse
reference blurbs ("Selects the builder", "A catalog of..."), and one
still framed pane_readiness as a "key".
what:
- Rewrite the workspace_builder, workspace_builder_paths, and
workspace_builder_options leads in second person, present tense
- Drop the remaining "key" framing in favor of naming the setting
- Leave the resolver order list, value table, alias list, and error
text untouched — reference precision stays as-is
why: The docs/ prose voice (concept before configuration, reader addressed directly, advanced parts marked opt-in) lived only in one page's git history. what: - Add docs/AGENTS.md describing the docs/ prose voice and audience, scoped as a complement to the root AGENTS.md - Exclude AGENTS.md from the Sphinx build so it is not an orphan doc
why: Claude Code reads CLAUDE.md; the symlink lets it pick up the same docs/ voice guidance without a second copy to keep in sync. what: - Symlink docs/CLAUDE.md -> AGENTS.md, mirroring the repository root - Exclude CLAUDE.md from the Sphinx build alongside AGENTS.md
why: Diagrams must paint with the page — instant, no layout shift, no staggered pop-in — and must survive gp-sphinx SPA swaps. Client-side mermaid.run() loses on every count: async render means CLS and a flash of source, and spa-nav.js takes its View-Transition snapshot before the SVG exists. Rendering to inline SVG at build time satisfies all of it with zero per-page JavaScript, and the swapped .article-container then carries finished SVG through SPA navigation with no re-render. what: - Add docs/_ext/mermaid_inline.py: a `mermaid` directive that defers to an HTML write-phase visitor, which shells to mmdc, content-hash-caches per theme, normalizes the SVG (unique id replacing mermaid's fixed "my-svg", explicit width/height from the viewBox, stripped max-width), and inlines light+dark variants; degrades to a text fallback with a one-time warning when the renderer is absent - Add docs/_static/css/gp-diagram.css: dual-SVG light/dark toggle on body[data-theme], intrinsic sizing, wide-diagram scroll wrapper - Wire docs/conf.py: register the extension, route plain mermaid fences via myst_fence_as_directive, exclude node_modules and the render cache from the Sphinx source tree - Pin the renderer locally (docs/package.json + pnpm lock/workspace); gitignore docs/node_modules and docs/_mermaid_cache - Add tests/test_docs_mermaid.py: NamedTuple-parametrized, test_id-first coverage of the normalizer, digest determinism, dual-theme emission, cache idempotency, missing-renderer fallback, and the setup() contract
why: The concept-first intro tells the reader a workspace builder "turns a workspace configuration into a live tmux session". A small pipeline diagram makes that sentence visual right where it is stated, before the reader reaches the reference table. what: - Add a left-to-right mermaid flowchart after the opening paragraph: `tmuxp load <workspace-file>` -> Workspace Builder -> Attach tmux session, with edge labels echoing the intro vocabulary
why: The left-to-right flowchart rendered ~1141px wide and scaled down hard in the content column, shrinking its labels. A top-down layout is ~276px wide, fits the column at full size, and reads as a vertical load -> build -> attach pipeline. what: - Switch the mermaid flowchart from LR to TD
why: At mermaid's default wrappingWidth (200px) the "tmuxp load <workspace-file>" node wrapped onto two lines. Raising it keeps node labels on a single line so each box sizes to its own text. what: - Set flowchart.wrappingWidth=500 via in-source mermaid frontmatter on the workspace-builders fence
why: Diagrams render at build time via mmdc + a headless Chrome (docs/_ext/mermaid_inline.py). The docs publish job had only uv and just, so it would silently degrade every diagram to a text fallback and ship docs without them. what: - Set up Node.js and pnpm in the docs build job - Cache ~/.cache/puppeteer keyed on docs/pnpm-lock.yaml - Install the docs JS deps and provision the version-matched Chrome via `pnpm -C docs rebuild puppeteer` before building
why: Diagrams rendered with mermaid's stock default/dark presets, whose purple boxes clashed with the site brand. Mapping mermaid's base-theme variables to gp-furo's tokens makes diagrams match the docs in both light and dark mode, with no extra page weight. what: - Add light/dark _PALETTES from gp-furo-tokens: brand-blue borders, near-background fills, foreground text, and the furo system font stack - Render via `mmdc -c` (theme=base + themeVariables) instead of the -t default/dark presets; bump the cache version to re-render - Test that each palette defines the required flowchart colour variables
why: _discover_chrome matched only puppeteer's Linux cache layout (chrome-linux64), so on macOS or Windows the build could not find an installed Chrome and would degrade diagrams to a text fallback. what: - Add _chrome_glob(platform): the puppeteer-cache glob per OS (linux, macOS .app bundle, win64), doctested - Resolve via _chrome_glob(sys.platform) in _discover_chrome
why: The diagram read uniformly: the literal command `tmuxp load <workspace-file>` looked like the prose concept nodes, and the edge-label background boxes hugged their text with no breathing room. what: - Inject theme-agnostic themeCSS into every rendered diagram: nodes tagged `:::cmd` render in the furo monospace stack so commands read as code (over the existing code-like fill), and edge labels gain padding; white-space:normal keeps a padded label wrapping instead of overflowing the box mermaid measured for it - Tag the `tmuxp load` node with `:::cmd` on the workspace-builders page - Bump the render cache version to re-render
why: On the rendered page (not in standalone mmdc output) the :::cmd label fell left because the build's headless Chrome lacks the exact monospace face, so the measured box is wider than the on-page text; and each edge label showed two stacked boxes once padded, because mermaid backs every edge label with three nested coloured elements. what: - Replace the themeCSS constant with _theme_css(theme): center node labels, and collapse mermaid's .labelBkg / .edgeLabel / rect backgrounds into a single padded chip on .edgeLabel p in the theme's label colour - Bump the render cache version to re-render
why: The :::cmd label rendered left-of-centre on browsers whose monospace face is narrower than the build's headless Chrome (e.g. macOS SF Mono): the shrink-to-fit table-cell sits at the left of the wider box mermaid measured. CSS that resizes the label to compensate (width/flex) corrupts mermaid's build-time measurement pass and ballooned the diagram to ~15000px tall. Separately, the render cache keyed only on source+theme, so a themeCSS/palette change served a stale SVG from docs/_mermaid_cache (which rm -rf docs/_build does not clear). what: - Center node labels with `display: table` + `margin: 0 auto`: shrink-to-fit positioning that re-centres without changing the measured size; verified centered even when the label renders far narrower than its box - Fold the full mermaid config (themeVariables + themeCSS) into the cache digest via a new `extra` arg so any styling change busts the cache; extract _mermaid_config(); _render now takes the config JSON - Update the cache-idempotency test for the new _render signature/theme
why: The example pane layouts used aafig ASCII art, which renders outside the build-time mermaid pipeline and ignores the site theme. mermaid's block-beta diagrams map directly onto tmux pane grids and inherit the furo palette, light/dark variants, and SPA handling. what: - Replace the four `.. aafig::` blocks (short-hand, 2/3/4 panes) with ```mermaid block-beta diagrams reproducing each layout - aafig is still used by docs/about_tmux.md, so the extension stays
why: block-beta diagrams render with a negative viewBox origin (e.g. `viewBox="-5 -97 148 194"`) and carry inner-element viewBoxes. The size regex assumed `viewBox="0 0 ..."` and matched an inner `0 0 10 10`, so the root SVG was sized 10x10 and the diagram rendered as a tiny icon. what: - Anchor the viewBox regex to the root `<svg>` tag and accept a negative min-x/min-y, taking width/height from the 3rd/4th numbers - Add a normalizer test case + doctest for the block-diagram viewBox
why: block-beta panes rendered with gaps, so a tmux pane layout read as separate boxes rather than a single tiled window. what: - Set block.padding=0 in the mermaid config so block panes share dividers and fill a contiguous rectangle, like a tmux window - Other diagram types ignore the block key
why: The example pane layouts used aafig ASCII art, then block-beta — neither shows real tmux geometry or matches the site's code styling. A purpose-built renderer draws faithful tmux layouts that read like the terminal, with no headless-browser dependency. what: - Add docs/_ext/tmux_layout.py: a `tmux-layout` directive taking a screen size and a named layout (even-vertical/horizontal, main-vertical/ horizontal, tiled) plus per-pane shell commands. It computes filling pane geometry and emits one inline SVG: panes tile with single dividers, text top-left, pygments-highlighted in the site's code font and colours (light/dark) via furo's own .highlight rules and `fill: currentColor`. Command builtins stay default-coloured; a gp prompt leads each line. Pure Python, theme-adaptive, no JS, SPA-safe - Add docs/_static/css/gp-tmux-layout.css: pane background = the pygments code background, code font-size, kerning/letter-spacing matching `pre` - Convert the examples (short-hand, 2/3/4 panes) from block-beta to tmux-layout; register the extension in conf.py - Add tests/test_docs_tmux_layout.py: NamedTuple-parametrized coverage of the geometry (fills the screen), highlighting, pane splitting, size parsing, SVG structure, and the setup() contract
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1071 +/- ##
=======================================
Coverage 82.56% 82.56%
=======================================
Files 31 31
Lines 2770 2770
Branches 518 518
=======================================
Hits 2287 2287
Misses 346 346
Partials 137 137 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
why: Record the v1.74.0 docs update for the changelog. what: - Add a Documentation entry for the build-time, theme-aware diagrams on the workspace-builders and examples pages
This was referenced Jul 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mermaiddirective renders fenced diagrams to inline SVG during the Sphinx build (viammdc), so diagrams paint with the page — no client-side mermaid runtime, no layout shift, no staggered pop-in, and they survive gp-sphinx's SPA navigation as live DOM.tmux-layoutdirective that draws real tmux pane layouts as inline SVG in pure Python — the aafig successor, tmux-aware. Authors declare a screen size and a named layout; panes tile the screen with single dividers, top-left shell commands, pygments-highlighted in the site's code font and colours..highlightrules, so they look native in either theme.docs/AGENTS.md(a docs-prose voice guide, symlinked fromdocs/CLAUDE.md) and rewrite the workspace-builders intro to lead with the concept rather than the config keys.mmdc+ headless Chrome); without it the build still succeeds but degrades a diagram to its source text with a single warning.Changes by area
Diagram extensions
docs/_ext/mermaid_inline.py:mermaiddirective → HTML write-phase visitor that shells tommdc, content-hash-caches per theme, normalizes the SVG (unique id, explicitwidth/heightfrom theviewBox, strippedmax-width), and inlines light+dark variants toggled by CSS. Falls back to a<pre>with a one-time warning when the renderer is absent.docs/_ext/tmux_layout.py:tmux-layoutdirective. Computes filling geometry for tmux's named layouts (even-vertical/horizontal,main-vertical/horizontal,tiled) and emits one inline SVG — panes tile with single dividers, text top-left, pygments shell highlighting reusing furo's.highlightrules viafill: currentColor. Pure Python, no headless browser.docs/_static/css/gp-diagram.css,gp-tmux-layout.css: theme-adaptive styling (light/dark), code-block-matched fonts and backgrounds.Docs content & voice
docs/configuration/workspace-builders.md: concept-first intro, atmuxp load → Workspace Builder → Attach tmux sessionflow diagram,libtmuxlinked.docs/configuration/examples.md: the pane-layout examples now usetmux-layout(faithful tmux geometry) instead of ASCII art.docs/AGENTS.md+docs/CLAUDE.md(symlink): the docs prose voice guide.Toolchain & CI
.github/workflows/docs.yml: Node/pnpm setup, a Puppeteer-browser cache, and a version-matched Chrome install before the docs build.docs/package.json+ pnpm lock/workspace: pin@mermaid-js/mermaid-cli.docs/node_modulesand the render cache are gitignored.Design decisions
mermaid.run()renders after paint (layout shift + flash + per-diagram stagger) and, with gp-sphinx's synchronous View-Transition swap, the diagram would flash in after the crossfade. Build-time inline SVG paints with the page and rides SPA swaps untouched. The cost — owned explicitly — is a headless-Chrome build dependency (mmdc); there is no pure-Node path, since mermaid needs real browser text metrics.tmux-layoutis a Python directive, not a mermaid extension: tmux layouts are pure geometry, where mermaid's browser-based layout engine is all cost and no benefit. A Python renderer needs no browser, and reusing furo's.highlightCSS makes the panes pixel-identical to the site's code blocks in both themes — which baked-in mermaid colours can't do.Test plan
uv run ruff check . --fix && uv run ruff format .— cleanuv run mypy .— cleanuv run py.test --reruns 0— green, including newtests/test_docs_mermaid.pyandtests/test_docs_tmux_layout.py(NamedTuple-parametrized: SVG normalization, render cache idempotency, missing-renderer fallback, tmux geometry fills the screen, shell highlighting, thesetup()contracts)just build-docs— succeeds with no new warnings against the existing baseline; diagrams render as inline themed SVG on the workspace-builders and examples pagesSetup required
The docs build environment must provide the diagram renderer, or mermaid diagrams degrade to text:
pnpm -C docs installpnpm -C docs rebuild puppeteer(installs the version-matched headless Chrome)The
docs.ymlworkflow already does this for CI.