| export function renderRichText(container, text) { |
| container.innerHTML = ""; |
| const lines = String(text).replaceAll("\r\n", "\n").split("\n"); |
| let list = null; |
| let codeLines = null; |
| let paragraph = []; |
|
|
| const flushParagraph = () => { |
| if (paragraph.length === 0) { |
| return; |
| } |
|
|
| const element = document.createElement("p"); |
| element.innerHTML = formatInline(paragraph.join(" ")); |
| container.appendChild(element); |
| paragraph = []; |
| }; |
|
|
| const flushList = () => { |
| if (list) { |
| container.appendChild(list); |
| list = null; |
| } |
| }; |
|
|
| const flushCode = () => { |
| if (!codeLines) { |
| return; |
| } |
|
|
| const pre = document.createElement("pre"); |
| pre.textContent = codeLines.join("\n"); |
| container.appendChild(pre); |
| codeLines = null; |
| }; |
|
|
| for (const line of lines) { |
| if (line.startsWith("```")) { |
| flushParagraph(); |
| flushList(); |
| if (codeLines) { |
| flushCode(); |
| } else { |
| codeLines = []; |
| } |
| continue; |
| } |
|
|
| if (codeLines) { |
| codeLines.push(line); |
| continue; |
| } |
|
|
| const listMatch = line.match(/^\s*[-*]\s+(.+)$/); |
| if (listMatch) { |
| flushParagraph(); |
| if (!list) { |
| list = document.createElement("ul"); |
| } |
| const item = document.createElement("li"); |
| item.innerHTML = formatInline(listMatch[1]); |
| list.appendChild(item); |
| continue; |
| } |
|
|
| if (!line.trim()) { |
| flushParagraph(); |
| flushList(); |
| continue; |
| } |
|
|
| flushList(); |
| paragraph.push(line.trim()); |
| } |
|
|
| flushParagraph(); |
| flushList(); |
| flushCode(); |
| } |
|
|
| export function escapeHtml(value) { |
| return String(value) |
| .replaceAll("&", "&") |
| .replaceAll("<", "<") |
| .replaceAll(">", ">"); |
| } |
|
|
| function formatInline(value) { |
| return escapeHtml(value) |
| .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>") |
| .replace(/\*(.+?)\*/g, "<em>$1</em>") |
| .replace(/`(.+?)`/g, "<code>$1</code>"); |
| } |
|
|