Turn the headings already in your document into navigation
Find Markdown headings outside code blocks, give each included heading an explicit collision-free anchor and download the complete document with one replaceable nested table of contents.
Markdown with table of contents
The original document with one replaceable generated block and explicit HTML anchors before included headings.
Every linked heading
| 5 | 2 | Equipment | equipment |
| 7 | 3 | Camera | camera |
| 9 | 2 | Results | results |
| 11 | 3 | Camera | camera-2 |
How it works
Why explicit anchors matter
CommonMark specifies what an ATX or Setext heading is. It does not specify that
## Getting started must become #getting-started, nor how a second heading
with the same words should be suffixed. Those choices belong to renderers and
hosting platforms.
The generated document therefore contains both sides of every link:
- [Camera](#camera-2)
<!-- tessalor-toc:anchor --><a id="camera-2"></a>
### Camera
The comment identifies an anchor that this tool may replace. The id is the
actual HTML fragment target. An unrelated anchor without that marker remains
authored content and is left alone.
How headings are found
The scanner works from top to bottom. A fence of at least three backticks or tildes starts a code block; only a fence using the same character and at least the same length closes it. Lines inside are never considered headings. Lines with four leading spaces or a leading tab are treated as indented code for the same reason.
Outside code, one to six leading # characters followed by whitespace form an
ATX heading. A text line followed by === or --- is recognised as a
single-line Setext level 1 or level 2 heading. Optional closing hashes on ATX
headings are removed from the visible label.
This is intentionally narrower than pretending to be a complete Markdown renderer. Container-specific extensions and multi-line Setext paragraphs can have parser-dependent behaviour; the tool does not silently promise to model every host.
How anchors are made
Visible inline link text and simple emphasis markers are reduced to their text,
then the label is Unicode-normalised and lower-cased. Combining marks are
removed, runs of characters other than letters and numbers become one hyphen,
and edge hyphens disappear. A heading made only from punctuation falls back to
section.
Duplicate detection happens after that normalisation. Café & Crème and
Cafe Creme both begin at cafe-creme, so the later heading becomes
cafe-creme-2 rather than pointing both entries at the first section.
The worked document
The default document has one title and these included headings:
| Source heading | Level | Inserted anchor |
|---|---|---|
| Equipment | 2 | equipment |
| Camera | 3 | camera |
| Results | 2 | results |
| Camera | 3 | camera-2 |
With levels 2 through 4 selected, the result is 4 linked headings, 1
duplicate resolved, and deepest included level 3. The table is placed
after the opening # Field notes title; nested level-3 entries receive two
spaces of list indentation.
Use the text diff maker to review the inserted lines against the source, or the word counter when the document’s content—not its navigation—is the question.
How it is done
- Normalise line endings and remove only a complete table-of-contents block and anchor lines previously generated by this tool.
- Scan lines in order, tracking backtick and tilde fences and ignoring four-space or tab-indented code before recognising ATX and Setext headings.
- Simplify each included heading to a lower-case Unicode slug, then append -2, -3 and later suffixes when normalised names collide.
- Insert the exact HTML anchor before each included heading and place one marked, nested list after an opening level-one title or at the document start.
What it assumes
- The selected shallowest level is not deeper than the selected maximum level.
- The scanner targets ordinary CommonMark ATX headings and single-line Setext headings; it is not a full extension-specific Markdown renderer.
- Raw HTML anchors are allowed by the destination renderer. A platform that strips authored HTML will also strip these explicit targets.
- Existing unmarked HTML ids and an incomplete generated marker block are treated as authored content and are never deleted.
Common questions
Does Markdown define the automatic id for a heading?
No. Core Markdown defines heading structure but not a host's generated fragment slug, and duplicate suffix rules vary. This tool writes explicit ids so its links and targets are part of the same document.
Will a heading shown inside a code example enter the table?
Not when it is inside a backtick or tilde fence, or an indented code block. The scanner tracks those blocks before testing whether a line has heading syntax.
Can I regenerate the table after editing headings?
Yes. The generated table has start and end comments and each generated anchor has its own marker. A later run removes those marked parts and builds one current copy.
Why might a link fail on a publishing platform?
Some platforms sanitise raw HTML and remove authored anchor elements. The Markdown remains readable, but the explicit targets require a renderer that permits ordinary inline HTML.