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.substring(2)}

) return true } if (line.startsWith('## ')) { elements.push(

{line.substring(3)}

) return true } if (line.startsWith('### ')) { elements.push(

{line.substring(4)}

) return true } if (line.startsWith('#### ')) { elements.push(

{line.substring(5)}

) return true } return false } function renderListLine(line: string, state: RenderState): boolean { if (line.startsWith('- ') || line.startsWith('* ')) { state.currentList.push(line) return true } return false } function renderLinkLine(line: string, index: number, elements: JSX.Element[]): boolean { if (line.includes('[') && line.includes('](')) { renderLink(line, index, elements) return true } return false } function renderBoldAndCodeLine(line: string, index: number, elements: JSX.Element[]): boolean { if (line.includes('**') || line.includes('`')) { renderBoldAndCode(line, index, elements) return true } return false } function renderParagraphOrBreak(line: string, index: number, elements: JSX.Element[]): void { if (line.trim() !== '') { elements.push(

{line}

) return } if (elements.length > 0) { const last = elements[elements.length - 1] as { type?: unknown } if (last?.type !== 'br') { elements.push(
) } } } function handleCodeBlock( _line: string, index: number, state: RenderState, elements: JSX.Element[] ): void { if (state.inCodeBlock) { 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( ) state.currentList = [] } } function createLinkElement( text: string, href: string, key: string, isExternal: boolean ): JSX.Element { const className = 'text-neon-green hover:text-neon-cyan underline transition-colors' if (isExternal) { return ( {text} ) } return ( {text} ) } function renderLink(line: string, index: number, elements: JSX.Element[]): void { const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g let lastIndex = 0 const parts: (string | JSX.Element)[] = [] let match while ((match = linkRegex.exec(line)) !== null) { if (!match[2]) { continue } if (!match[1] || match.index > lastIndex) { parts.push(line.substring(lastIndex, match.index)) } const href = match[2] const isExternal = href.startsWith('http') if (match[1]) { parts.push(createLinkElement(match[1], href, `link-${index}-${match.index}`, isExternal)) } lastIndex = match.index + match[0].length } if (lastIndex < line.length) { parts.push(line.substring(lastIndex)) } 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) } }) }