"use client"; import { useState, useEffect, useCallback, useRef } from "react"; import { ChevronDown, ChevronUp, EllipsisVertical, PanelRight, PanelRightClose, FileText, } from "lucide-react"; import { Dropdown } from "@docubook/ui-react/dropdown"; import { cn } from "../node/utils"; import Anchor from "./Anchor"; import { Context } from "./Context"; import Menu from "./Menu"; import { ThemeToggle } from "./Theme"; import { GitHubLink } from "./Navbar"; import Search from "./Search"; import type { TocItem } from "../node/types"; import { config as docuConfig, routes } from "../node/client-routes"; interface SidebarProps { tocs?: TocItem[]; title?: string; repoUrl?: string; className?: string; } export default function Sidebar({ tocs = [], title, repoUrl, className }: SidebarProps) { return ( <> ); } function DesktopSidebar({ className, repoUrl }: { className?: string; repoUrl?: string }) { return (
{/* Logo */}
{docuConfig.navbar?.logo?.src && ( {docuConfig.navbar.logo.alt )} {docuConfig.navbar?.logoText && ( {docuConfig.navbar.logoText} )}
{/* Search */}
{/* Context + Menu */}
{/* Bottom: Theme + GitHub */}
); } export function MobileBar({ tocs, title, repoUrl, }: { tocs: TocItem[]; title?: string; repoUrl?: string; }) { const [tocExpanded, setTocExpanded] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false); const [activeId, setActiveId] = useState(null); const barRef = useRef(null); useEffect(() => { if (!tocs.length) return; // Match the CSS scroll-margin-top (scroll-mt-54 = 216px) that anchor // jumps rest at, so the highlight agrees with where a jump lands. const firstEl = document.getElementById(tocs[0].href.slice(1)); const offset = firstEl ? parseFloat(getComputedStyle(firstEl).scrollMarginTop) || 100 : 100; const handleScroll = () => { let currentId: string | null = null; for (const toc of tocs) { const id = toc.href.slice(1); const el = document.getElementById(id); if (!el) continue; // Round: smooth anchor scrolls land at sub-pixel positions (e.g. // 216.4px), which would otherwise fail the <= offset comparison and // leave the previous heading highlighted. if (Math.round(el.getBoundingClientRect().top) <= offset) { currentId = id; } else { break; } } if (currentId) setActiveId(currentId); }; handleScroll(); window.addEventListener("scroll", handleScroll, { passive: true }); // Smooth anchor scrolls may not emit a trailing scroll event at the // target position — recompute once the animation actually settles. window.addEventListener("scrollend", handleScroll, { passive: true }); return () => { window.removeEventListener("scroll", handleScroll); window.removeEventListener("scrollend", handleScroll); }; }, [tocs]); useEffect(() => { if (!tocExpanded) return; const handler = (e: MouseEvent) => { if (barRef.current && !barRef.current.contains(e.target as Node)) { setTocExpanded(false); } }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [tocExpanded]); const activeSection = tocs.find((t) => t.href.slice(1) === activeId); const displayTitle = activeSection?.text || title || "On this page"; const toggleToc = useCallback(() => setTocExpanded((p) => !p), []); return (
} > {(docuConfig.navbar?.menu || []).map((item) => (
  • path === item.href || path.startsWith(item.href + "/") } activeClassName="text-primary font-semibold" className="block rounded px-2 py-2 text-sm" > {item.title}
  • ))}
    {tocExpanded && (
    {tocs.length > 0 ? ( { setTocExpanded(false); setActiveId(id); }} /> ) : (

    No headings on this page

    )}
    )}
    setDrawerOpen(false)} repoUrl={repoUrl} />
    ); } function MobileTocList({ tocs, activeId, onSelect, }: { tocs: TocItem[]; activeId: string | null; onSelect: (id: string) => void; }) { return ( ); } function MobileDrawer({ open, onClose, repoUrl, }: { open: boolean; onClose: () => void; repoUrl?: string; }) { useEffect(() => { if (open) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [open]); if (!open) return null; return (
    ); }