-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmarkdownRenderer.js
More file actions
144 lines (131 loc) · 5 KB
/
markdownRenderer.js
File metadata and controls
144 lines (131 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
(function initMarkdownRenderer(global) {
function escapeHtml(value) {
return String(value || "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function splitTableLine(line) {
return String(line || "")
.trim()
.replace(/^\|/, "")
.replace(/\|$/, "")
.split("|")
.map((cell) => String(cell || "").trim());
}
function isTableSeparator(line) {
return /^\s*\|?[\s:\-|]+\|?\s*$/.test(String(line || ""));
}
function renderInline(text) {
const escaped = escapeHtml(text);
return escaped
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
.replace(/\*(.+?)\*/g, "<em>$1</em>")
.replace(/`([^`]+)`/g, "<code>$1</code>");
}
function renderParagraph(block) {
// 将换行符替换为 <br>,并用 <p> 包裹以获得更好的段落间距
return `<p class="rich-text-paragraph">${renderInline(block).replace(/\n/g, "<br>")}</p>`;
}
function renderCodeBlock(block) {
const lines = String(block || "").split(/\r?\n/);
const body = lines.slice(1, -1).join("\n");
return `<pre class="rich-code"><code>${escapeHtml(body)}</code></pre>`;
}
function renderTable(headerLine, rowLines) {
const headers = splitTableLine(headerLine);
const headHtml = headers.map((cell) => `<th>${renderInline(cell)}</th>`).join("");
const bodyHtml = rowLines.map((line) => {
const cols = splitTableLine(line);
const cells = headers.map((_, idx) => `<td>${renderInline(cols[idx] || "")}</td>`).join("");
return `<tr>${cells}</tr>`;
}).join("");
return `<div class="rich-table-wrap"><table class="rich-table"><thead><tr>${headHtml}</tr></thead><tbody>${bodyHtml}</tbody></table></div>`;
}
function renderHeader(line) {
const match = line.match(/^(#{1,6})\s+(.*)$/);
if (!match) return renderParagraph(line);
const level = match[1].length;
const content = match[2];
return `<h${level}>${renderInline(content)}</h${level}>`;
}
function renderList(lines) {
const items = lines.map((line) => {
const content = line.replace(/^\s*-\s+/, "");
return `<li>${renderInline(content)}</li>`;
}).join("");
return `<ul>${items}</ul>`;
}
function render(markdownText) {
const text = String(markdownText || "").trim();
if (!text) return "";
const lines = text.split(/\r?\n/);
const out = [];
let i = 0;
while (i < lines.length) {
if (String(lines[i]).trim() === "") {
i += 1;
continue;
}
if (/^---+$/.test(lines[i].trim())) {
i += 1;
continue;
}
if (/^```/.test(lines[i])) {
let j = i + 1;
while (j < lines.length && !/^```/.test(lines[j])) j += 1;
if (j < lines.length) j += 1;
out.push(renderCodeBlock(lines.slice(i, j).join("\n")));
i = j;
continue;
}
if (i + 1 < lines.length && /^\s*\|.*\|\s*$/.test(lines[i]) && isTableSeparator(lines[i + 1])) {
let j = i + 2;
const rowLines = [];
while (j < lines.length && /^\s*\|.*\|\s*$/.test(lines[j])) {
rowLines.push(lines[j]);
j += 1;
}
out.push(renderTable(lines[i], rowLines));
i = j;
continue;
}
// Header
if (/^#{1,6}\s/.test(lines[i])) {
out.push(renderHeader(lines[i]));
i += 1;
continue;
}
// List
if (/^\s*-\s/.test(lines[i])) {
const listLines = [];
let j = i;
while (j < lines.length && /^\s*-\s/.test(lines[j])) {
listLines.push(lines[j]);
j += 1;
}
out.push(renderList(listLines));
i = j;
continue;
}
let j = i;
const block = [];
while (j < lines.length && String(lines[j]).trim() !== "") {
if (j + 1 < lines.length && /^\s*\|.*\|\s*$/.test(lines[j]) && isTableSeparator(lines[j + 1])) break;
if (/^```/.test(lines[j])) break;
if (/^#{1,6}\s/.test(lines[j])) break;
if (/^\s*-\s/.test(lines[j])) break;
block.push(lines[j]);
j += 1;
}
if (block.length > 0) {
out.push(renderParagraph(block.join("\n")));
}
i = j;
}
return out.join("");
}
global.MarkdownRenderer = { render };
})(window);