$1', $text);
$text = preg_replace('/^## (.+)$/m', '
$1
', $text);
$text = preg_replace('/^# (.+)$/m', '$1
', $text);
// Bold
$text = preg_replace('/\*\*(.+?)\*\*/', '$1', $text);
// Italic
$text = preg_replace('/\*(.+?)\*/', '$1', $text);
// Code
$text = preg_replace('/`(.+?)`/', '$1', $text);
// Blockquotes
$text = preg_replace('/^> (.+)$/m', '$1
', $text);
// Lists: Process line by line to correctly wrap consecutive list items
$text = $this->formatLists($text);
// Line breaks
$text = nl2br($text);
// Clean up multiple
tags
$text = preg_replace('/(
\s*){3,}/', '
', $text);
return $text;
}
private function formatLists(string $text): string
{
$lines = explode("\n", $text);
$result = [];
$inList = false;
foreach ($lines as $line) {
// Check if line is a list item (- or * or numbered)
if (preg_match('/^[-*] (.+)$/', $line, $matches)) {
if (!$inList) {
$result[] = '';
$inList = true;
}
$result[] = '- ' . $matches[1] . '
';
} elseif (preg_match('/^\d+\. (.+)$/', $line, $matches)) {
// Numbered list item
if (!$inList) {
$result[] = '';
$inList = true;
}
$result[] = '- ' . $matches[1] . '
';
} else {
// Not a list item
if ($inList) {
$result[] = '
';
$inList = false;
}
$result[] = $line;
}
}
// Close any open list
if ($inList) {
$result[] = '
';
}
return implode("\n", $result);
}
}