('*')) {
+ const y = getComputedStyle(el).overflowY;
+ if ((y === 'auto' || y === 'scroll') && el.scrollHeight > el.clientHeight) return el;
+ }
+ return null;
+}
+
+export function SidebarScrollKeeper() {
+ const pathname = usePathname();
+
+ useEffect(() => {
+ const remember = (e: MouseEvent) => {
+ const link = (e.target as Element | null)?.closest('#nd-sidebar a[href]');
+ const el = link && viewport();
+ if (el) sessionStorage.setItem(KEY, JSON.stringify({ top: el.scrollTop, at: Date.now() }));
+ };
+ document.addEventListener('click', remember, true);
+ return () => document.removeEventListener('click', remember, true);
+ }, []);
+
+ useEffect(() => {
+ let saved: { top: number; at: number } | null = null;
+ try {
+ saved = JSON.parse(sessionStorage.getItem(KEY) ?? 'null');
+ } catch {}
+ if (!saved || Date.now() - saved.at > FRESH_MS) return;
+ // The active item scrolls itself into view after this render; put the list back after it.
+ const restore = () => {
+ const el = viewport();
+ if (el && saved) el.scrollTop = saved.top;
+ };
+ restore();
+ const frame = requestAnimationFrame(restore);
+ const late = window.setTimeout(restore, 80);
+ const done = window.setTimeout(() => sessionStorage.removeItem(KEY), 400);
+ return () => {
+ cancelAnimationFrame(frame);
+ window.clearTimeout(late);
+ window.clearTimeout(done);
+ };
+ }, [pathname]);
+
+ return null;
+}
diff --git a/docs/lib/layout.shared.tsx b/docs/lib/layout.shared.tsx
index dbf6517fe..3de41c575 100644
--- a/docs/lib/layout.shared.tsx
+++ b/docs/lib/layout.shared.tsx
@@ -15,25 +15,34 @@ function Mark() {
export function baseOptions(): BaseLayoutProps {
return {
nav: {
+ url: '/guides/',
title: (
Warmbly
-
- Docs
-
+ Docs
),
},
githubUrl: 'https://github.com/warmbly/warmbly',
+ themeSwitch: { mode: 'light-dark' },
links: [
{
text: 'warmbly.com',
url: 'https://warmbly.com',
},
{
- text: 'Dashboard',
- url: 'https://app.warmbly.com',
+ type: 'custom',
+ on: 'nav',
+ secondary: true,
+ children: (
+
+ Open dashboard
+
+ ),
},
],
};