Repository navigation
feat: measurementCss option to stop label clipping (v0.3.0) - #1
Conversation
…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>
|
Warning Review limit reached
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 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. 📝 WalkthroughWalkthroughThis PR adds a ChangesMeasurement CSS Configuration
🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
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. Comment |
There was a problem hiding this comment.
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 winUpdate install tag to v0.3.0.
pnpm add github:Wave-RF/astro-themed-mermaid#v0.2.0points to the previous release, so users won’t getmeasurementCss. 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
📒 Files selected for processing (5)
README.mdindex.d.tsindex.mjspackage.jsontest/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!
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>
…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>
## 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>
## 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>
What
Adds a
measurementCssconfig 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 classicBuffer Consumer→Buffer Consumeclip.font.woff2inlining (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 atfont-weight: 500(or anyletter-spacing), Mermaid still measured them at the default400, 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 aforeignObject width="112.48", while the same label displays at weight 500 at112.81px— a sub-pixel overflow that crops the final glyph across every node.How
measurementCssis appended after the@font-facein the injected data-URL stylesheet. Callers pass the bare-selector form of their label rules: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—measurementCssconfig; CSS assembly refactored to acssPartsarray (@font-face+measurementCss);cssoption omitted when neither is set.index.d.ts— typedmeasurementCss?: stringwith 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 unlessmeasurementCssis set.🤖 Generated with Claude Code