Skip to content

Commit 3ff91f0

Browse files
authored
improvement(docs): clean up leftovers from the code-block alignment PR (#6825)
* improvement(docs): clear leftovers from the reverted revisions A cleanup pass over the final state. Every finding was residue from an approach this PR tried and abandoned, or a claim that stopped being true when it did. - Delete the copy-button svg sizing rule: a later rule sets `display: none` on that same element ungated, so sizing it was never observable. Superseded by the mask approach. - Drop the paragraph in page.tsx arguing about a custom Shiki factory. The factory was deleted; nothing configures one now. - Correct shiki-curl-json.ts, which still claimed the grammar "reaches the client path too". It does not — that was the justification for choosing a grammar over a transformer, so leaving it stated the opposite of the truth. Now records where it applies, where it does not, and why not to retry. - Correct the global.css section header, which claimed the component owns the shell while the next rule defines it here. - Qualify the `--copy-glyph` declarations with `:has(> svg[class*="lucide"])`, which the group's own comment asserts of every rule in it. - Correct `getCode`'s TSDoc: the gutter is a `::before`, and pseudo-element content never reaches `textContent`, so line numbers were never what the clone guards. It guards transformer-emitted `.nd-copy-ignore` nodes. - Compose `chipGeometryClass` and emcn's `ChipChevronDown` in the API example selector instead of restating their literals. - Merge the duplicated `div[role="region"]` rule. The tablist pair stays split: biome's `noDuplicateProperties` reads a nested `@variant` setting the same property as a duplicate and fails the build — recorded so it is not remerged. - Note that fumadocs ships its own gutter for `lines`-meta fences, which cannot be suppressed from here and would paint a second column. * fix(docs): drop a highlighter registration that can never fire fumadocs-openapi calls `renderCodeBlock` with a hard-coded `"json"` from both of its call sites (`request-tabs.js:76`, `response-tabs.js:48`), so the docs `CodeBlock` it routes through never receives a shell language. The `getHighlighter('js', { langs: [curlJsonBodyGrammar] })` registering the shell-scoped JSON-body injection therefore did nothing but await on every API sample render, and the docblock claiming the grammar covers those samples was wrong. - Delete the call and its imports. - State the grammar's real coverage: prose fences only, via `langs`. Both API reference paths are unreachable — samples are JSON, and the cURL usage tabs highlight client-side off fumadocs' own factory. - Correct `code-block.tsx`'s TSDoc, which still said API samples come from fumadocs' own renderer. They come through this component; `UsageTab` is the renderer that bypasses it. - Re-home a comment orphaned when two CSS rules merged — it had drifted onto the rule below and read as documenting it. - Drop a `.nd-copy-ignore` claim about transformers emitting those nodes; nothing here does, and upstream parity is the reason the clone exists.
1 parent e522bc4 commit 3ff91f0

5 files changed

Lines changed: 61 additions & 58 deletions

File tree

apps/docs/app/[lang]/[[...slug]]/page.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type React from 'react'
2-
import { getHighlighter, highlight } from 'fumadocs-core/highlight'
2+
import { highlight } from 'fumadocs-core/highlight'
33
import type { Root } from 'fumadocs-core/page-tree'
44
import { findNeighbour } from 'fumadocs-core/page-tree'
55
import type { ApiPageProps } from 'fumadocs-openapi/ui'
@@ -18,7 +18,6 @@ import { Heading } from '@/components/ui/heading'
1818
import { ResponseSection } from '@/components/ui/response-section'
1919
import { i18n } from '@/lib/i18n'
2020
import { getApiSpecContent, getAuthenticatedCodeSamples, openapi } from '@/lib/openapi'
21-
import { curlJsonBodyGrammar } from '@/lib/shiki-curl-json'
2221
import { simShikiOptions } from '@/lib/shiki-theme'
2322
import { type PageData, source } from '@/lib/source'
2423
import { DOCS_BASE_URL } from '@/lib/urls'
@@ -77,17 +76,8 @@ function stripLocalePrefix(url: string, lang: string): string {
7776
* rather than fumadocs-openapi's built-in one, so those blocks get the emcn copy control
7877
* instead of fumadocs' lucide clipboard. Mirrors the default renderer — same `highlight` call,
7978
* same `Pre` component, same `my-0` — differing only in which shell wraps the result.
80-
*
81-
* One asymmetry: `highlight` resolves fumadocs' shared `defaultShikiFactory`, while the renderer
82-
* this replaces uses whatever `shiki` factory the page was configured with. They are the same
83-
* object because that factory is also the default; passing a custom one would be honored on API
84-
* markdown and ignored here.
8579
*/
8680
async function ApiCodeBlock({ lang, code }: { lang: string; code: string }) {
87-
// Registers the injection on the shared highlighter `highlight` resolves; an injection is a
88-
// property of the highlighter, not a per-call option. Idempotent — already-loaded grammars are
89-
// skipped.
90-
await getHighlighter('js', { langs: [curlJsonBodyGrammar] })
9181
return (
9282
<CodeBlock className='my-0'>
9383
{await highlight(code, { lang, ...simShikiOptions, components: { pre: Pre } })}

apps/docs/app/global.css

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,15 +1603,17 @@ main article tbody tr:last-child td {
16031603

16041604
/* Code blocks — platform field chrome.
16051605
1606-
The shell itself (radius, border, fill) is owned by components/ui/code-block.tsx, the way
1607-
an emcn component owns its chrome. What remains here is the styling of fumadocs internals
1608-
that component cannot reach: the title row it renders from a `title` prop, the scroll
1609-
viewport, and the tab strip that `<Tabs>` puts above a fence. */
1610-
1611-
/* Local aliases, not new design values: each is an existing platform token whose light and
1612-
dark halves differ. Naming the pair once lets the rules below be written without a `.dark`
1613-
twin, which would otherwise have to restate their `:has()` selectors — the double-`:has()`
1614-
the tabbed group is matched by, and the sibling-combinator one the gutter is matched by. */
1606+
Three renderers emit these figures and only two go through components/ui/code-block.tsx, so
1607+
the shell and the fumadocs internals that component cannot reach — the title row, the scroll
1608+
viewport, the tab strip `<Tabs>` puts above a fence — are all defined here. The component
1609+
keeps only the copy control and the prose margin. */
1610+
1611+
/* Local aliases, not new design values: each names an existing platform token pair whose light
1612+
and dark halves differ (`--surface-5`/`--code-bg`, `--text-muted`/`--code-line-number`).
1613+
1614+
`--code-surface` has three consumers. `--code-gutter` has one, and is an alias anyway because
1615+
a `.dark` twin would have to restate the sibling-combinator `:has()` its consumer is matched
1616+
by — the expensive form, which re-checks on every line insertion. */
16151617
:root {
16161618
--code-surface: var(--surface-5);
16171619
--code-gutter: var(--text-muted);
@@ -1689,10 +1691,9 @@ figure.shiki > div:first-child:has(figcaption) svg {
16891691
16901692
The `!important` and the viewport selector are belt-and-braces, not strictly required —
16911693
fumadocs' declaration is (0,2,0) and these selectors are (0,3,1) and (0,4,1), so they
1692-
already win, and nothing declares this property on the viewport. They are kept because
1693-
getting this wrong puts the line numbers on top of the code, which shipped once already, and
1694-
because the specificity of `:has()` and `:not()` is easy to miscount in exactly the
1695-
direction that reintroduces it. Remove them only alongside a visual check.
1694+
already win, and nothing declares this property on the viewport. They are kept because the
1695+
specificity of `:has()` and `:not()` is easy to miscount in the direction that puts the line
1696+
numbers on top of the code. Remove them only alongside a visual check.
16961697
*/
16971698
figure.shiki:has(.line ~ .line),
16981699
figure.shiki:has(.line ~ .line) > div[role="region"],
@@ -1733,6 +1734,9 @@ figure.shiki code:has(.line ~ .line) .line::before {
17331734
the code surface. The viewport is the one box all three renderers agree on. */
17341735
figure.shiki > div[role="region"] {
17351736
background-color: var(--code-surface);
1737+
/* fumadocs ships 14px of vertical padding, the platform's viewer 8px; 10px splits them and
1738+
keeps a single-line fence from looking hollow at the tighter 21px line box. */
1739+
padding-block: 10px;
17361740
}
17371741

17381742
/* Shiki emits `--shiki-*-bg` custom properties under `defaultColor: false`; keep the `pre` clear
@@ -1741,13 +1745,6 @@ figure.shiki pre {
17411745
background-color: transparent;
17421746
}
17431747

1744-
/* Viewport padding — fumadocs ships 14px vertical; the platform's viewer uses 8px. Split the
1745-
difference at 10px, which keeps a single-line fence from looking hollow at the tighter
1746-
21px line box. */
1747-
figure.shiki > div[role="region"] {
1748-
padding-block: 10px;
1749-
}
1750-
17511748
/* Untitled blocks float the copy control over the code, so the last column has to clear it:
17521749
an 8px offset plus emcn's 20px icon button, with room to breathe. fumadocs reserves 32px,
17531750
which the glyphs run into.
@@ -1802,12 +1799,6 @@ figure.shiki button[aria-label="Copied Text"]:has(> svg[class*="lucide"]) {
18021799
color 150ms;
18031800
}
18041801

1805-
figure.shiki button[aria-label="Copy Text"] > svg[class*="lucide"],
1806-
figure.shiki button[aria-label="Copied Text"] > svg[class*="lucide"] {
1807-
width: 14px;
1808-
height: 14px;
1809-
}
1810-
18111802
figure.shiki button[aria-label="Copy Text"]:has(> svg[class*="lucide"]) {
18121803
@variant hover-hover {
18131804
background-color: var(--surface-active);
@@ -1849,11 +1840,11 @@ figure.shiki button[aria-label$="Text"]:has(> svg[class*="lucide"])::before {
18491840
-webkit-mask-size: contain;
18501841
}
18511842

1852-
figure.shiki button[aria-label="Copy Text"] {
1843+
figure.shiki button[aria-label="Copy Text"]:has(> svg[class*="lucide"]) {
18531844
--copy-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1 -2 24 24' fill='none' stroke='%23000' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.25 0.75H2.75C1.64543 0.75 0.75 1.64543 0.75 2.75V14.25'/%3E%3Crect x='5.25' y='5.25' width='14' height='14' rx='2'/%3E%3C/svg%3E");
18541845
}
18551846

1856-
figure.shiki button[aria-label="Copied Text"] {
1847+
figure.shiki button[aria-label="Copied Text"]:has(> svg[class*="lucide"]) {
18571848
--copy-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1 -2 24 24' fill='none' stroke='%23000' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.25 2.75L7.25 15.75L1.75 10.25'/%3E%3C/svg%3E");
18581849
}
18591850

@@ -1901,6 +1892,9 @@ div:has(> div > figure.shiki) > [role="tablist"] button {
19011892
color 150ms;
19021893
}
19031894

1895+
/* Kept as its own rule rather than nested into the block above: biome's
1896+
`noDuplicateProperties` reads a nested `@variant` setting the same property as a duplicate
1897+
declaration and fails the build. */
19041898
div:has(> div > figure.shiki) > [role="tablist"] button {
19051899
@variant hover-hover {
19061900
color: var(--text-body);

apps/docs/components/ui/api-example-selector.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
import type { ComponentProps } from 'react'
44
import { useId } from 'react'
5-
import { chipFieldSurfaceClass, chipFieldTextClass, chipHoverSurfaceClass, cn } from '@sim/emcn'
6-
import { ChevronDown } from '@sim/emcn/icons'
5+
import {
6+
ChipChevronDown,
7+
chipFieldSurfaceClass,
8+
chipFieldTextClass,
9+
chipGeometryClass,
10+
chipHoverSurfaceClass,
11+
cn,
12+
} from '@sim/emcn'
713
import type { APIPageClientOptions } from 'fumadocs-openapi/ui/client'
814

915
type FumadocsAPIExampleSelector = NonNullable<
@@ -35,10 +41,11 @@ export function APIExampleSelector({ items, value, onValueChange }: APIExampleSe
3541
* screen-reader behavior a custom listbox would have to rebuild.
3642
*/
3743
className={cn(
44+
chipGeometryClass,
3845
chipFieldSurfaceClass,
3946
chipFieldTextClass,
4047
chipHoverSurfaceClass,
41-
'h-[30px] w-full appearance-none ps-2 pe-8 text-left'
48+
'w-full appearance-none pe-8'
4249
)}
4350
>
4451
{items.map((item) => (
@@ -47,7 +54,7 @@ export function APIExampleSelector({ items, value, onValueChange }: APIExampleSe
4754
</option>
4855
))}
4956
</select>
50-
<ChevronDown className='-translate-y-1/2 pointer-events-none absolute end-2 top-1/2 size-[14px] text-[var(--text-icon)]' />
57+
<ChipChevronDown className='-translate-y-1/2 pointer-events-none absolute end-2 top-1/2' />
5158
</div>
5259
{selectedItem?.description && (
5360
<p className='text-[var(--text-muted)] text-caption'>{selectedItem.description}</p>

apps/docs/components/ui/code-block.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,24 @@ function CopyButton({ getCode }: { getCode: () => string }) {
3333
}
3434

3535
/**
36-
* Docs code block for prose fences, wired into the MDX `pre` mapping.
36+
* Docs code block for prose fences and the API reference's request/response samples — the MDX
37+
* `pre` mapping and fumadocs-openapi's `renderCodeBlock` both render it.
3738
*
38-
* The shell — radius, hairline, fill — is not set here. Request and response samples in the
39-
* API reference come from fumadocs-openapi's own renderer, so the two share chrome through a
40-
* `figure.shiki` rule in `global.css` instead; see the note there. What stays here is the
41-
* part only this path has: the copy control, and the `my-4` prose rhythm that API samples,
42-
* which sit flush in their panel, must not inherit.
39+
* The shell — radius, hairline, fill — is not set here. A third renderer, fumadocs-openapi's
40+
* `UsageTab`, emits these figures without going through any component, so all three share
41+
* chrome through a `figure.shiki` rule in `global.css` instead; see the note there. What stays
42+
* here is the copy control, and the `my-4` prose rhythm that API samples, which sit flush in
43+
* their panel, override with `my-0`.
4344
*/
4445
export function CodeBlock({ title, ...props }: React.ComponentProps<typeof FumadocsCodeBlock>) {
4546
const figureRef = useRef<HTMLElement>(null)
4647

4748
/**
48-
* Reads the block's text the way fumadocs does: from a clone, with `.nd-copy-ignore` nodes
49-
* replaced by newlines. Those nodes carry rendered gutter and diff markers, so copying the
50-
* live `textContent` would paste line numbers along with the code.
49+
* Reads the block's text the way fumadocs' own `CopyButton` does: from a clone, with
50+
* `.nd-copy-ignore` nodes replaced by newlines — kept in step with upstream so a fence that
51+
* gains such a node copies the same text there and here. (The line-number gutter is a
52+
* `::before`, and pseudo-element content never reaches `textContent`, so it is not what this
53+
* guards.)
5154
*/
5255
function getCode() {
5356
const pre = figureRef.current?.getElementsByTagName('pre').item(0)
@@ -65,8 +68,9 @@ export function CodeBlock({ title, ...props }: React.ComponentProps<typeof Fumad
6568
className={cn('my-4', props.className)}
6669
allowCopy={false}
6770
/**
68-
* fumadocs' own `className` is discarded rather than merged — its untitled-block variant
69-
* carries a `backdrop-blur-lg` that goes milky over an opaque fill.
71+
* The `className` fumadocs passes this render prop is deliberately neither destructured nor
72+
* merged — its untitled-block variant carries a `backdrop-blur-lg` that goes milky over an
73+
* opaque fill.
7074
*/
7175
Actions={() => (
7276
<div className={cn('flex items-center', title ? '-me-1' : 'absolute top-2 right-2 z-[1]')}>

apps/docs/lib/shiki-curl-json.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@ import type { LanguageRegistration } from 'shiki'
1414
* when it owns the opening brace. Entering mid-string, keys keep `string.quoted.double.json`
1515
* and stay string-colored, which is the entire difference this exists to remove. Hence the
1616
* hand-written patterns below, which name that scope directly.
17-
* - **A Shiki transformer.** A transformer can re-tokenize the body correctly, but it is a
18-
* function, and the API reference's request tabs highlight in the browser off a `shikiOptions`
19-
* object passed through RSC — where "Functions cannot be passed directly to Client
20-
* Components". A grammar is plain data, so it reaches the client path too.
17+
* - **A Shiki transformer.** A transformer re-tokenizes the body correctly, but it is a function,
18+
* and `shikiOptions` is forwarded into a client component — "Functions cannot be passed
19+
* directly to Client Components" takes down every API reference page. A grammar is plain data,
20+
* so it survives that boundary.
21+
*
22+
* Applies to prose fences only, via `langs` on the MDX pipeline. Not the API reference:
23+
* fumadocs-openapi calls `renderCodeBlock` with a hard-coded `"json"` for request and response
24+
* samples, so a shell injection can never fire there, and its cURL usage tabs highlight in the
25+
* browser off fumadocs' own factory — `ClientCodeBlockProvider` sits in a `"use client"` module
26+
* the package does not expose through its `exports` map, so reaching it means importing
27+
* `fumadocs-openapi/ui/base` from client code and dragging `remark` and
28+
* `@fumari/json-schema-ts` into the browser bundle. That broke the deployment once.
2129
*
2230
* The opening brace requires a `}`, a quoted key, or end-of-line after it. That is what keeps
2331
* `awk '{print $1}'` out, while still matching a body whose brace ends the line — Oniguruma

0 commit comments

Comments
 (0)