import * as React from "react" import { createPortal } from "react-dom" import { Icon } from "@icones/react" import { getDocHref, getDocPackages, getDocSectionLabel, getDocsHomeHref, resolveDocLocation, } from "../content/registry" import type { DocLocation, DocPage, DocTocItem, } from "../content/types" import { Actions, Container, HomeButton, SlashLine } from "../components/layout" import { mdxComponents } from "../components/mdx" import { useLocale, useTranslate } from "../lib/locale" const noTocItems: readonly DocTocItem[] = [] export default function ArticlePage() { const t = useTranslate() const docLocation = resolveDocLocation(window.location.pathname) const visibleTocIds = useVisibleTocIds(docLocation?.page.toc ?? noTocItems) const [mobilePanel, setMobilePanel] = React.useState< "navigation" | "toc" | null >(null) const mobilePanelRef = React.useRef(null) const navigationTriggerRef = React.useRef(null) const tocTriggerRef = React.useRef(null) React.useEffect(() => { if (!mobilePanel) return const previousOverflow = document.body.style.overflow document.body.style.overflow = "hidden" const unavailableAt = window.matchMedia( mobilePanel === "navigation" ? "(min-width: 48rem)" : "(min-width: 64rem)" ) const animationFrame = window.requestAnimationFrame(() => { mobilePanelRef.current ?.querySelector( 'a[href], button:not(:disabled), [tabindex]:not([tabindex="-1"])' ) ?.focus() }) function handleKeyDown(event: KeyboardEvent) { if (event.key === "Escape") { event.preventDefault() setMobilePanel(null) const trigger = mobilePanel === "navigation" ? navigationTriggerRef.current : tocTriggerRef.current trigger?.focus() return } if (event.key !== "Tab") return const focusableElements = Array.from( mobilePanelRef.current?.querySelectorAll( 'a[href], button:not(:disabled), [tabindex]:not([tabindex="-1"])' ) ?? [] ) const firstElement = focusableElements[0] const lastElement = focusableElements.at(-1) if (!firstElement || !lastElement) return if (event.shiftKey && document.activeElement === firstElement) { event.preventDefault() lastElement.focus() } else if (!event.shiftKey && document.activeElement === lastElement) { event.preventDefault() firstElement.focus() } } function handleBreakpointChange(event: MediaQueryListEvent) { if (event.matches) setMobilePanel(null) } document.addEventListener("keydown", handleKeyDown) unavailableAt.addEventListener("change", handleBreakpointChange) return () => { window.cancelAnimationFrame(animationFrame) document.removeEventListener("keydown", handleKeyDown) unavailableAt.removeEventListener("change", handleBreakpointChange) document.body.style.overflow = previousOverflow } }, [mobilePanel]) if (!docLocation) return const currentIndex = docLocation.package.pages.indexOf(docLocation.page) const previousPage = docLocation.package.pages[currentIndex - 1] const nextPage = docLocation.package.pages[currentIndex + 1] const previous = previousPage ? { locale: docLocation.locale, package: docLocation.package, page: previousPage, } : undefined const next = nextPage ? { locale: docLocation.locale, package: docLocation.package, page: nextPage, } : undefined return ( <>

{docLocation.package.label}

{mobilePanel && typeof document !== "undefined" && createPortal(
, document.body )}
Copyright © 2026
) } function Sidebar({ current }: { current: DocLocation }) { const overview = current.package.pages.find( (page) => page.section === "overview" ) const groups = current.package.pages .filter((page) => page.section !== "overview") .reduce>( (result, page) => { const group = result.at(-1) if (group?.section === page.section) { group.pages.push(page) } else { result.push({ section: page.section, pages: [page] }) } return result }, [] ) return (
    {overview && (
  • )} {groups.map((group) => (
  • {getDocSectionLabel(group.section, current.locale)}

      {group.pages.map((page) => (
    • ))}
  • ))}
) } function SidebarPageLink({ current, page, prominent = false, }: { current: DocLocation page: DocPage prominent?: boolean }) { return ( {page.title} ) } function Header({ docLocation }: { docLocation: DocLocation }) { return (
  • {docLocation.package.label}
  • /
  • {docLocation.page.section !== "overview" && ( <>
  • {getDocSectionLabel( docLocation.page.section, docLocation.locale )}
  • /
  • )}
  • {docLocation.page.title}

{docLocation.page.title}

{docLocation.page.description}

) } function ArticleContent({ page }: { page: DocPage }) { const Content = page.Content return (
) } function TableOfContents({ activeIds, items, }: { activeIds: readonly string[] items: readonly DocTocItem[] }) { const t = useTranslate() const label = t({ zh: "本页内容", us: "On this page" }) const rootRef = React.useRef(null) const railRef = React.useRef(null) const [marker, setMarker] = React.useState<{ height: number top: number } | null>(null) const activeKey = activeIds.join("\n") React.useLayoutEffect(() => { const root = rootRef.current const rail = railRef.current if (!root || !rail) return function measureMarker() { const activeIdSet = new Set(activeIds) const activeRows = Array.from( root!.querySelectorAll("[data-toc-id]") ).filter((row) => activeIdSet.has(row.dataset.tocId ?? "")) if (activeRows.length === 0 || rail!.offsetHeight === 0) { setMarker(null) return } const railRect = rail!.getBoundingClientRect() const firstRect = activeRows[0]!.getBoundingClientRect() const lastRect = activeRows.at(-1)!.getBoundingClientRect() setMarker({ top: firstRect.top - railRect.top, height: lastRect.bottom - firstRect.top, }) } measureMarker() const resizeObserver = new ResizeObserver(measureMarker) resizeObserver.observe(root) window.addEventListener("resize", measureMarker) return () => { resizeObserver.disconnect() window.removeEventListener("resize", measureMarker) } }, [activeKey, activeIds]) return ( ) } function TocItem({ activeIds, item, }: { activeIds: readonly string[] item: DocTocItem }) { const active = activeIds.includes(item.id) return (
  • {item.items && item.items.length > 0 && (
      {item.items.map((child) => ( ))}
    )}
  • ) } function TocAnchor({ active, href, itemId, title, }: { active: boolean href: string itemId: string title: string }) { return ( ) } type TocVisibilityEntry = { id: string parentIds: readonly string[] } function useVisibleTocIds(items: readonly DocTocItem[]) { const entries = React.useMemo(() => flattenTocItems(items), [items]) const [activeIds, setActiveIds] = React.useState([]) React.useEffect(() => { if (entries.length === 0) { setActiveIds([]) return } let animationFrame: number | undefined const articleElement = document.querySelector( "[data-article-content]" ) if (!articleElement) return const article = articleElement function update() { animationFrame = undefined const headings = entries.flatMap((entry) => { const element = document.getElementById(entry.id) return element ? [{ element, entry }] : [] }) const viewportTop = getVisibleViewportTop() const viewportBottom = window.innerHeight const articleBottom = article.getBoundingClientRect().bottom const visibleIds = new Set() headings.forEach(({ element, entry }, index) => { const sectionTop = element.getBoundingClientRect().top const sectionBottom = headings[index + 1]?.element.getBoundingClientRect().top ?? articleBottom if (sectionBottom > viewportTop && sectionTop < viewportBottom) { entry.parentIds.forEach((id) => visibleIds.add(id)) visibleIds.add(entry.id) } }) const nextActiveIds = entries .map((entry) => entry.id) .filter((id) => visibleIds.has(id)) setActiveIds((current) => arraysEqual(current, nextActiveIds) ? current : nextActiveIds ) } function scheduleUpdate() { if (animationFrame !== undefined) return animationFrame = window.requestAnimationFrame(update) } scheduleUpdate() const resizeObserver = new ResizeObserver(scheduleUpdate) resizeObserver.observe(article) window.addEventListener("scroll", scheduleUpdate, { passive: true }) window.addEventListener("resize", scheduleUpdate) return () => { if (animationFrame !== undefined) { window.cancelAnimationFrame(animationFrame) } resizeObserver.disconnect() window.removeEventListener("scroll", scheduleUpdate) window.removeEventListener("resize", scheduleUpdate) } }, [entries]) return activeIds } function flattenTocItems( items: readonly DocTocItem[], parentIds: readonly string[] = [] ): readonly TocVisibilityEntry[] { return items.flatMap((item) => [ { id: item.id, parentIds }, ...flattenTocItems(item.items ?? [], [...parentIds, item.id]), ]) } function getVisibleViewportTop() { return Array.from( document.querySelectorAll("[data-article-sticky-header]") ).reduce((bottom, element) => { const rect = element.getBoundingClientRect() return rect.top <= 0 && rect.bottom > 0 ? Math.max(bottom, rect.bottom) : bottom }, 0) } function arraysEqual(left: readonly string[], right: readonly string[]) { return ( left.length === right.length && left.every((value, index) => value === right[index]) ) } function Footer({ next, previous, }: { next?: DocLocation previous?: DocLocation }) { return (
    {previous && ( )} {next && ( )}
    ) } function Surround({ href, title, description, align = "right", }: { title: string description: string align?: "left" | "right" href: string }) { return (
    {align === "left" ? ( ) : ( )}

    {title}

    {description}

    ) } function ArticleNotFound() { const locale = useLocale() const t = useTranslate() return (

    {t({ zh: "未找到文档", us: "Document not found" })}

    {t({ zh: "请求的文档包或页面不存在。", us: "The requested package or document does not exist.", })}

    {t({ zh: "打开文档", us: "Open the documentation" })}
    ) }