From d06b360b1d3ca825776d152ef1aaae8d9b97c07a Mon Sep 17 00:00:00 2001 From: Waleed Date: Tue, 9 Dec 2025 21:58:54 -0800 Subject: [PATCH] fix(docs): fix copy page button and header hook (#2284) --- apps/docs/app/[lang]/[[...slug]]/page.tsx | 11 +++- apps/docs/app/global.css | 6 ++ apps/docs/app/llms.mdx/[[...slug]]/route.ts | 12 +++- .../copy-page-button.tsx => page-actions.tsx} | 37 +++++------- apps/docs/components/ui/heading.tsx | 58 +++++++++++++++++++ apps/docs/package.json | 1 + bun.lock | 2 + 7 files changed, 103 insertions(+), 24 deletions(-) rename apps/docs/components/{ui/copy-page-button.tsx => page-actions.tsx} (65%) create mode 100644 apps/docs/components/ui/heading.tsx diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index f20d2604c..9fc1e7c3b 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -6,9 +6,10 @@ import Link from 'next/link' import { notFound } from 'next/navigation' import { PageNavigationArrows } from '@/components/docs-layout/page-navigation-arrows' import { TOCFooter } from '@/components/docs-layout/toc-footer' +import { LLMCopyButton } from '@/components/page-actions' import { StructuredData } from '@/components/structured-data' import { CodeBlock } from '@/components/ui/code-block' -import { CopyPageButton } from '@/components/ui/copy-page-button' +import { Heading } from '@/components/ui/heading' import { source } from '@/lib/source' export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) { @@ -202,7 +203,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
- +
@@ -214,6 +215,12 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l components={{ ...defaultMdxComponents, CodeBlock, + h1: (props) => , + h2: (props) => , + h3: (props) => , + h4: (props) => , + h5: (props) => , + h6: (props) => , }} /> diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css index 2a3effa4a..2968717af 100644 --- a/apps/docs/app/global.css +++ b/apps/docs/app/global.css @@ -2,6 +2,12 @@ @import "fumadocs-ui/css/neutral.css"; @import "fumadocs-ui/css/preset.css"; +/* Prevent overscroll bounce effect on the page */ +html, +body { + overscroll-behavior: none; +} + @theme { --color-fd-primary: #802fff; /* Purple from control-bar component */ --font-geist-sans: var(--font-geist-sans); diff --git a/apps/docs/app/llms.mdx/[[...slug]]/route.ts b/apps/docs/app/llms.mdx/[[...slug]]/route.ts index 2f143fdc4..2a58b9567 100644 --- a/apps/docs/app/llms.mdx/[[...slug]]/route.ts +++ b/apps/docs/app/llms.mdx/[[...slug]]/route.ts @@ -1,5 +1,6 @@ import { notFound } from 'next/navigation' import { type NextRequest, NextResponse } from 'next/server' +import { i18n } from '@/lib/i18n' import { getLLMText } from '@/lib/llms' import { source } from '@/lib/source' @@ -7,7 +8,16 @@ export const revalidate = false export async function GET(_req: NextRequest, { params }: { params: Promise<{ slug?: string[] }> }) { const { slug } = await params - const page = source.getPage(slug) + + let lang: (typeof i18n.languages)[number] = i18n.defaultLanguage + let pageSlug = slug + + if (slug && slug.length > 0 && i18n.languages.includes(slug[0] as typeof lang)) { + lang = slug[0] as typeof lang + pageSlug = slug.slice(1) + } + + const page = source.getPage(pageSlug, lang) if (!page) notFound() return new NextResponse(await getLLMText(page), { diff --git a/apps/docs/components/ui/copy-page-button.tsx b/apps/docs/components/page-actions.tsx similarity index 65% rename from apps/docs/components/ui/copy-page-button.tsx rename to apps/docs/components/page-actions.tsx index 880600d45..7c86c5392 100644 --- a/apps/docs/components/ui/copy-page-button.tsx +++ b/apps/docs/components/page-actions.tsx @@ -1,55 +1,50 @@ 'use client' import { useState } from 'react' +import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button' import { Check, Copy } from 'lucide-react' const cache = new Map() -interface CopyPageButtonProps { +export function LLMCopyButton({ + markdownUrl, +}: { + /** + * A URL to fetch the raw Markdown/MDX content of page + */ markdownUrl: string -} - -export function CopyPageButton({ markdownUrl }: CopyPageButtonProps) { - const [copied, setCopied] = useState(false) +}) { const [isLoading, setLoading] = useState(false) - - const handleCopy = async () => { + const [checked, onClick] = useCopyButton(async () => { const cached = cache.get(markdownUrl) - if (cached) { - await navigator.clipboard.writeText(cached) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - return - } + if (cached) return navigator.clipboard.writeText(cached) setLoading(true) + try { await navigator.clipboard.write([ new ClipboardItem({ 'text/plain': fetch(markdownUrl).then(async (res) => { const content = await res.text() cache.set(markdownUrl, content) + return content }), }), ]) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } catch (err) { - console.error('Failed to copy:', err) } finally { setLoading(false) } - } + }) return (