Skip to content

feat: measurementCss option to stop label clipping (v0.3.0) - #1

Merged
EricAndrechek merged 2 commits into
mainfrom
fix/measurement-css-label-clipping
Jun 9, 2026
Merged

EricAndrechek merged 2 commits into
mainfrom
fix/measurement-css-label-clipping

Conversation

@EricAndrechek

Copy link
Copy Markdown
Member

What

Adds a measurementCss config option: arbitrary CSS injected into the build-time render page (alongside the inlined @font-face) so Mermaid measures labels with the same metrics the host stylesheet displays them at.

Why

Mermaid sizes each node's <foreignObject> by measuring the label in the build-time Chromium; the browser then displays it. Any drift where display is wider than measurement crops the last glyph — the classic Buffer Consumer → Buffer Consume clip.

font.woff2 inlining (v0.1.1) closed the font half of the drift (measuring with Arial instead of Inter). But it left the label-metrics half open: if your CSS renders node labels at font-weight: 500 (or any letter-spacing), Mermaid still measured them at the default 400, so every box came out ~1px too narrow and the longest label in each node clipped on the right.

Verified empirically against mermaid-isomorphic@3.1.0: a node measured at weight 400 produced a foreignObject width="112.48", while the same label displays at weight 500 at 112.81px — a sub-pixel overflow that crops the final glyph across every node.

How

measurementCss is appended after the @font-face in the injected data-URL stylesheet. Callers pass the bare-selector form of their label rules:

themedMermaid({
  font: { family: '"Inter Variable", sans-serif', woff2: "/abs/inter.woff2" },
  measurementCss:
    ".nodeLabel p{font-weight:500;letter-spacing:-0.005em;padding-inline:1.5px;}",
});

The one gotcha (documented in README + the type)

Selectors must not include the svg[aria-roledescription…] ancestor. Mermaid measures the label before it's parented by the final flowchart <svg>, so the scoped form matches at display time but is a no-op at measure time. Use bare .nodeLabel p. Confirmed by probe: the scoped selector left the measured box unchanged; the bare selector widened it to exactly the displayed width.

Changes

  • index.mjs — measurementCss config; CSS assembly refactored to a cssParts array (@font-face + measurementCss); css option omitted when neither is set.
  • index.d.ts — typed measurementCss?: string with the bare-selector caveat.
  • README.md — new Label clipping section + config-table row.
  • test/plugin.test.mjs — 3 tests: appended-after-font, works-without-woff2, omitted-when-empty.
  • package.json — 0.2.0 → 0.3.0.

node --test: 15/15 green. Backward compatible — no behavior change unless measurementCss is set.

🤖 Generated with Claude Code

…0.3.0)

Mermaid sizes each node box by MEASURING the label in the build-time
Chromium, then the browser DISPLAYS it. If the host stylesheet renders
labels at a heavier weight (or any letter-spacing) than the default 400
the boxes were measured at, every box is ~1px too narrow and the last
glyph clips (`Buffer Consumer` -> `Buffer Consume`). The `font.woff2`
inlining (v0.1.1) fixed the FONT half of this drift; this adds the
label-METRICS half.

New `measurementCss` config: CSS injected into the build-time render page
alongside the @font-face, so the build measures labels with the same
weight / letter-spacing / padding the browser shows them at. Selectors
must be BARE (`.nodeLabel p`), not `svg[aria-roledescription…] .nodeLabel
p` — Mermaid measures the label before that wrapper exists, so the scoped
form is a no-op at measure time.

Also injects measurementCss when no woff2 is given, and omits the `css`
option entirely when neither is set (rehype-mermaid keeps its defaults).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@EricAndrechek, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 41 minutes and 41 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e964c0a2-7810-4b4a-bd5c-9e2760f7973a

📥 Commits

Reviewing files that changed from the base of the PR and between 7c7e0ff and e3c368f.

📒 Files selected for processing (1)
  • README.md
📝 Walkthrough

Walkthrough

This PR adds a measurementCss configuration option to themedMermaid() that injects build-time CSS into Mermaid's label measurement page. The implementation refactors CSS data URL creation to use a cssParts accumulator, combining optional @font-face rules with caller-supplied measurement CSS into a single data URL passed to rehypeMermaidOptions.

Changes

Measurement CSS Configuration

Layer / File(s) Summary
Configuration type and contract
index.d.ts, README.md
ThemedMermaidConfig interface adds measurementCss?: string field with documentation. README config table documents the new option for injecting label-metric CSS at measure time.
CSS accumulation and data URL injection
index.mjs
Parameter JSDoc and configuration destructuring add measurementCss support. CSS data URL creation refactors from a dedicated fontCssDataUrl variable to a cssParts accumulator that collects @font-face (when font.woff2 exists) and measurementCss, building a single data URL conditionally passed as the css option to rehypeMermaidOptions.
Feature documentation and guidance
README.md
New "Label clipping" section explains label-measure/display drift during Mermaid SSR and documents how measurementCss provides matching build-time inputs using unscoped CSS selectors.
Test coverage for measurementCss behavior
test/plugin.test.mjs
Three unit tests verify measurementCss appends after @font-face when woff2 is provided, injects as sole CSS payload when no font is supplied, and is omitted when neither font nor measurementCss is configured.
Package version update
package.json
Version bumped from 0.2.0 to 0.3.0.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature addition: a new measurementCss option that addresses label clipping, with version bump to v0.3.0.
Description check ✅ Passed The description comprehensively explains the what, why, and how of the measurementCss feature, with technical details, empirical validation, usage examples, and documented gotchas—directly aligned with the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/measurement-css-label-clipping
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/measurement-css-label-clipping

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

26-26: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update install tag to v0.3.0.

pnpm add github:Wave-RF/astro-themed-mermaid#v0.2.0 points to the previous release, so users won’t get measurementCss. Update it to #v0.3.0.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ff8c2e90-940e-4a7b-a418-f83d974a2162

📥 Commits

Reviewing files that changed from the base of the PR and between 938f765 and 7c7e0ff.

📒 Files selected for processing (5)
  • README.md
  • index.d.ts
  • index.mjs
  • package.json
  • test/plugin.test.mjs
📜 Review details
🔇 Additional comments (4)
index.d.ts (1)

30-38: LGTM!

README.md (1)

138-183: LGTM!

index.mjs (1)

57-63: LGTM!

Also applies to: 74-75, 81-142

test/plugin.test.mjs (1)

42-74: LGTM!

Comment thread package.json Resolved
The install example still pinned #v0.2.0, so users following the README
would miss the new measurementCss option. (CodeRabbit catch.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EricAndrechek added a commit to Wave-RF/WaveHouse that referenced this pull request Jun 8, 2026
…agram zoom

Addresses the live-demo PR feedback round (diagrams + preview comment):

- Mermaid node labels clipped their last glyph on the live preview
  (`Buffer Consumer` -> `Buffer Consume`): the build-time Chromium measured
  each box at the default font-weight 400 while global.css displays labels
  at weight 500, so every box was ~1px too narrow. Fixed upstream in
  astro-themed-mermaid via a new `measurementCss` option (v0.3.0) that lets
  the consumer inject its label metrics into the render page so Mermaid
  measures what the browser displays. mermaid-theme.mjs passes the node
  label weight/letter-spacing + a 1.5px safety pad and resets cluster
  titles (overflow:visible pills) to default. Verified 0/116 nodes clip.

- `.diagram-pair` no longer flips to a side-by-side row at >=1500px; the
  DIY-vs-WaveHouse comparison stacks vertically at full content width
  (the side-by-side variant shrank each diagram to ~450px on wide monitors).

- Click-to-zoom lightbox for every diagram (MermaidZoom.astro): small
  diagrams scale up, wide ones show at natural size with pan-scroll. View-
  transition-safe; the clone is re-id'd so its id-scoped <style> survives.

- AGENTS.md gains a top-level "Authoring Mermaid diagrams" directive:
  favour vertical (TB/TD) over horizontal (LR) so diagrams fit the page.

- Richer docs-preview sticky comment: the head commit's subject (linked)
  and the upload time, so a stale preview is obvious (ci.yml).

The astro-themed-mermaid pin is the v0.3.0 fix commit
(Wave-RF/astro-themed-mermaid#1, draft pending your merge); switch the pin
to #v0.3.0 once that PR is merged and tagged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EricAndrechek added a commit to Wave-RF/WaveHouse that referenced this pull request Jun 8, 2026
## What

This branch rebuilds the docs landing experience around a **live**
`@wavehouse/sdk` consumer and, in the process, turns the docs from "a
polished landing bolted onto stock Starlight" into one coherent,
branded, accessible system. Four loosely-coupled workstreams plus the
fixes the review gate shook loose:

1. **Live-demo hero panel** — the landing page now renders real data
from [stats.wavehouse.dev](https://stats.wavehouse.dev).
2. **Site-wide design-system pass** — one radius scale, one button
shape, AA-correct light theme, branded Starlight chrome, a wide-screen
layout that uses the space.
3. **Mermaid diagrams** — fixed the label-clipping bug, made big
diagrams readable (vertical layout + click-to-zoom), and set a house
style.
4. **Docs-accuracy corrections** + **backend/test fixes** surfaced by
getting `make ci` honestly green and 7 rounds of dual-reviewer
self-review.

A focused **"Where this deviates from Astro/Starlight defaults"**
section is near the bottom, since that's easy to lose track of.

---

## 1. Live-demo hero panel (`docs/src/components/LiveDemo.astro`, new)

The hero's visual column is a real SDK consumer against the public
read-only role on the WaveHouse-Stats dogfood deployment
(`PUBLIC_WAVEHOUSE_STATS_URL`-overridable):

- **Live counts** — stars, forks, events-over-7d (168h), open
issues/PRs, lifetime total — from **one cached `gh_summary` pipe call**.
`setCount()` writes every `[data-wh-live="<key>"]` on the page, so the
same fetch also feeds the hero star chip and the closer-band chip.
- **Live activity feed** — backfilled from the server-curated/deduped
cached `gh_activity_recent` pipe, then a live SSE `stream()` tail. The
**stream connects before the backfill** so nothing lands in the gap; a
composite-key dedup absorbs the overlap and webhook redeliveries.
- **Live "wow" metrics** — `⚡ ingested → live in ~N ms` (per-event,
30-sample median, skew-clamped → Stats#33) and an **events/min
sparkline** (`gh_events_per_minute` pipe → Stats#34).
- **Three-tier graceful degradation**: `gh_summary` → per-number pipes;
`gh_activity_recent` → a structured-query backfill; both gone → a
layout-stable SSR skeleton with an "offline" pill. A stats-stack
rollback makes the panel slower, never blank.
- **SSE normalization** (load-bearing): live rows are raw producer
payloads, so `normTs()` converts zone-less ClickHouse timestamps →
ISO-8601 UTC; the wire filter is type-only (an `action` IN-filter would
drop rows whose `action` is absent), with a `VERBS` allowlist as the
final arbiter.
- **View-transition lifecycle**: per-mount re-init + teardown (SSE
`close()`, all timers). `is:global` styles because feed rows are
runtime-created DOM.

`Hero.astro` drops `terminalLines`, the `.wh-terminal` block, and the
`.wh-hero__mesh` SVG; the `pnpm add` hint becomes a **click-to-copy
command chip**; the "View on GitHub" `↗` glyph finally renders
(Starlight parses `icon: external` into an object, so the old string
compare never matched).

Built on the upstream pipes filed + merged on WaveHouse-Stats: **#18**
(`gh_activity_recent`), **#19** (`gh_summary`). Per-visitor load is 2
cached, singleflight-collapsed pipe hits + SSE (was 3 uncached reads +
SSE).

## 2. Site-wide design-system / UI consistency pass
(`docs/src/styles/global.css`)

A measured audit (every page × both themes, computed-style harness)
drove a unification:

- **Radius** — ~11 ad-hoc values collapsed to one scale
(`--radius-xs/sm/md/lg/pill` = 4/8/12/16/999px). All buttons 8px;
cards/panels/code-frames/tables 12px (no more 1px frame lip).
- **Buttons** — the same CTA rendered three ways; the closer-band pills
are now 8px rectangles matching the hero.
- **Light theme (AA)** — accent-as-text was 2.6:1; new
`--wh-accent-text` role (6:1) rewired through
links/sidebar/focus-ring/stats. Status hues (amber/emerald/rose) got
light overrides; page bg stepped darker so white cards read as layers
(was 1.04:1, flat).
- **Layout** — `--sl-content-width` is now a responsive track (45rem →
50rem ≥72rem → 58rem ≥100rem; prose held to 46rem so text stays ~70–75ch
while code/tables/diagrams use the width). `.main-frame` caps + centers
at ≥100rem, killing ~590px of one-sided dead space at 2560px. **Zero
horizontal overflow across 14 pages × 8 widths (320–2560) × both
themes.**
- **Grid background** — was a hero-only shrinking center-blob, invisible
in light; now two page-level fixed layers (full-bleed signal-field grid
+ breathing hero glow), visible in both themes, scoped via
`body:has(.wh-hero)`. Required
`body:has(.wh-hero){background:transparent}` to defeat Starlight's
layered opaque body background.
- **Starlight chrome** — pagination, tables, blockquotes, search
trigger, and the sidebar (group eyebrows + hover + current item) are now
branded. (The old sidebar selector `…nav > ul > li > a` matched 0
elements — the DOM has no nested `nav` — so it was a silent no-op until
rewritten.)

## 3. Mermaid diagrams — clipping fix, vertical layout, click-to-zoom

The most visible regressions you flagged on the preview:

- **Label clipping (`Buffer Consumer` → `Buffer Consume`).** Root cause:
the build-time Chromium measures each node's box, then the browser
displays it — and `global.css` renders node labels at **font-weight
500** while Mermaid measured them at the default **400**, so every box
came out ~1px too narrow and the longest label in each node clipped on
the right. Present on `main` too (across all 116 nodes), which is why it
kept coming back. Fixed **upstream** in
[`astro-themed-mermaid`](https://github.com/Wave-RF/astro-themed-mermaid)
(new `measurementCss` option, v0.3.0) that injects the consumer's label
metrics into the render page so Mermaid measures what the browser shows.
`mermaid-theme.mjs` passes the node-label weight/letter-spacing + a
1.5px safety pad; selectors are bare (`.nodeLabel p`) because Mermaid
measures before the flowchart `<svg>` wrapper exists. **Verified 0/116
nodes clip** after the fix.
- **Diagrams "side-by-side and hard to see."** The diagrams themselves
are byte-identical to `main` (same source, same Mermaid config, no
version bump) — nothing in the geometry regressed. What you were seeing
is the `.diagram-pair` wrapper (the DIY-vs-WaveHouse comparison): it
flipped to a **side-by-side row at ≥1500px**, shrinking each diagram to
~450px on a wide monitor. That ≥1500px variant is **removed** — the pair
now always stacks vertically at full content width.
- **Click-to-zoom** (`MermaidZoom.astro`, new) — clicking any diagram
opens it in a lightbox: small diagrams scale up (capped 2.5×), wide ones
show at natural size with pan-scroll for full detail. Esc / backdrop /
button closes; keyboard-focusable (Enter). View-transition-safe (one
delegated `document` listener, re-enhanced on `astro:page-load`). The
clone is re-id'd so its id-scoped inline `<style>` (font, themed fills)
and `url(#…)` refs keep working.
- **House style** — `AGENTS.md` §"Authoring Mermaid diagrams": author
vertically (`TB`/`TD`) over horizontal (`LR`) so diagrams fit the page;
never sit two large diagrams side-by-side; keep labels short.

> **Cross-repo note:** the `astro-themed-mermaid` dependency is pinned
to the v0.3.0 **fix commit** while
[Wave-RF/astro-themed-mermaid#1](Wave-RF/astro-themed-mermaid#1)
(draft) awaits a human merge — the PR-discipline gate blocks an agent
from publishing/merging it. Once it's merged and tagged, the pin moves
from the SHA to `#v0.3.0`.

## 4. Docs-accuracy corrections (prose, surfaced by the review gate)

Each is a code↔docs accuracy fix: all ten `?table=` curls quoted
(unquoted `?` aborts on zsh before curl runs — the first runnable
landing command was one); `/v1/admin/query` example gains its missing
`Authorization` header; the silent 10k `limit` cap, `time_range` `until`
semantics, and admin-only callouts + error tables documented across
`/v1/schema*` and `/v1/dlq/stats`; the DLQ payload corrected to the
`EventMessage` envelope (+`X-DLQ-*` headers); `wh.dlq.stream()` marked
non-functional (#197); the fictional "SSE/WS fan-out" removed from
`ingest-pipeline.md`; schema-refresh 404 caveats in the quickstarts; and
the false "secretless ⇒ no token validates / pure public deployment"
framing removed from `api.md` / `access-control.md` / `configuration.md`
(see #291 below).

## 5. Backend / test fixes (surfaced by the gate)

- **`fix(mq)` #287** — embedded NATS installed its own SIGINT handler
(`NoSigs` unset), racing graceful shutdown → panic (exit 2, coverage
counters never flushed → 0.0% gate) or `os.Exit(0)` past all defers.
Both option literals get `NoSigs: true`; both owners
`WaitForShutdown()`.
- **`fix(e2e)` 30s orchestrator budget** — removing the NATS early-exit
exposed a ~15s OTel exporter dial-backoff on clean shutdown (filed
**#288**); the old 10s budget SIGKILLed before the coverage flush.
- **Test de-flakes (cherry-picked from #283)** — batching size-trigger
re-anchored to `startTime`; the 1ms deadline-500 test rewrapped in
`waitForCondition`.

## 6. Docs preview / tooling

- **Sticky preview comment** (`ci.yml`) now shows the **commit subject
(linked)** and **upload time (UTC)** alongside the URL, so a stale
preview is obvious at a glance.
- `screenshot.mjs`: `networkidle` → `load` (the SSE stream never goes
idle); the screenshot-ready signal moved to the visual column's
entrance, independent of live data.
- `@wavehouse/sdk` is a docs **workspace dep**; `check-docs` /
`dev-docs` gained a `build-ts` prerequisite.

---

## Where this deviates from Astro/Starlight defaults

The bits a future reader (or Starlight upgrade) needs to know are
non-stock:

**Config (`docs/astro.config.mjs`)**
- **Component overrides** — `Hero`, `SiteTitle`, `Footer`, `Head` are
all replaced with local components. `SiteTitle` renders a custom
`<WaveMark/>` (theme-aware `currentColor`) and Starlight's `logo` is
deliberately omitted so it never renders; `Footer` re-renders
Starlight's `EditLink`/`LastUpdated`/`Pagination` itself (overriding
`Footer` drops them) and now also mounts `<MermaidZoom/>`; `Head`
live-swaps the theme-adaptive favicon.
- **Expressive Code** — `borderRadius: calc(0.75rem - 1px)` (so the
frame's outer corner lands at exactly 12px; EC adds the border width and
derives inner corners from the same base), custom
`codeFontFamily`/`uiFontFamily`, `frames.shadowColor`, themes pinned to
`github-dark`/`github-light`, and `shiki.langAlias` (`env→bash`,
`dns→ini`).
- **Markdown pipeline** — `syntaxHighlight.excludeLangs: ["mermaid"]`;
remark `[remarkMath, mermaid.remarkInjectClassdefs]`; rehype
`[mermaid.rehypeMermaid, rehypeKatex]`. Mermaid is the in-house
[`astro-themed-mermaid`](https://github.com/Wave-RF/astro-themed-mermaid)
integration (build-time SVG via headless Chromium + a per-diagram disk
render cache), themed entirely from CSS variables.
- **Plugins beyond stock** — `starlight-image-zoom`,
`starlight-llm-tools`, and `starlight-links-validator` (**conditionally
dropped** when `WAVEHOUSE_DOCS_WATCH` is set, so the dev rebuild loop
doesn't fail on a mid-edit dangling link).
- **Other** — `trailingSlash: "never"`; explicit favicon set + full Open
Graph / Twitter image meta that Starlight doesn't emit; `customCss` =
the design system + KaTeX; custom `sidebar`; `lastUpdated`; Tailwind 4
via `@tailwindcss/vite` inside a Starlight site.

**CSS-level Starlight overrides (`global.css`)** — remapped `--sl-*`
tokens (`--sl-color-text-accent`, the responsive `--sl-content-width`,
`--sl-sidebar-width`); branded
pagination/tables/blockquotes/search-trigger/sidebar; `.main-frame`
ultrawide cap; and the `body:has(.wh-hero){background:transparent}`
workaround for Starlight's layered opaque body background.

**Deploy** — the site is **not** built by Cloudflare Workers Builds (no
headless browser there for Mermaid); it ships from the CI tail steps
(`wrangler deploy` on main, `wrangler versions upload` per PR), and a
`cloudflare-md-router` Worker provides `.md` content negotiation.
Workers Builds must stay disconnected from the Worker or it
double-deploys.

---

## ⚠️ Security finding filed, not fixed here — #291

The docs gate surfaced a pre-existing auth-forgery vulnerability: a
secretless deployment (the default compose quickstart, no
`WH_AUTH_JWT_SECRET`) verifies HMAC tokens against `[]byte("")`, so
anyone can forge `{"role":"admin"}` signed with the empty string and
reach `/v1/admin/query`. Verified against golang-jwt v5.3.1. Per
maintainer decision it's tracked in **#291** for a security-reviewed
fail-closed fix and kept **out of this docs PR** — which only removes
the sentences that restated the masking "secretless ⇒ no token
validates" claim.

## Verification

- `make ci` green locally; `astro check` + full docs build green;
markdownlint/biome/typecheck clean.
- Mermaid: 0/116 node labels clip after the fix (measured on the rebuilt
site); click-to-zoom verified open/clone/Esc across pages; diagram-pair
confirmed vertical.
- Live demo: headless Playwright across three data paths (pipes-live,
new-pipes-blocked, full blackout) + the 10 viewport×theme sweep,
no-overflow, view-transition round-trip.

## Follow-ups

- Merge + tag
[Wave-RF/astro-themed-mermaid#1](Wave-RF/astro-themed-mermaid#1)
as **v0.3.0**, then switch the docs pin from the SHA to `#v0.3.0`.
- #291 (auth fail-closed fix), #286 (query-builder playground), #288
(OTel shutdown backoff).
- WaveHouse-Stats #20 (embed capacity / SSE fan-out), #21 (stats page as
public destination).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@EricAndrechek
EricAndrechek marked this pull request as ready for review June 9, 2026 11:50
@EricAndrechek
EricAndrechek merged commit 6f1d631 into main Jun 9, 2026
1 check passed
@EricAndrechek
EricAndrechek deleted the fix/measurement-css-label-clipping branch June 9, 2026 13:44
EricAndrechek added a commit that referenced this pull request Jun 9, 2026
## Summary

Adds a `mermaidConfig` escape-hatch to `themedMermaid(config)` so
callers can pass arbitrary **non-color** Mermaid settings (e.g. `gantt`,
`er`, `pie`, `htmlLabels`, `maxTextSize`) without this module having to
enumerate each one.

The caller's `mermaidConfig` is spread **beneath** the module's own
settings inside `rehypeMermaidOptions.mermaidConfig` — the module's
`theme`/`themeVariables`/`fontFamily`/`securityLevel`/`flowchart`/`sequence`
are written *after* the spread, so they always win. This preserves the
**color-agnostic invariant** (#1) and the `securityLevel` floor (#9): a
caller cannot override the theme/color/security settings through this
hatch.

Because the new key lands inside `rehypeMermaidOptions`, which is
already hashed into `diagramKey`, escape-hatch settings invalidate the
render cache automatically (invariant #4) — no stale-render risk.

### Changes
- **`index.mjs`** — accept `config.mermaidConfig` (default `{}`);
`...mermaidConfig` spread first in the `mermaidConfig` literal, module
keys after; `@param` JSDoc added.
- **`index.d.ts`** — `mermaidConfig?: Record<string, unknown>` with a
JSDoc note (non-color only; module settings win).
- **`README.md`** — Config-table row + a one-line note. Also corrected
the adjacent "factory returns **three** things" return-value table to
**four** (it omitted the recommended cached `rehypeMermaid` drop-in) — a
pre-existing accuracy gap surfaced by docs review.
- **`test/plugin.test.mjs`** — two `node:test` guards.

## Test plan
- `pnpm run verify` (Biome check + `node --test`) — green, **17/17**
tests pass.
- New guard (a): `themedMermaid({ mermaidConfig: { gantt: { useWidth:
1200 } } }).rehypeMermaidOptions.mermaidConfig.gantt` deep-equals `{
useWidth: 1200 }` — passthrough flows through.
- New guard (b): `themedMermaid({ mermaidConfig: { theme: "dark",
securityLevel: "loose" } })` still yields `theme: "base"` and
`securityLevel: "strict"` — the module wins.
- `/prepush`: `pre-push-reviewer` → ship_it, `docs-reviewer` → ship_it
(0 findings each).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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