// js/sdk/SdkTOC.jsx // // Auto-built right-rail "On this page" TOC. Scans
for h2[id] // and h3[id] on mount and renders them as anchored links. Optional // IntersectionObserver activates the entry whose section is in view. // // Mount: //
// ... // // // // Props: // editPath — path under beava-website/project/ for the page's // source HTML, used in the "Edit on GitHub" link. const SdkTOC = ({ editPath = '' }) => { const [items, setItems] = React.useState([]); const [active, setActive] = React.useState(null); React.useEffect(() => { // Pull h2/h3 with id attrs from
. Method-headers (.method-head) // wrap a

sibling in some pages; either form is fine — we just // scan tag + id. const main = document.querySelector('main.content'); if (!main) return; const heads = main.querySelectorAll('h2[id], h3[id]'); const list = Array.from(heads).map(el => ({ id: el.id, label: (el.dataset.tocLabel || el.textContent || el.id).trim(), level: el.tagName === 'H3' ? 3 : 2, })); setItems(list); if (list.length === 0) return; if (typeof IntersectionObserver === 'undefined') return; const observed = new Map(); const onIntersect = entries => { // Track which sections are currently intersecting; pick the // top-most one (lowest boundingClientRect.top above 88px) as // active. This mirrors the natural reading position. entries.forEach(e => observed.set(e.target.id, e)); const inView = Array.from(observed.values()) .filter(e => e.isIntersecting) .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top); if (inView.length > 0) setActive(inView[0].target.id); }; const obs = new IntersectionObserver(onIntersect, { rootMargin: '-88px 0px -60% 0px', threshold: 0, }); heads.forEach(h => obs.observe(h)); return () => obs.disconnect(); }, []); const editHref = editPath ? `https://github.com/beava-dev/beava/edit/main/beava-website/project/${editPath}` : null; return (
On this page
{editHref && ( Edit on GitHub )} Ask in Discord
); }; window.SdkTOC = SdkTOC;