@@ -645,14 +645,18 @@ export const SIM_ARTIFACT_SHELL = `<script>
645645 const page = document.querySelector('.page[data-layout="docs"]')
646646 if (!page || page.dataset.shell === 'ready') return
647647 const main = page.querySelector('main') || page
648- const headings = [...main.querySelectorAll('h2, h3')]
649- if (headings.length === 0) return
648+ if (main.querySelectorAll('h2, h3').length === 0) return
650649 page.dataset.shell = 'ready'
651650
651+ // In a multi-tab document each tab is its own page: the TOC is built from
652+ // the ACTIVE panel's headings only and rebuilt on every tab switch.
653+ const tocScope = () => main.querySelector('[data-tab-panel].is-active') || main
654+ let headings = []
655+ let tocLinks = []
656+
652657 const ICON_ATTRS = 'viewBox="-1 -2 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.55" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"'
653658 const slug = (text, i) =>
654659 (text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'section') + '-' + i
655- headings.forEach((h, i) => { if (!h.id) h.id = slug(h.textContent || '', i) })
656660
657661 // Right rail — the clerk TOC.
658662 const right = document.createElement('nav')
@@ -679,15 +683,6 @@ export const SIM_ARTIFACT_SHELL = `<script>
679683 const thumbPath = document.createElementNS(SVG_NS, 'path')
680684 thumb.appendChild(thumbPath)
681685 tocItems.append(track, thumb)
682- const tocLinks = []
683- for (const h of headings) {
684- const a = document.createElement('a')
685- a.href = '#' + h.id
686- a.textContent = h.textContent
687- a.dataset.depth = h.tagName === 'H2' ? '2' : '3'
688- tocItems.appendChild(a)
689- tocLinks.push(a)
690- }
691686 right.append(rightTitle, tocItems)
692687
693688 // Top bar with the page title and the theme toggle.
@@ -872,7 +867,29 @@ export const SIM_ARTIFACT_SHELL = `<script>
872867 new ResizeObserver(refresh).observe(tocItems)
873868 document.addEventListener('scroll', spy, { passive: true })
874869 window.addEventListener('resize', refresh)
875- refresh()
870+
871+ // (Re)builds the TOC from the active scope — the whole page, or the
872+ // active tab's panel. Each tab switch produces a fresh rail, so every
873+ // tab reads as its own page.
874+ let slugSeq = 0
875+ const populateToc = () => {
876+ headings = [...tocScope().querySelectorAll('h2, h3')]
877+ headings.forEach((h) => { if (!h.id) h.id = slug(h.textContent || '', slugSeq++) })
878+ for (const a of [...tocItems.querySelectorAll('a')]) a.remove()
879+ tocLinks = []
880+ for (const h of headings) {
881+ const a = document.createElement('a')
882+ a.href = '#' + h.id
883+ a.textContent = h.textContent
884+ a.dataset.depth = h.tagName === 'H2' ? '2' : '3'
885+ tocItems.appendChild(a)
886+ tocLinks.push(a)
887+ }
888+ right.style.display = headings.length === 0 ? 'none' : ''
889+ refresh()
890+ }
891+ document.addEventListener('sim-tab-change', populateToc)
892+ populateToc()
876893 }
877894
878895 // In-page anchors must scroll, never navigate. Under the preview's
0 commit comments