Files
supabase/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx
Katerina Skroumpelou f77e8e75b6 docs: wire @supabase/server v1 into the reference pipeline (#47570)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Docs update.

*
https://docs-git-docs-wire-server-v1-reference-supabase.vercel.app/docs/reference/server/introduction
* 
<img width="417" height="628" alt="Screenshot 2026-07-06 at 6 13 33 PM"
src="https://github.com/user-attachments/assets/9fc27b04-038b-4434-8855-94051f898b5d"
/>

## What is the current behavior?

`@supabase/server` has no reference documentation page in the Supabase
docs. The library publishes a TypeDoc spec to GitHub Pages but the docs
pipeline was not wired up to consume it.

## What is the new behavior?

- Adds `spec/reference/server/v1/` with a `config.json` (category order:
Middleware, Primitives, Adapters, Errors, Types) and `partials/` for the
introduction and installing pages.
- Adds a `download.server.v1` Makefile target that fetches
`https://supabase.github.io/server/spec.json` into
`spec/reference/server/v1/server.json`, and wires it into the top-level
`download` target so it runs with the rest.
- Registers `server-v1` in `SUPPORTS_NEW_REFERENCE_PROCESS` so the build
pipeline picks up the new spec directory and generates
`content/reference/server/v1/` at build time.
- Seeds the generated `docs/ref/server/` partials (introduction and
installing) that the reference router serves.

## Additional context

The TypeDoc spec is produced by `@supabase/server`'s `docs.yml` workflow
on every push to `main`, so `make download.server.v1` will always pull
the latest published API surface. The companion PR in the server repo
([supabase/server#95](https://github.com/supabase/server/pull/95)) adds
the `@category` tags that the pipeline requires for symbols to appear in
navigation.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a new **Server SDK** item under **Reference**, linking to
`/reference/server` and marked with a **New** badge.
* Published **Server Reference v1** documentation for
`@supabase/server`, including **Introduction** and **Installing** pages.

* **Chores / Improvements**
* Enhanced the reference documentation generation to include Server v1
content.
* Improved reference detail handling (including clearer TypeDoc output
such as **Deprecated** notes).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
2026-07-07 17:08:51 +03:00

196 lines
7.4 KiB
TypeScript

import { getCustomContent } from '~/lib/custom-content/getCustomContent'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
import { X } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { Dispatch, Fragment, SetStateAction, useEffect } from 'react'
import { useKey } from 'react-use'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, cn } from 'ui'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
import type { DropdownMenuItem } from '../Navigation.types'
import { MenuItem, useActiveMenuLabel } from './GlobalNavigationMenu'
import { GLOBAL_MENU_ITEMS } from './NavigationMenu.constants'
const DEFAULT_EASE = [0.24, 0.25, 0.05, 1]
const container = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { duration: 0.15, staggerChildren: 0.05, ease: DEFAULT_EASE } },
exit: { opacity: 0, transition: { duration: 0.15 } },
}
const listItem = {
hidden: { opacity: 0, y: 10 },
show: { opacity: 1, y: 0, transition: { duration: 0.25, ease: DEFAULT_EASE } },
exit: { opacity: 0, transition: { duration: 0.05 } },
}
const itemClassName =
'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:rounded-sm'
const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => {
const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS)
return section[0].menuItems ? (
<AccordionItem
value={section[0].label}
className={cn(
'relative',
activeLabel === section[0].label && 'text-foreground!',
itemClassName
)}
>
<AccordionTrigger className="py-1">{section[0].label}</AccordionTrigger>
<AccordionContent>
{section[0].menuItems?.map((menuItem, menuItemIndex) => (
<Fragment key={`desktop-docs-menu-section-${menuItemIndex}`}>
{menuItem
.filter((item) => item.enabled !== false)
.map((item) =>
!item.href ? (
<div
key={item.label}
className="font-mono tracking-wider flex items-center text-foreground-muted text-xs uppercase rounded-md p-2 leading-none"
>
{item.label}
</div>
) : (
<MenuItem
key={item.label}
href={item.href}
title={item.label}
community={item.community}
new={item.new}
icon={item.icon}
/>
)
)}
</Fragment>
))}
</AccordionContent>
</AccordionItem>
) : (
<Link
href={section[0].href || '#'}
className={cn(activeLabel === section[0].label && 'text-foreground!', itemClassName)}
>
{section[0].label}
</Link>
)
}
const Menu = () => (
<Accordion type="multiple" className="space-y-1 mt-2.5">
{GLOBAL_MENU_ITEMS.filter((section) => section[0].enabled !== false).map((section, index) => (
<AccordionMenuItem key={index} section={section} />
))}
</Accordion>
)
interface Props {
open: boolean
setOpen: Dispatch<SetStateAction<boolean>>
}
const GlobalMobileMenu = ({ open, setOpen }: Props) => {
const isLoggedIn = useIsLoggedIn()
const isUserLoading = useIsUserLoading()
const pathname = usePathname()
const { navigationLogo } = getCustomContent(['navigation:logo'])
// Close mobile menu on route change
useEffect(() => {
setOpen(false)
}, [pathname])
useKey('Escape', () => setOpen(false))
return (
<LazyMotion features={domAnimation}>
<AnimatePresence mode="wait">
{open && (
<m.div
variants={container}
initial="hidden"
animate="show"
exit="exit"
className="bg-overlay fixed overflow-hidden inset-0 z-50 h-screen max-h-screen w-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-svh transform"
>
<div className="absolute px-5 h-(--header-height) flex items-center justify-between w-screen left-0 top-0 z-50 bg-overlay before:content[''] before:absolute before:w-full before:h-3 before:inset-0 before:top-full before:bg-linear-to-b before:from-background-overlay before:to-transparent">
<Link href="/" className="flex items-center gap-2">
<Image
className="cursor-pointer hidden dark:block"
src={navigationLogo?.dark ?? '/docs/supabase-dark.svg'}
priority
width={96}
height={24}
alt="Supabase Logo"
/>
<Image
className="cursor-pointer block dark:hidden"
src={navigationLogo?.light ?? '/docs/supabase-light.svg'}
priority
width={96}
height={24}
alt="Supabase Logo"
/>
<span className="font-mono text-sm font-medium text-brand-link mb-px">DOCS</span>
</Link>
<div className="flex gap-4 items-center">
<ThemeToggle contentClassName="bg-surface-200" />
<button
onClick={() => setOpen(false)}
type="button"
className="inline-flex items-center justify-center focus:ring-brand bg-surface-100 hover:bg-surface-200 focus:outline-hidden focus:ring-2 focus:ring-inset border border-default bg-surface-100/75 text-foreground-light rounded-sm min-w-[30px] w-[30px] h-[30px]"
>
<span className="sr-only">Close menu</span>
<X />
</button>
</div>
</div>
<div className="max-h-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-svh overflow-y-auto pt-12 pb-32 px-3">
<Menu />
</div>
<div className="absolute bottom-0 left-0 right-0 top-auto w-full bg-alternative flex items-stretch p-4 gap-4">
{!isUserLoading && (
<>
{isLoggedIn ? (
<Button block size="medium" asChild>
<Link href="https://supabase.com/dashboard/projects">Dashboard</Link>
</Button>
) : (
<>
<Button block size="medium" variant="default" asChild>
<Link href="https://supabase.com/dashboard/sign-in">Sign in</Link>
</Button>
<Button block size="medium" asChild>
<Link href="https://supabase.com/dashboard/new">Start your project</Link>
</Button>
</>
)}
</>
)}
</div>
</m.div>
)}
</AnimatePresence>
<AnimatePresence mode="wait">
{open && (
<m.div
variants={container}
initial="hidden"
animate="show"
exit="exit"
className="bg-alternative fixed overflow-hidden inset-0 z-40 h-screen w-screen transform"
/>
)}
</AnimatePresence>
</LazyMotion>
)
}
export default GlobalMobileMenu