diff --git a/apps/docs/src/components/page-actions.tsx b/apps/docs/src/components/page-actions.tsx index a35373c8a6..818c28fdaf 100644 --- a/apps/docs/src/components/page-actions.tsx +++ b/apps/docs/src/components/page-actions.tsx @@ -19,6 +19,36 @@ import { cva } from 'class-variance-authority'; const cache = new Map(); +function toIndexMarkdownUrl(markdownUrl: string): string | null { + if (!markdownUrl.endsWith('.mdx')) return `${markdownUrl.replace(/\/$/, '')}/index.mdx`; + + const withoutExtension = markdownUrl.slice(0, -'.mdx'.length); + if (withoutExtension.endsWith('/index')) return null; + + return `${withoutExtension}/index.mdx`; +} + +async function fetchMarkdownWithFallback(markdownUrl: string): Promise<{ content: string; resolvedUrl: string }> { + const res = await fetch(markdownUrl); + if (res.ok) { + return { content: await res.text(), resolvedUrl: markdownUrl }; + } + + const fallbackUrl = toIndexMarkdownUrl(markdownUrl); + if (!fallbackUrl) { + throw new Error(`Failed to fetch markdown from ${markdownUrl} (${res.status})`); + } + + const fallbackRes = await fetch(fallbackUrl); + if (fallbackRes.ok) { + return { content: await fallbackRes.text(), resolvedUrl: fallbackUrl }; + } + + throw new Error( + `Failed to fetch markdown from ${markdownUrl} (${res.status}) and ${fallbackUrl} (${fallbackRes.status})`, + ); +} + export function LLMCopyButton({ /** * A URL to fetch the raw Markdown/MDX content of page @@ -29,7 +59,8 @@ export function LLMCopyButton({ }) { const [isLoading, setLoading] = useState(false); const [checked, onClick] = useCopyButton(async () => { - const cached = cache.get(markdownUrl); + const fallbackUrl = toIndexMarkdownUrl(markdownUrl); + const cached = cache.get(markdownUrl) ?? (fallbackUrl ? cache.get(fallbackUrl) : undefined); if (cached) return navigator.clipboard.writeText(cached); setLoading(true); @@ -37,11 +68,11 @@ export function LLMCopyButton({ try { await navigator.clipboard.write([ new ClipboardItem({ - 'text/plain': fetch(markdownUrl).then(async (res) => { - const content = await res.text(); + 'text/plain': fetchMarkdownWithFallback(markdownUrl).then(({ content, resolvedUrl }) => { cache.set(markdownUrl, content); + cache.set(resolvedUrl, content); - return content; + return new Blob([content], { type: 'text/plain' }); }), }), ]);