Skip to content

fix: exclude headings inside blockquotes from sidebar TOC#2691

Open
Bowl42 wants to merge 3 commits intodocsifyjs:developfrom
Bowl42:fix/exclude-blockquote-headings-from-toc
Open

fix: exclude headings inside blockquotes from sidebar TOC#2691
Bowl42 wants to merge 3 commits intodocsifyjs:developfrom
Bowl42:fix/exclude-blockquote-headings-from-toc

Conversation

@Bowl42
Copy link
Contributor

@Bowl42 Bowl42 commented Mar 4, 2026

Summary

  • Issue: Headings inside blockquotes (e.g. > # Title) are incorrectly added to the sidebar table of contents. They should only render as headings within the blockquote content, not appear as navigation items.
  • Root cause: The heading compiler (heading.js) unconditionally pushes every heading to compiler.toc, with no awareness of whether it's inside a blockquote. When the blockquote compiler (blockquote.js) calls this.parser.parse(tokens), any headings within are processed and added to the TOC.
  • Fix: Introduce a blockquoteDepth counter on the compiler instance. The blockquote compiler increments it before parsing child tokens and decrements it in a finally block. The heading compiler checks this counter and skips TOC insertion when depth > 0. A counter (rather than a boolean flag) correctly handles nested blockquotes.

Changes

src/core/render/compiler.js

  • Initialize this.blockquoteDepth = 0 in the Compiler constructor
  • Pass compiler: this to blockquoteCompiler() so it can access the compiler instance

src/core/render/compiler/blockquote.js

  • Accept compiler from the options parameter
  • Increment compiler.blockquoteDepth before parsing child tokens
  • Decrement in a finally block to ensure cleanup even if parsing throws

src/core/render/compiler/heading.js

  • Wrap compiler.toc.push(nextToc) in a check: only push when !compiler.blockquoteDepth
  • Headings inside blockquotes are still rendered in the page content — they just won't appear in the sidebar

Test plan

  • Create a page with a heading inside a blockquote: > # Quoted Title
  • Verify the heading renders correctly inside the blockquote in the main content
  • Verify the heading does NOT appear in the sidebar TOC
  • Verify normal headings (outside blockquotes) still appear in the sidebar TOC
  • Verify nested blockquotes (> > # Nested) also exclude headings from TOC
  • Verify callout blockquotes (> [!NOTE]) with headings also exclude those headings from TOC

Fixes #1951

🤖 Generated with Claude Code

Headings inside blockquotes (e.g. `> # Title`) were being added to the
sidebar table of contents, which is incorrect — they are quoted content,
not page structure headings.

The fix introduces an `insideBlockquote` flag on the compiler instance:
- The blockquote compiler sets this flag to `true` before parsing its
  child tokens, and resets it to `false` afterward
- The heading compiler checks this flag and skips adding the heading to
  `compiler.toc` when inside a blockquote

The headings are still rendered correctly in the page content — they
just no longer appear in the sidebar navigation.

Fixes docsifyjs#1951

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Mar 4, 2026

@Bowl42 is attempting to deploy a commit to the Docsify Team on Vercel.

A member of the Team first needs to authorize it.

Bowl42 and others added 2 commits March 4, 2026 21:12
Replace the boolean `insideBlockquote` flag with a `blockquoteDepth`
counter to correctly handle nested blockquotes. The boolean approach
would reset to `false` when an inner blockquote finished parsing,
causing headings in the outer blockquote after the nested one to
incorrectly appear in the sidebar TOC.

Also wrap the parse call in try/finally to ensure the counter is
always decremented, and initialize `blockquoteDepth` in the Compiler
constructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the markdown rendering pipeline to prevent headings rendered inside blockquotes from being added to the compiler-generated table of contents (TOC), likely to avoid polluting sidebar/subsidebar navigation with headings from callout blocks.

Changes:

  • Track blockquote nesting depth on the Compiler instance (blockquoteDepth).
  • Increment/decrement blockquoteDepth while parsing renderer.blockquote tokens.
  • Gate TOC insertion in headingCompiler so headings are only added when not inside a blockquote.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/core/render/compiler/heading.js Skips pushing heading entries into compiler.toc when inside a blockquote.
src/core/render/compiler/blockquote.js Adds depth tracking around blockquote parsing to support TOC suppression.
src/core/render/compiler.js Initializes blockquoteDepth and passes compiler into blockquoteCompiler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Member

@sy-records sy-records left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your PR.

This can be placed on this instead of being imported into the compiler.

Comment on lines +43 to +49
compiler.blockquoteDepth++;
try {
const body = this.parser.parse(tokens);
return `${openTag}${body}${closeTag}`;
} finally {
compiler.blockquoteDepth--;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
compiler.blockquoteDepth++;
try {
const body = this.parser.parse(tokens);
return `${openTag}${body}${closeTag}`;
} finally {
compiler.blockquoteDepth--;
}
this.__docsifyBlockquoteDepth = (this.__docsifyBlockquoteDepth || 0) + 1;
let body;
try {
body = this.parser.parse(tokens);
} finally {
this.__docsifyBlockquoteDepth = Math.max(0, this._blockquoteDepth - 1);
}

Comment on lines +25 to +27
if (!compiler.blockquoteDepth) {
compiler.toc.push(nextToc);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!compiler.blockquoteDepth) {
compiler.toc.push(nextToc);
}
if (!this.__docsifyBlockquoteDepth || this.__docsifyBlockquoteDepth === 0) {
compiler.toc.push(nextToc);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Title in block quotes are displayed in the sidebar

3 participants