|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using System.Text; |
| 5 | +using System.Text.RegularExpressions; |
| 6 | +using System.Threading.Tasks; |
| 7 | + |
| 8 | +namespace ChartGenerator |
| 9 | +{ |
| 10 | + public class HtmlConverter |
| 11 | + { |
| 12 | + public string ConvertToHTML(string aiResponse) |
| 13 | + { |
| 14 | + StringBuilder htmlBuilder = new StringBuilder(); |
| 15 | + htmlBuilder.AppendLine("<html>"); |
| 16 | + htmlBuilder.AppendLine("<head>"); |
| 17 | + htmlBuilder.AppendLine("<title>AI Response</title>"); |
| 18 | + htmlBuilder.AppendLine("<style>"); |
| 19 | + htmlBuilder.AppendLine("body { font-family: Arial, sans-serif; line-height:1.6;}"); |
| 20 | + htmlBuilder.AppendLine("h1, h2 { color: #33, margin-bottom: 10px; }"); |
| 21 | + htmlBuilder.AppendLine("p, li { margin-bottom: 5px;}"); |
| 22 | + htmlBuilder.AppendLine("blockquote { border-left: 3px solid #ccc; margin: 10; padding-left: 10px; color: #555; }"); |
| 23 | + htmlBuilder.AppendLine("span {display: inline-block; padding: 5px 10px; background-color: #f0f0f0; border-radius: 8px; font-size: 14px; font-family: Consolas, monospace; }"); |
| 24 | + htmlBuilder.AppendLine("</style>"); |
| 25 | + //htmlBuilder.AppendLine("<script type=\"text/javascript\">"); |
| 26 | + //htmlBuilder.AppendLine("function reportHeight() {"); |
| 27 | + //htmlBuilder.AppendLine(" var height = document.body.scrollHeight;"); |
| 28 | + //htmlBuilder.AppendLine(" window.dispatchEvent(new CustomEvent('reportHeight', { detail: height })); "); |
| 29 | + //htmlBuilder.AppendLine(" window.location.href = 'autofit://' + height;"); |
| 30 | + //htmlBuilder.AppendLine("}"); |
| 31 | + //htmlBuilder.AppendLine("window.onload = reportHeight;"); |
| 32 | + //htmlBuilder.AppendLine("window.onresize = reportHeight;"); |
| 33 | + //htmlBuilder.AppendLine("</script>"); |
| 34 | + htmlBuilder.AppendLine("</head>"); |
| 35 | + htmlBuilder.AppendLine("<body>"); |
| 36 | + |
| 37 | + var paragraphs = aiResponse.Split(new[] { "<br>", "\n\n" }, StringSplitOptions.RemoveEmptyEntries); |
| 38 | + for (int i = 0; i < paragraphs.Count(); i++) |
| 39 | + { |
| 40 | + var paragraph = paragraphs[i]; |
| 41 | + if (IsHeading(paragraph)) |
| 42 | + { |
| 43 | + int level = 0; |
| 44 | + var title = TrimHashFromTitle(paragraph, out level); |
| 45 | + title = ConvertToHtmlBold(title); |
| 46 | + if (level == 1) |
| 47 | + htmlBuilder.AppendLine($"<h1>{title}</h1>"); |
| 48 | + else if (level == 2) |
| 49 | + htmlBuilder.AppendLine($"<h2>{title}</h2>"); |
| 50 | + else if (level == 3) |
| 51 | + htmlBuilder.AppendLine($"<h3>{title}</h3>"); |
| 52 | + else if (level == 4) |
| 53 | + htmlBuilder.AppendLine($"<h4>{title}</h4>"); |
| 54 | + else if (level == 5) |
| 55 | + htmlBuilder.AppendLine($"<h5>{title}</h5>"); |
| 56 | + else if (level == 6) |
| 57 | + htmlBuilder.AppendLine($"<h6>{title}</h6>"); |
| 58 | + } |
| 59 | + // Implement list parsing here |
| 60 | + // else if (IsList(paragraph)) |
| 61 | + // { |
| 62 | + // ConvertToHtmlList(paragraph, htmlBuilder); |
| 63 | + // } |
| 64 | + else if (IsQuote(paragraph)) |
| 65 | + { |
| 66 | + htmlBuilder.AppendLine($"<blockquote>{paragraph.Trim()}</blockquote>"); |
| 67 | + } |
| 68 | + else if (IsURL(paragraph)) |
| 69 | + { |
| 70 | + htmlBuilder.AppendLine($"<a href=\"{paragraph.Trim()}\">{paragraph.Trim()}</a>"); |
| 71 | + } |
| 72 | + else if (HasURLText(paragraph)) |
| 73 | + { |
| 74 | + ConvertMarkdownToHtml(paragraph, htmlBuilder); |
| 75 | + } |
| 76 | + else if (ContainsBoldPattern(paragraph)) |
| 77 | + { |
| 78 | + htmlBuilder.AppendLine($"<p>{ConvertToHtmlBold(paragraph)}</p>"); |
| 79 | + } |
| 80 | + else if (paragraph.Contains("```")) |
| 81 | + { |
| 82 | + htmlBuilder.AppendLine("<table style='border: 1px solid black; border-collapse: collapse; width:100%; '>"); |
| 83 | + htmlBuilder.AppendLine("<tr><td>"); |
| 84 | + htmlBuilder.AppendLine("<pre style='margin:10px !important; font-size: 14px; line-height: 1.5; font-family: Consolas, monospace;'>"); |
| 85 | + htmlBuilder.AppendLine("<code>"); |
| 86 | + var codeSnippetIndex = 0; |
| 87 | + for (codeSnippetIndex = i + 1; codeSnippetIndex < paragraphs.Count(); codeSnippetIndex++) |
| 88 | + { |
| 89 | + var codeSnippet = paragraphs[codeSnippetIndex]; |
| 90 | + if (codeSnippet.Contains("```")) |
| 91 | + break; |
| 92 | + htmlBuilder.AppendLine(codeSnippet); |
| 93 | + } |
| 94 | + htmlBuilder.AppendLine("</code></pre>"); |
| 95 | + htmlBuilder.AppendLine("</td></tr></table>"); |
| 96 | + i = codeSnippetIndex; |
| 97 | + } |
| 98 | + else if (HasBackticks(paragraph)) |
| 99 | + { |
| 100 | + FormatBackTicks(htmlBuilder, paragraph); |
| 101 | + } |
| 102 | + else |
| 103 | + { |
| 104 | + htmlBuilder.AppendLine($"<p>{paragraph.Trim()}</p>"); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + htmlBuilder.AppendLine("</body>"); |
| 109 | + htmlBuilder.AppendLine("</html>"); |
| 110 | + return htmlBuilder.ToString(); |
| 111 | + } |
| 112 | + public string TrimHashFromTitle(string title, out int headingLevel) |
| 113 | + { |
| 114 | + headingLevel = 0; |
| 115 | + |
| 116 | + // Loop from 4 to 1 to check for "####", "###", "##", and "#" |
| 117 | + for (int i = 4; i > 0; i--) |
| 118 | + { |
| 119 | + string hashes = new string('#', i); |
| 120 | + int index = title.IndexOf(hashes); |
| 121 | + if (index != -1) |
| 122 | + { |
| 123 | + headingLevel = i; |
| 124 | + // Trim the found hashes from the title and any leading/trailing spaces |
| 125 | + return title.Substring(0, index).Trim() + title.Substring(index + i).Trim(); |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + // If no hashes are found, return the original title |
| 130 | + return title; |
| 131 | + } |
| 132 | + private bool IsHeading(string text) |
| 133 | + { |
| 134 | + // Detect headings, e.g., all caps or specific prefixes |
| 135 | + return text == text.ToUpper() || Regex.IsMatch(text, @"^#"); |
| 136 | + } |
| 137 | + |
| 138 | + private bool IsList(string text) |
| 139 | + { |
| 140 | + // Detect bullet points or ordered lists |
| 141 | + return text.StartsWith("- ");// || Regex.IsMatch(text, @"^\d+\."); |
| 142 | + } |
| 143 | + |
| 144 | + private bool IsQuote(string text) |
| 145 | + { |
| 146 | + // Simplistic quote detection |
| 147 | + return text.StartsWith("\"") && text.EndsWith("\""); |
| 148 | + } |
| 149 | + |
| 150 | + private bool HasBackticks(string text) |
| 151 | + { |
| 152 | + var backtickPattern = @"`[^`]*`"; |
| 153 | + return Regex.IsMatch(text, backtickPattern); |
| 154 | + } |
| 155 | + |
| 156 | + private void FormatBackTicks(StringBuilder htmlBuilder, string text) |
| 157 | + { |
| 158 | + var backtickPattern = @"`([^`]*)`"; |
| 159 | + var formattedText = Regex.Replace(text, backtickPattern, "<span>$1</span>"); |
| 160 | + htmlBuilder.AppendLine($"<p>{formattedText}</p>"); |
| 161 | + } |
| 162 | + |
| 163 | + private bool IsURL(string text) |
| 164 | + { |
| 165 | + // URL detection based on simple regex |
| 166 | + var directURLPattern = @"^(http|https):\/\/[^\s$.?#].[^\s]*$"; |
| 167 | + return Regex.IsMatch(text, directURLPattern); |
| 168 | + } |
| 169 | + private bool HasURLText(string markdown) |
| 170 | + { |
| 171 | + // Define the regex pattern to extract URLs from Markdown links. |
| 172 | + string pattern = @"\[(.*?)\]\((http[s]?:\/\/[^\s\)]+)\)"; |
| 173 | + |
| 174 | + // Create a match to extract the text and URL from the Markdown link. |
| 175 | + Match match = Regex.Match(markdown, pattern, RegexOptions.IgnoreCase); |
| 176 | + if (match.Success) |
| 177 | + { |
| 178 | + return IsURL(match.Groups[2].Value); |
| 179 | + } |
| 180 | + return false; |
| 181 | + } |
| 182 | + private static string ConvertMarkdownToHtml(string markdown, StringBuilder htmlBuilder) |
| 183 | + { |
| 184 | + // Define the regex pattern to extract URLs from Markdown links. |
| 185 | + string pattern = @"\[(.*?)\]\((http[s]?:\/\/[^\s\)]+)\)"; |
| 186 | + |
| 187 | + // Create a match to extract the text and URL from the Markdown link. |
| 188 | + Match match = Regex.Match(markdown, pattern, RegexOptions.IgnoreCase); |
| 189 | + if (match.Success) |
| 190 | + { |
| 191 | + string linkText = match.Groups[1].Value; // Group 1 contains the link text. |
| 192 | + string url = match.Groups[2].Value; // Group 2 contains the URL. |
| 193 | + |
| 194 | + htmlBuilder.AppendLine($"<p><a href=\"{url.Trim()}\">{linkText.Trim()}</a></p>"); |
| 195 | + } |
| 196 | + |
| 197 | + // Return original text if no Markdown link is found. |
| 198 | + return markdown; |
| 199 | + } |
| 200 | + |
| 201 | + private string ConvertToHtmlBold(string input) |
| 202 | + { |
| 203 | + string pattern = @"\*\*(.*?)\*\*"; |
| 204 | + string result = Regex.Replace(input, pattern, "<strong>$1</strong>"); |
| 205 | + if (HasBackticks(result)) |
| 206 | + { |
| 207 | + var backtickPattern = @"`([^`]*)`"; |
| 208 | + var formattedText = Regex.Replace(result, backtickPattern, "<span>$1</span>"); |
| 209 | + return formattedText; |
| 210 | + } |
| 211 | + return result; |
| 212 | + } |
| 213 | + |
| 214 | + private bool ContainsBoldPattern(string input) |
| 215 | + { |
| 216 | + string pattern = @"\*\*.+?\*\*"; |
| 217 | + return Regex.IsMatch(input, pattern); |
| 218 | + } |
| 219 | + |
| 220 | + private void ConvertToHtmlList(string paragraph, StringBuilder htmlBuilder) |
| 221 | + { |
| 222 | + var items = paragraph.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); |
| 223 | + htmlBuilder.AppendLine("<ol>"); |
| 224 | + foreach (var item in items) |
| 225 | + { |
| 226 | + htmlBuilder.AppendLine($"<li>{item.Trim().TrimStart('-').Trim()}</li>"); |
| 227 | + } |
| 228 | + htmlBuilder.AppendLine("</ol>"); |
| 229 | + } |
| 230 | + } |
| 231 | +} |
0 commit comments