diff --git a/server/modules/rendering/markdown-github-alerts/renderer.js b/server/modules/rendering/markdown-github-alerts/renderer.js index 5bf2d639..6f8b8ad3 100644 --- a/server/modules/rendering/markdown-github-alerts/renderer.js +++ b/server/modules/rendering/markdown-github-alerts/renderer.js @@ -4,7 +4,7 @@ module.exports = { init (md, conf) { - // Plugin to parse GitHub-style alerts + // Plugin to parse GitHub-style alerts using renderer approach md.use(githubAlertsPlugin, conf) } } @@ -19,64 +19,63 @@ function githubAlertsPlugin(md, options = {}) { 'caution': { class: 'is-danger' } } - // Add custom rule to transform GitHub alert syntax - md.core.ruler.before('normalize', 'github_alerts', function(state) { - let pos = 0 - let lines = state.src.split('\n') - let processedLines = [] - - while (pos < lines.length) { - const line = lines[pos] - - // Check if this line starts a potential GitHub alert blockquote - if (line.trim().startsWith('> [!')) { - const alertMatch = line.match(/^>\s*\[!(NOTE|INFO|TIP|WARNING|IMPORTANT|CAUTION)\](?:\s+(.+?))?\s*$/i) - - if (alertMatch) { - const alertType = alertMatch[1].toLowerCase() - const customTitle = alertMatch[2] ? alertMatch[2].trim() : null - - // Check if this is a valid alert type - const alertConfig = alertTypes[alertType] - if (alertConfig) { - // Collect all blockquote lines for this alert - let blockquoteLines = [] - let currentPos = pos + 1 - - // Add the title line if there's a custom title - if (customTitle && options.customTitle !== false) { - blockquoteLines.push(`> **${customTitle}**`) - } - - // Collect subsequent blockquote lines - while (currentPos < lines.length && lines[currentPos].trim().startsWith('>')) { - blockquoteLines.push(lines[currentPos]) - currentPos++ - } + // Store original renderer + const defaultRender = md.renderer.rules.blockquote_open || function(tokens, idx, options, env, slf) { + return slf.renderToken(tokens, idx, options) + } - // If we have content, add the class attribute to the last line - if (blockquoteLines.length > 0) { - const lastLineIndex = blockquoteLines.length - 1 - blockquoteLines[lastLineIndex] += ` {.${alertConfig.class}}` - } else { - // If no content, create a line with just the class - blockquoteLines.push(`> {.${alertConfig.class}}`) + // Override blockquote_open renderer + md.renderer.rules.blockquote_open = function(tokens, idx, opts, env, slf) { + const nextToken = tokens[idx + 1] + + // Check if the next token is a paragraph containing inline content + if (nextToken && nextToken.type === 'paragraph_open') { + const inlineToken = tokens[idx + 2] // The inline token after paragraph_open + + if (inlineToken && inlineToken.type === 'inline' && inlineToken.children) { + const firstChild = inlineToken.children[0] + + if (firstChild && firstChild.type === 'text') { + const content = firstChild.content + const alertMatch = content.match(/^\[!(NOTE|INFO|TIP|WARNING|IMPORTANT|CAUTION)\](?:\s+(.+?))?$/i) + + if (alertMatch) { + const alertType = alertMatch[1].toLowerCase() + const customTitle = alertMatch[2] ? alertMatch[2].trim() : null + + const alertConfig = alertTypes[alertType] + if (alertConfig) { + // Add CSS class to blockquote + tokens[idx].attrJoin('class', alertConfig.class) + + // Handle custom title or remove alert tag + if (customTitle && options.customTitle !== false) { + // Replace with bold custom title + firstChild.content = `**${customTitle}**` + } else if (customTitle && options.customTitle === false) { + // Custom titles disabled but title provided - remove the entire alert line + firstChild.content = '' + } else { + // No custom title - remove the alert tag + firstChild.content = '' + } + + // If we emptied the first text node, remove it + if (firstChild.content === '') { + inlineToken.children.shift() + // Also remove the following softbreak if it exists + if (inlineToken.children[0] && inlineToken.children[0].type === 'softbreak') { + inlineToken.children.shift() + } + } } - - processedLines = processedLines.concat(blockquoteLines) - - pos = currentPos - continue } } } - - processedLines.push(line) - pos++ } - state.src = processedLines.join('\n') - }) + return defaultRender(tokens, idx, opts, env, slf) + } }