{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "documentation-page",
  "title": "Documentation Page",
  "description": "Navigation, an in-page table of contents with the current section, content, and page actions.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/app-sidebar.json",
    "https://uaiblocks.dev/r/breadcrumb-trail.json",
    "https://uaiblocks.dev/r/uai-utils.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/blocks/documentation-page.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { ArrowLeft, ArrowRight } from \"lucide-react\";\nimport {\n  type ComponentProps,\n  type CSSProperties,\n  createContext,\n  useContext,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useRef,\n  useState,\n} from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  AppSidebar,\n  type AppSidebarProps,\n  type AppSidebarVariant,\n} from \"@/components/ui/uai/app-sidebar\";\nimport {\n  BreadcrumbTrail,\n  type BreadcrumbTrailProps,\n  type BreadcrumbTrailVariant,\n} from \"@/components/ui/uai/breadcrumb-trail\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const DOCUMENTATION_PAGE_VARIANTS = [\"columns\", \"stacked\", \"compact\"] as const;\nexport type DocumentationPageVariant = (typeof DOCUMENTATION_PAGE_VARIANTS)[number];\nexport type DocumentationPageProps = ComponentProps<\"div\"> & {\n  variant?: DocumentationPageVariant;\n  /** The id of the section marked as current in the table of contents. */\n  activeSection?: string;\n  defaultActiveSection?: string;\n  onActiveSectionChange?: (section: string) => void;\n};\n\ntype DocsContext = {\n  id: string;\n  variant: DocumentationPageVariant;\n  activeSection: string;\n  setActiveSection: (section: string) => void;\n};\nconst Context = createContext<DocsContext | null>(null);\nfunction useDocs(part: string) {\n  const context = useContext(Context);\n  if (!context) throw new Error(`${part} must be used within DocumentationPage`);\n  return context;\n}\nconst SectionContext = createContext<string | null>(null);\nconst TocContext = createContext<string | null>(null);\n\nconst sidebarVariants: Record<DocumentationPageVariant, AppSidebarVariant> = {\n  columns: \"inset\",\n  stacked: \"panel\",\n  compact: \"compact\",\n};\nconst breadcrumbVariants: Record<DocumentationPageVariant, BreadcrumbTrailVariant> = {\n  columns: \"chevron\",\n  stacked: \"slash\",\n  compact: \"chevron\",\n};\n\nconst documentationPageLayoutVariants = cva(\n  \"grid min-w-0 grid-cols-[minmax(0,1fr)] [grid-template-areas:'nav'_'toc'_'main']\",\n  {\n    variants: {\n      variant: {\n        columns:\n          \"gap-5 @min-[720px]:grid-cols-[220px_minmax(0,1fr)] @min-[720px]:grid-rows-[auto_1fr] @min-[720px]:gap-x-8 @min-[720px]:[grid-template-areas:'nav_toc'_'nav_main'] @min-[980px]:grid-cols-[220px_minmax(0,1fr)_180px] @min-[980px]:grid-rows-[auto] @min-[980px]:[grid-template-areas:'nav_main_toc']\",\n        stacked:\n          \"gap-5 @min-[720px]:grid-cols-[minmax(0,1fr)_200px] @min-[720px]:gap-x-10 @min-[720px]:[grid-template-areas:'nav_nav'_'main_toc']\",\n        compact:\n          \"gap-3 @min-[720px]:grid-cols-[200px_minmax(0,1fr)] @min-[720px]:grid-rows-[auto_1fr] @min-[720px]:gap-x-5 @min-[720px]:[grid-template-areas:'nav_toc'_'nav_main']\",\n      },\n    },\n  },\n);\n\n/** Documentation with navigation, an in-page table of contents, content, and page actions. */\nexport function DocumentationPage({\n  variant = \"columns\",\n  activeSection,\n  defaultActiveSection = \"\",\n  onActiveSectionChange,\n  children,\n  className,\n  ...props\n}: DocumentationPageProps) {\n  const id = useId();\n  const [internal, setInternal] = useState(defaultActiveSection);\n  const current = activeSection ?? internal;\n  const latest = useRef({ current, activeSection, onActiveSectionChange });\n  latest.current = { current, activeSection, onActiveSectionChange };\n  const setActiveSection = useRef((next: string) => {\n    const state = latest.current;\n    if (next === state.current) return;\n    if (state.activeSection === undefined) setInternal(next);\n    state.onActiveSectionChange?.(next);\n  }).current;\n  return (\n    <Context.Provider value={{ id, variant, activeSection: current, setActiveSection }}>\n      <div\n        data-slot=\"documentation-page\"\n        className={cn(\n          \"@container box-border min-w-0 text-[13px]/[18px] text-foreground\",\n          className,\n        )}\n        {...props}\n        data-variant={variant}\n      >\n        <div\n          data-slot=\"documentation-page-layout\"\n          className={documentationPageLayoutVariants({ variant })}\n        >\n          {children}\n        </div>\n      </div>\n    </Context.Provider>\n  );\n}\n\n/** Site navigation, an App Sidebar. Compose App Sidebar parts inside it. */\nexport function DocumentationPageNav({ className, ...props }: Omit<AppSidebarProps, \"variant\">) {\n  const { variant } = useDocs(\"DocumentationPageNav\");\n  return (\n    <AppSidebar\n      {...props}\n      variant={sidebarVariants[variant]}\n      className={cn(\"h-auto w-full min-w-0 [grid-area:nav] @min-[720px]:self-start\", className)}\n    />\n  );\n}\n\n/** The page content, rendered as an article named by DocumentationPageTitle. */\nexport function DocumentationPageMain({ className, ...props }: ComponentProps<\"article\">) {\n  const { id, variant } = useDocs(\"DocumentationPageMain\");\n  return (\n    <article\n      aria-labelledby={`${id}-title`}\n      data-slot=\"documentation-page-main\"\n      className={cn(\n        \"grid min-w-0 content-start [grid-area:main]\",\n        variant === \"compact\" ? \"gap-4\" : \"gap-6\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function DocumentationPageHeader({ className, ...props }: ComponentProps<\"header\">) {\n  const { variant } = useDocs(\"DocumentationPageHeader\");\n  return (\n    <header\n      data-slot=\"documentation-page-header\"\n      className={cn(\n        \"grid min-w-0 border-b\",\n        variant === \"compact\" ? \"gap-2 pb-3\" : \"gap-3 pb-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** Page hierarchy, a Breadcrumb Trail. Compose BreadcrumbTrailItem parts inside it. */\nexport function DocumentationPageBreadcrumb(props: Omit<BreadcrumbTrailProps, \"variant\">) {\n  const { variant } = useDocs(\"DocumentationPageBreadcrumb\");\n  return <BreadcrumbTrail {...props} variant={breadcrumbVariants[variant]} />;\n}\n\nexport function DocumentationPageTitle({ className, ...props }: ComponentProps<\"h1\">) {\n  const { id, variant } = useDocs(\"DocumentationPageTitle\");\n  return (\n    <h1\n      data-slot=\"documentation-page-title\"\n      className={cn(\n        \"m-0 font-semibold tracking-[-0.015em] text-balance\",\n        variant === \"compact\" ? \"text-xl/[26px]\" : \"text-[28px]/[34px]\",\n        className,\n      )}\n      {...props}\n      id={`${id}-title`}\n    />\n  );\n}\n\nexport function DocumentationPageDescription({ className, ...props }: ComponentProps<\"p\">) {\n  const { variant } = useDocs(\"DocumentationPageDescription\");\n  return (\n    <p\n      data-slot=\"documentation-page-description\"\n      className={cn(\n        \"m-0 max-w-[64ch] text-pretty text-muted-foreground\",\n        variant === \"compact\" ? \"text-[13px]/[18px]\" : \"text-[15px]/[22px]\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** Page actions such as copying the page link, editing the source, or sending feedback. */\nexport function DocumentationPageActions({\n  \"aria-label\": label = \"Page actions\",\n  className,\n  ...props\n}: ComponentProps<\"div\">) {\n  useDocs(\"DocumentationPageActions\");\n  return (\n    // biome-ignore lint/a11y/useSemanticElements: a labelled group of actions, not a fieldset.\n    <div\n      role=\"group\"\n      aria-label={label}\n      data-slot=\"documentation-page-actions\"\n      className={cn(\"flex flex-wrap items-center gap-1.5\", className)}\n      {...props}\n    />\n  );\n}\n\ntype ActionStyleProps = { emphasis?: \"primary\" | \"secondary\" };\nconst documentationPageActionVariants = cva(\n  \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-full border-0 px-3 has-[>svg]:px-3 text-[12.5px] font-medium whitespace-nowrap no-underline transition-[background-color,filter,scale] duration-[120ms,120ms,140ms] ease-[ease-out,ease-out,cubic-bezier(0.23,1,0.32,1)] py-0 focus-visible:ring-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:outline-solid active:scale-[0.97] motion-reduce:transition-none motion-reduce:active:scale-100 [&_svg:not([class*='size-'])]:size-3.5\",\n  {\n    variants: {\n      emphasis: {\n        primary: \"bg-primary text-primary-foreground hover:bg-primary hover:brightness-108\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklab,var(--secondary)_85%,var(--foreground))]\",\n      },\n      compact: { true: \"h-[26px]\", false: \"h-7\" },\n    },\n  },\n);\n\nexport function DocumentationPageAction({\n  emphasis = \"secondary\",\n  type = \"button\",\n  className,\n  ...props\n}: ComponentProps<\"button\"> & ActionStyleProps) {\n  const { variant } = useDocs(\"DocumentationPageAction\");\n  return (\n    <Button\n      data-slot=\"documentation-page-action\"\n      variant={emphasis === \"primary\" ? \"default\" : \"secondary\"}\n      className={cn(\n        documentationPageActionVariants({ emphasis, compact: variant === \"compact\" }),\n        className,\n      )}\n      {...props}\n      type={type}\n      data-emphasis={emphasis}\n    />\n  );\n}\n\nexport function DocumentationPageActionLink({\n  emphasis = \"secondary\",\n  className,\n  ...props\n}: ComponentProps<\"a\"> & ActionStyleProps) {\n  const { variant } = useDocs(\"DocumentationPageActionLink\");\n  return (\n    <Button\n      asChild\n      variant={emphasis === \"primary\" ? \"default\" : \"secondary\"}\n      className={cn(\n        documentationPageActionVariants({ emphasis, compact: variant === \"compact\" }),\n        className,\n      )}\n    >\n      <a data-slot=\"documentation-page-action-link\" {...props} data-emphasis={emphasis} />\n    </Button>\n  );\n}\n\n/** Prose wrapper with spacing, inline code, and code block styles. */\nexport function DocumentationPageContent({ className, ...props }: ComponentProps<\"div\">) {\n  useDocs(\"DocumentationPageContent\");\n  return (\n    <div\n      data-slot=\"documentation-page-content\"\n      className={cn(\n        \"grid min-w-0 gap-8 text-sm/[22px] text-pretty\",\n        \"[&>*]:mx-0 [&>*]:mb-0 [&>*+*]:mt-4 [&>:first-child]:mt-0\",\n        \"[&_p]:max-w-[72ch] [&_p]:text-[color-mix(in_oklab,var(--foreground)_88%,var(--muted-foreground))]\",\n        \"[&_code]:rounded-md [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-px [&_code]:font-mono [&_code]:text-[0.88em]\",\n        \"[&_pre]:overflow-x-auto [&_pre]:rounded-[10px] [&_pre]:bg-card [&_pre]:px-3.5 [&_pre]:py-3 [&_pre]:text-[12px]/[18px]\",\n        \"[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-[length:inherit] [&_pre_code]:shadow-none\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** A linkable section. Its `id` is the target of a table of contents link. */\nexport function DocumentationPageSection({\n  id,\n  className,\n  ...props\n}: ComponentProps<\"section\"> & { id: string }) {\n  useDocs(\"DocumentationPageSection\");\n  const titleId = useId();\n  return (\n    <SectionContext.Provider value={titleId}>\n      <section\n        aria-labelledby={titleId}\n        data-slot=\"documentation-page-section\"\n        className={cn(\"grid min-w-0 scroll-mt-4 gap-3\", className)}\n        {...props}\n        id={id}\n      />\n    </SectionContext.Provider>\n  );\n}\n\nexport function DocumentationPageSectionTitle({ className, ...props }: ComponentProps<\"h2\">) {\n  const id = useContext(SectionContext);\n  if (!id) {\n    throw new Error(\"DocumentationPageSectionTitle must be used within DocumentationPageSection\");\n  }\n  return (\n    <h2\n      data-slot=\"documentation-page-section-title\"\n      className={cn(\"m-0 text-[17px]/6 font-medium tracking-[-0.01em]\", className)}\n      {...props}\n      id={id}\n    />\n  );\n}\n\n/**\n * In-page links. The link for the section nearest the top of the viewport is marked current;\n * selecting a link marks it current immediately.\n */\nexport function DocumentationPageToc({\n  \"aria-label\": label,\n  \"aria-labelledby\": labelledBy,\n  className,\n  ...props\n}: ComponentProps<\"nav\">) {\n  const context = useDocs(\"DocumentationPageToc\");\n  const titleId = useId();\n  const ref = useRef<HTMLElement>(null);\n  const { setActiveSection } = context;\n  useEffect(() => {\n    if (typeof IntersectionObserver !== \"function\") return;\n    const targets = Array.from(ref.current?.querySelectorAll<HTMLAnchorElement>(\"a[href]\") ?? [])\n      .map((link) => link.hash.slice(1))\n      .map((hash) => (hash ? document.getElementById(decodeURIComponent(hash)) : null))\n      .filter((target): target is HTMLElement => Boolean(target));\n    if (!targets.length) return;\n    const visible = new Set<Element>();\n    const observer = new IntersectionObserver(\n      (entries) => {\n        for (const entry of entries) {\n          if (entry.isIntersecting) visible.add(entry.target);\n          else visible.delete(entry.target);\n        }\n        const first = targets.find((target) => visible.has(target));\n        if (first) setActiveSection(first.id);\n      },\n      { rootMargin: \"0px 0px -65% 0px\" },\n    );\n    for (const target of targets) observer.observe(target);\n    return () => observer.disconnect();\n  }, [setActiveSection]);\n  return (\n    <TocContext.Provider value={titleId}>\n      <nav\n        ref={ref}\n        aria-label={label}\n        aria-labelledby={label ? labelledBy : (labelledBy ?? titleId)}\n        data-slot=\"documentation-page-toc\"\n        className={cn(\n          \"grid min-w-0 gap-2 [grid-area:toc]\",\n          context.variant === \"compact\" ? \"p-0\" : \"py-0.5\",\n          context.variant === \"stacked\" &&\n            \"@min-[720px]:sticky @min-[720px]:top-4 @min-[720px]:self-start\",\n          context.variant === \"columns\" &&\n            \"@min-[980px]:sticky @min-[980px]:top-4 @min-[980px]:self-start\",\n          className,\n        )}\n        {...props}\n      />\n    </TocContext.Provider>\n  );\n}\n\nexport function DocumentationPageTocTitle({ className, ...props }: ComponentProps<\"p\">) {\n  const id = useContext(TocContext);\n  if (!id) throw new Error(\"DocumentationPageTocTitle must be used within DocumentationPageToc\");\n  return (\n    <p\n      data-slot=\"documentation-page-toc-title\"\n      className={cn(\"m-0 text-[11.5px]/4 font-medium text-subtle-foreground\", className)}\n      {...props}\n      id={id}\n    />\n  );\n}\n\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nexport function DocumentationPageTocList({ className, children, ...props }: ComponentProps<\"ul\">) {\n  const { activeSection } = useDocs(\"DocumentationPageTocList\");\n  const ref = useRef<HTMLUListElement>(null);\n  const [marker, setMarker] = useState<{ y: number; height: number }>();\n  useIsomorphicLayoutEffect(() => {\n    const list = ref.current;\n    if (!list) return;\n    const measure = () => {\n      const active = list.querySelector<HTMLElement>(\n        \"[data-slot=documentation-page-toc-item][data-active]\",\n      );\n      if (!active || active.offsetHeight === 0) return setMarker(undefined);\n      setMarker({ y: active.offsetTop, height: active.offsetHeight });\n    };\n    measure();\n    if (typeof ResizeObserver !== \"function\") return;\n    const observer = new ResizeObserver(measure);\n    observer.observe(list);\n    return () => observer.disconnect();\n  }, [activeSection]);\n  // One measured marker (a pseudo-element, so the list keeps only <li> children) slides to the\n  // current link; until it is measured the link paints its own border.\n  const style = marker\n    ? ({\n        \"--toc-marker-y\": `${marker.y}px`,\n        \"--toc-marker-height\": `${marker.height}px`,\n      } as CSSProperties)\n    : undefined;\n  return (\n    <ul\n      ref={ref}\n      data-slot=\"documentation-page-toc-list\"\n      className={cn(\n        \"group/toc relative m-0 grid list-none gap-0 border-s p-0\",\n        \"before:pointer-events-none before:absolute before:top-0 before:-start-px before:h-(--toc-marker-height) before:w-px before:translate-y-(--toc-marker-y) before:bg-foreground before:opacity-0 before:transition-[translate,height] before:duration-200 before:ease-out-quint data-ready:before:opacity-100 motion-reduce:before:transition-none\",\n        className,\n      )}\n      {...props}\n      style={{ ...props.style, ...style }}\n      data-ready={marker ? \"\" : undefined}\n    >\n      {children}\n    </ul>\n  );\n}\n\nexport type DocumentationPageTocItemProps = Omit<ComponentProps<\"a\">, \"href\"> & {\n  /** The id of the target DocumentationPageSection. */\n  section: string;\n  /** Indent level: 2 for sections, 3 for subsections. */\n  level?: 2 | 3;\n};\nexport function DocumentationPageTocItem({\n  section,\n  level = 2,\n  onClick,\n  className,\n  children,\n  ...props\n}: DocumentationPageTocItemProps) {\n  const context = useDocs(\"DocumentationPageTocItem\");\n  const active = context.activeSection === section;\n  return (\n    <li data-slot=\"documentation-page-toc-entry\" className=\"grid min-w-0\">\n      <a\n        data-slot=\"documentation-page-toc-item\"\n        className={cn(\n          \"-ms-px block min-w-0 border-s py-1 pr-2 text-[12.5px]/4 no-underline wrap-anywhere transition-[color] duration-120 ease-out focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none\",\n          level === 3 ? \"pl-[22px]\" : \"pl-3\",\n          active\n            ? \"border-s-foreground font-medium text-foreground group-data-ready/toc:border-s-transparent\"\n            : \"border-s-transparent font-normal text-subtle-foreground hover:text-foreground\",\n          className,\n        )}\n        {...props}\n        href={`#${section}`}\n        aria-current={active ? \"location\" : undefined}\n        data-active={active || undefined}\n        onClick={(event) => {\n          onClick?.(event);\n          if (!event.defaultPrevented) context.setActiveSection(section);\n        }}\n      >\n        {children}\n      </a>\n    </li>\n  );\n}\n\n/** Previous and next page links at the end of the content. */\nexport function DocumentationPagePager({\n  \"aria-label\": label = \"Pagination\",\n  className,\n  ...props\n}: ComponentProps<\"nav\">) {\n  useDocs(\"DocumentationPagePager\");\n  return (\n    <nav\n      aria-label={label}\n      data-slot=\"documentation-page-pager\"\n      className={cn(\n        \"grid grid-cols-[repeat(auto-fit,minmax(min(100%,200px),1fr))] gap-2 pt-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function DocumentationPagePagerLink({\n  direction,\n  label,\n  className,\n  children,\n  ...props\n}: ComponentProps<\"a\"> & {\n  direction: \"previous\" | \"next\";\n  /** Visible eyebrow; defaults to \"Previous\" or \"Next\". */\n  label?: string;\n}) {\n  const { variant } = useDocs(\"DocumentationPagePagerLink\");\n  const next = direction === \"next\";\n  const Icon = next ? ArrowRight : ArrowLeft;\n  return (\n    <a\n      rel={next ? \"next\" : \"prev\"}\n      data-slot=\"documentation-page-pager-link\"\n      data-direction={direction}\n      className={cn(\n        \"grid min-w-0 gap-0.5 bg-card text-foreground no-underline transition-[background-color] duration-120 ease-[ease-out] hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&_svg]:transition-transform [&_svg]:duration-180 [&_svg]:ease-out-quint motion-reduce:transition-none motion-reduce:[&_svg]:transition-none\",\n        next\n          ? \"col-[-2/-1] justify-items-end text-end hover:[&_svg]:translate-x-0.5\"\n          : \"justify-items-start text-start hover:[&_svg]:-translate-x-0.5\",\n        variant === \"compact\" ? \"rounded-xl px-3 py-2\" : \"rounded-[14px] px-3.5 py-3\",\n        className,\n      )}\n      {...props}\n    >\n      <span className=\"inline-flex items-center gap-1 text-[12px] text-subtle-foreground\">\n        {next ? null : <Icon size={12} className=\"size-3\" strokeWidth={1.75} aria-hidden=\"true\" />}\n        {label ?? (next ? \"Next\" : \"Previous\")}\n        {next ? <Icon size={12} className=\"size-3\" strokeWidth={1.75} aria-hidden=\"true\" /> : null}\n      </span>\n      <span className=\"font-medium wrap-anywhere\">{children}</span>\n    </a>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/uai/documentation-page.tsx"
    }
  ],
  "categories": [
    "content",
    "navigation"
  ],
  "type": "registry:block"
}