From b47549437a83899d6bc1a7830d726bde7427e5f4 Mon Sep 17 00:00:00 2001 From: gbanyan Date: Mon, 17 Nov 2025 21:09:11 +0800 Subject: [PATCH] Add scroll-synced active state to post TOC --- components/post-toc.tsx | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/components/post-toc.tsx b/components/post-toc.tsx index 836a6f0..f369e2d 100644 --- a/components/post-toc.tsx +++ b/components/post-toc.tsx @@ -10,6 +10,7 @@ interface TocItem { export function PostToc() { const [items, setItems] = useState([]); + const [activeId, setActiveId] = useState(null); useEffect(() => { const headings = Array.from( @@ -23,6 +24,28 @@ export function PostToc() { depth: el.tagName === 'H3' ? 3 : 2 })); setItems(mapped); + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const id = (entry.target as HTMLElement).id; + if (id) { + setActiveId(id); + } + } + }); + }, + { + // Trigger when heading is in upper 40% of viewport + rootMargin: '0px 0px -60% 0px', + threshold: 0.1 + } + ); + + headings.forEach((el) => observer.observe(el)); + + return () => observer.disconnect(); }, []); if (items.length === 0) return null; @@ -37,7 +60,11 @@ export function PostToc() {
  • {item.text}