(markdown)
| 1189 | |
| 1190 | // Markdown 转 HTML 函数 |
| 1191 | function convertMarkdownToHTML(markdown) { |
| 1192 | if (!markdown) return ''; |
| 1193 | |
| 1194 | let html = markdown; |
| 1195 | |
| 1196 | // 先处理代码块,避免其中的特殊字符被转义 |
| 1197 | const codeBlocks = []; |
| 1198 | html = html.replace(/```([\s\S]*?)```/g, (match, code) => { |
| 1199 | const index = codeBlocks.length; |
| 1200 | codeBlocks.push(code); |
| 1201 | return `__CODE_BLOCK_${index}__`; |
| 1202 | }); |
| 1203 | |
| 1204 | const inlineCodes = []; |
| 1205 | html = html.replace(/`([^`]+)`/g, (match, code) => { |
| 1206 | const index = inlineCodes.length; |
| 1207 | inlineCodes.push(code); |
| 1208 | return `__INLINE_CODE_${index}__`; |
| 1209 | }); |
| 1210 | |
| 1211 | // 处理图片和链接(在转义之前) |
| 1212 | // 在图片容器后面补一个换行符,确保插入后换行 |
| 1213 | html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<div class="nsn-resizable-img-container" contenteditable="false" style="display: inline-block; position: relative; border: 1px dashed transparent; margin: 4px;"><img src="$2" alt="$1" style="max-width: 300px; height: auto; display: block; border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border: 1px solid #e5e7eb;"><div class="nsn-resize-handle" style="position: absolute; bottom: -3px; right: -3px; width: 8px; height: 8px; background: #409eff; cursor: nw-resize; border-radius: 2px; display: none;"></div></div><br>'); |
| 1214 | // 将 Markdown 链接生成为可编辑/可弹窗的链接元素,和“链接”按钮一致 |
| 1215 | html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<span class="nsn-editable-link" data-href="$2">$1</span>'); |
| 1216 | |
| 1217 | // 转义 HTML 特殊字符(但保留已处理的HTML标签) |
| 1218 | const htmlTags = []; |
| 1219 | html = html.replace(/<[^>]+>/g, (match) => { |
| 1220 | const index = htmlTags.length; |
| 1221 | htmlTags.push(match); |
| 1222 | return `__HTML_TAG_${index}__`; |
| 1223 | }); |
| 1224 | |
| 1225 | html = html.replace(/&/g, '&') |
| 1226 | .replace(/</g, '<') |
| 1227 | .replace(/>/g, '>'); |
| 1228 | |
| 1229 | // 恢复HTML标签 |
| 1230 | htmlTags.forEach((tag, index) => { |
| 1231 | html = html.replace(`__HTML_TAG_${index}__`, tag); |
| 1232 | }); |
| 1233 | |
| 1234 | // 标题 |
| 1235 | html = html.replace(/^### (.*$)/gm, '<h3 style="font-size: 1.1em; color: #374151; margin: 16px 0 8px 0;">$1</h3>'); |
| 1236 | html = html.replace(/^## (.*$)/gm, '<h2 style="font-size: 1.3em; color: #374151; margin: 16px 0 8px 0; border-bottom: 1px solid #f3f4f6; padding-bottom: 4px;">$1</h2>'); |
| 1237 | html = html.replace(/^# (.*$)/gm, '<h1 style="font-size: 1.5em; color: #374151; margin: 16px 0 8px 0; border-bottom: 2px solid #e5e7eb; padding-bottom: 6px;">$1</h1>'); |
| 1238 | |
| 1239 | // 粗体 |
| 1240 | html = html.replace(/\*\*(.*?)\*\*/g, '<strong style="font-weight: 600;">$1</strong>'); |
| 1241 | |
| 1242 | // 斜体 |
| 1243 | html = html.replace(/\*(.*?)\*/g, '<em style="font-style: italic;">$1</em>'); |
| 1244 | |
| 1245 | // 引用 |
| 1246 | html = html.replace(/^> (.*$)/gm, '<blockquote style="border-left: 4px solid #409eff; background: #f0f9ff; margin: 12px 0; padding: 8px 12px; font-style: italic;">$1</blockquote>'); |
| 1247 | |
| 1248 | // 无序列表 |
no outgoing calls
no test coverage detected