import React from 'react' import Link from 'next/link' interface RenderState { currentList: string[] inCodeBlock: boolean codeBlockContent: string[] } export function renderMarkdown(markdown: string): JSX.Element[] { const lines = markdown.split('\n') const elements: JSX.Element[] = [] const state: RenderState = { currentList: [], inCodeBlock: false, codeBlockContent: [], } lines.forEach((line, index) => { processLine(line, index, state, elements) }) closeListIfNeeded('', lines.length, state, elements) return elements } function processLine(line: string, index: number, state: RenderState, elements: JSX.Element[]): void { if (line.startsWith('```')) { handleCodeBlock(line, index, state, elements) return } if (state.inCodeBlock) { state.codeBlockContent.push(line) return } closeListIfNeeded(line, index, state, elements) if (renderHeading(line, index, elements)) { return } if (renderListLine(line, state)) { return } if (renderLinkLine(line, index, elements)) { return } if (renderBoldAndCodeLine(line, index, elements)) { return } renderParagraphOrBreak(line, index, elements) } function renderHeading(line: string, index: number, elements: JSX.Element[]): boolean { if (line.startsWith('# ')) { elements.push(
{line}
) return } if (elements.length > 0) { const last = elements[elements.length - 1] as { type?: unknown } if (last?.type !== 'br') { elements.push(
{state.codeBlockContent.join('\n')}
)
state.codeBlockContent = []
state.inCodeBlock = false
} else {
state.inCodeBlock = true
}
}
function closeListIfNeeded(
line: string,
index: number,
state: RenderState,
elements: JSX.Element[]
): void {
if (state.currentList.length > 0 && !line.startsWith('- ') && !line.startsWith('* ') && line.trim() !== '') {
elements.push(
{parts}
) } function renderBoldAndCode(line: string, index: number, elements: JSX.Element[]): void { const parts: (string | JSX.Element)[] = [] const codeRegex = /`([^`]+)`/g let codeMatch let lastIndex = 0 while ((codeMatch = codeRegex.exec(line)) !== null) { if (codeMatch.index > lastIndex) { const beforeCode = line.substring(lastIndex, codeMatch.index) processBold(beforeCode, parts) } parts.push(
{codeMatch[1]}
)
lastIndex = codeMatch.index + codeMatch[0].length
}
if (lastIndex < line.length) {
const remaining = line.substring(lastIndex)
processBold(remaining, parts)
}
elements.push({parts.length > 0 ? parts : line}
) } function processBold(text: string, parts: (string | JSX.Element)[]): void { const boldParts = text.split(/(\*\*[^*]+\*\*)/g) boldParts.forEach((part, i) => { if (part.startsWith('**') && part.endsWith('**')) { parts.push({part.slice(2, -2)}) } else if (part) { parts.push(part) } }) }