From 8ffa008777c635491fb4e14315d4a9f1ec83fedd Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Wed, 30 Sep 2026 18:26:02 -0500 Subject: [PATCH] fix(site) Keep API examples in the selected port why: A TypeScript reference page included Python examples below its own snippet, despite already offering a port switcher in the toolbar. what: - Render only the selected symbol's own examples - Remove the unused cross-language example tab component and styles - Check a freshly rendered TypeScript capture reference for foreign code while retaining the port switcher verification: The browser check fails against the old page with four Python blocks. The outer loop passes in 58.13 seconds, below its 60-second budget. --- site/scripts/check-dev.mjs | 8 +- site/scripts/check-navigation.mjs | 14 +++ site/src/components/api/ApiEntry.astro | 14 +-- site/src/components/api/ApiExampleTabs.astro | 113 ------------------- site/src/pages/reference/[...slug].astro | 46 -------- site/src/styles/api.css | 75 ------------ 6 files changed, 23 insertions(+), 247 deletions(-) delete mode 100644 site/src/components/api/ApiExampleTabs.astro diff --git a/site/scripts/check-dev.mjs b/site/scripts/check-dev.mjs index 1c3066aa..4d09bb3c 100644 --- a/site/scripts/check-dev.mjs +++ b/site/scripts/check-dev.mjs @@ -7,7 +7,7 @@ import { dev } from 'astro' import { chromium, firefox, webkit } from 'playwright' import { PORTS, productAvailable } from '../src/lib/ports.ts' import { checkClipboard } from './check-clipboard.mjs' -import { checkApiNavigation, checkNavigation } from './check-navigation.mjs' +import { checkApiExampleOwnership, checkApiNavigation, checkNavigation } from './check-navigation.mjs' import { checkNativeLayout } from './check-native-layout.mjs' const workspacePortCount = PORTS.filter((port) => productAvailable(port, 'workspace')).length @@ -333,6 +333,12 @@ try { }) server = await startServer() await checkApiNavigation(page, `http://127.0.0.1:${server.address.port}/en`) + await server.stop() + Object.assign(process.env, { + LIBTMUX_DOCS_PORT: 'ts', LIBTMUX_DOCS_BASE: '/en/ts/latest/', + }) + server = await startServer() + await retryReload(() => checkApiExampleOwnership(page, `http://127.0.0.1:${server.address.port}/en`)) } finally { await browser?.close() await server.stop() diff --git a/site/scripts/check-navigation.mjs b/site/scripts/check-navigation.mjs index 27f933fb..1430f5b4 100644 --- a/site/scripts/check-navigation.mjs +++ b/site/scripts/check-navigation.mjs @@ -138,3 +138,17 @@ export async function checkNavigation(page, base) { } console.log('Redirects: legacy query/section links and the JavaScript-disabled fallback pass') } + +/** Port references keep examples in the selected language. */ +export async function checkApiExampleOwnership(page, base) { + await page.goto(`${base}/ts/latest/reference/pane-pane-capture/`, { waitUntil: 'load' }) + const languages = await page.locator('main pre[data-language]').evaluateAll((blocks) => + blocks.map((block) => block.getAttribute('data-language'))) + assert(languages.length > 0, 'The capture reference keeps its own example') + assert(languages.every((language) => language === 'typescript' || language === 'ts'), + `TypeScript reference contains another language: ${languages.join(', ')}`) + assert.equal(await page.locator('main .api-example-tabs').count(), 0) + assert.equal(await page.locator('[data-page-port-switcher]').count(), 1, + 'Readers can still switch ports from the page toolbar') + console.log('Reference examples: TypeScript examples stay visible without other languages') +} diff --git a/site/src/components/api/ApiEntry.astro b/site/src/components/api/ApiEntry.astro index 99a7dac8..e24d70b7 100644 --- a/site/src/components/api/ApiEntry.astro +++ b/site/src/components/api/ApiEntry.astro @@ -53,18 +53,8 @@ interface Props { * bases belong here too. */ heading?: boolean - /** - * Suppress this entry's own examples. - * - * The reference page renders a language-switching Examples section below - * the entry when a counterpart port carries the same example. Left on, the - * page's own language then appeared twice — inline here and as the first - * tab there. The caller knows which of the two is showing; this entry does - * not. - */ - examples?: boolean } -const { symbol, index, sourceUrl, port, alternativesFor, heading = false, member = false, examples: showExamples = true } = Astro.props as Props +const { symbol, index, sourceUrl, port, alternativesFor, heading = false, member = false } = Astro.props as Props const anchor = symbol.publicId ?? symbol.id const kind = symbol.kind @@ -138,7 +128,7 @@ const prename = heading && anchor.endsWith(symbol.name) const bases = heading ? ((symbol as { extends?: string[] }).extends ?? []) : [] -const examples = showExamples ? (symbol.doc?.examples ?? []) : [] +const examples = symbol.doc?.examples ?? [] const references = symbol.doc?.references ?? [] /** diff --git a/site/src/components/api/ApiExampleTabs.astro b/site/src/components/api/ApiExampleTabs.astro deleted file mode 100644 index b17c0faa..00000000 --- a/site/src/components/api/ApiExampleTabs.astro +++ /dev/null @@ -1,113 +0,0 @@ ---- -import type { ApiSymbol, SymbolIndex } from '@libtmux/api-model' -import { Code } from 'astro-expressive-code/components' -import { codeLang } from '../../lib/highlight' -import { PORT_NAME } from '../../lib/api-models' -import ApiDoc from './ApiDoc.astro' - -/** - * The same example, in each language that has one. - * - * learn.microsoft.com puts a C#/VB/F# switcher on its example blocks, and the - * reason it works is that the examples are the same example — one task, told - * in several languages. The concept map already records which symbols are - * counterparts, so where a counterpart carries examples they belong here - * rather than on a page the reader has to go and find. - * - * The page's own language is always the first group; the caller drops the - * whole section when it has none, because a language switcher whose every tab - * is a language the reader did not ask for is not a switcher. - * - * CSS-only tabs: a radio per language, labels styled as the tab strip, and - * the checked radio revealing its panel through a sibling selector. No script, - * so it works on a page with none, and no shared state, so two of these on a - * page do not fight. The radios are visually hidden rather than absent — - * removing them takes the keyboard with them. - * - * The blocks themselves are Expressive Code, the same component every fenced - * block on this site renders through. They used to be raw Shiki markup with a - * stylesheet of their own, which is why an example here had a different - * typeface, a different size and no copy button — a second code system on a - * page that already had one. - * - * The intro above each block goes through `ApiDoc`, so a cross-reference in it - * links, the way it does in every other piece of doc-comment prose on the - * page. It used to render as raw text, backticks and all. - */ -interface Props { - /** - * Panels in display order; the first is the page's own language. - * - * Each carries the symbol index its intros resolve against, because a - * counterpart's prose is written in another port's dialect and names that - * port's symbols: resolving `` `Server.sessions` `` on a Rust panel against - * the Python index would link the wrong page or, more often, nothing. - */ - groups: { - port: string - examples: { lang: string; code: string; intro?: string }[] - index: SymbolIndex - context?: ApiSymbol - }[] - /** Distinguishes this instance's radio group from any other on the page. */ - name: string -} -const { groups, name } = Astro.props as Props - -/* - * One rule per position: the nth checked radio shows the nth panel. - * - * Counted inside their own wrapper, not among the tab strip's siblings: the - * strip is a `div` too, so `.api-example-panel:nth-of-type(1)` matched *it* - * and the first tab showed nothing at all. Generated because the count varies - * with how many languages carry an example. - */ -const panelRules = groups - .map( - (_, i) => - `.api-example-tabs:has(.api-example-radio:nth-of-type(${i + 1}):checked) ` + - `.api-example-panels > :nth-child(${i + 1}){display:block}`, - ) - .join('') ---- - -