import { remark } from 'remark' import remarkHtml from 'remark-html' import remarkGfm from 'remark-gfm' // Simple markdown processor with clean output const processor = remark() .use(remarkGfm) // Support for GitHub Flavored Markdown (tables, strikethrough, etc.) // Das Ergebnis geht über dangerouslySetInnerHTML in die öffentliche Seite. // Das Standard-Schema lässt Überschriften, Listen, Tabellen und http(s)- bzw. // mailto-Links durch und entfernt Skripte, Event-Attribute, eingebettete // Rahmen und javascript:-Ziele. .use(remarkHtml, { sanitize: true }) /** * Maskiert Text für die Ausgabe in HTML. Für Werte, die nicht durch den * Markdown-Prozessor laufen – etwa Titel in der Druckfassung. */ export function escapeHtml(value: string): string { return String(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } export async function markdownToHtml(markdown: string): Promise { if (!markdown || typeof markdown !== 'string') { return '' } try { // Simple preprocessing for line breaks const preprocessed = simplePreprocessLineBreaks(markdown) const result = await processor.process(preprocessed) return result.toString() } catch (error) { console.error('Error processing markdown:', error) // Fallback: return sanitized markdown with basic formatting return sanitizeAndFormatMarkdown(markdown) } } // Synchronous version for use in components that can't use async export function markdownToHtmlSync(markdown: string): string { if (!markdown || typeof markdown !== 'string') { return '' } try { // Simple preprocessing for line breaks const preprocessed = simplePreprocessLineBreaks(markdown) const result = processor.processSync(preprocessed) return result.toString() } catch (error) { console.error('Error processing markdown synchronously:', error) // Fallback: return sanitized markdown with basic formatting return sanitizeAndFormatMarkdown(markdown) } } // Simple preprocessing for consistent line breaks function simplePreprocessLineBreaks(markdown: string): string { return markdown // Normalize line endings .replace(/\r\n/g, '\n') .replace(/\r/g, '\n') // Add two spaces before single line breaks for hard breaks .replace(/([^\n])\n([^\n])/g, '$1 \n$2') } // Fallback function for basic markdown formatting function sanitizeAndFormatMarkdown(text: string): string { // Simple, clean markdown processing return text // Escape HTML .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') // Basic markdown formatting .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/`(.*?)`/g, '$1') // Headers .replace(/^### (.*$)/gm, '

$1

') .replace(/^## (.*$)/gm, '

$1

') .replace(/^# (.*$)/gm, '

$1

') // Lists .replace(/^\* (.*$)/gm, '• $1') .replace(/^- (.*$)/gm, '• $1') .replace(/^(\d+)\. (.*$)/gm, '$1. $2') // Line breaks .replace(/\n/g, '
') // Clean up multiple
tags .replace(/

/g, '
') }