| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- 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, '<span class="error-tag">❌ 错误</span>');
-
- 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 `<details class="${cssClass}-details"><summary class="${cssClass}-summary">${emoji} ${tagName}</summary></details>`;
- }
-
- return `<details class="${cssClass}-details" ${cssClass === 'thinking' || cssClass === 'tool' ? 'open' : ''}><summary class="${cssClass}-summary">${emoji} ${tagName}</summary>${trimmedContent}</details>`;
- });
- }
-
- /**
- * 后处理:添加代码高亮和样式
- */
- private postprocessCodeBlocks(html: string): string {
- // 使用正则表达式匹配代码块
- const codeBlockRegex = /<pre><code\s*(?:class="language-([^"]+)")?>([\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 `<pre class="prism-code language-${language}"><code class="language-${language}">${highlighted}</code></pre>`;
- } catch (e) {
- // 如果高亮失败,返回原始代码块
- return `<pre class="prism-code language-${language}"><code class="language-${language}">${code}</code></pre>`;
- }
- });
-
- return html;
- }
- }
|