/**
* 轻量级 Markdown 渲染,用于聊天气泡中的文本。
* 支持:链接、粗体、斜体、行内代码、代码块、换行。
*/
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
}
function renderInline(text: string): string {
// 行内代码 `code` — 优先处理,避免干扰其他标记
let html = text.replace(/`([^`]+)`/g, '$1');
// 图片 
html = html.replace(
/!\[([^\]]*)]\(([^)]+)\)/g,
'',
);
// 链接 [text](url)
html = html.replace(
/\[([^\]]+)]\(([^)]+)\)/g,
(_, label, url) => {
const safeUrl = url.startsWith('http://') || url.startsWith('https://') || url.startsWith('mailto:')
? url
: `http://${url}`;
return `${label}`;
},
);
// 粗体 **text** 或 __text__
html = html.replace(/\*\*(.+?)\*\*/g, '$1');
html = html.replace(/__(.+?)__/g, '$1');
// 斜体 *text* 或 _text_
html = html.replace(/\*(.+?)\*/g, '$1');
html = html.replace(/_(.+?)_/g, '$1');
// 删除线 ~~text~~
html = html.replace(/~~(.+?)~~/g, '
$1');
return html;
}
export function renderMarkdown(text: string): string {
const lines = text.split('\n');
const result: string[] = [];
let inCodeBlock = false;
let codeBuf: string[] = [];
let codeLang = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('```')) {
if (inCodeBlock) {
// 关闭代码块
const langClass = codeLang ? ` class="lang-${escapeHtml(codeLang)}"` : '';
result.push(`
${escapeHtml(codeBuf.join('\n'))}`);
codeBuf = [];
codeLang = '';
inCodeBlock = false;
} else {
inCodeBlock = true;
codeLang = line.slice(3).trim();
}
continue;
}
if (inCodeBlock) {
codeBuf.push(line);
continue;
}
// 空行
if (line.trim() === '') {
result.push('${content}
`); } // 未闭合的代码块 if (inCodeBlock) { result.push(`${escapeHtml(codeBuf.join('\n'))}`);
}
return result.join('\n');
}