import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { marked } from 'marked';
import Prism from 'prismjs';
// 导入需要的语言支持
import 'prismjs/components/prism-clike';
import 'prismjs/components/prism-markup';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-typescript';
import 'prismjs/components/prism-python';
import 'prismjs/components/prism-go';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-csharp';
import 'prismjs/components/prism-php';
import 'prismjs/components/prism-ruby';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-sql';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-yaml';
import 'prismjs/components/prism-markdown';
@Pipe({
name: 'markdown',
standalone: true
})
export class MarkdownPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(value: string): SafeHtml {
if (!value) {
return '';
}
// 预处理:将标签替换为美化版本
const processedContent = this.preprocessTags(value);
// 配置marked(使用基本配置)
marked.setOptions({
gfm: true,
breaks: true
});
// 解析markdown
let html = marked.parse(processedContent) as string;
// 后处理:添加代码高亮和样式
html = this.postprocessCodeBlocks(html);
// 安全注入HTML
return this.sanitizer.bypassSecurityTrustHtml(html);
}
/**
* 预处理:将文本标签替换为美化版本和可折叠区域
*/
private preprocessTags(content: string): string {
// 处理 [思考] 标签及其内容
content = this.processTagWithContent(content, '思考', '💭', 'thinking');
// 处理 [工具] 标签及其内容
content = this.processTagWithContent(content, '工具', '🛠️', 'tool');
// 处理 [错误] 标签
content = content.replace(/\[错误\]/g, '❌ 错误');
return content;
}
/**
* 处理标签及其后的内容,转换为可折叠区域
*/
private processTagWithContent(content: string, tagName: string, emoji: string, cssClass: string): string {
// 正则表达式匹配 [标签] 及其后的内容(直到下一个标签或字符串结束)
const pattern = new RegExp(`\\[${tagName}\\]\\s*([\\s\\S]*?)(?=\\n\\s*\\[|$)`, 'g');
return content.replace(pattern, (match, tagContent) => {
// 清理内容前后的空白
const trimmedContent = tagContent.trim();
if (!trimmedContent) {
return `${emoji} ${tagName}
${emoji} ${tagName}
${trimmedContent}
([\s\S]*?)<\/code><\/pre>/gi;
html = html.replace(codeBlockRegex, (match, lang, code) => {
const language = lang || 'plaintext';
try {
// 使用Prism高亮代码
const highlighted = Prism.highlight(code, Prism.languages[language] || Prism.languages['plaintext'], language);
return `${highlighted}
`;
} catch (e) {
// 如果高亮失败,返回原始代码块
return `${code}
`;
}
});
return html;
}
}