diff --git a/scripts/verify-static-output.ts b/scripts/verify-static-output.ts index 3796aa6..cabfa30 100644 --- a/scripts/verify-static-output.ts +++ b/scripts/verify-static-output.ts @@ -22,6 +22,8 @@ const apiSymbolsByRoute = new Map( apiSymbolSets[entrypoint.symbolSet as keyof typeof apiSymbolSets], ]) ); +const docsHome = docsCatalog.find((page) => page.route === '/docs'); +if (!docsHome) throw new Error('Missing /docs catalog entry'); const marketing = [ { route: '/', ...marketingRouteMetadata['/'] }, @@ -30,19 +32,26 @@ const marketing = [ title: page.title, description: page.description, })), + { + route: '/docs', + title: `${docsHome.title} | Askr`, + description: docsHome.description, + }, { route: '/404', ...marketingRouteMetadata['/404'] }, ].map((page) => ({ ...page, layout: 'marketing' })); -const docs = docsCatalog.map((page) => ({ - route: page.route, - title: `${page.title} | Askr`, - description: page.description, - headings: page.headings.map(({ id }) => ({ id })), - apiSymbols: apiSymbolsByRoute - .get(page.route) - ?.map(({ anchor }) => ({ anchor })), - layout: 'docs', -})); +const docs = docsCatalog + .filter((page) => page.route !== '/docs') + .map((page) => ({ + route: page.route, + title: `${page.title} | Askr`, + description: page.description, + headings: page.headings.map(({ id }) => ({ id })), + apiSymbols: apiSymbolsByRoute + .get(page.route) + ?.map(({ anchor }) => ({ anchor })), + layout: 'docs', + })); const expectations = [...marketing, ...docs]; const expectedRoutes = new Set(expectations.map(({ route }) => route)); @@ -143,7 +152,7 @@ for (const expectation of expectations) { for (const marker of [ 'Documentation navigation', 'Search docs', - 'Open documentation navigation', + 'Toggle documentation navigation', 'On this page', 'Breadcrumb', 'docs-pagination', diff --git a/src/pages/_routes.tsx b/src/pages/_routes.tsx index f4eedb8..5efab8a 100644 --- a/src/pages/_routes.tsx +++ b/src/pages/_routes.tsx @@ -6,6 +6,7 @@ import { } from './marketing/_routes'; import { DocsLayout } from './docs/_layout'; import { docsCatalog } from './docs/catalog'; +import { MarketingLayout } from './marketing/_layout'; export type { RouteMetadata } from './marketing/_routes'; @@ -22,8 +23,15 @@ export const routeMetadata: Readonly> = { export const routeRegistry = createRouteRegistry(() => { registerMarketingRoutes(); + const docsHome = docsCatalog.find((page) => page.route === '/docs'); + if (!docsHome) throw new Error('Missing /docs catalog entry'); + group({ layout: MarketingLayout }, () => { + route('/docs', lazy(docsHome.loader), { meta: routeMetadata['/docs'] }); + }); + group({ layout: DocsLayout }, () => { for (const page of docsCatalog) { + if (page.route === '/docs') continue; route(page.route, lazy(page.loader), { meta: routeMetadata[page.route], }); @@ -41,8 +49,20 @@ export async function createStaticRouteRegistry() { const registry = createRouteRegistry(() => { registerMarketingRoutes(); + const docsHome = docsCatalog.find((page) => page.route === '/docs'); + if (!docsHome) throw new Error('Missing /docs catalog entry'); + group({ layout: MarketingLayout }, () => { + let component = components.get(docsHome.loader); + if (!component) { + component = lazy(docsHome.loader); + components.set(docsHome.loader, component); + } + route('/docs', component, { meta: routeMetadata['/docs'] }); + }); + group({ layout: DocsLayout }, () => { for (const page of docsCatalog) { + if (page.route === '/docs') continue; let component = components.get(page.loader); if (!component) { component = lazy(page.loader); diff --git a/src/pages/docs/_layout.tsx b/src/pages/docs/_layout.tsx index 01fdaa1..d09d24d 100644 --- a/src/pages/docs/_layout.tsx +++ b/src/pages/docs/_layout.tsx @@ -1,54 +1,39 @@ -import { state, type Props } from '@askrjs/askr'; +import type { Props } from '@askrjs/askr'; import { Link, currentRoute } from '@askrjs/askr/router'; +import { + SidebarInset, + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarScope, + SidebarTrigger, +} from '@askrjs/themes/components'; import { BookOpenIcon, CompassIcon, Layers3Icon, LibraryIcon, - MenuIcon, MonitorIcon, - PanelLeftCloseIcon, - PanelLeftOpenIcon, PanelsTopLeftIcon, RocketIcon, RouteIcon, ServerIcon, WrenchIcon, - XIcon, } from '@askrjs/lucide'; -import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarHeader, - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - SidebarTrigger, -} from '@askrjs/themes/components'; -import { ThemeScope } from '@askrjs/themes/theme'; -import { AskrBrand, GitHubMark, SiteThemeToggle } from '../marketing/_layout'; +import { SiteLayout } from '../site-layout'; import { docsByRoute, docsSections, docsTableOfContents, resolveDocsRoute, } from './catalog'; -import { DocsSearch } from './search'; - -const STORAGE_KEY = 'askr-docs-sidebar-collapsed'; -const adoptedShells = new WeakSet(); -function DocsNavigation({ - collapsed = false, - close, -}: { - collapsed?: boolean; - close?: () => void; -}) { +function DocsNavigation({ close }: { close?: () => void }) { const activePath = resolveDocsRoute(currentRoute()); const sectionIcons = [ BookOpenIcon, @@ -62,12 +47,9 @@ function DocsNavigation({ CompassIcon, LibraryIcon, ] as const; + return ( - + {docsSections.map((section, sectionIndex) => { const active = section.pages.some((page) => page.route === activePath); const SectionIcon = sectionIcons[sectionIndex] ?? BookOpenIcon; @@ -78,66 +60,60 @@ function DocsNavigation({ groups.set(label, pages); return groups; }, new Map()); + return ( -
- - - - - - - - - {active && !collapsed && ( - - {Array.from(subsections).map(([label, pages]) => ( -
- {label !== section.label && ( - {label} - )} - - {pages.map((page) => ( - - + + + + + + + + {active && ( + + {Array.from(subsections).map(([label, pages]) => ( +
+ {label !== section.label && ( + {label} + )} + + {pages.map((page) => ( + + + - - {page.title} - {page.status !== 'stable' && ( - - • - - )} - - - - ))} - -
- ))} -
- )} - -
+ {page.title} + {page.status !== 'stable' && ( + + • + + )} + + + + ))} + +
+ ))} + + )} + ); })}
@@ -147,12 +123,14 @@ function DocsNavigation({ function TableOfContents() { const page = docsByRoute.get(resolveDocsRoute(currentRoute())); const headings = page ? docsTableOfContents(page) : []; + if (!headings.length) return null; + return (