using Markdig; using System.Text.RegularExpressions; namespace WebApp.Services; /// /// Helper class for rendering markdown to HTML. /// public static class MarkdownHelper { private static readonly MarkdownPipeline _pipeline = new MarkdownPipelineBuilder() .UseAdvancedExtensions() .Build(); // Compiled regex patterns for stripping markdown private static readonly Regex MarkdownLinkRegex = new(@"\[([^\]]+)\]\([^\)]+\)", RegexOptions.Compiled); private static readonly Regex BoldRegex = new(@"\*\*([^\*]+)\*\*", RegexOptions.Compiled); private static readonly Regex ItalicRegex = new(@"\*([^\*]+)\*", RegexOptions.Compiled); private static readonly Regex InlineCodeRegex = new(@"`([^`]+)`", RegexOptions.Compiled); private static readonly Regex HeaderRegex = new(@"#+\s+", RegexOptions.Compiled); private static readonly Regex NewlineRegex = new(@"\n+", RegexOptions.Compiled); private static readonly Regex HtmlTagRegex = new(@"<[^>]+>", RegexOptions.Compiled); /// /// Converts markdown text to HTML. /// /// The markdown text to convert. /// HTML string ready for rendering. public static string ToHtml(string? markdown) { if (string.IsNullOrWhiteSpace(markdown)) return string.Empty; var html = Markdown.ToHtml(markdown, _pipeline); // Add target="_blank" and rel="noopener noreferrer" to all links html = Regex.Replace(html, @"]*?)href=""([^""]*?)""([^>]*?)>", match => { var beforeHref = match.Groups[1].Value; var href = match.Groups[2].Value; var afterHref = match.Groups[3].Value; // Check if target is already present if (Regex.IsMatch(beforeHref + afterHref, @"target\s*=", RegexOptions.IgnoreCase)) { return match.Value; // Return unchanged if target already exists } // Add target="_blank" and rel="noopener noreferrer" return $""; }, RegexOptions.IgnoreCase); return html; } /// /// Strips markdown formatting from content and returns plain text for preview. /// /// The markdown content to strip. /// Maximum length of the preview. If 0 or negative, no truncation is performed. /// Plain text preview with markdown formatting removed. public static string StripMarkdownPreview(string? content, int maxLength = 0) { if (string.IsNullOrWhiteSpace(content)) return string.Empty; // Strip markdown formatting for preview using compiled regex var plainText = MarkdownLinkRegex.Replace(content, "$1"); // Replace markdown links with link text plainText = BoldRegex.Replace(plainText, "$1"); // Remove bold plainText = ItalicRegex.Replace(plainText, "$1"); // Remove italic plainText = InlineCodeRegex.Replace(plainText, "$1"); // Remove inline code plainText = HeaderRegex.Replace(plainText, ""); // Remove headers plainText = NewlineRegex.Replace(plainText, " "); // Replace newlines with spaces plainText = HtmlTagRegex.Replace(plainText, ""); // Remove HTML tags plainText = plainText.Trim(); // Truncate if maxLength is specified and content exceeds it if (maxLength > 0 && plainText.Length > maxLength) { return plainText.Substring(0, maxLength - 3) + "..."; } return plainText; } }