/** * Router + layout: sticky top bar, left sidebar (sticky column on desktop, overlay * panel on mobile), doc content, slim footer. basename comes from Vite's BASE_URL so * the site works under the GitHub Pages subpath ("//docs/"); scroll restores to * top on route change (hash targets excluded). */ import { useEffect, useState } from "react"; import { BrowserRouter, Outlet, Route, Routes, useLocation } from "react-router"; import { Nav } from "./components/nav"; import { Sidebar } from "./components/sidebar"; import { Footer } from "./components/footer"; import { DocPage } from "./pages/doc-page"; /** * Last history entry whose scroll was already handled. Module-level so it survives * the locale-keyed remount of the whole tree: switching language re-mounts Layout, * and without this guard the navigation effect would re-run (jumping to the hash or * to the top) and defeat LocaleScope's scroll preservation. */ let handledLocationKey = ""; function Layout() { const { pathname, hash, key } = useLocation(); const [menuOpen, setMenuOpen] = useState(false); // Genuine route change: jump to top; with a hash scroll to the target once it is // in the DOM. Also close the mobile sidebar. useEffect(() => { setMenuOpen(false); if (key === handledLocationKey) return; handledLocationKey = key; if (hash) { // Anchors of CJK headings arrive percent-encoded in the URL hash. const el = document.getElementById(decodeURIComponent(hash.slice(1))); if (el) { el.scrollIntoView(); return; } } window.scrollTo(0, 0); }, [pathname, hash, key]); return (
); } export function AppRouter() { const basename = import.meta.env.BASE_URL.replace(/\/+$/, "") || "/"; return ( }> } /> } /> ); }