-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathMarkdownContent.razor.cs
More file actions
169 lines (143 loc) · 5.28 KB
/
MarkdownContent.razor.cs
File metadata and controls
169 lines (143 loc) · 5.28 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using ColorCode;
using ColorCode.Compilation.Languages;
using ColorCode.Styling;
using Markdig;
using Markdown.ColorCode;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace BootstrapBlazor.Components;
/// <summary>
/// MarkdownContent component
/// </summary>
public partial class MarkdownContent
{
/// <summary>
/// Gets or sets the content. Default value is null.
/// </summary>
[Parameter]
[EditorRequired]
public string? Content { get; set; }
[Inject, NotNull]
private ILogger<MarkdownContent>? Logger { get; set; }
private string? _markdown;
private MarkdownPipeline? _markdownPipeline;
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
_markdownPipeline = new MarkdownPipelineBuilder()
.UsePipeTables()
.UseAdvancedExtensions()
.UseColorCode(styleDictionary: StyleDictionary.DefaultLight, additionalLanguages: new List<ILanguage>()
{
new Json(),
new CSharp(),
new Cpp(),
new Css(),
new Html(),
new JavaScript(),
new Php(),
})
.UseAutoLinks()
.UseEmojiAndSmiley()
.UseMediaLinks()
.UseCitations()
.UseMathematics()
.UseDiagrams()
.Build();
}
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();
_markdown = GetMarkdown(Content);
}
private string GetMarkdown(string? toHtml)
{
var html = "";
if (string.IsNullOrEmpty(toHtml))
{
return html;
}
try
{
// 处理未封闭的 think 标签
toHtml = HandleUnclosedThinkTags(toHtml);
// 处理正常的 think 标签
var thinkPattern = @"<\s*think\b[^>]*>(.*?)<\s*/\s*think\s*>";
toHtml = Regex.Replace(toHtml, thinkPattern, @"<div class=""think"">$1</div>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
toHtml = RemoveEmbeddingsElement(toHtml);
html = Markdig.Markdown.ToHtml(toHtml, _markdownPipeline);
var pattern = "(<div style=\"color:#DADADA;background-color:#1E1E1E;\"><pre>(.*?)</pre></div>)";
var matches = Regex.Matches(html, pattern, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
for (var i = matches.Count - 1; i >= 0; i--)
{
var match = matches[i].ToString();
var id = "copy" + i;
var replacement = $"<button data-clipboard-target=\"#{id}\" class=\"float-end copyBtn mt-0\">Copy</button>" + match;
html = html.Remove(matches[i].Index, matches[i].Length).Insert(matches[i].Index, replacement);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "{GetMakrDown} method throw exception", nameof(GetMarkdown));
}
return html;
}
private static string HandleUnclosedThinkTags(string content)
{
if (string.IsNullOrEmpty(content))
return content;
// 匹配开始标签
var openTagPattern = @"<\s*think\b[^>]*>";
var closeTagPattern = @"<\s*/\s*think\s*>";
var openTags = Regex.Matches(content, openTagPattern);
var closeTags = Regex.Matches(content, closeTagPattern);
// 如果开始标签数量等于结束标签数量,说明标签都是配对的
if (openTags.Count == closeTags.Count)
return content;
// 处理未封闭的标签
var parts = Regex.Split(content, openTagPattern);
if (parts.Length <= 1)
return content;
var result = parts[0]; // 保留第一部分的内容
for (int i = 1; i < parts.Length; i++)
{
var part = parts[i];
// 检查这部分是否已经包含结束标签
if (!part.Contains("</think>", StringComparison.OrdinalIgnoreCase))
{
// 没有结束标签,添加一个完整的 think 标签包装
result += $"<think>{part}</think>";
}
else
{
// 已经有结束标签,保持原样
result += $"<think>{part}";
}
}
return result;
}
private static string RemoveEmbeddingsElement(string data)
{
if (string.IsNullOrEmpty(data))
{
return "";
}
string pattern = @"\[EMBEDDINGS\](.*?)\[/EMBEDDINGS\]";
var matches = Regex.Matches(data, pattern, RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
if (matches.Count == 0)
{
return data;
}
return Regex.Replace(data, pattern, "", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);
}
}