llm_log_frontend/src/components/TOC.astro

47 lines
1.7 KiB
Plaintext

---
const { headings } = Astro.props;
// 仅提取 h2 和 h3 作为目录
const toc = headings.filter((h: any) => h.depth === 2 || h.depth === 3);
---
<nav class="sticky top-32 max-h-[calc(100vh-10rem)] overflow-y-auto hidden xl:block min-w-[200px] w-64 pr-4">
<h3 class="text-sm font-bold text-gray-900 dark:text-white uppercase tracking-wider mb-4 border-l-4 border-blue-500 pl-2">目录大纲</h3>
<ul class="flex flex-col gap-2.5 text-sm">
{toc.map((heading: any) => (
<li class={heading.depth === 3 ? "ml-4" : ""}>
<a
href={`#${heading.slug}`}
class="toc-link text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400 transition-all duration-200 block line-clamp-2"
>
{heading.text}
</a>
</li>
))}
</ul>
</nav>
<script>
// 客户端监听滚动并高亮目录
document.addEventListener('astro:page-load', () => setupTOC()); // 预留后续无缝过渡支持
setupTOC();
function setupTOC() {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const id = entry.target.id;
document.querySelectorAll('.toc-link').forEach((link) => {
link.classList.remove('text-blue-600', 'dark:text-blue-400', 'font-semibold', 'translate-x-1');
if (link.getAttribute('href') === `#${id}`) {
link.classList.add('text-blue-600', 'dark:text-blue-400', 'font-semibold', 'translate-x-1');
}
});
}
});
}, { rootMargin: '0px 0px -80% 0px' });
document.querySelectorAll('article h2, article h3').forEach((section) => {
observer.observe(section);
});
}
</script>