Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion site/scripts/check-dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
14 changes: 14 additions & 0 deletions site/scripts/check-navigation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
14 changes: 2 additions & 12 deletions site/src/components/api/ApiEntry.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 ?? []

/**
Expand Down
113 changes: 0 additions & 113 deletions site/src/components/api/ApiExampleTabs.astro

This file was deleted.

46 changes: 0 additions & 46 deletions site/src/pages/reference/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { ApiModel, ApiSymbol } from '@libtmux/api-model'
import { compareMembers, conceptsFor, docSummaryText, memberSignals, moduleOf, modulesIn, sourceUrl as apiSourceUrl, symbolsForProduct } from '@libtmux/api-model'
import mentionsData from '../../data/mentions.json'
import ApiEntry from '../../components/api/ApiEntry.astro'
import ApiExampleTabs from '../../components/api/ApiExampleTabs.astro'
import PageToolbar from '../../components/PageToolbar.astro'
import { pagePortsFor } from '../../lib/page-ports-for'
import { pageBreadcrumbs } from '../../lib/sidebar'
Expand Down Expand Up @@ -265,43 +264,6 @@ const counterparts = model
})
: []

/**
* The same example in each language that has one, this port first.
*
* Only where the concept map knows a counterpart — an example is a claim that
* two pieces of code do the same thing, and nothing else here can make it.
*
* And only where *this* page's language has one to make the claim with.
* Without that gate a symbol with no example of its own still got an
* "Examples" heading, under which every tab was another language:
* `/reference/ts/server-server/` opened on Python doctest output. A reader on
* a TypeScript page asked for TypeScript, and a Python-only answer under that
* heading reads as the TypeScript example rather than as a substitute for a
* missing one. An example is an offer to copy, and there was nothing there to
* copy.
*/
const ownExamples = owner?.doc?.examples ?? []
const exampleGroups = ownExamples.length === 0 ? [] : [
{ port: model?.port ?? '', examples: ownExamples, index, context: owner },
...counterparts.flatMap((c) => {
const id = conceptEntries.map((x) => x.symbols[c.port]).find((v) => typeof v === 'string')
const otherModel = API_MODELS[c.port]
const other = id && otherModel
? otherModel.symbols.find((sym) => (sym.publicId ?? sym.id) === id)
: undefined
if (!other?.doc?.examples?.length || !otherModel) return []
// That port's own index, not this page's: the intro is written in the
// counterpart's doc dialect and names the counterpart's symbols, so
// resolving it here would link the wrong page or, more often, nothing.
return [{
port: c.port,
examples: other.doc.examples,
index: indexFor(otherModel, (sym) => referenceHref(c.port, sym.publicId ?? sym.id) ?? withRoot('/reference/')),
context: other,
}]
}),
]

const sourceUrl = (symbol: ApiSymbol): string | undefined => model ? apiSourceUrl(model, symbol) : undefined

const members = owner && model ? model.symbols.filter((s) => s.parent === owner.id).sort(byUsefulness) : []
Expand Down Expand Up @@ -814,17 +776,9 @@ const breadcrumbs = await pageBreadcrumbs(title, slug ? `reference/${slug}` : 'r
port={model?.port}
heading={OWNER_KINDS.has(owner.kind)}
member={!OWNER_KINDS.has(owner.kind)}
examples={exampleGroups.length <= 1}
/>
</div>

{exampleGroups.length > 1 && (
<section class="mt-8">
<h2 class="text-lg font-semibold">Examples</h2>
<ApiExampleTabs groups={exampleGroups} name={`ex-${owner.slug ?? 'x'}`} />
</section>
)}

<p class="mt-2 text-sm opacity-70">
{declared.length} declared, {inherited.length} inherited
</p>
Expand Down
75 changes: 0 additions & 75 deletions site/src/styles/api.css
Original file line number Diff line number Diff line change
Expand Up @@ -360,81 +360,6 @@
text-decoration: underline;
}

/*
* Cross-language example tabs, driven by radios rather than script.
*
* The radios sit before the panels so a sibling selector can reach them, and
* are clipped rather than `display: none` — a hidden input is not focusable,
* which would take the keyboard away from a control that is otherwise
* perfectly operable with it.
*/
.api-example-tabs {
margin-top: 0.75rem;
border: 1px solid var(--color-background-border, #e2e8f0);
border-radius: 0.375rem;
overflow: hidden;
}

.api-example-tabstrip {
display: flex;
flex-wrap: wrap;
background: var(--color-background-secondary, #f8f9fb);
border-bottom: 1px solid var(--color-background-border, #e2e8f0);
}

.api-example-radio {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(50%);
overflow: hidden;
}

.api-example-tab {
padding: 0.4rem 0.85rem;
font-size: 0.85em;
cursor: pointer;
border-bottom: 2px solid transparent;
opacity: 0.65;
}

.api-example-radio:checked + .api-example-tab {
opacity: 1;
color: var(--theme-accent);
border-bottom-color: var(--theme-accent);
}

.api-example-radio:focus-visible + .api-example-tab {
outline: 2px solid var(--focus-ring-color);
outline-offset: -2px;
}

.api-example-panel {
display: none;
padding: 0.75rem 0.9rem;
}

/* The panels hold Expressive Code blocks, which carry their own theming, so
nothing here reads Shiki's custom properties any more. What the panel does
own is the gap: Expressive Code's own block margin would double the
padding this panel already supplies. */
.api-example-panel > .expressive-code {
margin: 0;
}

.api-example-panel > .expressive-code + .expressive-code,
.api-example-panel > .api-example-intro + .expressive-code {
margin-top: 0.75rem;
}

/* Each radio reveals the panel at its own position. Generated in the
component's own style block, since the count varies per instance. */
.api-example-intro {
margin: 0 0 0.5rem;
font-size: 0.9em;
opacity: 0.85;
}

/* A language with no mapped counterpart: shown, so its absence is a fact
rather than a gap in a list that varies page to page. */
.api-lang-absent {
Expand Down
Loading