Skip to content

Commit 9290a76

Browse files
committed
fix(tutorial): avoid TOC anchor injection inside code blocks
Skip fenced code blocks when generating heading anchors and sanitize existing anchor markup so document outline labels are not polluted by HTML. Made-with: Cursor
1 parent 31a26b2 commit 9290a76

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

web/src/views/Tutorial.vue

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,24 +264,42 @@ async function renderMarkdown(markdown) {
264264
function addHeaderIds(markdown) {
265265
const lines = markdown.split('\n')
266266
const toc = []
267+
let inFencedCodeBlock = false
267268
268269
const processedLines = lines.map((line, index) => {
270+
// Skip heading parsing inside fenced code blocks (```), otherwise code lines
271+
// starting with # will be incorrectly treated as document headings.
272+
if (/^\s*```/.test(line)) {
273+
inFencedCodeBlock = !inFencedCodeBlock
274+
return line
275+
}
276+
if (inFencedCodeBlock) {
277+
return line
278+
}
279+
269280
const match = line.match(/^(#{1,6})\s+(.+)$/)
270281
if (match) {
271282
const level = match[1].length
272-
const text = match[2]
283+
const rawHeading = match[2]
284+
const existingAnchorMatch = rawHeading.match(/<a\s+name="([^"]+)"\s*><\/a>/i)
285+
const id = existingAnchorMatch ? existingAnchorMatch[1] : `section-${index}`
286+
const text = rawHeading
287+
.replace(/<a\s+name="[^"]+"\s*><\/a>/gi, '')
288+
.replace(/<[^>]+>/g, '')
273289
.replace(/[🛡️🚀🧠📋🎯🔌⚡💼❓💡📖📚📊🔧🚨🔧]/g, '')
274290
.trim()
275291
276292
if (text && level <= 3) {
277-
const id = `section-${index}`
278293
toc.push({
279294
id,
280295
level,
281296
text,
282297
line: index + 1
283298
})
284-
return `${match[1]} <a name="${id}"></a>${match[2]}`
299+
if (existingAnchorMatch) {
300+
return line
301+
}
302+
return `${match[1]} <a name="${id}"></a>${rawHeading}`
285303
}
286304
}
287305
return line

0 commit comments

Comments
 (0)