1document.addEventListener('DOMContentLoaded', () => { 2 const toc = document.getElementById('sidebar-toc'); 3 if (!toc) return; 4 5 const links = toc.querySelectorAll('a'); 6 const content = document.querySelector('.prose'); 7 if (!content) return; 8 9 const headers = Array.from(content.querySelectorAll('h1, h2, h3, h4, h5, h6')); 10 if (headers.length === 0) return; 11 12 // Smooth scrolling for TOC links 13 links.forEach(link => { 14 link.addEventListener('click', (e) => { 15 e.preventDefault(); 16 const targetId = link.getAttribute('href').substring(1); 17 const targetHeader = document.getElementById(targetId); 18 19 if (targetHeader) { 20 // Adjust for sticky header if you have one, or just a bit of padding 21 const headerOffset = 100; 22 const elementPosition = targetHeader.getBoundingClientRect().top; 23 const offsetPosition = elementPosition + window.pageYOffset - headerOffset; 24 25 window.scrollTo({ 26 top: offsetPosition, 27 behavior: "smooth" 28 }); 29 30 // Update URL hash without jumping 31 history.pushState(null, null, '#' + targetId); 32 } 33 }); 34 }); 35 36 // ScrollSpy implementation using IntersectionObserver 37 const observerOptions = { 38 root: null, 39 rootMargin: '-10% 0px -80% 0px', // Trigger when header is near the top 40 threshold: 0 41 }; 42 43 const observerCallback = (entries) => { 44 entries.forEach(entry => { 45 if (entry.isIntersecting) { 46 const activeId = entry.target.id; 47 48 // Remove active class from all links 49 links.forEach(link => { 50 link.classList.remove('active'); 51 // Also expand/collapse parents if implementing nested collapsing 52 }); 53 54 // Add active class to corresponding link 55 const activeLink = toc.querySelector(`a[href="#${activeId}"]`); 56 if (activeLink) { 57 activeLink.classList.add('active'); 58 59 // Optional: Scroll TOC to keep active item in view 60 const tocRect = toc.getBoundingClientRect(); 61 const linkRect = activeLink.getBoundingClientRect(); 62 63 if (linkRect.top < tocRect.top || linkRect.bottom > tocRect.bottom) { 64 activeLink.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); 65 } 66 } 67 } 68 }); 69 }; 70 71 const observer = new IntersectionObserver(observerCallback, observerOptions); 72 headers.forEach(header => observer.observe(header)); 73 74 // Fallback/highlight on load if hash exists 75 if (window.location.hash) { 76 const activeLink = toc.querySelector(`a[href="${window.location.hash}"]`); 77 if (activeLink) activeLink.classList.add('active'); 78 } 79});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.