Mist detail page: replace broken symbol anchors with a proper markdown TOC
Mist detail page: replace broken symbol anchors with a proper markdown TOC
Background
The mist detail page had a "Symbol Anchors" sidebar panel that listed the mist's
symbol_anchors field. That panel was designed for code file views, where symbols
are short identifiers (InsertOp, wire_fetch_mpack) and clicking one jumps to a
definition. On a markdown mist it misfired: markdown "symbols" are full heading
strings, which truncate to identical-looking labels, are not clickable, and convey
no useful information.
Example from gabriel/mist-clone-mpack-deep-dive.md:
mist-clone-mpack-deep-dive.md::Clone… (×20, all truncated the same)
The panel was actively degrading the UX — it looked broken because it was broken.
It has been removed from mist_detail.html as of this commit.
The right replacement is a heading-based table of contents that extracts ##
and ### headings from the markdown source and renders them as clickable
scroll-anchors in the sidebar — the same UX pattern GitHub uses for READMEs, but
integrated into the MuseHub sidebar design.
Goal
- Long markdown mists have a sidebar TOC showing
##/###headings as clickable links - Clicking a heading link smooth-scrolls to that heading in the rendered content
- The active heading is highlighted as the user scrolls (scroll-spy)
- Short mists (no headings, or fewer than 2 headings) hide the TOC entirely — no empty panel
- The panel renders correctly on code mists (no headings → panel hidden)
Design
Heading extraction
Client-side JS: after the markdown is rendered into #artifact-content, query
querySelectorAll('h2, h3') and build the TOC list dynamically. No server-side
changes needed — the rendered HTML already has the headings.
Each heading needs a stable id attribute for anchor targeting. The markdown
renderer should add slug IDs to headings (e.g. ## The full clone path →
<h2 id="the-full-clone-path">). Verify the current renderer (md_html) already
does this; if not, the JS builder can inject the IDs itself before building links.
TOC panel HTML (injected by JS, replaces the removed symbol-anchors block)
<div class="ms-sidebar-card ms-toc-card" id="ms-toc">
<div class="ms-sidebar-card-title">
<!-- icon("list", 13) -->
Contents
</div>
<ul class="ms-toc-list">
<li class="ms-toc-item ms-toc-h2">
<a href="#the-full-clone-path" class="ms-toc-link">The full clone path</a>
</li>
<li class="ms-toc-item ms-toc-h3">
<a href="#why-null-oids-exist" class="ms-toc-link ms-toc-indent">Why null OIDs exist</a>
</li>
…
</ul>
</div>
ms-toc-indent adds 12px left padding to visually nest ### under ##.
Scroll-spy
IntersectionObserver on all h2/h3 elements; when one enters the viewport, add
ms-toc-link--active to its corresponding TOC entry and remove it from all others.
Minimum heading threshold
If the heading count is < 2, do not render the TOC panel. A one-heading document doesn't benefit from a TOC.
Phases
Phase 1 — Heading IDs and static TOC injection
TOC_01Verify (or add) slug-id generation to markdown headings in the rendered outputTOC_02JS: extracth2/h3from#artifact-contentafter page loadTOC_03JS: build and inject#ms-tocsidebar cardTOC_04CSS:ms-toc-list,ms-toc-item,ms-toc-link,ms-toc-indentstylesTOC_05Hide panel when heading count < 2
Phase 2 — Scroll-spy
TOC_06IntersectionObserverhighlights active heading in TOCTOC_07Active link style (ms-toc-link--active) — subtle, not distracting
Phase 3 — Code mist and non-markdown handling
TOC_08Panel is absent (not empty) on code mists and MIDI mistsTOC_09Panel is absent on very short markdown mists with < 2 headings
Acceptance criteria
- Visiting
gabriel/mist-clone-mpack-deep-dive.mdshows a "Contents" sidebar panel with 6–8 section links matching the##headings in the document - Clicking "The full clone path" smooth-scrolls to that section
- Scrolling past "The null-OID bug" highlights that entry in the TOC
- Visiting a code mist (
.py,.ts) shows no TOC panel - No symbol-anchors panel ever appears anywhere on the mist detail page
Out of scope
h1headings in the TOC (they're typically the document title — already in the page header)h4/h5/h6nesting- Server-side TOC generation (client-side is sufficient and avoids a render pipeline change)
- Applying the same TOC to
issue_detail.htmlorblobviews — separate issues - Removing or redesigning
symbol_anchorsfrom the data model — the field stays, the display is just hidden until it's implemented correctly