Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# Temporary content cloning directory
_tmp/

# Compiled versioned documentation snapshots
/versioned/

# Reference files (compiled into the repo root from documentdb/docs; anchored
# so the patterns cannot swallow tracked paths like app/docs/reference/)
/api-reference/
Expand Down
4 changes: 4 additions & 0 deletions app/components/Breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export default function Breadcrumb({ type, category, name }: {
}) {
return (
<nav className="mb-6 text-sm text-gray-400">
<Link href="/docs" className="hover:text-blue-400 transition-colors">
Docs
</Link>
<span className="mx-2">/</span>
<Link href="/docs/reference" className="hover:text-blue-400 transition-colors">
Reference
</Link>
Expand Down
30 changes: 30 additions & 0 deletions app/components/DocsBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Link from "next/link";

export interface Crumb {
title: string;
href?: string;
}

/**
* Breadcrumb trail for documentation pages:
* Docs / [version] / Section / Page
* The last crumb (no href) is the page being viewed.
*/
export default function DocsBreadcrumb({ crumbs }: { crumbs: Crumb[] }) {
return (
<nav className="mb-6 text-sm text-gray-400">
{crumbs.map((crumb, index) => (
<span key={`${crumb.title}-${index}`}>
{index > 0 && <span className="mx-2">/</span>}
{crumb.href ? (
<Link href={crumb.href} className="hover:text-blue-400 transition-colors">
{crumb.title}
</Link>
) : (
<span className="text-white">{crumb.title}</span>
)}
</span>
))}
</nav>
);
}
145 changes: 145 additions & 0 deletions app/components/DocsSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import Link from "next/link";
import VersionSwitcher from "./VersionSwitcher";
import { VersionSwitcherEntry } from "../services/versionService";

export interface SidebarNavItem {
title: string;
href: string;
active?: boolean;
}

export interface DocsSidebarProps {
/** null when viewing current docs; the version label on archived pages. */
version: string | null;
/** Where the top back link goes: /docs for current, /docs/versions/<v> for a version. */
backHref: string;
backLabel: string;
sectionTitle: string;
/** Pages of the section being viewed. */
nav: SidebarNavItem[];
/** All sections in the SAME version context (never leaves the version). */
sections: SidebarNavItem[];
switcherEntries: VersionSwitcherEntry[];
}

function navLinkClass(active?: boolean): string {
return `block w-full text-left px-4 py-2.5 rounded-lg text-sm transition-all duration-200 ${active
? "bg-blue-500/20 text-blue-300 border border-blue-500/30"
: "text-gray-300 hover:text-white hover:bg-neutral-700/50"
}`;
}

/**
* Shared sidebar for current and versioned documentation pages. Every link in
* it stays inside the version being viewed, so readers never fall out of an
* archived version by navigating.
*/
export function DocsSidebarContent({
version,
backHref,
backLabel,
sectionTitle,
nav,
sections,
switcherEntries,
}: DocsSidebarProps) {
return (
<>
{/* Header: back link + section title + version state */}
<div className="p-6 border-b border-neutral-700/50">
<Link
href={backHref}
className="text-blue-400 hover:text-blue-300 text-sm mb-3 flex items-center transition-colors"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
{backLabel}
</Link>
<p className="text-2xl font-bold text-white">{sectionTitle}</p>
{version && (
<p className="mt-1 inline-flex items-center rounded-full bg-amber-500/15 border border-amber-500/40 px-2.5 py-0.5 text-xs font-semibold text-amber-300">
{version} · archived
</p>
)}
<div className="mt-4">
<VersionSwitcher entries={switcherEntries} />
</div>
</div>

{/* Section pages */}
<div className="flex-1 p-4 overflow-y-auto">
<nav className="space-y-1">
{nav.map((item) => (
<Link key={item.href} href={item.href} className={navLinkClass(item.active)}>
{item.title}
</Link>
))}
</nav>

{/* Other sections, same version context */}
{sections.length > 0 && (
<div className="mt-6 border-t border-neutral-700/50 pt-4">
<p className="px-4 pb-2 text-xs font-semibold uppercase tracking-wider text-gray-500">
All sections
</p>
<nav className="space-y-1">
{sections.map((item) => (
<Link
key={item.href}
href={item.href}
className={`block w-full px-4 py-2 rounded-lg text-sm transition-colors ${item.active
? "text-white font-medium"
: "text-gray-400 hover:text-white hover:bg-neutral-700/40"
}`}
>
{item.title}
</Link>
))}
</nav>
</div>
)}
</div>

{/* Footer meta links */}
<div className="p-4 border-t border-neutral-700/50 space-y-1">
<Link
href="/docs/release-notes"
className="block px-4 py-1.5 text-xs text-gray-400 hover:text-white transition-colors"
>
Release notes
</Link>
<Link
href="/docs/versions"
className="block px-4 py-1.5 text-xs text-gray-400 hover:text-white transition-colors"
>
All documentation versions
</Link>
</div>
</>
);
}

/** Desktop sidebar wrapper. */
export default function DocsSidebar(props: DocsSidebarProps) {
return (
<div className="hidden w-80 bg-neutral-800/50 backdrop-blur-sm border-r border-neutral-700/50 md:flex flex-col">
<DocsSidebarContent {...props} />
</div>
);
}

/** Mobile disclosure variant of the same navigation. */
export function DocsSidebarMobile(props: DocsSidebarProps) {
return (
<details className="mb-6 rounded-lg border border-neutral-700/50 bg-neutral-800/50 md:hidden">
<summary className="cursor-pointer px-4 py-3 text-sm font-semibold text-gray-200">
{props.sectionTitle} navigation
{props.version ? ` (${props.version})` : ""}
</summary>
<div className="border-t border-neutral-700/50 flex flex-col">
<DocsSidebarContent {...props} />
</div>
</details>
);
}
55 changes: 55 additions & 0 deletions app/components/VersionSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Link from "next/link";
import { VersionSwitcherEntry } from "../services/versionService";

/**
* Static version dropdown shown on every documentation page. Server-rendered
* (a <details> disclosure, no client JS) so it works in the static export.
* The active entry is marked; every other entry links to the best equivalent
* of the page being viewed in that version.
*/
export default function VersionSwitcher({ entries }: { entries: VersionSwitcherEntry[] }) {
const active = entries.find((entry) => entry.active);

return (
<details className="relative block text-left">
<summary className="cursor-pointer list-none flex w-full items-center justify-between gap-2 rounded-lg border border-neutral-700/60 bg-neutral-900/60 px-3 py-2 text-xs font-medium text-gray-300 hover:text-white hover:border-neutral-500 transition-colors">
<span className="inline-flex items-center gap-2 truncate">
<svg className="h-3.5 w-3.5 shrink-0 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M7 7h10M7 12h10M7 17h6" />
</svg>
{active?.label ?? "Select version"}
</span>
<svg className="h-3 w-3 shrink-0 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</summary>
<div className="absolute z-20 mt-2 w-full min-w-56 rounded-lg border border-neutral-700/60 bg-neutral-800 p-1 shadow-xl">
{entries.map((entry) =>
entry.active ? (
<span
key={entry.label}
className="block rounded-md px-3 py-2 text-xs font-semibold text-white bg-neutral-700/40"
>
{entry.label} ✓
</span>
) : (
<Link
key={entry.label}
href={entry.href}
className="block rounded-md px-3 py-2 text-xs text-gray-300 hover:bg-neutral-700/60 hover:text-white transition-colors"
>
{entry.label}
</Link>
)
)}
<Link
href="/docs/versions"
className="block rounded-md px-3 py-2 text-xs text-blue-400 hover:bg-neutral-700/60 hover:text-blue-300 transition-colors border-t border-neutral-700/60 mt-1 pt-2"
>
All versions →
</Link>
</div>
</details>
);
}
Loading